package org.exist.xquery.functions.map; import org.exist.dom.QName; import org.exist.xquery.*; import org.exist.xquery.functions.fn.FunDistinctValues; import org.exist.xquery.value.*; import java.text.Collator; import java.util.Comparator; import java.util.List; import java.util.Map; /** * Abstract base class for map types. A map item is also a function item. This class thus extends * {@link FunctionReference} to allow the item to be called in a dynamic function * call. * * @author Wolfgang Meier */ public abstract class AbstractMapType extends FunctionReference implements Map.Entry<AtomicValue, Sequence>, Iterable<Map.Entry<AtomicValue, Sequence>>, Lookup.LookupSupport { private static final Comparator<AtomicValue> DEFAULT_COMPARATOR = new FunDistinctValues.ValueComparator(null); // the signature of the function which is evaluated if the map is called as a function item private static final FunctionSignature ACCESSOR = new FunctionSignature( new QName("get", MapModule.NAMESPACE_URI, MapModule.PREFIX), "Internal accessor function for maps.", new SequenceType[]{ new FunctionParameterSequenceType("key", Type.ATOMIC, Cardinality.EXACTLY_ONE, "The key to look up") }, new SequenceType(Type.ITEM, Cardinality.ZERO_OR_MORE)); private InternalFunctionCall accessorFunc = null; protected XQueryContext context; public AbstractMapType(XQueryContext context) { super(null); this.context = context; } public abstract Sequence get(AtomicValue key); public abstract AbstractMapType put(AtomicValue key, Sequence value) throws XPathException; public abstract boolean contains(AtomicValue key); public abstract Sequence keys(); public abstract AbstractMapType remove(AtomicValue key); public abstract int getKeyType(); public abstract int size(); @Override public int getItemType() { return Type.MAP; } @Override public int getType() { return Type.MAP; } @Override public Sequence setValue(Sequence value) { throw new UnsupportedOperationException(); } @Override public void analyze(AnalyzeContextInfo contextInfo) throws XPathException { getAccessorFunc().analyze(contextInfo); } @Override public Sequence eval(Sequence contextSequence) throws XPathException { return getAccessorFunc().eval(contextSequence); } @Override public void setArguments(List<Expression> arguments) throws XPathException { getAccessorFunc().setArguments(arguments); } @Override public void resetState(boolean postOptimization) { getAccessorFunc().resetState(postOptimization); } protected Comparator<AtomicValue> getComparator(String collation) throws XPathException { if (collation != null) { final Collator collator = this.context.getCollator(collation); return new FunDistinctValues.ValueComparator(collator); } return DEFAULT_COMPARATOR; } /** * Return the accessor function. Will be created on demand. */ protected InternalFunctionCall getAccessorFunc() { initFunction(); return accessorFunc; } /** * Lazy initialization of the accessor function. Creating * this for every map would be too expensive and we thus * only instantiate it on demand. */ protected void initFunction() { if (this.accessorFunc != null) {return;} final Function fn = new AccessorFunc(this.context); this.accessorFunc = new InternalFunctionCall(fn); } /** * The accessor function which will be evaluated if the map is called * as a function item. */ private class AccessorFunc extends BasicFunction { public AccessorFunc(XQueryContext context) { super(context, ACCESSOR); } public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathException { return AbstractMapType.this.get((AtomicValue) args[0].itemAt(0)); } } }