package org.exist.xquery.functions.map; import org.exist.dom.QName; import org.exist.xquery.*; import org.exist.xquery.value.*; import java.util.Map; /** * Implements all functions of the map module. */ public class MapFunction extends BasicFunction { private static final QName QN_MERGE = new QName("merge", MapModule.NAMESPACE_URI, MapModule.PREFIX); private static final QName QN_SIZE = new QName("size", MapModule.NAMESPACE_URI, MapModule.PREFIX); private static final QName QN_ENTRY = new QName("entry", MapModule.NAMESPACE_URI, MapModule.PREFIX); private static final QName QN_GET = new QName("get", MapModule.NAMESPACE_URI, MapModule.PREFIX); private static final QName QN_PUT = new QName("put", MapModule.NAMESPACE_URI, MapModule.PREFIX); private static final QName QN_CONTAINS = new QName("contains", MapModule.NAMESPACE_URI, MapModule.PREFIX); private static final QName QN_KEYS = new QName("keys", MapModule.NAMESPACE_URI, MapModule.PREFIX); private static final QName QN_REMOVE = new QName("remove", MapModule.NAMESPACE_URI, MapModule.PREFIX); private static final QName QN_FOR_EACH = new QName("for-each", MapModule.NAMESPACE_URI, MapModule.PREFIX); @Deprecated private static final QName QN_NEW = new QName("new", MapModule.NAMESPACE_URI, MapModule.PREFIX); @Deprecated private static final QName QN_FOR_EACH_ENTRY = new QName("for-each-entry", MapModule.NAMESPACE_URI, MapModule.PREFIX); public final static FunctionSignature FNS_MERGE = new FunctionSignature( QN_MERGE, "Returns a map that combines the entries from a number of existing maps.", new SequenceType[] { new FunctionParameterSequenceType("maps", Type.MAP, Cardinality.ZERO_OR_MORE, "Existing maps to merge to create a new map.") }, new SequenceType(Type.MAP, Cardinality.EXACTLY_ONE) ); public final static FunctionSignature FNS_SIZE = new FunctionSignature( QN_SIZE, "Returns the number of entries in the supplied map.", new SequenceType[] { new FunctionParameterSequenceType("input", Type.MAP, Cardinality.EXACTLY_ONE, "Any map to determine the size of.") }, new SequenceType(Type.INTEGER, Cardinality.EXACTLY_ONE) ); public final static FunctionSignature FNS_KEYS = new FunctionSignature( QN_KEYS, "Returns a sequence containing all the key values present in a map.", new SequenceType[]{ new FunctionParameterSequenceType(MapModule.PREFIX, Type.MAP, Cardinality.EXACTLY_ONE, "The map") }, new SequenceType(Type.ATOMIC, Cardinality.ZERO_OR_MORE) ); public final static FunctionSignature FNS_CONTAINS = new FunctionSignature( QN_CONTAINS, "Tests whether a supplied map contains an entry for a given key.", new SequenceType[] { new FunctionParameterSequenceType(MapModule.PREFIX, Type.MAP, Cardinality.EXACTLY_ONE, "The map"), new FunctionParameterSequenceType("key", Type.ATOMIC, Cardinality.EXACTLY_ONE, "The key to look up") }, new SequenceType(Type.BOOLEAN, Cardinality.EXACTLY_ONE) ); public final static FunctionSignature FNS_GET = new FunctionSignature( QN_GET, "Returns the value associated with a supplied key in a given map.", new SequenceType[] { new FunctionParameterSequenceType(MapModule.PREFIX, Type.MAP, Cardinality.EXACTLY_ONE, "The map"), new FunctionParameterSequenceType("key", Type.ATOMIC, Cardinality.EXACTLY_ONE, "The key to look up") }, new SequenceType(Type.ITEM, Cardinality.ZERO_OR_MORE) ); public final static FunctionSignature FNS_PUT = new FunctionSignature( QN_PUT, "Returns a map containing all the contents of the supplied map, but with an additional entry, which replaces any existing entry for the same key.", new SequenceType[] { new FunctionParameterSequenceType(MapModule.PREFIX, Type.MAP, Cardinality.EXACTLY_ONE, "The map"), new FunctionParameterSequenceType("key", Type.ATOMIC, Cardinality.EXACTLY_ONE, "The key for the entry to insert"), new FunctionParameterSequenceType("value", Type.ITEM, Cardinality.ZERO_OR_MORE, "The value for the entry to insert") }, new SequenceType(Type.MAP, Cardinality.EXACTLY_ONE) ); public final static FunctionSignature FNS_ENTRY = new FunctionSignature( QN_ENTRY, "Creates a map that contains a single entry (a key-value pair).", new SequenceType[] { new FunctionParameterSequenceType("key", Type.ATOMIC, Cardinality.EXACTLY_ONE, "The key"), new FunctionParameterSequenceType("value", Type.ITEM, Cardinality.ZERO_OR_MORE, "The associated value") }, new SequenceType(Type.MAP, Cardinality.EXACTLY_ONE) ); public final static FunctionSignature FNS_REMOVE = new FunctionSignature( QN_REMOVE, "Constructs a new map by removing an entry from an existing map.", new SequenceType[] { new FunctionParameterSequenceType(MapModule.PREFIX, Type.MAP, Cardinality.EXACTLY_ONE, "The map"), new FunctionParameterSequenceType("key", Type.STRING, Cardinality.EXACTLY_ONE, "The key to remove") }, new SequenceType(Type.MAP, Cardinality.EXACTLY_ONE) ); public final static FunctionSignature FNS_FOR_EACH = new FunctionSignature( QN_FOR_EACH, "takes any map as its $input argument and applies the supplied function to each entry in the map, in implementation-dependent order; the result is the sequence obtained by concatenating the results of these function calls. " + "The function supplied as $action takes two arguments. It is called supplying the key of the map entry as the first argument, and the associated value as the second argument.", new SequenceType[] { new FunctionParameterSequenceType("input", Type.MAP, Cardinality.EXACTLY_ONE, "The map"), new FunctionParameterSequenceType("action", Type.FUNCTION_REFERENCE, Cardinality.EXACTLY_ONE, "The function to be called for each entry") }, new SequenceType(Type.ITEM, Cardinality.ZERO_OR_MORE) ); /* Deprecated below */ @Deprecated public final static FunctionSignature FNS_NEW_0 = new FunctionSignature( QN_NEW, "Constructs and returns an empty map whose collation is the default collation in the static context.", null, new SequenceType(Type.MAP, Cardinality.EXACTLY_ONE), "Use the computer map constructor `map {}` instead." ); @Deprecated public final static FunctionSignature FNS_NEW_N = new FunctionSignature( QN_NEW, "Constructs and returns an empty map whose collation is the default collation in the static context.", new SequenceType[] { new FunctionParameterSequenceType("maps", Type.MAP, Cardinality.ZERO_OR_MORE, "Existing maps to combine into the new map.") }, new SequenceType(Type.MAP, Cardinality.EXACTLY_ONE), FNS_MERGE ); @Deprecated public final static FunctionSignature FNS_NEW_N_COLLATION = new FunctionSignature( QN_NEW, "Constructs and returns an empty map whose collation is given in the second argument.", new SequenceType[] { new FunctionParameterSequenceType("maps", Type.MAP, Cardinality.ZERO_OR_MORE, "Existing maps to combine into the new map."), new FunctionParameterSequenceType("collation", Type.STRING, Cardinality.EXACTLY_ONE, "The collation to use for the new map.") }, new SequenceType(Type.MAP, Cardinality.EXACTLY_ONE), FNS_MERGE ); @Deprecated public final static FunctionSignature FNS_FOR_EACH_ENTRY = new FunctionSignature( QN_FOR_EACH_ENTRY, "takes any map as its $input argument and applies the supplied function to each entry in the map, in implementation-dependent order; the result is the sequence obtained by concatenating the results of these function calls. " + "The function supplied as $action takes two arguments. It is called supplying the key of the map entry as the first argument, and the associated value as the second argument.", new SequenceType[] { new FunctionParameterSequenceType("input", Type.MAP, Cardinality.EXACTLY_ONE, "The map"), new FunctionParameterSequenceType("action", Type.FUNCTION_REFERENCE, Cardinality.EXACTLY_ONE, "The function to be called for each entry") }, new SequenceType(Type.ITEM, Cardinality.ZERO_OR_MORE), FNS_FOR_EACH ); private AnalyzeContextInfo cachedContextInfo; public MapFunction(final XQueryContext context, final FunctionSignature signature) { super(context, signature); } @Override public void analyze(final AnalyzeContextInfo contextInfo) throws XPathException { cachedContextInfo = new AnalyzeContextInfo(contextInfo); super.analyze(contextInfo); } public Sequence eval(final Sequence[] args, final Sequence contextSequence) throws XPathException { if (isCalledAs(QN_NEW.getLocalPart())) { return newMap(args); } else if (isCalledAs(QN_MERGE.getLocalPart())) { return merge(args); } else if (isCalledAs(QN_SIZE.getLocalPart())) { return size(args); } else if (isCalledAs(QN_KEYS.getLocalPart())) { return keys(args); } else if (isCalledAs(QN_CONTAINS.getLocalPart())) { return contains(args); } else if (isCalledAs(QN_GET.getLocalPart())) { return get(args); } else if (isCalledAs(QN_PUT.getLocalPart())) { return put(args); } else if (isCalledAs(QN_ENTRY.getLocalPart())) { return entry(args); } else if (isCalledAs(QN_REMOVE.getLocalPart())) { return remove(args); } else if (isCalledAs(QN_FOR_EACH.getLocalPart()) || isCalledAs(QN_FOR_EACH_ENTRY.getLocalPart())) { return forEach(args); } return null; } private Sequence remove(final Sequence[] args) { final AbstractMapType map = (AbstractMapType) args[0].itemAt(0); return map.remove((AtomicValue) args[1].itemAt(0)); } private Sequence keys(final Sequence[] args) { final AbstractMapType map = (AbstractMapType) args[0].itemAt(0); return map.keys(); } private Sequence contains(final Sequence[] args) { final AbstractMapType map = (AbstractMapType) args[0].itemAt(0); return BooleanValue.valueOf(map.contains((AtomicValue) args[1].itemAt(0))); } private Sequence get(final Sequence[] args) { final AbstractMapType map = (AbstractMapType) args[0].itemAt(0); return map.get((AtomicValue) args[1].itemAt(0)); } private Sequence put(final Sequence[] args) throws XPathException { final AbstractMapType map = (AbstractMapType) args[0].itemAt(0); return map.put((AtomicValue) args[1].itemAt(0), args[2]); } private Sequence entry(final Sequence[] args) throws XPathException { final AtomicValue key = (AtomicValue) args[0].itemAt(0); return new SingleKeyMapType(this.context, null, key, args[1]); } private Sequence size(final Sequence[] args) throws XPathException { final AbstractMapType map = (AbstractMapType) args[0].itemAt(0); return new IntegerValue(map.size(), Type.INTEGER); } private Sequence merge(final Sequence[] args) throws XPathException { if (args.length == 0) { return new MapType(this.context); } final MapType map = new MapType(this.context, null); for (final SequenceIterator i = args[0].unorderedIterator(); i.hasNext(); ) { final AbstractMapType m = (AbstractMapType) i.nextItem(); map.add(m); } return map; } @Deprecated private Sequence newMap(final Sequence[] args) throws XPathException { if (args.length == 0) { return new MapType(this.context); } String collation = null; if (args.length == 2) { collation = args[1].getStringValue(); } final MapType map = new MapType(this.context, collation); for (final SequenceIterator i = args[0].unorderedIterator(); i.hasNext(); ) { final AbstractMapType m = (AbstractMapType) i.nextItem(); map.add(m); } return map; } private Sequence forEach(final Sequence[] args) throws XPathException { final AbstractMapType map = (AbstractMapType) args[0].itemAt(0); final FunctionReference ref = (FunctionReference) args[1].itemAt(0); ref.analyze(cachedContextInfo); final ValueSequence result = new ValueSequence(); for (final Map.Entry<AtomicValue, Sequence> entry : map) { final Sequence s = ref.evalFunction(null, null, new Sequence[] { entry.getKey(), entry.getValue() }); result.addAll(s); } return result; } }