| /* |
| * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| package com.apple.internal.jobjc.generator.utils; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeMap; |
| |
| /** |
| * Functional programming constructs and utilities. Java for Lisp and Haskell nerds. |
| */ |
| public abstract class Fp { |
| /** |
| * Multiple dynamic dispatch (multi-methods) for Java. |
| * |
| * This is implemented with Java reflection: |
| * Class.getDeclaredMethod and Method.invoke. |
| * It is about 20-40 times slower than chains of |
| * "if instanceof" statements. |
| */ |
| public static abstract class Dispatcher{ |
| /** |
| * Shorthand, works only if no arg is null. |
| */ |
| public static final <R> R dispatch(Class clazz, Object instance, String method, Object... args) throws NoSuchMethodException{ |
| Class[] types = new Class[args.length]; |
| for(int i = 0; i < args.length; i++) types[i] = args[i].getClass(); |
| return (R) dispatch(clazz, instance, method, args, types); |
| } |
| |
| /** |
| * Dispatch `args` of `types` to `method` on `clazz` for `instance`. If `method` is static, `instance` should be null. |
| */ |
| public static final <R> R dispatch(Class clazz, Object instance, String method, Object[] args, Class[] types) throws NoSuchMethodException{ |
| try{ |
| java.lang.reflect.Method m = clazz.getDeclaredMethod(method, types); |
| m.setAccessible(true); |
| return (R) m.invoke(instance, args); |
| } |
| catch(NoSuchMethodException x){ |
| if(clazz.getSuperclass() != null) return (R) dispatch(clazz.getSuperclass(), instance, method, args, types); |
| else throw x; |
| } |
| catch(Exception x){ |
| throw new RuntimeException(x); |
| } |
| } |
| } |
| |
| /** |
| * The "Maybe" type encapsulates an optional value. A value of type |
| * "Maybe a" either contains a value of type "a" (represented as "Just a"), |
| * or it is empty (represented as "Nothing"). |
| * |
| * http://haskell.org/ghc/docs/latest/html/libraries/base/Data-Maybe.html |
| */ |
| public static abstract class Maybe<A>{ |
| public abstract boolean isJust(); |
| public abstract boolean isNothing(); |
| public abstract A fromJust() throws ClassCastException; |
| public abstract A fromMaybe(final A fallback); |
| |
| public static class Nothing<A> extends Maybe<A>{ |
| @Override public A fromJust() throws ClassCastException { throw new ClassCastException("Cannot extract value from Nothing."); } |
| @Override public A fromMaybe(A fallback) { return fallback; } |
| @Override public boolean isJust() { return false; } |
| @Override public boolean isNothing() { return true; } |
| } |
| public static class Just<A> extends Maybe<A>{ |
| public final A a; |
| public Just(A a){ this.a = a; } |
| @Override public A fromJust(){ return a; } |
| @Override public A fromMaybe(A fallback) { return a; } |
| @Override public boolean isJust() { return true; } |
| @Override public boolean isNothing() { return false; } |
| } |
| } |
| |
| public static class NonNull<A>{ |
| public final A obj; |
| public NonNull(A o){ |
| if(o==null) throw new RuntimeException("o may not be null."); |
| this.obj = o; |
| } |
| } |
| |
| // Closures |
| public static interface Map0<A>{ A apply(); } |
| public static interface Map1<A,B>{ B apply(final A a); } |
| public static interface Map2<A,B,C>{ C apply(final A a, final B b); } |
| |
| public static class CacheMap<K extends Comparable<K>,V>{ |
| private Map<K,V> cache = new TreeMap<K,V>(); |
| public V get(K key, Map0<V> create){ |
| if(cache.containsKey(key)) return cache.get(key); |
| V value = create.apply(); |
| cache.put(key, value); |
| return value; |
| } |
| } |
| |
| public static class Curry2to1<A,B,C> implements Map1<B,C>{ |
| private Map2<A,B,C> target; private A a; |
| public Curry2to1(Map2<A, B, C> targett, A aa) { target = targett; a = aa; } |
| public C apply(B b) { return target.apply(a, b); } |
| } |
| |
| // Tuple |
| public static class Pair <A,B> implements Comparable<Pair<A,B>>{ |
| public final A a; public final B b; |
| public Pair(final A aa, final B bb){ a=aa; b=bb; } |
| @Override public int hashCode(){ return (a==null ? 0 : a.hashCode()) + (b==null ? 0 : b.hashCode()); } |
| @Override public boolean equals(Object o){ |
| if(!(o instanceof Pair)) return false; |
| Pair<?,?> p = (Pair<?,?>) o; |
| return QA.bothNullOrEquals(a, p.a) && QA.bothNullOrEquals(b, p.b); |
| } |
| @Override public String toString(){ return "(" + a + ", " + b + ")"; } |
| public int compareTo(Pair<A, B> o){ return toString().compareTo(o.toString()); } |
| } |
| |
| /** |
| * @return [fn(x) | x <- items] |
| */ |
| public static <A,B> List<B> map(Map1<A,B> fn, final Collection<A> xs){ |
| ArrayList<B> rs = new ArrayList<B>(xs.size()); |
| for(A x : xs) rs.add(fn.apply(x)); |
| return rs; |
| } |
| |
| public static <A,B,C> List<C> map2(Map2<A,B,C> fn, final Collection<A> as, final Collection<B> bs){ |
| assert as.size() == bs.size(); |
| ArrayList<C> cs = new ArrayList<C>(as.size()); |
| Iterator<A> aiter = as.iterator(); |
| Iterator<B> biter = bs.iterator(); |
| while(aiter.hasNext() && biter.hasNext()) |
| cs.add(fn.apply(aiter.next(), biter.next())); |
| return cs; |
| } |
| |
| /** |
| * Same as map, but does not retain results. |
| */ |
| public static <A> void each(Map1<A,?> fn, final Collection<A> xs){ |
| for(A x : xs) fn.apply(x); |
| } |
| |
| /** |
| * @return [x | x <- items, take(x)] |
| */ |
| public static <A> List<A> filter(Map1<A,Boolean> take, final Collection<A> xs){ |
| List<A> rs = new ArrayList<A>(xs.size()); |
| for(A x : xs) if(take.apply(x)) rs.add(x); |
| return rs; |
| } |
| |
| /** |
| * @return [x | x <- items, take(x)] |
| */ |
| public static <A> Set<A> filterSet(Map1<A,Boolean> take, final Collection<A> xs){ |
| Set<A> rs = new HashSet<A>(xs.size()); |
| for(A x : xs) if(take.apply(x)) rs.add(x); |
| return rs; |
| } |
| |
| /** |
| * @return the first x in items that satisfies take(x), or null if none |
| */ |
| public static <X> X find(Map1<X,Boolean> take, final Collection<X> xs){ |
| for(X x : xs) if(take.apply(x)) return x; |
| return null; |
| } |
| |
| public static <A,B> A foldl(final Map2<A,B,A> f, A a, final Collection<B> xs){ |
| for(B b : xs) a = f.apply(a, b); |
| return a; |
| } |
| |
| /** |
| * @return All x : p(x) == true |
| */ |
| public static <A> boolean all(Map1<A,Boolean> p, Collection<A> xs) { |
| for(A x : xs) if(!p.apply(x)) return false; |
| return true; |
| } |
| |
| /** |
| * @return Any x : p(x) == true |
| */ |
| public static <A> boolean any(Map1<A,Boolean> p, Collection<A> xs) { |
| for(A x : xs) if(p.apply(x)) return true; |
| return false; |
| } |
| |
| public static <A> String join(final String sep, final Collection<A> xs) { |
| if(xs.size() == 0) return ""; |
| if(xs.size() == 1) return xs.iterator().next().toString(); |
| return Fp.foldl(new Fp.Map2<String, A, String>(){ |
| public String apply(String a, A b) { |
| String sb = b==null? "null" : b.toString(); |
| return a == null ? sb : a + sep + sb; |
| }}, null, xs); |
| } |
| |
| public static Map2<Integer,Integer,Integer> operatorPlus = new Map2<Integer, Integer, Integer>(){ |
| public Integer apply(Integer a, Integer b) { return (int)a + (int)b;} |
| }; |
| |
| public static int sum(Collection<Integer> xs){ return foldl(operatorPlus, 0, xs); } |
| |
| public static <A> List<A> append(Collection<A> xs, Collection<A> ys) { |
| List<A> rs = new ArrayList<A>(xs.size() + ys.size()); |
| rs.addAll(xs); |
| rs.addAll(ys); |
| return rs; |
| } |
| |
| public static <A> Set<A> appendSet(Collection<A> xs, Collection<A> ys) { |
| Set<A> rs = new HashSet<A>(xs.size() + ys.size()); |
| rs.addAll(xs); |
| rs.addAll(ys); |
| return rs; |
| } |
| |
| public static <K,V> Map<K,V> litMap(K key, V value, Object... pairs){ |
| Map ret = new HashMap(1 + pairs.length/2); |
| ret.put(key, value); |
| for(int i = 0; i < pairs.length; i += 2) |
| ret.put(pairs[i], pairs[i+1]); |
| return ret; |
| } |
| } |