AntiPatterns
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> 
 JarCasting
 JarCasting