package org.solemnsilence.util; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.json.JSONArray; /** * Python-inspired painkillers for Java * * @author jens.knutson@gmail.com * */ public class Py { public interface Truthiable { public boolean isTruthy(); } // NOTE: "truthy()" is by far the most popular function in this file, and // it serves as the basis for many of the other functions below. If you // don't know how it works, take a second to review it - it's easy and // really useful in taking away some of the lameness and pain of // overly verbose code. /** * Test the "truthiness" of an object, ala Python - see http://goo.gl/JebVU * * The following will all return <code>false</code>: * <ul> * <li>null * <li>Any empty collection/array * <li>a empty string, i.e.: "" * <li>a Number (like Integer or Long, or their primitive equivalents) with * a value of 0 * <li>Anything with the Truthiable interface where .isTruthy() returns * false * <li>A Boolean (or primitive equiv.) which evaluates to false * </ul> * * Anything else will return true. * * @param obj * @return is obj "truthy" */ public static boolean truthy(Object obj) { if (obj == null) { return false; } else if (obj instanceof Truthiable) { return ((Truthiable) obj).isTruthy(); } else if (obj instanceof Collection) { return (!((Collection<?>) obj).isEmpty()); } else if (obj instanceof Iterable) { return (((Iterable<?>) obj).iterator().hasNext()); } else if (obj instanceof Object[]) { return (((Object[]) obj).length > 0); } else if (obj instanceof Number) { return (((Number) obj).longValue() != 0); } else if (obj instanceof CharSequence) { return (((CharSequence) obj).length() > 0); } else if (obj instanceof JSONArray) { return (((JSONArray) obj).length() > 0); } else if (obj instanceof Boolean) { return (((Boolean) obj).booleanValue()); } else if (obj instanceof long[]) { return (((long[]) obj).length > 0); } else if (obj instanceof int[]) { return (((int[]) obj).length > 0); } else if (obj instanceof short[]) { return (((short[]) obj).length > 0); } else if (obj instanceof byte[]) { return (((byte[]) obj).length > 0); } else if (obj instanceof char[]) { return (((char[]) obj).length > 0); } else if (obj instanceof boolean[]) { return (((boolean[]) obj).length > 0); } else if (obj instanceof float[]) { return (((float[]) obj).length > 0); } else if (obj instanceof double[]) { return (((double[]) obj).length > 0); } return true; } /** * Return true *only* if *every* object in the varargs array is truthy * * Call truthy() on each object in the varargs - this is called all() in * Python, so that's what I'm calling it here. * * @param objects * @return */ public static boolean all(Object... objects) { if (!truthy(objects)) { // is our varargs list null or empty? return false; } for (Object obj : objects) { if (!truthy(obj)) { return false; } } return true; } /** * Return true if *any* object in the varargs array is truthy * * Calls truthy() on each object in the varargs - this is called any() in * Python, so that's what I'm calling it here. * * @param objects * @return */ public static boolean any(Object... objects) { if (!truthy(objects)) { // is our varargs list null or empty? return false; } for (Object obj : objects) { if (truthy(obj)) { return true; } } return false; } /** * Calls {@link #range(int, int)} with a <code>start</code> param of 0 * * @param stop * @return {@link Py#Ranger} */ public static Ranger range(int stop) { return range(0, stop); } /** * Generally, for-each loops > traditional for loops, but the latter is * extremely resource friendly. This class makes it easy to do for-each * loops for a given range of <code>int</code>s. * <p> * Wherever you'd write this (which is surprisingly easy to get wrong)... * * <pre> * <code> * for (int i = 0; i < rangeStop; i++) { * doStuff(i); * } * </code> * </pre> * * ...instead, write this: * * <pre> * <code> * for (IntValue valueHolder : range(numIds)) { * doStuff(valueHolder.value); * } * </code> * </pre> * * <em>(note the use of <code>range()</code>, implying a static import of the function, which is recommended)</em> * <p> * Inspired by Python's range() function, with which it shares similar * behavior (e.g.: if you do range(-5), you will iterate zero times, since * there are no numbers between 0 and -5 if you're counting forwards. This * is also the same behavior you get when using those values in an * equivalent for loop in Java, or when using those values with Ruby's range * operator) * * @param start * @param stop * @return {@link Py#Ranger} */ public static Ranger range(int start, int stop) { return new Ranger(start, stop); } /** * Simple function to create inline Collections without all the extra noise * * The main utility here is for creating inline 'foreach' loops, like to * check if an arbitrary number of vars are null, e.g.: * * for (Object x : list(foo, bar, baz, quux) { if (x == null) throw new * SomeException("You screwed up!"); } * * * @param objects * arbitrary number of objects. * * @return a List from the objects param */ public static <T> List<T> list(T... objects) { return new ArrayList<T>(Arrays.asList(objects)); } // get around empty constructors complaining about zero-arg varargs calls // like the one above when the list content // uses generics public static <T> List<T> list() { return new ArrayList<T>(); } public static <T> List<T> list(Collection<T> someCollection) { return new ArrayList<T>(someCollection); } public static <T> List<T> list(Iterable<T> things) { // Why isn't this in Collections somewhere? return list(things.iterator()); } public static <T> List<T> list(Iterator<T> things) { // Why isn't this in Collections somewhere? List<T> result = new ArrayList<T>(); while (things.hasNext()) { result.add(things.next()); } return result; } /** * Just like Py.list(), but it returns a Set. * * @param objects * arbitrary number of objects. * * @return a Set from the objects param */ public static <T> PySet<T> set(T... objects) { return set(Arrays.asList(objects)); } public static <T> PySet<T> set(Collection<T> someCollection) { return new PySet<T>(someCollection); } /** * Like Py.list(), but returns an immutable sequence. * * Named after the same concept in Python, which in turn was named after the * pre-existing mathematical concept of a "tuple" * * @param objects * arbitrary number of objects. * * @return an immutible List */ public static <T> List<T> tuple(T... objects) { return Collections.unmodifiableList(list(objects)); } public static <T> List<T> tuple(List<T> someList) { return Collections.unmodifiableList(someList); } /** * Just like Py.set(), but it returns an immutable Set. * * Called "frozen"set because that's what Python calls it - might as well, * given the context and purpose of this class. * * @param objects * arbitrary number of objects. * * @return an immutable Set from the objects param */ public static <T> Set<T> frozenset(T... objects) { return Collections.unmodifiableSet(set(objects)); } public static <T> Set<T> frozenset(Set<T> someSet) { return Collections.unmodifiableSet(someSet); } // Python : dict :: Java : Map public static <K, V> Map<K, V> map(Map<K, V> otherMap) { return new ConcurrentHashMap<K, V>(otherMap); } public static <K, V> Map<K, V> map(List<K> keys, List<V> values) { if (keys.size() != values.size()) { throw new IllegalArgumentException("key and value lists MUST be the same size!"); } Map<K, V> newMap = map(); for (int i = 0; i < keys.size(); i++) { newMap.put(keys.get(i), values.get(i)); } return newMap; } // returns an initialized but empty Map. public static <K, V> Map<K, V> map() { return new ConcurrentHashMap<K, V>(); } public static <K, V> Map<K, V> frozenmap(Map<K, V> otherMap) { return Collections.unmodifiableMap(map(otherMap)); } public static <K, V> Map<K, V> frozenmap(List<K> keys, List<V> values) { return Collections.unmodifiableMap(map(keys, values)); } // returns an initialized but empty Map. public static <K, V> Map<K, V> frozenmap() { return Collections.unmodifiableMap(new HashMap<K, V>()); } /** * A Set implementation that offers an interface similar to Python's set * objects, which offer methods for the simple, standard terms of set theory * (e.g.: "union", "intersection", etc). * * These methods all create new sets instead of modifying existing ones in * place. In keeping with the theme of this class, this is similar to what * the Python methods of the same name will do. * <p/> * This is different than what Java's sets do, but Java's sets use different * method names which match their behavior, so I believe this shouldn't * cause anyone to trip up. * * @author jknutson * * @param <T> */ public static class PySet<T> extends LinkedHashSet<T> { private static final long serialVersionUID = 2423791518942099628L; public PySet(Collection<T> other) { super(other); } /** * Return a new set with elements from this set and all elements from * <code>others</code>. * * @param others * , one or more collections with * @return (see above) */ public PySet<T> getUnion(Collection<T>... others) { PySet<T> newCopy = set(this); for (Collection<T> other : others) { newCopy.addAll(other); } return newCopy; } /** * Return a new set with elements common to this set and all elements * from <code>others</code>. * * This method is separate from {{@link #getIntersection(Collection...)} * because until Java 1.7's SafeVarargs annotation, there was no way to * call a varargs method using generic collections without getting a * (bogus) type safety error. * * @param other * @return */ public PySet<T> getIntersection(Collection<T> other) { PySet<T> newCopy = set(this); // NOTE: .retainAll() can be thought of as .retainOnly() or // "remove all except these". // Javadoc: http: // goo.gl/HF6vn newCopy.retainAll(other); return newCopy; } /** * Return a new set with elements common to this set and all elements * from <code>others</code>. * * @param others * @return */ public PySet<T> getIntersection(Collection<T>... others) { PySet<T> newCopy = set(this); for (Collection<T> other : others) { // NOTE: .retainAll() can be thought of as .retainOnly() or // "remove all except these". // Javadoc: http: // goo.gl/HF6vn newCopy.retainAll(other); } return newCopy; } /** * Return a new set with elements in this set which do not exist in * <code>other</code>. * * @param other * @return */ public PySet<T> getDifference(Collection<T> other) { // Return a new set with elements in the set that are not in the // others. PySet<T> newCopy = set(this); newCopy.removeAll(other); return newCopy; } /** * Return a new set with elements in this set which do not exist in any * of the <code>others</code>. * * @param others * @return */ public PySet<T> getDifference(Collection<T>... others) { // Return a new set with elements in the set that are not in the // others. PySet<T> newCopy = set(this); for (Collection<T> other : others) { newCopy.removeAll(other); } return newCopy; } /** * Return a new set with elements in this set which do not exist in any * of the <code>others</code>. * * @param other * @return */ public PySet<T> getSymmetricDifference(Collection<T>... others) { // Return a new set with elements in either the set or other but not // both. PySet<T> union = set(this); for (Collection<T> other : others) { union.addAll(other); } PySet<T> intersection = set(this); for (Collection<T> other : others) { if (intersection.isEmpty()) { // Don't do any more work if the intersection can't be // anything other than an empty set. break; } intersection = intersection.getIntersection(other); } return union.getDifference(intersection); } } /** * See docs on {@link Py#range(int, int)} * * @author <a href="mailto:jens.knutson@gmail.com">Jens Knutson</a> * */ public static class Ranger implements Iterable<Ranger.IntValue>, Iterator<Ranger.IntValue> { /** * Just a simple holder for an <code>int</code>. * * It's designed for performance, so it uses <code>int</code> instead of * <code>Integer</code> to avoid any auto(un)boxing, and it allows * direct access to the value instead of using a method, since this can * be meaningfully faster for loops with many iterations, esp when those * inner loops are on one's UI thread(!) * * @author <a href="mailto:jens.knutson@gmail.com">Jens Knutson</a> * */ public static class IntValue { public int value; public IntValue(int initialValue) { value = initialValue; } } private final Ranger.IntValue currentValue; private final int stopBefore; private Ranger(int start, int stop) { validateArgs(start, stop); // doing this here means that in .hasNext() we can just use the // post-increment operator, which is crazy-fast this.currentValue = new IntValue(start - 1); this.stopBefore = stop; } private void validateArgs(int start, int stop) { String errMsg = null; if (start == Integer.MIN_VALUE) { errMsg = "Sorry, ranges can't start with Integer.MIN_VALUE. Lame, I know, since it looks like it would " + "work anyway, but I didn't have time to properly test all the combinations of ranges which " + "might use Intger.MIN/MAX, so... 'Patches Accepted'"; throw new IllegalArgumentException(errMsg); } } @Override public Iterator<Ranger.IntValue> iterator() { return this; } @Override public boolean hasNext() { currentValue.value++; return currentValue.value < stopBefore; } @Override public Ranger.IntValue next() { return currentValue; } @Override public void remove() { throw new RuntimeException("Not supported"); } } }