package org.exist.xquery.functions.map; import com.github.krukow.clj_lang.IPersistentMap; import com.github.krukow.clj_lang.ITransientMap; import com.github.krukow.clj_lang.PersistentHashMap; import com.github.krukow.clj_lang.PersistentTreeMap; import org.exist.xquery.*; import org.exist.xquery.value.*; import java.util.Iterator; import java.util.Map; /** * Full implementation of the map type based on a persistent, * immutable tree map. */ public class MapType extends AbstractMapType { // underlying map: a persistent, immutable tree map private IPersistentMap<AtomicValue, Sequence> map; private int type = Type.ANY_TYPE; public MapType(XQueryContext context) throws XPathException { this(context, (String) null); } public MapType(XQueryContext context, String collation) throws XPathException { super(context); // if there's no collation, we'll use a hash map for better performance if (collation == null) {this.map = PersistentHashMap.EMPTY;} else {this.map = PersistentTreeMap.create(getComparator(collation), null);} } public MapType(XQueryContext context, String collation, AtomicValue key, Sequence value) throws XPathException { super(context); if (collation == null) {this.map = PersistentHashMap.EMPTY;} else {this.map = PersistentTreeMap.create(getComparator(collation), null);} this.type = key.getType(); this.map = this.map.assoc(key, value); } protected MapType(XQueryContext context, IPersistentMap<AtomicValue, Sequence> other, int type) { super(context); this.map = other; this.type = type; } public void add(AbstractMapType other) { if (other.size() == 1) { setKeyType(other.getKey().getType()); map = map.assoc(other.getKey(), other.getValue()); } else if (other.size() > 0) { setKeyType(other.getKeyType()); if (map instanceof PersistentHashMap) { ITransientMap<AtomicValue, Sequence> tmap = ((PersistentHashMap)map).asTransient(); for (final Map.Entry<AtomicValue, Sequence> entry : other) { tmap = tmap.assoc(entry.getKey(), entry.getValue()); } map = tmap.persistentMap(); } else { for (final Map.Entry<AtomicValue, Sequence> entry : other) map = map.assoc(entry.getKey(), entry.getValue()); } } } public void add(AtomicValue key, Sequence value) { setKeyType(key.getType()); this.map = this.map.assoc(key, value); } public Sequence get(AtomicValue key) { key = convert(key); if (key == null) {return Sequence.EMPTY_SEQUENCE;} final Map.Entry<AtomicValue, Sequence> e = this.map.entryAt(key); return e == null ? Sequence.EMPTY_SEQUENCE : e.getValue(); } @Override public AbstractMapType put(AtomicValue key, final Sequence value) throws XPathException { return new MapType(this.context, this.map.assoc(key, value), type); } public boolean contains(AtomicValue key) { key = convert(key); if (key == null) {return false;} return this.map.containsKey(key); } public Sequence keys() { final ValueSequence seq = new ValueSequence(); for (final Map.Entry<AtomicValue, Sequence> entry: this.map) { seq.add(entry.getKey()); } return seq; } public AbstractMapType remove(AtomicValue key) { try { return new MapType(this.context, this.map.without(key), type); } catch (final Exception e) { return this; } } @Override public int size() { return map.count(); } @Override public Iterator<Map.Entry<AtomicValue, Sequence>> iterator() { return map.iterator(); } @Override public AtomicValue getKey() { if (map.count() == 0) {return null;} final Iterator<Map.Entry<AtomicValue,Sequence>> iter = this.map.iterator(); return iter.next().getKey(); } @Override public Sequence getValue() { return mapToSequence(this.map); } /** * Get a Sequence from an internal map representation */ private Sequence mapToSequence(final IPersistentMap<AtomicValue, Sequence> map) { if (map.count() == 0) { return null; } final Iterator<Map.Entry<AtomicValue,Sequence>> iter = map.iterator(); return iter.next().getValue(); } private void setKeyType(int newType) { if (type == Type.ANY_TYPE) {type = newType;} else if (type != newType) { type = Type.ITEM; if (map instanceof PersistentHashMap) { try { PersistentTreeMap tmap = PersistentTreeMap.create(getComparator(null), null); for (final Map.Entry<AtomicValue, Sequence> entry : map) { tmap = tmap.assoc(entry.getKey(), entry.getValue()); } map = tmap; } catch (final XPathException e) { } } } } private AtomicValue convert(AtomicValue key) { if (type != Type.ANY_TYPE && type != Type.ITEM) { try { return key.convertTo(type); } catch (final XPathException e) { return null; } } return key; } @Override public int getKeyType() { return type; } }