package client.net.sf.saxon.ce.expr.instruct; import client.net.sf.saxon.ce.expr.*; import client.net.sf.saxon.ce.om.*; import client.net.sf.saxon.ce.pattern.NameTest; import client.net.sf.saxon.ce.trans.XPathException; import client.net.sf.saxon.ce.tree.util.URI; import client.net.sf.saxon.ce.type.*; import client.net.sf.saxon.ce.value.*; import client.net.sf.saxon.ce.value.StringValue; import java.util.HashMap; /** * The Bindery class holds information about variables and their values. From Saxon 8.1, it is * used only for global variables: local variables are now held in the XPathContext object. * * Variables are identified by a Binding object. Values will always be of class Value. */ public final class Bindery { private ValueRepresentation[] globals; // values of global variables and parameters private boolean[] busy; // set to true while variable is being evaluated private HashMap<StructuredQName, ValueRepresentation> globalParameters; // supplied global parameters /** * Define how many slots are needed for global variables * @param map the SlotManager that keeps track of slot allocation for global variables. */ public void allocateGlobals(SlotManager map) { int n = map.getNumberOfVariables()+1; globals = new ValueRepresentation[n]; busy = new boolean[n]; for (int i=0; i<n; i++) { globals[i] = null; busy[i] = false; } } /** * Define global parameters * @param params The ParameterSet passed in by the user, eg. from the command line */ public void defineGlobalParameters(HashMap<StructuredQName, ValueRepresentation> params) { globalParameters = params; } /** * Use global parameter. This is called when a global xsl:param element is processed. * If a parameter of the relevant name was supplied, it is bound to the xsl:param element. * Otherwise the method returns false, so the xsl:param default will be evaluated. * @param qName The name of the parameter * @param slot The slot number allocated to the parameter * @param requiredType The declared type of the parameter * @param context the XPath dynamic evaluation context * @return true if a parameter of this name was supplied, false if not */ public boolean useGlobalParameter(StructuredQName qName, int slot, SequenceType requiredType, XPathContext context) throws XPathException { if (globals != null && globals[slot] != null) { return true; } if (globalParameters==null) { return false; } Object obj = globalParameters.get(qName); if (obj==null) { return false; } // If the supplied value is a document node, and the document node has a systemID that is an absolute // URI, and the absolute URI does not already exist in the document pool, then register it in the document // pool, so that the document-uri() function will find it there, and so that a call on doc() will not // reload it. if (obj instanceof DocumentInfo) { String systemId = ((DocumentInfo)obj).getSystemId(); try { if (systemId != null && new URI(systemId, true).isAbsolute()) { DocumentPool pool = context.getController().getDocumentPool(); if (pool.find(systemId) == null) { pool.add(((DocumentInfo)obj), systemId); } } } catch (URI.URISyntaxException err) { // ignore it } } ValueRepresentation val = null; if (obj instanceof ValueRepresentation) { val = (ValueRepresentation)obj; } if (val==null) { val = EmptySequence.getInstance(); } val = applyFunctionConversionRules(val, requiredType, context); XPathException err = TypeChecker.testConformance(val, requiredType, context); if (err != null) { throw err; } globals[slot] = val; return true; } /** * Apply the function conversion rules to a value, given a required type. * @param value a value to be converted * @param requiredType the required type * @param context the conversion context * @return the converted value * @throws XPathException if the value cannot be converted to the required type */ public static Value applyFunctionConversionRules( ValueRepresentation value, SequenceType requiredType, final XPathContext context) throws XPathException { final TypeHierarchy th = context.getConfiguration().getTypeHierarchy(); final ItemType requiredItemType = requiredType.getPrimaryType(); ItemType suppliedItemType = (value instanceof NodeInfo ? new NameTest(((NodeInfo)value)) : ((Value)value).getItemType(th)); SequenceIterator iterator = Value.asIterator(value); if (requiredItemType.isAtomicType()) { // step 1: apply atomization if necessary if (!suppliedItemType.isAtomicType()) { iterator = Atomizer.getAtomizingIterator(iterator); suppliedItemType = suppliedItemType.getAtomizedItemType(); } // step 2: convert untyped atomic values to target item type if (th.relationship(suppliedItemType, BuiltInAtomicType.UNTYPED_ATOMIC) != TypeHierarchy.DISJOINT) { ItemMappingFunction converter = new ItemMappingFunction() { public Item mapItem(Item item) throws XPathException { if (item instanceof UntypedAtomicValue) { ConversionResult val = ((UntypedAtomicValue)item).convert( (AtomicType)requiredItemType, true); if (val instanceof ValidationFailure) { ValidationFailure vex = (ValidationFailure)val; throw vex.makeException(); } return (AtomicValue)val; } else { return item; } } }; iterator = new ItemMappingIterator(iterator, converter, true); } // step 3: apply numeric promotion if (requiredItemType.equals(BuiltInAtomicType.DOUBLE)) { ItemMappingFunction promoter = new ItemMappingFunction() { public Item mapItem(Item item) throws XPathException { if (item instanceof NumericValue) { return ((AtomicValue)item).convert( BuiltInAtomicType.DOUBLE, true).asAtomic(); } else { throw new XPathException( "Cannot promote non-numeric value to xs:double", "XPTY0004", context); } } }; iterator = new ItemMappingIterator(iterator, promoter, true); } else if (requiredItemType.equals(BuiltInAtomicType.FLOAT)) { ItemMappingFunction promoter = new ItemMappingFunction() { public Item mapItem(Item item) throws XPathException { if (item instanceof DoubleValue) { throw new XPathException( "Cannot promote xs:double value to xs:float", "XPTY0004", context); } else if (item instanceof NumericValue) { return ((AtomicValue)item).convert( BuiltInAtomicType.FLOAT, true).asAtomic(); } else { throw new XPathException( "Cannot promote non-numeric value to xs:float", "XPTY0004", context); } } }; iterator = new ItemMappingIterator(iterator, promoter, true); } // step 4: apply URI-to-string promotion if (requiredItemType.equals(BuiltInAtomicType.STRING) && th.relationship(suppliedItemType, BuiltInAtomicType.ANY_URI) != TypeHierarchy.DISJOINT) { ItemMappingFunction promoter = new ItemMappingFunction() { public Item mapItem(Item item) throws XPathException { if (item instanceof AnyURIValue) { return new StringValue(item.getStringValueCS()); } else { return item; } } }; iterator = new ItemMappingIterator(iterator, promoter, true); } } return Value.asValue(SequenceExtent.makeSequenceExtent(iterator)); } /** * Provide a value for a global variable * @param binding identifies the variable * @param value the value of the variable */ public void defineGlobalVariable(GlobalVariable binding, ValueRepresentation value) { globals[binding.getSlotNumber()] = value; } /** * Set/Unset a flag to indicate that a particular global variable is currently being * evaluated. Note that this code is not synchronized, so there is no absolute guarantee that * two threads will not both evaluate the same global variable; however, apart from wasted time, * it is harmless if they do. * @param binding the global variable in question * @return true if evaluation of the variable should proceed; false if it is found that the variable has now been * evaluated in another thread. * @throws client.net.sf.saxon.ce.trans.XPathException If an attempt is made to set the flag when it is already set, this means * the definition of the variable is circular. */ public boolean setExecuting(GlobalVariable binding) throws XPathException { int slot = binding.getSlotNumber(); if (busy[slot]) { // The global variable is being evaluated in this thread. This shouldn't happen, because // we have already tested for circularities. If it does happen, however, we fail cleanly. throw new XPathException.Circularity("Circular definition of variable " + binding.getVariableQName().getDisplayName()); } busy[slot] = false; return true; } /** * Indicate that a global variable is not currently being evaluated * @param binding the global variable */ public void setNotExecuting(GlobalVariable binding) { int slot = binding.getSlotNumber(); busy[slot] = false; } /** * Save the value of a global variable, and mark evaluation as complete. * @param binding the global variable in question * @param value the value that this thread has obtained by evaluating the variable * @return the value actually given to the variable. Exceptionally this will be different from the supplied * value if another thread has evaluated the same global variable concurrently. The returned value should be * used in preference, to ensure that all threads agree on the value. They could be different if for example * the variable is initialized using the collection() function. */ public synchronized ValueRepresentation saveGlobalVariableValue(GlobalVariable binding, ValueRepresentation value) { int slot = binding.getSlotNumber(); if (globals[slot] != null) { // another thread has already evaluated the value return globals[slot]; } else { busy[slot] = false; globals[slot] = value; return value; } } /** * Get the value of a global variable * @param binding the Binding that establishes the unique instance of the variable * @return the Value of the variable if defined, null otherwise. */ public ValueRepresentation getGlobalVariableValue(GlobalVariable binding) { return globals[binding.getSlotNumber()]; } /** * Get the value of a global variable whose slot number is known * @param slot the slot number of the required variable * @return the Value of the variable if defined, null otherwise. */ public ValueRepresentation getGlobalVariable(int slot) { return globals[slot]; } } // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. // If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. // This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.