All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.almworks.jira.structure.api.util.La Maven / Gradle / Ivy

There is a newer version: 17.25.3
Show newest version
package com.almworks.jira.structure.api.util;

import com.almworks.integers.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Array;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * 

La is a utility generic-purpose class for functional expressions.

* * @author Igor Sereda */ public abstract class La implements Function, Predicate { private static final La SELF = new La() { public Object la(Object argument) { return argument; } }; private static final La TRUE = new La() { public Object la(Object argument) { return Boolean.TRUE; } }; private static final La FALSE = new La() { public Object la(Object argument) { return Boolean.FALSE; } }; private static final La NOT_NULL = new La() { public Boolean la(Object argument) { return argument != null; } }; private static final La TO_STRING = new La(String.class) { @Override public String la(Object argument) { return argument == null ? null : argument.toString(); } }; private final Class rClass; protected La() { rClass = null; } /** * Use this constructor to have class-aware instance, which is needed only for * few methods like {@link #array}. * * @param rClass the class of the result */ protected La(Class rClass) { this.rClass = rClass; } public abstract R la(T argument); // ============== constructors ================ @NotNull public static La adapt(final Function f) { if (f == null) throw new NullPointerException(); return new La() { @Override public R la(T argument) { return f.apply(argument); } }; } @NotNull public static La constant(final R result) { if (result == Boolean.TRUE) return (La) TRUE; if (result == Boolean.FALSE) return (La) FALSE; return new La() { public R la(T argument) { return result; } }; } public static La constantFalse() { return (La)FALSE; } public static La constantTrue() { return (La)TRUE; } @NotNull public static La isEqual(@Nullable final T value) { final boolean isNull = value == null; return new La() { public Boolean la(T argument) { return isNull ? argument == null : value.equals(argument); } }; } // we could declare the parameter as Collection, but then we'd lose type inference for the calling line @NotNull public static La inCollection(@Nullable final Collection collection) { if (collection == null) return constant(false); return new La() { public Boolean la(T argument) { return collection.contains(argument); } }; } @NotNull public static La instanceOf(@Nullable final Class instanceClass) { if (instanceClass == null) return constant(false); return new La() { public Boolean la(T argument) { return instanceClass.isInstance(argument); } }; } @NotNull public static La cast(@Nullable Class clazz) { if (clazz == null) return constant(null); return new La() { @Override public T la(Object argument) { return clazz.isInstance(argument) ? clazz.cast(argument) : null; } }; } @NotNull public static La compose(final La g, final La f) { if (f == null) throw new NullPointerException(); if (g == null) throw new NullPointerException(); return new La(g.rClass) { public C la(A argument) { return g.la(f.la(argument)); } }; } @NotNull @SuppressWarnings("unchecked") public static La self() { return (La) SELF; } @NotNull public static La fromMap(final Map map, Class rClass) { if (map == null) throw new NullPointerException(); return new La(rClass) { @Override public R la(T argument) { return map.get(argument); } }; } // ============== utilities ================ @Override public final R apply(T from) { return la(from); } @Override public boolean test(T input) { return accepts(input); } @NotNull public La apply(final La f) { return compose(this, f); } @NotNull public La supply(final La g) { return compose(g, this); } @NotNull public ArrayList filter(@Nullable Iterable collection) { ArrayList r = new ArrayList<>(); if (collection != null) { for (D item : collection) { if (accepts(item)) { r.add(item); } } } return r; } @NotNull public Iterator filter(@Nullable Iterator iterator) { return iterator == null ? Collections.emptyIterator() : new FilterIterator<>(iterator); } @NotNull public Iterable filterIterable(@Nullable final Iterable iterable) { if (iterable == null) { return Collections.emptyList(); } return () -> new FilterIterator<>(iterable.iterator()); } public boolean accepts(T value) { return acceptsResult(la(value)); } private boolean acceptsResult(R value) { return value != null && !value.equals(false) && !value.equals(0) && !value.equals(0L); } @NotNull public HashSet hashSet(@Nullable Collection from) { if (from == null) return new HashSet<>(); return addTo(from, new HashSet<>(from.size()), true); } @NotNull public LinkedHashSet linkedHashSet(@Nullable Collection from) { if (from == null) return new LinkedHashSet<>(); return addTo(from, new LinkedHashSet<>(from.size()), true); } @NotNull public List arrayList(@Nullable T ... from) { return from == null || from.length == 0 ? Collections.emptyList() : arrayList(Arrays.asList(from)); } public List arrayList(@Nullable Collection from) { return arrayList(from, true); } @NotNull public Iterable iterable(@Nullable final Iterable from) { return () -> La.this.iterator(from); } @NotNull public Iterator iterator(Iterable from) { final Iterator source = from == null ? null : from.iterator(); if (source == null) return Collections.emptyIterator(); return new Iterator() { public boolean hasNext() { return source.hasNext(); } public R next() { return la(source.next()); } public void remove() { source.remove(); } }; } @NotNull public List arrayList(@Nullable Collection from, boolean acceptFalsy) { if (from == null) return new ArrayList<>(); return addTo(from, new ArrayList<>(from.size()), acceptFalsy); } public List arrayList(@Nullable Iterator from) { return arrayList(from, true); } public List arrayList(@Nullable Iterator from, boolean acceptFalsy) { if (from == null) return new ArrayList<>(); return addTo(from, new ArrayList<>(), acceptFalsy); } @NotNull public static LongList longList(@Nullable Collection collection, @NotNull La la) { if (collection == null || collection.isEmpty()) { return LongList.EMPTY; } LongArray result = new LongArray(collection.size()); for (T element : collection) { Long value = la.la(element); if (value != null && value > 0) { result.add(value); } } return result; } @NotNull public static IntList intList(@Nullable Collection collection, @NotNull La la) { if (collection == null || collection.isEmpty()) { return IntList.EMPTY; } IntArray result = new IntArray(collection.size()); for (T element : collection) { Integer value = la.la(element); if (value != null && value > 0) { result.add(value); } } return result; } @NotNull public > C addTo(@Nullable Collection from, @NotNull C to, boolean acceptFalsy) { if (from != null) { addTo(from.iterator(), to, acceptFalsy); } return to; } @NotNull public > C addTo(@Nullable Iterator from, @NotNull C to, boolean acceptFalsy) { if (from != null) { while (from.hasNext()) { T t = from.next(); R r = la(t); if (acceptFalsy || acceptsResult(r)) { to.add(r); } } } return to; } public Iterator transform(Iterator iterator) { return new TransformIterator<>(iterator); } public Iterable transformIterable(final Iterable iterable) { return () -> new TransformIterator<>(iterable.iterator()); } @NotNull public R[] array(@Nullable Collection from) { return array(from, true); } @NotNull public R[] array(@Nullable Collection from, boolean acceptFalsy) { Class cls = rClass; if (cls == null) { throw new UnsupportedOperationException("cannot create array without rClass (" + from + ")"); } R[] result = (R[]) Array.newInstance(cls, from == null ? 0 : from.size()); if (from != null) { int i = 0; for (T t : from) { if (i >= result.length) { throw new IllegalStateException("collection changed on the go"); } R r = la(t); if (acceptFalsy || acceptsResult(r)) { result[i++] = r; } } if (i < result.length) { result = Arrays.copyOf(result, i); } } return result; } @NotNull public Map mapInto(@Nullable Collection fromCollection, @NotNull Map toMap) { return mapInto(fromCollection, toMap, La.self()); } @NotNull public Map mapInto(@Nullable Collection fromCollection, @NotNull Map toMap, @NotNull La valueFunction) { if (fromCollection != null) { for (C value : fromCollection) { R key = la(value); if (key != null) { toMap.put(key, valueFunction.la(value)); } } } return toMap; } @NotNull public Map hashMap(@Nullable Collection collection) { return hashMap(collection, La.self()); } @NotNull public Map hashMap(@Nullable Collection collection, @NotNull La valueFunction) { HashMap map = new HashMap<>(collection == null ? 0 : collection.size()); return mapInto(collection, map, valueFunction); } @NotNull public Map linkedHashMap(@Nullable Collection collection, @NotNull La valueFunction) { LinkedHashMap map = new LinkedHashMap<>(collection == null ? 0 : collection.size()); return mapInto(collection, map, valueFunction); } public int indexOf(@Nullable List list, R sample) { if (list == null) return -1; for (int i = 0; i < list.size(); i++) { R v = la(list.get(i)); if (sample == null && v == null || sample != null && sample.equals(v)) return i; } return -1; } public int lastIndexOf(@Nullable List list, R sample) { if (list == null) return -1; for (int i = list.size() - 1; i >= 0; --i) { R v = la(list.get(i)); if (sample == null && v == null || sample != null && sample.equals(v)) return i; } return -1; } @Nullable public X find(@Nullable Collection collection, R sample) { if (collection == null || collection.isEmpty()) return null; for (X value : collection) { R r = la(value); if (r == null && sample == null) { return value; } else if (r != null && r.equals(sample)) { return value; } } return null; } public boolean any(@Nullable Iterable sequence) { if (sequence == null) return false; for (T value : sequence) if (accepts(value)) return true; return false; } @NotNull public La not() { final La positive = this; return new La() { public Boolean la(T argument) { return !positive.accepts(argument); } }; } public La and(final La second) { final La first = this; return new La() { public Boolean la(T argument) { return first.accepts(argument) && second.accepts(argument); } }; } public La memoize() { final La delegate = this; final Map map = new HashMap<>(); return new La() { @Override public R la(T argument) { R result = map.get(argument); if (result == null && !map.containsKey(argument)) { result = delegate.la(argument); map.put(argument, result); } return result; } }; } public La memoizeConcurrent() { final La delegate = this; final ConcurrentMap map = new ConcurrentHashMap<>(); return new La() { @Override public R la(T argument) { assert argument != null; R result = map.get(argument); if (result == null) { result = delegate.la(argument); assert result != null; map.putIfAbsent(argument, result); } return result; } }; } public Comparator comparator(Comparator comparator) { return new LaComparator<>(this, comparator); } public static > Comparator comparator(La function) { return function.comparator(new ComparableComparator<>()); } public > La> liftToList() { final La transform = this; return new La>() { @Override public List la(C argument) { if (argument == null) { return null; } Iterator it = argument.iterator(); if (it.hasNext()) { return transform.arrayList(it); } else { return Collections.emptyList(); } } }; } public static La notNull() { return NOT_NULL; } public static La stringValue() { return TO_STRING; } public static La notNull(final La la, final B onNull) { return new La(la.rClass) { @Override public B la(A argument) { B result = la.la(argument); return result == null ? onNull : result; } }; } public Collection image(Collection set) { return image(set, false); } private Collection image(final Collection set, final boolean includeFalsy) { if (set == null) return Collections.emptyList(); return new AbstractCollection() { @NotNull public Iterator iterator() { Iterator it = transform(set.iterator()); if (!includeFalsy) { it = La.self().filter(it); } return it; } public int size() { if (includeFalsy) { return set.size(); } int count = 0; for (T v : set) { if (accepts(v)) { count++; } } return count; } }; } public static class LaComparator implements Comparator { private final La myFunction; private final Comparator myComparator; public LaComparator(La function, Comparator comparator) { if (function == null) { throw new IllegalArgumentException("function is null"); } if (comparator == null) { throw new IllegalArgumentException("comparator is null"); } myFunction = function; myComparator = comparator; } @Override public int compare(A o1, A o2) { B b1 = myFunction.apply(o1); B b2 = myFunction.apply(o2); return myComparator.compare(b1, b2); } } public static class ComparableComparator> implements Comparator { @Override public int compare(B b1, B b2) { if (b1 == null) { return b2 == null ? 0 : -1; } if (b2 == null) { return 1; } return b1.compareTo(b2); } } public class FilterIterator implements Iterator { private final Iterator myOuter; private Boolean hasNext = null; private D next; public FilterIterator(Iterator iterator) { myOuter = iterator; } @Override public boolean hasNext() { if (hasNext == null) { hasNext = calcHasNext(); } return hasNext; } private boolean calcHasNext() { while (myOuter.hasNext()) { next = myOuter.next(); if (accepts(next)) return true; } return false; } @Override public D next() { if (!hasNext()) throw new NoSuchElementException(); hasNext = null; return next; } @Override public void remove() { throw new UnsupportedOperationException(); } } public class TransformIterator implements Iterator { private final Iterator mySourceIterator; public TransformIterator(Iterator iterator) { mySourceIterator = iterator; } @Override public boolean hasNext() { return mySourceIterator.hasNext(); } @Override public R next() { return la(mySourceIterator.next()); } @Override public void remove() { mySourceIterator.remove(); } } }