/* * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source 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, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.quercus.env; import com.caucho.quercus.marshal.Marshal; import com.caucho.quercus.marshal.MarshalFactory; import com.caucho.vfs.WriteStream; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.Array; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; /** * Represents a PHP array value. */ abstract public class ArrayValue extends Value { private static final Logger log = Logger.getLogger(ArrayValue.class.getName()); protected static final StringValue KEY = new ConstStringValue("key"); protected static final StringValue VALUE = new ConstStringValue("value"); public static final GetKey GET_KEY = new GetKey(); public static final GetValue GET_VALUE = new GetValue(); protected Entry _current; protected ArrayValue() { } /** * Returns the type. */ @Override public String getType() { return "array"; } /** * Returns the ValueType. */ @Override public ValueType getValueType() { return ValueType.ARRAY; } /** * Converts to a boolean. */ @Override public boolean toBoolean() { return getSize() != 0; } /** * Converts to a long. */ @Override public long toLong() { if (getSize() > 0) return 1; else return 0; } /** * Converts to a double. */ @Override public double toDouble() { return toLong(); } /** * Converts to a string. */ @Override public String toString() { return "Array"; } /** * Converts to an object. */ public Object toObject() { return null; } /** * Converts to a java object. */ @Override public Object toJavaObject() { return this; } // // Conversions // /** * Converts to an object. */ @Override public Value toArray() { return this; } /** * Converts to an array value */ @Override public ArrayValue toArrayValue(Env env) { return this; } /** * Converts to an object. */ @Override public Value toObject(Env env) { Value obj = env.createObject(); for (Entry entry = getHead(); entry != null; entry = entry._next) { Value key = entry.getKey(); // php/03oe obj.putField(env, key.toString(), entry.getValue()); } return obj; } /** * Converts to a java List object. */ @Override @SuppressWarnings("unchecked") public <T> Collection<T> toJavaCollection(Env env, Class<? extends Collection<T>> type) { Collection<T> coll = null; if (type.isAssignableFrom(HashSet.class)) { coll = new HashSet<T>(); } else if (type.isAssignableFrom(TreeSet.class)) { coll = new TreeSet<T>(); } else { try { coll = type.newInstance(); } catch (Throwable e) { log.log(Level.FINE, e.toString(), e); env.warning(L.l("Can't assign array to {0}", type.getName())); return null; } } for (Entry entry = getHead(); entry != null; entry = entry._next) { coll.add((T)entry.getValue().toJavaObject()); } return coll; } /** * Converts to a java List object. */ @SuppressWarnings("unchecked") @Override public <T> List<T> toJavaList(Env env, Class<? extends List<T>> type) { List<T> list = null; if (type.isAssignableFrom(ArrayList.class)) { list = new ArrayList<T>(); } else if (type.isAssignableFrom(LinkedList.class)) { list = new LinkedList<T>(); } else if (type.isAssignableFrom(Vector.class)) { list = new Vector<T>(); } else { try { list = type.newInstance(); } catch (Throwable e) { log.log(Level.FINE, e.toString(), e); env.warning(L.l("Can't assign array to {0}", type.getName())); return null; } } for (Entry entry = getHead(); entry != null; entry = entry._next) { list.add((T)entry.getValue().toJavaObject()); } return list; } /** * Converts to a java object. */ @SuppressWarnings("unchecked") @Override public <K,V> Map<K,V> toJavaMap(Env env, Class<? extends Map<K,V>> type) { Map<K,V> map = null; if (type.isAssignableFrom(TreeMap.class)) { map = new TreeMap(); } else if (type.isAssignableFrom(LinkedHashMap.class)) { map = new LinkedHashMap(); } else { try { map = (Map) type.newInstance(); } catch (Throwable e) { log.log(Level.FINE, e.toString(), e); env.warning(L.l("Can't assign array to {0}", type.getName())); return null; } } for (Entry entry = getHead(); entry != null; entry = entry._next) { map.put((K)entry.getKey().toJavaObject(), (V)entry.getValue().toJavaObject()); } return map; } /** * Returns true for an array. */ @Override public boolean isArray() { return true; } /** * Copy as a return value */ @Override public Value copyReturn() { return copy(); // php/3a5e } /** * Copy for assignment. */ @Override abstract public Value copy(); /** * Copy for serialization */ @Override abstract public Value copy(Env env, IdentityHashMap<Value,Value> map); /** * Returns the size. */ @Override abstract public int getSize(); /** * Returns the count(). */ @Override public int getCount(Env env) { return getSize(); } /** * Returns the count(). */ @Override public int getCountRecursive(Env env) { env.stub("recursive count of array unimplemented"); return getSize(); } /** * Returns true if the value is empty */ @Override public boolean isEmpty() { return getSize() == 0; } /** * Clears the array */ abstract public void clear(); @Override public int cmp(Value rValue) { return cmpImpl(rValue, 1); } private int cmpImpl(Value rValue, int resultIfKeyMissing) { // "if key from operand 1 is not found in operand 2 then // arrays are uncomparable, otherwise - compare value by value" // php/335h if (!rValue.isArray()) return 1; int lSize = getSize(); int rSize = rValue.toArray().getSize(); if (lSize != rSize) return lSize < rSize ? -1 : 1; for (Map.Entry<Value,Value> entry : entrySet()) { Value lElementValue = entry.getValue(); Value rElementValue = rValue.get(entry.getKey()); if (!rElementValue.isset()) return resultIfKeyMissing; int cmp = lElementValue.cmp(rElementValue); if (cmp != 0) return cmp; } return 0; } /** * Returns true for less than */ @Override public boolean lt(Value rValue) { // php/335h return cmpImpl(rValue, 1) < 0; } /** * Returns true for less than or equal to */ @Override public boolean leq(Value rValue) { // php/335h return cmpImpl(rValue, 1) <= 0; } /** * Returns true for greater than */ @Override public boolean gt(Value rValue) { // php/335h return cmpImpl(rValue, -1) > 0; } /** * Returns true for greater than or equal to */ @Override public boolean geq(Value rValue) { // php/335h return cmpImpl(rValue, -1) >= 0; } /** * Adds a new value. */ @Override public Value put(Value key, Value value) { append(key, value); return value; } /** * Add */ @Override abstract public Value put(Value value); /** * Add to front. */ abstract public ArrayValue unshift(Value value); /** * Splices. */ abstract public ArrayValue splice(int begin, int end, ArrayValue replace); /** * Slices. */ public ArrayValue slice(Env env, int start, int end, boolean isPreserveKeys) { ArrayValueImpl array = new ArrayValueImpl(); Iterator<Map.Entry<Value,Value>> iter = array.getIterator(env); for (int i = 0; i < end && iter.hasNext(); i++) { Map.Entry<Value,Value> entry = iter.next(); if (start <= i) { Value key = entry.getKey(); Value value = entry.getValue(); if ((key.isString()) || isPreserveKeys) array.put(key, value); else array.put(value); } } return array; } /** * Returns the value as an array. */ @Override public Value getArray(Value index) { Value value = get(index); Value array = value.toAutoArray(); if (value != array) { value = array; put(index, value); } return value; } /** * Returns the value as an argument which may be a reference. */ @Override abstract public Value getArg(Value index, boolean isTop); /** * Returns the field value, creating an object if it's unset. */ @Override public Value getObject(Env env, Value fieldName) { Value value = get(fieldName); Value object = value.toAutoObject(env); if (value != object) { value = object; put(fieldName, value); } return value; } /** * Sets the array ref. */ @Override abstract public Var putRef(); /** * Creatse a tail index. */ abstract public Value createTailKey(); /** * Returns a union of this array and the rValue as array. * If the rValue is not an array, the returned union contains the elements * of this array only. * * To append a value to this ArrayValue use the {@link #put(Value)} method. */ @Override public Value add(Value rValue) { rValue = rValue.toValue(); if (! rValue.isArray()) return copy(); ArrayValue result = new ArrayValueImpl(this); Iterator<Map.Entry<Value,Value>> iter = ((ArrayValue) rValue).getIterator(Env.getInstance()); while (iter.hasNext()) { Map.Entry<Value,Value> entry = iter.next(); Value key = entry.getKey(); if (result.get(key) == UnsetValue.UNSET) result.put(key, entry.getValue()); } return result; } @Override public Iterator<Map.Entry<Value, Value>> getIterator(Env env) { return new EntryIterator(getHead()); } public Iterator<Map.Entry<Value, Value>> getIterator() { return new EntryIterator(getHead()); } @Override public Iterator<Value> getKeyIterator(Env env) { return new KeyIterator(getHead()); } @Override public Iterator<Value> getValueIterator(Env env) { return new ValueIterator(getHead()); } /** * Gets a new value. */ @Override abstract public Value get(Value key); /** * Returns the value in the array as-is. * (i.e. without calling toValue() on it). */ public Value getRaw(Value key) { return get(key); } /** * Removes a value. */ @Override abstract public Value remove(Value key); /** * Returns the array ref. */ @Override abstract public Var getRef(Value index); /** * Returns an iterator of the entries. */ public Set<Value> keySet() { return new KeySet(); } /** * Returns a set of all the of the entries. */ public Set<Map.Entry<Value,Value>> entrySet() { return new EntrySet(); } /** * Returns a collection of the values. */ public Collection<Value> values() { return new ValueCollection(); } /** * Convenience for lib. */ public void put(String key, String value) { // XXX: this needs an Env arg because of i18n // XXX: but some modules have arrays that are static constants put(StringValue.create(key), StringValue.create(value)); } /** * Convenience for lib. */ public void put(Env env, String key, String value) { put(env.createStringOld(key), env.createStringOld(value)); } /** * Convenience for lib. */ public void put(String key, char value) { // XXX: this needs an Env arg because of i18n put(StringValue.create(key), StringValue.create(value)); } /** * Convenience for lib. */ public void put(String key, long value) { // XXX: this needs an Env arg because of i18n put(StringValue.create(key), LongValue.create(value)); } /** * Convenience for lib. */ public void put(Env env, String key, long value) { put(env.createStringOld(key), LongValue.create(value)); } /** * Convenience for lib. */ public void put(String key, double value) { // XXX: this needs an Env arg because of i18n put(StringValue.create(key), new DoubleValue(value)); } /** * Convenience for lib. */ public void put(String key, boolean value) { // XXX: this needs an Env arg because of i18n put(StringValue.create(key), value ? BooleanValue.TRUE : BooleanValue.FALSE); } /** * Convenience for lib. */ public void put(Env env, String key, boolean value) { put(env.createStringOld(key), value ? BooleanValue.TRUE : BooleanValue.FALSE); } /** * Convenience for lib. */ public void put(String value) { // XXX: this needs an Env arg because of i18n put(StringValue.create(value)); } /** * Convenience for lib. */ public void put(long value) { put(LongValue.create(value)); } /** * Appends as an argument - only called from compiled code * * XXX: change name to appendArg */ abstract public ArrayValue append(Value key, Value value); /** * Appends as an argument - only called from compiled code * * XXX: change name to appendArg */ public ArrayValue append(Value value) { put(value); return this; } /** * Puts all of the arg elements into this array. */ public void putAll(ArrayValue array) { for (Map.Entry<Value, Value> entry : array.entrySet()) put(entry.getKey(), entry.getValue()); } /** * Convert to an array. */ public static Value toArray(Value value) { value = value.toValue(); if (value instanceof ArrayValue) return value; else return new ArrayValueImpl().put(value); } /** * Prints the value. * @param env */ @Override public void print(Env env) { env.print("Array"); } /** * Pops the top value. */ abstract public Value pop(); /** * Shuffles the array */ abstract public void shuffle(); /** * Returns the head. */ abstract protected Entry getHead(); /** * Returns the tail. */ abstract protected Entry getTail(); /** * Returns the current value. */ @Override public Value current() { if (_current != null) return _current.getValue(); else return BooleanValue.FALSE; } /** * Returns the current key */ @Override public Value key() { if (_current != null) return _current.getKey(); else return NullValue.NULL; } /** * Returns true if there are more elements. */ @Override public boolean hasCurrent() { return _current != null; } /** * Returns the next value. */ @Override public Value next() { if (_current != null) _current = _current._next; return current(); } /** * Returns the previous value. */ public Value prev() { if (_current != null) _current = _current._prev; return current(); } /** * The each iterator */ public Value each() { if (_current == null) return NullValue.NULL; ArrayValue result = new ArrayValueImpl(); result.put(LongValue.ZERO, _current.getKey()); result.put(KEY, _current.getKey()); result.put(LongValue.ONE, _current.getValue()); result.put(VALUE, _current.getValue()); _current = _current._next; return result; } /** * Returns the first value. */ public Value reset() { _current = getHead(); return current(); } /** * Returns the last value. */ public Value end() { _current = getTail(); return current(); } /** * Returns the corresponding key if this array contains the given value * * @param value to search for in the array * * @return the key if it is found in the array, NULL otherwise */ abstract public Value contains(Value value); /** * Returns the corresponding key if this array contains the given value * * @param value to search for in the array * * @return the key if it is found in the array, NULL otherwise */ abstract public Value containsStrict(Value value); /** * Returns the corresponding value if this array contains the given key * * @param key to search for in the array * * @return the value if it is found in the array, NULL otherwise */ abstract public Value containsKey(Value key); /** * Returns an object array of this array. This is a copy of this object's * backing structure. Null elements are not included. * * @return an object array of this array */ public Map.Entry<Value, Value>[] toEntryArray() { ArrayList<Map.Entry<Value, Value>> array = new ArrayList<Map.Entry<Value, Value>>(getSize()); for (Entry entry = getHead(); entry != null; entry = entry._next) array.add(entry); Map.Entry<Value, Value>[]result = new Entry[array.size()]; return array.toArray(result); } /** * Sorts this array based using the passed Comparator * * @param comparator the comparator for sorting the array * @param resetKeys true if the keys should not be preserved * @param strict true if alphabetic keys should not be preserved */ public void sort(Comparator<Map.Entry<Value, Value>> comparator, boolean resetKeys, boolean strict) { Entry []entries; entries = new Entry[getSize()]; int i = 0; for (Entry entry = getHead(); entry != null; entry = entry._next) { entries[i++] = entry; } Arrays.sort(entries, comparator); clear(); long base = 0; if (! resetKeys) strict = false; for (int j = 0; j < entries.length; j++) { Value key = entries[j].getKey(); if (resetKeys && (! (key instanceof StringValue) || strict)) put(LongValue.create(base++), entries[j].getValue()); else put(entries[j].getKey(), entries[j].getValue()); } } /* * Serializes the value. * * @param sb holds result of serialization * @param serializeMap holds reference indexes */ @Override public void serialize(Env env, StringBuilder sb, SerializeMap serializeMap) { sb.append("a:"); sb.append(getSize()); sb.append(":{"); serializeMap.incrementIndex(); for (Entry entry = getHead(); entry != null; entry = entry._next) { entry.getKey().serialize(env, sb); entry.getRawValue().serialize(env, sb, serializeMap); } sb.append("}"); } /** * Exports the value. */ @Override public void varExport(StringBuilder sb) { sb.append("array ("); sb.append("\n"); //boolean isFirst = true; for (Entry entry = getHead(); entry != null; entry = entry._next) { sb.append(" "); entry.getKey().varExport(sb); sb.append(" => "); entry.getValue().varExport(sb); sb.append(",\n"); } sb.append(")"); } /** * Resets all numerical keys with the first index as base * * @param base the initial index * @param strict if true, string keys are also reset */ public boolean keyReset(long base, boolean strict) { Entry []entries; entries = new Entry[getSize()]; int i = 0; for (Entry entry = getHead(); entry != null; entry = entry._next) { entries[i++] = entry; } clear(); for (int j = 0; j < entries.length; j++) { Value key = entries[j].getKey(); if (! (key instanceof StringValue) || strict) put(LongValue.create(base++), entries[j].getValue()); else put(entries[j].getKey(), entries[j].getValue()); } return true; } /** * Test for equality * * @param rValue rhs ArrayValue to compare to * * @return true if this is equal to rValue, false otherwise */ @Override public boolean eq(Value rValue) { if (rValue == null) return false; for (Map.Entry<Value, Value> entry: entrySet()) { Value entryValue = entry.getValue(); Value entryKey = entry.getKey(); Value rEntryValue = rValue.get(entryKey); if ((rEntryValue instanceof ArrayValue) && ! entryValue.eq((ArrayValue) rEntryValue)) return false; if (! entryValue.eq(rEntryValue)) return false; } return true; } /** * Test for === * * @param rValue rhs ArrayValue to compare to * * @return true if this is equal to rValue, false otherwise */ @Override public boolean eql(Value rValue) { if (rValue == null) return false; else if (getSize() != rValue.getSize()) return false; rValue = rValue.toValue(); if (! (rValue instanceof ArrayValue)) return false; ArrayValue rArray = (ArrayValue) rValue; Iterator<Map.Entry<Value,Value>> iterA = entrySet().iterator(); Iterator<Map.Entry<Value,Value>> iterB = rArray.entrySet().iterator(); while (iterA.hasNext() && iterB.hasNext()) { Map.Entry<Value,Value> entryA = iterA.next(); Map.Entry<Value,Value> entryB = iterB.next(); if (! entryA.getKey().eql(entryB.getKey())) return false; if (! entryA.getValue().eql(entryB.getValue())) return false; } if (iterA.hasNext() || iterB.hasNext()) return false; else return true; } @Override public void varDumpImpl(Env env, WriteStream out, int depth, IdentityHashMap<Value, String> valueSet) throws IOException { out.println("array(" + getSize() + ") {"); for (Map.Entry<Value,Value> mapEntry : entrySet()) { varDumpEntry(env, out, depth + 1, valueSet, mapEntry); out.println(); } printDepth(out, 2 * depth); out.print("}"); } protected void varDumpEntry(Env env, WriteStream out, int depth, IdentityHashMap<Value, String> valueSet, Map.Entry<Value, Value> mapEntry) throws IOException { ArrayValue.Entry entry = (ArrayValue.Entry) mapEntry; entry.varDumpImpl(env, out, depth, valueSet); } @Override protected void printRImpl(Env env, WriteStream out, int depth, IdentityHashMap<Value, String> valueSet) throws IOException { out.println("Array"); printDepth(out, 8 * depth); out.println("("); for (Map.Entry<Value,Value> mapEntry : entrySet()) { ArrayValue.Entry entry = (ArrayValue.Entry) mapEntry; entry.printRImpl(env, out, depth, valueSet); } printDepth(out, 8 * depth); out.println(")"); } protected void printREntry(Env env, WriteStream out, int depth, IdentityHashMap<Value, String> valueSet, Map.Entry<Value, Value> mapEntry) throws IOException { ArrayValue.Entry entry = (ArrayValue.Entry) mapEntry; entry.printRImpl(env, out, depth, valueSet); } public static final class Entry implements Map.Entry<Value,Value>, Serializable { /** * */ private static final long serialVersionUID = 1L; final Value _key; Value _value; Var _var; Entry _prev; Entry _next; Entry _nextHash; public Entry(Value key) { _key = key; _value = NullValue.NULL; } public Entry(Value key, Value value) { _key = key; _value = value; } public Entry(Entry entry) { _key = entry._key; if (entry._var != null) _var = entry._var; else _value = entry._value.copyArrayItem(); } public Entry getNext() { return _next; } public Value getRawValue() { return _var != null ? _var : _value; } public Value getValue() { return _var != null ? _var.toValue() : _value; } public Value getKey() { return _key; } public Value toValue() { // The value may be a var // XXX: need test return _var != null ? _var.toValue() : _value; } /** * Argument used/declared as a ref. */ public Var toRefVar() { // php/376a if (_var != null) return _var; else { _var = new Var(_value); return _var; } } /** * Converts to an argument value. */ public Value toArgValue() { return _var != null ? _var.toValue() : _value; } public Value setValue(Value value) { Value oldValue = _value; _value = value; _var = null; return oldValue; } public Value set(Value value) { Value oldValue = _value; if (value instanceof Var) _var = (Var) value; else if (_var != null) _var.set(value); else _value = value; return oldValue; } /** * Converts to a variable reference (for function arguments) */ public Value toRef() { if (_var == null) _var = new Var(_value); return new RefVar(_var); } /** * Converts to a variable reference (for function arguments) */ public Value toArgRef() { if (_var == null) _var = new Var(_value); return new RefVar(_var); } public Value toArg() { if (_var == null) _var = new Var(_value); return _var; } public void varDumpImpl(Env env, WriteStream out, int depth, IdentityHashMap<Value, String> valueSet) throws IOException { printDepth(out, 2 * depth); out.print("["); if (_key instanceof StringValue) out.print("\"" + _key + "\""); else out.print(_key); out.println("]=>"); printDepth(out, 2 * depth); getRawValue().varDump(env, out, depth, valueSet); } protected void printRImpl(Env env, WriteStream out, int depth, IdentityHashMap<Value, String> valueSet) throws IOException { printDepth(out, 8 * depth); out.print(" ["); out.print(_key); out.print("] => "); if (getRawValue() != null) getRawValue().printR(env, out, depth + 1, valueSet); out.println(); } private void printDepth(WriteStream out, int depth) throws java.io.IOException { for (int i = depth; i > 0; i--) out.print(' '); } @Override public String toString() { return "ArrayValue.Entry[" + getKey() + "]"; } } /** * Returns the field keys. */ public Value []getKeyArray(Env env) { int len = getSize(); Value []keys = new Value[len]; Iterator<Value> iter = getKeyIterator(env); for (int i = 0; i < len; i++) { keys[i] = iter.next(); } return keys; } /** * Returns the field values. */ public Value []getValueArray(Env env) { int len = getSize(); Value []values = new Value[len]; Iterator<Value> iter = getValueIterator(env); for (int i = 0; i < len; i++) { values[i] = iter.next(); } return values; } /** * Takes the values of this array and puts them in a java array */ public Value[] keysToArray() { Value[] values = new Value[getSize()]; int i = 0; for (Entry ptr = getHead(); ptr != null; ptr = ptr.getNext()) { values[i++] = ptr.getKey(); } return values; } /** * Takes the values of this array and puts them in a java array */ public Value[] valuesToArray() { Value[] values = new Value[getSize()]; int i = 0; for (Entry ptr = getHead(); ptr != null; ptr = ptr.getNext()) { values[i++] = ptr.getValue(); } return values; } /** * Returns the keys. */ public Value getKeys() { return new ArrayValueImpl(keysToArray()); } /** * Returns the keys. */ public Value getValues() { return new ArrayValueImpl(valuesToArray()); } /** * Takes the values of this array, unmarshals them to objects of type * <i>elementType</i>, and puts them in a java array. */ @Override public <T> T[] valuesToArray(Env env, Class<T> elementType) { int size = getSize(); T[] array = T[].class.cast(Array.newInstance(elementType, size)); MarshalFactory factory = env.getModuleContext().getMarshalFactory(); Marshal elementMarshal = factory.create(elementType); int i = 0; for (Entry ptr = getHead(); ptr != null; ptr = ptr.getNext()) { Array.set(array, i++, elementMarshal.marshal(env, ptr.getValue(), elementType)); } return array; } @Override public int hashCode() { return getSize(); } @Override public boolean equals(Object o) { if (! (o instanceof ArrayValue)) return false; // toValue() for CopyArrayValue ArrayValue array = (ArrayValue) ((Value) o).toValue(); return cmp(array) == 0; } public class EntrySet extends AbstractSet<Map.Entry<Value,Value>> { EntrySet() { } @Override public int size() { return ArrayValue.this.getSize(); } @Override public Iterator<Map.Entry<Value,Value>> iterator() { return new EntryIterator(getHead()); } } public class KeySet extends AbstractSet<Value> { KeySet() { } @Override public int size() { return ArrayValue.this.getSize(); } @Override public Iterator<Value> iterator() { return new KeyIterator(getHead()); } } public class ValueCollection extends AbstractCollection<Value> { ValueCollection() { } @Override public int size() { return ArrayValue.this.getSize(); } @Override public Iterator<Value> iterator() { return new ValueIterator(getHead()); } } public static class EntryIterator implements Iterator<Map.Entry<Value,Value>> { private Entry _current; EntryIterator(Entry head) { _current = head; } public boolean hasNext() { return _current != null; } public Map.Entry<Value,Value> next() { if (_current != null) { Map.Entry<Value,Value> next = _current; _current = _current._next; return next; } else return null; } public void remove() { throw new UnsupportedOperationException(); } } public static class KeyIterator implements Iterator<Value> { private Entry _current; KeyIterator(Entry head) { _current = head; } public boolean hasNext() { return _current != null; } public Value next() { if (_current != null) { Value next = _current.getKey(); _current = _current._next; return next; } else return null; } public void remove() { throw new UnsupportedOperationException(); } } public static class ValueIterator implements Iterator<Value> { private Entry _current; ValueIterator(Entry head) { _current = head; } public boolean hasNext() { return _current != null; } public Value next() { if (_current != null) { Value next = _current.getValue(); _current = _current._next; return next; } else return null; } public void remove() { throw new UnsupportedOperationException(); } } public static class ValueComparator implements Comparator<Map.Entry<Value,Value>> { public static final ValueComparator CMP = new ValueComparator(); private ValueComparator() { } public int compare(Map.Entry<Value,Value> aEntry, Map.Entry<Value,Value> bEntry) { try { Value aValue = aEntry.getValue(); Value bValue = bEntry.getValue(); if (aValue.eq(bValue)) return 0; else if (aValue.lt(bValue)) return -1; else return 1; } catch (Throwable e) { throw new RuntimeException(e); } } } public static class KeyComparator implements Comparator<Map.Entry<Value,Value>> { public static final KeyComparator CMP = new KeyComparator(); private KeyComparator() { } public int compare(Map.Entry<Value,Value> aEntry, Map.Entry<Value,Value> bEntry) { try { Value aKey = aEntry.getKey(); Value bKey = bEntry.getKey(); if (aKey.eq(bKey)) return 0; else if (aKey.lt(bKey)) return -1; else return 1; } catch (Throwable e) { throw new RuntimeException(e); } } } public static abstract class AbstractGet { public abstract Value get(Map.Entry<Value, Value> entry); } public static class GetKey extends AbstractGet { public static final GetKey GET = new GetKey(); private GetKey() { } @Override public Value get(Map.Entry<Value, Value> entry) { return entry.getKey(); } } public static class GetValue extends AbstractGet { public static final GetValue GET = new GetValue(); private GetValue() { } @Override public Value get(Map.Entry<Value, Value> entry) { return entry.getValue(); } } }