package xapi.fu; import xapi.fu.Filter.Filter2; import static xapi.fu.MappableIterable.mappable; import javax.validation.constraints.NotNull; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.function.Predicate; /** * @author James X. Nelson (james@wetheinter.net) * Created on 07/11/15. */ public interface X_Fu { In1Out1<String, String> STRING_DUPLICATE = s -> s + s; static <T> T[] array(T... t) { return t; } static <T> T[] push(T[] ts, T t) { return Fu.jutsu.pushOnto(ts, t); } static <T> T[] blank(T[] ts) { return Fu.jutsu.emptyArray(ts, ts.length); } static <T> T[] blank(T[] ts, int length) { return Fu.jutsu.emptyArray(ts, length); } static <T> T[] copy(T[] ts, int length) { return Fu.jutsu.arrayCopy(ts, length); } static int getLength(Object obj) { return Fu.jutsu.getLength(obj); } static void setValue(Object obj, int index, Object value) { Fu.jutsu.setValue(obj, index, value); } static Object getValue(Object obj, int index) { return Fu.jutsu.getValue(obj, index); } static <T> T[] pushCopy(T[] ts, T t) { return Fu.jutsu.pushCopy(ts, t); } static <T extends CharSequence> Predicate<T> notEmpty() { return item -> item != null && item.length() > 0; } static boolean returnTrue() { return true; } static boolean returnFalse() { return false; } static <T> boolean returnTrue(T ignored) { return true; } static <T> boolean returnFalse(T ignored) { return false; } static <T> boolean returnNotNull(T t) { return t != null; } static <T> boolean isNull(T t) { return t == null; } static <T> boolean notNull(final T value) { return value != null; } static <T> Filter<T> alwaysTrue() { return X_Fu::returnTrue; } static <T> Filter<T> alwaysFalse() { return X_Fu::returnFalse; } static <T> Filter<T> notNull() { return X_Fu::notNull; } static <T> String reduceToString(Iterable<T> data, In1Out1<T, String> serializer, String separator) { return reduceToString(map(data, serializer), separator); } static <F, T> Iterable<T> map(Iterable<F> data, In1Out1<F, T> converter) { return mappable(data).map(converter); } static String reduceToString(Iterable<? extends CharSequence> data, String separator) { StringBuilder b = new StringBuilder(); final Iterator<? extends CharSequence> itr = data.iterator(); if (itr.hasNext()) { b.append(itr.next()); while (itr.hasNext()) { b.append(separator); b.append(itr.next()); } } return b.toString(); } static String getLambdaMethodName(Object o) { return Fu.jutsu.lambdaName(o); } static boolean isLambda(Object o) { if (o == null) { return false; } final Class<? extends Object> cl = o.getClass(); return // cl.isSynthetic() && // gwt currently lacks this method :-/ cl.getName().toLowerCase().contains("$$lambda$"); } static <T> boolean noneNull(final T value, final T value1) { return value != null && value1 != null; } static <T> boolean equal(final T value, final T value1) { // cover the most permutations first. if (value == value1) { // if both were the same reference or null, return quickly return true; } // If either was null, that's a fail. if (!noneNull(value, value1)) { return false; } final Class<?> cls = value.getClass(); final Class<?> cls1 = value1.getClass(); if (cls == cls1) { if (cls.isArray()) { // for array types, just iterate items... final int len = Fu.jutsu.getLength(value); final int len1 = Fu.jutsu.getLength(value1); if (len != len1) { return false; } for (int i = 0; i < len; i++) { if (!equal(Fu.jutsu.getValue(value, i), Fu.jutsu.getValue(value1, i))) { return false; } } return true; } // two objects with the same class, just evaluate equality return value.equals(value1); } if (cls.isAssignableFrom(cls1)) { if (areComparable(value, value1)) { // We'll let comparable objects return 0 for us to short-circuit equals(). return ((Comparable)value).compareTo(value1) == 0; } if (value instanceof Collection) { // both are collections. Check the size() before we check elements. int mySize = ((Collection)value).size(); int yourSize = ((Collection)value1).size(); if (mySize != yourSize) { return false; } } if (value instanceof Map) { // both are maps. Check the size() before we check elements. int mySize = ((Map)value).size(); int yourSize = ((Map)value1).size(); if (mySize != yourSize) { return false; } } if (value instanceof Iterable) { // both are iterable. return iterEqual((Iterable)value, (Iterable)value1); } if (value instanceof Iterator) { // both are iterators return iterEqual((Iterator)value, (Iterator) value1); } // let the type that is NOT assignable decide. // This allows you to make types that can "dominate" other types equality methods. // For example, an object that knows how to compare itself to a String... // Strings will still only compare to each other, but your non-string object can do // something other than .toString() when it is being checked. return value1.equals(value); } // in case one of the values is not assignable to the other, we allow them a chance to call .equals(); if (cls1.isAssignableFrom(cls)) { // let the more specific type check equality return value.equals(value1); } // don't even check objects that aren't assignable in at least one direction. return false; } static <T> boolean areComparable(T value, T value1) { final Class<?> comp = comparableClassFor(value); if (comp == null) { return false; } final Class<?> comp1 = comparableClassFor(value1); return comp == comp1; } /** * Returns the comparable class for the given object, * if it is comparable to itself; * that is: * interface Type extends Comparable<Type> {}. * will allow Type.class to be returned. * * interface Type extends Supertype, Comparable <Supertype> {} * will return null, as only directly comparable objects will */ static Class<?> comparableClassFor(Object x) { return comparableClassFor(x, Filter.<Class<?>, Class<?>, Class<?>>filter2(X_Fu::equal)); } static Class<?> comparableClassFor(Object x, Filter2<Class<?>, Class<?>, Class<?>> filter) { if (x instanceof Comparable) { final Class<?> c; Type[] genericInterfaces, actualArgs; Type type; ParameterizedType p; if ((c = x.getClass()) == String.class) { return c; } if ((genericInterfaces = Fu.jutsu.getGenericInterfaces(c)) != null) { for (int i = 0; i < genericInterfaces.length; ++i) { if (((type = genericInterfaces[i]) instanceof ParameterizedType) && ((p = (ParameterizedType)type).getRawType() == Comparable.class) ) { if ((actualArgs = p.getActualTypeArguments()) != null && actualArgs.length == 1 && actualArgs[0] == c) { // type arg is c return c; } } // short circuit if a Comparable match return null; } } } return null; } static boolean iterEqual(Iterable v1, Iterable v2) { if (v1 == null) { return v2 == null; } if (v2 == null) { return false; } return iterEqualNullsafe(v1.iterator(), v2.iterator()); } static boolean iterEqual(Iterator v1, Iterator v2) { if (v1 == null) { return v2 == null; } if (v2 == null) { return false; } return iterEqualNullsafe(v1, v2); } static boolean iterEqualNullsafe(@NotNull Iterator v1, @NotNull Iterator v2) { while (v1.hasNext()) { if (!v2.hasNext()) { return false; } if (!equal(v1.next(), v2.next())) { return false; } } return true; } static <T> T getZeroeth(T[] value) { return value[0]; } static <T> void setZeroeth(T[] values, T value) { values[0] = value; } }