/antipatterns

Collection of helpers to extend Java functionality

Primary LanguageJavaApache License 2.0Apache-2.0

AntiPatterns

Maven metadata URL GitHub Build Status

What?

antipatterns is a collection of helpers and weird stuff to make Java development easier and support harder :)

Features

Access private methods/fields/constructors in a type-safe manner

import com.github.fluorumlabs.antipatterns.AntiPatterns;

...

@TargetClass(Boolean.class)
private interface BooleanMirror {
    @Static
    @DirectField
    void FALSE(Boolean value);

    static BooleanMirror attach() {
        return AntiPatterns.attachStatic(BooleanMirror.class);
    }
}

// Change Boolean.FALSE to true
BooleanMirror.attach().FALSE(true);

...

// Add fluent API to third-party libraries
private interface FluentList<T> extends AntiPatterns.Attachable<List<T>> {
    @ReturnType(boolean.class)
    FluentList<T> add(T value);

    @ReturnType(boolean.class)
    FluentList<T> addAll(@ArgumentType(Collection.class) TestFluentAPI<T> other);

    static <T> FluentList<T> attach(List<T> instance) {
        return AntiPatterns.attach(FluentList.class, instance);
    }
}

...

FluentList<String> fluentList = FluentList.attach(new ArrayList<String>());
fluentList.add("1").add("2").add("3");
List<String> list = fluentList.instance();

...

// Create singleton API bridges
@TargetClass.API
public interface PrivateMatcherAPI {
    int getMatchedGroupIndex(Matcher that, String name);
    
    static PrivateMatcherAPI create() {
        return AntiPatterns.attachStatic(PrivateMatcherAPI.class);
    }
}

Upgrade object instance to a subclass

import com.github.fluorumlabs.antipatterns.AntiPatterns;

...

public class WebpageRouteRegistry extends ApplicationRouteRegistry {
    @Override
    public Optional<Class<? extends Component>> getNavigationTarget(String pathString, List<String> segments) {
        ...
    }
}

...

// upgrade ApplicationRouteRegistry to WebpageRouteRegistry
RouteRegistry newRegistry = AntiPatterns.upgrade(getRouteRegistry(), WebpageRouteRegistry.class);

// make a shallow clone
Entity sameButDifferent = AntiPatterns.shallowClone(entity);

Access trusted MethodHandles.Lookup instance

import com.github.fluorumlabs.antipatterns.AntiPatterns;

...

MethodHandle Matcher_getMatchedGroupIndex = AntiPatterns.lookupAll().findVirtual(Matcher.class, "getMatchedGroupIndex", MethodType.methodType(int.class, String.class));

String interpolation

import static com.github.fluorumlabs.antipatterns.AntiPatterns.interpolate;

...

// Replace tokens with actual values of current user
String result = interpolate("Hello, ${user.firstName} ${user.lastName}!", user -> getCurrentUser());

// String interpolation with format specifiers
String result = interpolate("${percent %.2f}% completed", percent -> 100*progressValue);

Ignoring run-time exceptions

import com.github.fluorumlabs.antipatterns.AntiPatterns;

...

// Wrap suppliers
AntiPatterns.guarded(() -> AntiPatterns.of(Integer.parse("234234j"))).ifPresent(...);

// Wrap functions
someStringOptional.map(AntiPatterns.guarded(id -> Integer.parse(id)).ifPresent(...);

// Wrap runnables
AntiPatterns.guarded(() -> discussionService.incrementViewCount(root));

Getting Optional elements of array or List,

import com.github.fluorumlabs.antipatterns.AntiPatterns;

...

// Get first element of list/array or Optional.empty() if list/array is null or empty
AntiPatterns.getFirst(someList).ifPresent(...);

// Get 16-th element of list/array or Optional.empty() if list/array is null or has less then 17 elements
AntiPatterns.get(someList, 16).ifPresent(...);

Trying sequentially suppliers of Optional

import com.github.fluorumlabs.antipatterns.AntiPatterns;

...

// First try getEntityFromUrl, then, if it fails, getEntityFromSession, otherwise obtain default entity
SomeEntity entity = AntiPatterns.trySequentially(this::getEntityFromUrl, 
                                              this::getEntityFromSession, 
                                              this::getDefaultEntity)
    .orElseThrow(EntityNotFound::new);

Simple builders for arrays and maps

import static com.github.fluorumlabs.antipatterns.AntiPatterns.array;
import static com.github.fluorumlabs.antipatterns.AntiPatterns.hashMap;

...

// Build array of strings
String[] options = array("option1", "option2");

// Build hashmap with entries [("option1", optionObject1), ("option2", optionObject2)]
Map<String,Option> options = hashMap(option1 -> optionObject1, 
                                     option2 -> optionObject2);

RegExp helpers

import com.github.fluorumlabs.antipatterns.AntiPatterns;

...

// Pattern matching [[...]]
private static final Pattern TEMPLATE_PATTERN = Pattern.compile("\\[\\[([^\\]]+)\\]\\]");
// Properties
private Map<String,String> properties;

...

// Replace tokens [[...]] with properties
String result = AntiPatterns.replaceFunctional(TEMPLATE_PATTERN, message, groups -> properties.get(groups[1]));

// Get pattern matches as stream
Stream<String[]> groups = AntiPatterns.matchAsStream(TEMPLATE_PATTERN, message);

Safe casting of objects

import com.github.fluorumlabs.antipatterns.AntiPatterns;

...

// Cast baseEntity to Entity, or return Optional.empty() if it's not possible
Optional<Entity> entity = AntiPatterns.safeCast(baseEntity, Entity.class);

// Cast baseEntity to Entity in stream
Stream<Entity> entities = baseEntities.stream()
    .map(AntiPatterns.safeCast(Entity.class))
    .filter(Objects::nonNull);

Usage

<dependency>
   <groupId>com.github.fluorumlabs</groupId>
   <artifactId>antipatterns</artifactId>
   <version>1.0.0-beta1</version>
</dependency>