Package winterwell.utils.containers

Source Code of winterwell.utils.containers.Containers

/* (c) Winterwell 2008-2011
*
*/
package winterwell.utils.containers;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.AbstractList;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
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.logging.Level;

import winterwell.utils.IFilter;
import winterwell.utils.IFn;
import winterwell.utils.IProperties;
import winterwell.utils.Key;
import winterwell.utils.Printer;
import winterwell.utils.ReflectionUtils;
import winterwell.utils.Utils;
import winterwell.utils.reporting.Log;

/**
* Helpful utility methods for working with Collections and arrays.
*
* @author daniel
*
*/
public final class Containers {

  /**
   * More self-documenting version of Pair<Collection<X>> for returning
   * added/deleted pairs.
   *
   * @author miles
   *
   * @param <X>
   */
  public static final class Changes<X> {
    private final List<X> added;
    private final List<X> deleted;

    public Changes(List<X> added, List<X> deleted) {
      this.added = added;
      this.deleted = deleted;
    }

    public List<X> getAdded() {
      return added;
    }

    public List<X> getDeleted() {
      return deleted;
    }

    public boolean isEmpty() {
      return added.isEmpty() && deleted.isEmpty();
    }

    public int size() {
      return added.size() + deleted.size();
    }

    @Override
    public String toString() {
      return "[Added: " + Printer.toString(added) + "  Deleted: "
          + Printer.toString(deleted) + "]";
    }
  }

  public static final IProperties EMPTY_PROPERTY_BAG = new IProperties() {
    @Override
    public <T> boolean containsKey(Key<T> key) {
      return false;
    }

    @Override
    public <T> T get(Key<T> key) {
      return null;
    }

    @Override
    public Collection<Key> getKeys() {
      return Collections.emptySet();
    }

    @Override
    public boolean isTrue(Key<Boolean> key) {
      return false;
    }

    @Override
    public <T> T put(Key<T> key, T value) {
      throw new UnsupportedOperationException();
    }
  };

  public static <T> AbstractIterable<T> addPeekMethod(final Iterator<T> it) {
    return new AbstractIterable<T>() {
      @Override
      protected T next2() {
        return it.hasNext() ? it.next() : null;
      }
    };
  }

  public static final <X> IFilter<X> And(final IFilter<X> filterA,
      final IFilter<X> filterB) {
    return new IFilter<X>() {
      @Override
      public boolean accept(X x) {
        return filterA.accept(x) && filterB.accept(x);
      }
    };
  }

  /**
   * Throws an IllegalArgumentException if 'array' is not an array (eg. an int
   * value)
   */
  public static List<Object> arrayAsList(final Object array) {
    if (!array.getClass().isArray())
      throw new IllegalArgumentException("Backing object is not an array");
    return new AbstractList<Object>() {
      @Override
      public Object get(int index) {
        return Array.get(array, index);
      }

      @Override
      public Object set(int index, Object value) {
        Object old = get(index);
        Array.set(array, index, value);
        return old;
      }

      @Override
      public int size() {
        return Array.getLength(array);
      }

      @Override
      public String toString() {
        return Printer.toString(this, ", ");
      }
    };
  }

  public static List<Double> asList(final double[] xs) {
    return new AbstractList<Double>() {
      @Override
      public Double get(int index) {
        return xs[index];
      }

      @Override
      public Double set(int index, Double element) {
        double old = xs[index];
        xs[index] = element;
        return old;
      }

      @Override
      public int size() {
        return xs.length;
      }
    };
  }

  public static List<Integer> asList(final int[] xs) {
    return new AbstractList<Integer>() {
      @Override
      public Integer get(int index) {
        return xs[index];
      }

      @Override
      public Integer set(int index, Integer element) {
        int old = xs[index];
        xs[index] = element;
        return old;
      }

      @Override
      public int size() {
        return xs.length;
      }
    };
  }

  public static List<Long> asList(final long[] xs) {
    return new AbstractList<Long>() {
      @Override
      public Long get(int index) {
        return xs[index];
      }

      @Override
      public Long set(int index, Long element) {
        long old = xs[index];
        xs[index] = element;
        return old;
      }

      @Override
      public int size() {
        return xs.length;
      }
    };
  }

  public static List<Object> asList(final Object[] objects) {
    return new AbstractList<Object>() {
      @Override
      public Object get(int index) {
        return objects[index];
      }

      @Override
      public Object set(int index, Object element) {
        Object old = objects[index];
        objects[index] = element;
        return old;
      }

      @Override
      public int size() {
        return objects.length;
      }
    };
  }

  public static List<String> asList(final String[] strings) {
    return new AbstractList<String>() {
      @Override
      public String get(int index) {
        return strings[index];
      }

      @Override
      public String set(int index, String element) {
        String old = strings[index];
        strings[index] = element;
        return old;
      }

      @Override
      public int size() {
        return strings.length;
      }
    };
  }

  /**
   * Chop up a list into (almost) equal-sized pieces. The last piece may be
   * longer than the others.
   *
   * @param <X>
   * @param data
   * @param pieces
   * @return
   * @testedby {@link ContainersTest#testChop()}
   */
  public static <X> List<List<X>> chop(List<X> data, int pieces) {
    assert !data.isEmpty();
    assert pieces > 0;
    int n = data.size() / pieces;
    assert n > 0;
    int i = 0;
    List<List<X>> slices = new ArrayList<List<X>>(pieces);
    for (int p = 0; p < pieces; p++) {
      List<X> slice = new ArrayList<X>(n);
      for (int sn = (p + 1) * n; i < sn; i++) {
        X x = data.get(i);
        slice.add(x);
      }
      slices.add(slice);
    }
    List<X> lastSlice = last(slices);
    for (; i < data.size(); i++) {
      X x = data.get(i);
      lastSlice.add(x);
    }
    return slices;
  }

  /**
   * Compare any objects. Uses {@link #compare(Object, Object)}.
   */
  public static Comparator comparator() {
    return new Comparator() {
      @Override
      public int compare(Object o1, Object o2) {
        return Containers.compare(o1, o2);
      }
    };
  }

  /**
   * Compare any two objects. Falls back to hashcodes if need be. null counts
   * as large (so they will be sorted to the end in a normal sort)
   *
   * @param a
   * @param b
   * @return 1 if a>b, -1 if b<a, 0 if a equals b.
   * @throws ClassCastException
   *             if a is Comparable but objects to b
   */
  public static int compare(Object a, Object b) {
    if (a == b)
      return 0;
    if (a == null)
      return 1;
    if (b == null)
      return -1;
    if (a instanceof Comparable && b instanceof Comparable)
      return ((Comparable) a).compareTo(b);
    if (a.equals(b))
      return 0;
    // Arbitrary
    int ahash = a.hashCode();
    int bhash = b.hashCode();
    if (ahash == bhash) {
      Log.report("containers", "compare(): hashCode() not unique for "
          + a + ", " + b + "; sorting may go awry", Level.FINE);
      return 0;
    }
    return ahash < bhash ? -1 : 1;
  }

  /**
   * @param small
   *            Can be empty but never null
   * @param large
   *            Can be empty but never null
   * @return true if small is contained within large, false otherwise
   */
  public static boolean contains(Collection small, Collection large) {
    for (Object object : small) {
      if (!large.contains(object))
        return false;
    }
    return true;
  }

  /**
   * Convenience when working with arrays.
   *
   * @param s
   *            Can be null
   * @param strings
   * @return true if s (or an equals() String) is in strings
   */
  public static boolean contains(String s, String[] strings) {
    for (String string : strings) {
      if (s == null ? string == null : s.equals(string))
        return true;
    }
    return false;
  }

  /**
   * Returns a pair (or rather, an object) containing the set differences
   * between current and old, ie (added: current \ old, deleted: old \
   * current)
   *
   * @param current
   *            Cannot be null
   * @param old
   *            Cannot be null
   * @return
   */
  public static <X> Changes<X> differences(Collection<X> current,
      Collection<X> old) {
    return new Changes<X>(setDifference(current, old), setDifference(old,
        current));
  }

  /**
   * Filter out duplicates. Get the distinct/unique items from a collection.
   * This is like running the collection through a Set, but it preserves the
   * order.
   *
   * @param items
   * @return items (in the order they came) with any duplicates removed.
   */
  public static <X> List<X> distinct(Collection<X> items) {
    ArrayList<X> distinct = new ArrayList<X>(items.size());
    HashSet<X> set = new HashSet<X>(items.size());
    for (X x : items) {
      if (set.contains(x)) {
        continue;
      }
      set.add(x);
      distinct.add(x);
    }
    return distinct;
  }

  /**
   * Return the set of values in a collection (probably a list...)
   *
   * @param data
   * @return
   */
  public static <X> Set<X> entrySet(Collection<X> data) {
    HashSet<X> values = new HashSet<X>();
    for (X datum : data) {
      values.add(datum);
    }
    return values;
  }

  /**
   * @param filter
   * @param list
   * @return a new list, which contains those elements that the filter
   *         accepts.
   */
  public static <X, X2 extends X> List<X2> filter(IFilter<X> filter,
      Collection<X2> list) {
    ArrayList<X2> out = new ArrayList();
    for (X2 x : list) {
      if (filter.accept(x)) {
        out.add(x);
      }
    }
    return out;
  }

  /**
   * @param filter
   * @param tree
   * @return first node in tree which passes the filter
   */
  public static <T extends Tree> T first(IFilter<T> filter, T tree) {
    List<T> nodes = tree.flatten();
    for (T tree2 : nodes) {
      if (filter.accept(tree2))
        return tree2;
    }
    return null;
  }

  /**
   * Lenient access to the start of a collection
   *
   * @param filter
   *            can be null. If set, the method will pick the first element to
   *            pass filter.accept().
   * @param collection
   *            can be null.
   * @return the first element, if there is one, or null.
   */
  public static <X> X first(IFilter<X> filter, Collection<X> collection) {
    if (collection == null || collection.isEmpty())
      return null;
    if (filter == null)
      return collection.iterator().next();
    for (X x : collection) {
      if (filter.accept(x))
        return x;
    }
    return null;
  }

  public static <X> X first(Iterable<X> collection) {
    return get(collection, 0);
  }

  /**
   * WARNING: circular references will cause an infinite loop
   *
   * @param <X>
   * @param nestedCollections
   * @return all the non-collection elements of nestedCollections
   */
  public static <X> List<X> flatten(Collection<? extends X> nestedCollections) {
    ArrayList list = new ArrayList();
    flatten2(list, nestedCollections);
    return list;
  }

  static void flatten2(ArrayList list, Collection nestedCollections) {
    for (Object object : nestedCollections) {
      if (object instanceof Collection) {
        flatten2(list, (Collection) object);
      } else {
        list.add(object);
      }
    }
  }

  /**
   * @param collection
   * @param n
   * @return the nth element in the collection.
   */
  public static <X> X get(Iterable<X> collection, int n) {
    if (collection instanceof List)
      return ((List<X>) collection).get(n);
    Iterator<X> it = collection.iterator();
    for (int i = 0; i < n - 1; i++) {
      it.next();
    }
    return it.next();
  }

  /**
   * @param klass
   *            This covers sub-classes too
   * @param list
   *            Can be empty, must not be null
   * @return The first object of the given class, or null
   */
  public static <X> X getByClass(Class<X> klass, List<?> list) {
    for (Object object : list) {
      if (ReflectionUtils.isa(object.getClass(), klass))
        return (X) object;
    }
    return null;
  }

  /**
   * Get a list version of an iterable - possibly copying it out to do so.
   *
   * @param iterable
   * @return A List version of the input. If the input is itself a List this
   *         will return the input!
   */
  public static <X> List<X> getList(Iterable<X> iterable) {
    // Why waste effort?
    if (iterable instanceof List)
      return (List<X>) iterable;
    // if (iterable instanceof Collection)
    // return new ArrayList<X>((Collection) iterable);
    ArrayList<X> list = iterable instanceof Collection ? new ArrayList<X>(
        ((Collection) iterable).size()) : new ArrayList<X>();
    for (X x : iterable) {
      list.add(x);
    }
    return list;
  }

  public static Map<String, Object> getMap(IProperties props) {
    Collection<Key> keys = props.getKeys();
    Map<String, Object> map = new HashMap<String, Object>(keys.size());
    for (Key k : keys) {
      map.put(k.getName(), props.get(k));
    }
    return map;
  }

  /**
   * Get a set version of an iterable - possibly copying it out to do so.
   *
   * @param iterable
   * @return A Set version of the input. If the input is itself a List this
   *         will return the input! Otherwise you get a new HashSet for fast
   *         access.
   */
  public static <X> Set<X> getSet(Iterable<X> iterable) {
    // Why waste effort?
    if (iterable instanceof Set)
      return (Set<X>) iterable;
    if (iterable instanceof Collection)
      return new HashSet<X>((Collection) iterable);
    HashSet<X> list = new HashSet<X>();
    for (X x : iterable) {
      list.add(x);
    }
    return list;
  }

  /**
   * Get the keys to a map, sorted by their values.
   *
   * @param map
   * @return The keys of map, sorted by value. Lowest first. E.g. {carol:3,
   *         alice:1, bob:2} would result in [alice, bob, carol]
   */
  public static <K, V> List<K> getSortedKeys(final Map<K, V> map) {
    List<K> keys = new ArrayList<K>(map.keySet());
    Collections.sort(keys, getValueComparator(map));
    return keys;
  }

  private static <K, V> Comparator<K> getValueComparator(final Map<K, V> map) {
    return new Comparator<K>() {
      @Override
      public int compare(K k1, K k2) {
        if (k1.equals(k2))
          return 0;
        Object v1 = map.get(k1);
        Object v2 = map.get(k2);
        int comp = Containers.compare(v1, v2);
        if (comp == 0)
          return Containers.compare(k1, k2);
        return comp;
      }
    };
  }

  /**
   * Copy of map whose keySet() method returns keys in order sorted by value,
   * smallest(true)/largest(false) first.
   *
   * @param map
   *            Must NOT itselfbe a value-sorted map or you will get an
   *            infinite loop!
   *
   * @param maxNum
   *            -1 for all
   * @testedby {@link ContainersTest#testGetValueSortedMap()}
   */
  public static <K, V> Map<K, V> getValueSortedMap(final Map<K, V> map,
      final boolean smallestFirst, final int maxNum) {
    return new HashMap<K, V>(map) {
      /**
       *
       */
      private static final long serialVersionUID = 1L;

      @Override
      public Set<K> keySet() {
        List<K> keys = getSortedKeys(map);
        // sub set
        if (maxNum != -1) {
          keys = subList(keys, 0, maxNum);
        }
        if (!smallestFirst) {
          Collections.reverse(keys);
        }
        return Containers.listAsSet(keys);
      }
    };
  }

  /**
   * Find an object in an array, using equals() for comparison. Faster methods
   * exist for sorted arrays of known type:
   * {@link Arrays#binarySearch(Object[], Object)} and friends.
   *
   * @param x
   * @param array
   * @return index of x in array, or -1 if not present
   */
  public static int indexOf(Object x, Object array) {
    assert array.getClass().isArray() : array + " is not an array";
    int n = Array.getLength(array);
    for (int i = 0; i < n; i++) {
      Object y = Array.get(array, i);
      if (x == null ? y == null : x.equals(y))
        return i;
    }
    return -1;
  }

  public static int indexOf(String x, String[] array) {
    for (int i = 0; i < array.length; i++) {
      if (x == null ? array[i] == null : x.equals(array[i]))
        return i;
    }
    return -1;
  }

  /**
   * @param a
   * @param b
   * @return a new set which is the intersection of a and b (i.e. contains
   *         only those elements which were in both of them). The original
   *         sets are unchanged. Can be empty, never null.
   */
  public static <X> Set<X> intersection(Collection<? extends X> a,
      Collection<? extends X> b) {
    HashSet overlap = new HashSet(a);
    overlap.retainAll(b);
    return overlap;
  }

  /**
   * @param a
   * @param b
   * @return true if a and b share at least one element
   * @Deprecated use built in Collections.disjoint(a, b); Left here as an
   *             aide-memoire.
   */
  @Deprecated
  public static boolean intersects(Collection<?> a, Collection<?> b) {
    return !Collections.disjoint(a, b);
  }

  public static <X> Iterable<X> iterable(final Iterator<X> iterator) {
    return new Iterable<X>() {
      private boolean used;

      @Override
      public Iterator<X> iterator() {
        assert !used;
        used = true;
        return iterator;
      }
    };
  }

  public static <X> X last(List<X> list) {
    return list.get(list.size() - 1);
  }

  /**
   * Wrap a List as a Set. All edits will write through to the list. The add()
   * method will check to avoid duplicates. NB This cannot be used to retrieve
   * the set of list of entries for iteration see
   * {@link Containers#entrySet(Collection)} for that. This should not be used
   * for big lists as the add method iterates over the entire list...
   */
  public static <X> Set<X> listAsSet(final List<X> list) {
    return new AbstractSet<X>() {
      @Override
      public boolean add(X x) {
        // no duplicates
        if (list.contains(x))
          return false;
        return list.add(x);
      }

      @Override
      public Iterator<X> iterator() {
        return list.iterator();
      }

      @Override
      public boolean remove(Object x) {
        return list.remove(x);
      }

      @Override
      public int size() {
        return list.size();
      }
    };
  }

  /**
   * Produces a new List of values by mapping each value in list through a
   * transformation function.
   *
   * @param fn
   * @param list
   * @return [fn applied to each member of list]
   */
  public static <I, O> List<O> map(IFn<I, O> fn, Collection<? extends I> list) {
    ArrayList after = new ArrayList(list.size());
    for (I object : list) {
      O o = fn.apply(object);
      after.add(o);
    }
    return after;
  }

  public static final <X> IFilter<X> Not(final IFilter<X> filter) {
    return new IFilter<X>() {
      @Override
      public boolean accept(X x) {
        return !filter.accept(x);
      }
    };
  }

  /**
   * Treat an object like a map. One must question the sanity of this, but it
   * can be useful.
   */
  public static Map<String, Object> objectAsMap(Object x) {
    return new ObjectMap(x);
  }

  public static final <X> IFilter<X> Or(final IFilter<X> filterA,
      final IFilter<X> filterB) {
    return new IFilter<X>() {
      @Override
      public boolean accept(X x) {
        return filterA.accept(x) || filterB.accept(x);
      }
    };
  }

  public static <X> double plus(IProperties obj, Key<Double> key, double dx) {
    Double x = obj.get(key);
    if (x == null) {
      x = 0.0;
    }
    x += dx;
    obj.put(key, x);
    return x;
  }

  public static <X> int plus(IProperties obj, Key<Integer> key, int dx) {
    Integer x = obj.get(key);
    if (x == null) {
      x = 0;
    }
    x += dx;
    obj.put(key, x);
    return x;
  }

  public static <X> double plus(Map<X, Double> counts, X key, double dx) {
    Double x = counts.get(key);
    double y = x == null ? dx : x + dx;
    counts.put(key, y);
    return y;
  }

  public static <X> int plus(Map<X, Integer> counts, X key, int dx) {
    Integer x = counts.get(key);
    if (x == null) {
      x = 0;
    }
    x += dx;
    counts.put(key, x);
    return x;
  }

  /**
   * Replace all instances that are equals to old with replacement.
   *
   * @param list
   *            This is modified in place
   * @param old
   *            Can be null
   * @param replacement
   *            Can be null
   * @return count of the number of edits
   */
  public static <X> int replace(List<X> list, X old, X replacement) {
    int cnt = 0;
    for (int i = 0, n = list.size(); i < n; i++) {
      if (Utils.equals(list.get(i), old)) {
        list.set(i, replacement);
        cnt++;
      }
    }
    return cnt;
  }

  /**
   * Convenience method, mainly for use with asserts. More
   * reliable/straightforward for comparing Collections than equals()
   *
   * @param a
   * @param b
   * @return true if a and b have the same members. Ignores ordering and any
   *         duplicates.
   */
  public static boolean same(Collection a, Collection b) {
    return differences(a, b).isEmpty();
  }

  public static boolean same(Collection a, Object... b) {
    return differences(a, Arrays.asList(b)).isEmpty();
  }

  /**
   * Set difference, current \ old.
   *
   * @param current
   *            Cannot be null
   * @param old
   *            Cannot be null
   * @return An ArrayList of differences ignoring order.
   */
  public static <X> List<X> setDifference(Collection<X> current,
      Collection<X> old) {
    assert current != null && old != null;
    ArrayList<X> added = new ArrayList<X>();
    Set<X> oldSet = getSet(old);
    for (X currentX : current) {
      if (!old.contains(currentX)) { // X was not there before
        added.add(currentX);
      }
    }
    return added;
  }

  /**
   * Quick if iterable is actually a Collection. Otherwise we need to run
   * through the iterable to count its elements. So be careful with this.
   *
   * @param iterable
   * @return the size of this iterable
   */
  public static int size(Iterable iterable) {
    if (iterable instanceof Collection)
      return ((Collection) iterable).size();
    int cnt = 0;
    int max = Integer.MAX_VALUE;
    for (Object x : iterable) {
      cnt++;
      if (cnt == max)
        throw new IllegalArgumentException(iterable + " is too big");
    }
    return cnt;
  }

  /**
   * A more flexible (and dangerous) version of subList.
   * <p>
   * Unlike List.subList(), this will NOT share structure with the original
   * list.
   *
   * @param list
   *            Can be null in which case null is returned.
   *
   * @param start
   *            Inclusive. Can be negative for distance from the end. E.g. -1
   *            indicates "all but the last character" (zero indicates
   *            "from the beginning"). Can be longer than the actual list, in
   *            which case an empty list is returned.
   * @param end
   *            Exclusive. Can be negative for distance from the end. E.g. -1
   *            indicates "all but the last character" (zero indicates
   *            "up to the end"). Can be longer than the actual list, in which
   *            case it is reduced. If end is negative and too large, an empty
   *            list will be returned.
   *
   * @return The sublist. Can be empty. Only ever null if the list is null.
   *         Can be safely edited without affecting the original list.
   *
   *
   * @testedby {@link ContainersTest#testSubList()}
   */
  public static <X> List<X> subList(List<X> list, int start, int end) {
    if (list == null)
      return null;
    // start from end?
    if (start < 0) {
      start = list.size() + start;
      if (start < 0) {
        start = 0;
      }
    }
    // from end?
    if (end <= 0) {
      end = list.size() + end;
      if (end < start)
        return new ArrayList(0);
      ;
    }
    // too long?
    if (end > list.size()) {
      end = list.size();
    }
    // OK (copy to avoid shared structure)
    if (start == 0 && end == list.size())
      return new ArrayList(list);
    return new ArrayList(list.subList(start, end));
  }

  /**
   * A more flexible (and dangerous) version of subList.
   *
   * @param list
   *            Not null
   *
   * @param start
   *            Inclusive. Can be negative for distance from the end. E.g. -1
   *            indicates "all but the last character" (zero indicates
   *            "from the beginning"). Can be longer than the actual list, in
   *            which case an empty list is returned.
   * @param end
   *            Exclusive. Can be negative for distance from the end. E.g. -1
   *            indicates "all but the last character" (zero indicates
   *            "up to the end"). Can be longer than the actual list, in which
   *            case it is reduced. If end is negative and too large, an empty
   *            list will be returned.
   *
   * @return A copied out subarray. Can be empty.
   *
   * @testedby {@link ContainersTest#testSubList()}
   */
  public static long[] subList(long[] list, int start, int end) {
    // start from end?
    if (start < 0) {
      start = list.length + start;
      if (start < 0) {
        start = 0;
      }
    }
    // from end?
    if (end <= 0) {
      end = list.length + end;
      if (end < start)
        return new long[0];
    }
    // too long?
    if (end > list.length) {
      end = list.length;
    }
    // OK
    // if (start == 0 && end == list.length)
    // return list;
    return Arrays.copyOfRange(list, start, end);
  }

  /**
   * Turn a NxM list of lists into MxN. Must all be same length, or
   * IllegalArgumentException will be thrown (this is checked early with
   * size() before doing the transpose)
   */
  public static <A> List<List<A>> transpose(List<List<A>> in) {
    if (in.isEmpty())
      return new ArrayList<List<A>>();
    Integer size = in.get(0).size();
    if (size == 0)
      throw new IllegalArgumentException(
          "Input lists must not be of size zero -- the transpose representation is impossible");
    for (List<A> in_row : in) {
      if (in_row.size() != size)
        throw new IllegalArgumentException(
            "Input lists must all be of same size");
    }
    List<List<A>> out = new ArrayList<List<A>>();
    for (Integer idx = 0; idx < size; idx++) {
      List<A> out_row = new ArrayList<A>(in.size());
      for (Integer jdx = 0; jdx < in.size(); jdx++) {
        out_row.add(in.get(jdx).get(idx));
      }
      out.add(out_row);
    }
    return out;
  }

  private Containers() {
    // static
  }

}

/**
* Treat the fields of an object like a map. One must question the sanity of
* this, but it can be useful.
*
* @author daniel
*
*/
final class ObjectMap extends AbstractMap2<String, Object> {

  private final Class klass;
  private final Object object;

  public ObjectMap(Object x) {
    this.object = x;
    klass = x.getClass();
  }

  @Override
  public Object get(Object key) {
    // TODO search down for fields in super-classes
    String k = (String) key;
    try {
      Field f = klass.getField(k);
      return f.get(object);
    } catch (NoSuchFieldException ex) {
      try {
        Field f = klass.getDeclaredField(k);
        f.setAccessible(true);
        return f.get(object);
      } catch (NoSuchFieldException e) {
        return null;
      } catch (Exception e) {
        throw Utils.runtime(e);
      }
    } catch (Exception e) {
      throw Utils.runtime(e);
    }
  }

  @Override
  public Set<String> keySet() throws UnsupportedOperationException {
    Field[] fields = klass.getFields();
    Set<String> keys = new HashSet<String>(fields.length);
    for (Field field : fields) {
      keys.add(field.getName());
    }
    return keys;
  }

  @Override
  public Object put(String key, Object value) {
    Object prev = get(key);
    ReflectionUtils.setPrivateField(object, key, value);
    return prev;
  }
}
TOP

Related Classes of winterwell.utils.containers.Containers

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.