Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2013 Saxonica Limited.
// 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.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
package net.sf.saxon.trans;
import net.sf.saxon.Configuration;
import net.sf.saxon.Controller;
import net.sf.saxon.expr.*;
import net.sf.saxon.expr.instruct.SlotManager;
import net.sf.saxon.expr.sort.LocalOrderComparer;
import net.sf.saxon.functions.StringFn;
import net.sf.saxon.functions.SystemFunctionCall;
import net.sf.saxon.functions.Tokenize;
import net.sf.saxon.lib.ConversionRules;
import net.sf.saxon.lib.StringCollator;
import net.sf.saxon.om.*;
import net.sf.saxon.pattern.IdrefTest;
import net.sf.saxon.pattern.PatternFinder;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.tree.iter.*;
import net.sf.saxon.tree.iter.ListIterator;
import net.sf.saxon.type.*;
import net.sf.saxon.value.AtomicValue;
import net.sf.saxon.value.DoubleValue;
import net.sf.saxon.value.NumericValue;
import net.sf.saxon.value.UntypedAtomicValue;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.*;
/**
* KeyManager manages the set of key definitions in a stylesheet, and the indexes
* associated with these key definitions. It handles xsl:sort-key as well as xsl:key
* definitions.
*
*
The memory management in this class is subtle, with extensive use of weak references.
* The idea is that an index should continue to exist in memory so long as both the compiled
* stylesheet and the source document exist in memory: if either is removed, the index should
* go too. The document itself holds no reference to the index. The compiled stylesheet (which
* owns the KeyManager) holds a weak reference to the index. The index, of course, holds strong
* references to the nodes in the document. The Controller holds a strong reference to the
* list of indexes used for each document, so that indexes remain in memory for the duration
* of a transformation even if the documents themselves are garbage collected.
*
*
Potentially there is a need for more than one index for a given key name, depending
* on the primitive type of the value provided to the key() function. An index is built
* corresponding to the type of the requested value; if subsequently the key() function is
* called with the same name and a different type of value, then a new index is built.
*
*
For XSLT-defined keys, equality matching follows the rules of the eq operator, which means
* that untypedAtomic values are treated as strings. In backwards compatibility mode, all
* values are converted to strings.
*
*
This class is also used for internal indexes constructed (a) to support the idref() function,
* and (b) (in Saxon-EE only) to support filter expressions of the form /a/b/c[d=e], where the
* path expression being filtered must be a single-document context-free path rooted at a document node,
* where exactly one of d and e must be dependent on the focus, and where certain other conditions apply
* such as the filter predicate not being positional. The operator in this case may be either "=" or "eq".
* If it is "eq", then the semantics are very similar to xsl:key indexes, except that use of non-comparable
* types gives an error rather than a non-match. If the operator is "=", however, then the rules for
* handling untypedAtomic values are different: these must be converted to the type of the other operand.
* In this situation the following rules apply. Assume that the predicate is [use=value], where use is
* dependent on the focus (the indexed value), and value is the sought value.
*
*
*
If value is a type other than untypedAtomic, say T, then we build an index for type T, in which any
* untypedAtomic values that arise in evaluating "use" are converted to type T. A conversion failure results
* in an error. A value of a type that is not comparable to T also results in an error.
*
If value is untypedAtomic, then we build an index for every type actually encountered in evaluating
* the use expression (treating untypedAtomic as string), and then search each of these indexes. (Note that
* it is not an error if the use expression returns a mixture of say numbers and dates, provided that the
* sought value is untypedAtomic).
*
*
* @author Michael H. Kay
*/
public class KeyManager implements Serializable {
private HashMap keyMap;
// one entry for each named key; the entry contains
// a KeyDefinitionSet holding the key definitions with that name
private transient WeakHashMap>> docIndexes;
// one entry for each document that is in memory;
// the entry contains a HashMap mapping the fingerprint of
// the key name plus the primitive item type
// to the HashMap that is the actual index
// of key/value pairs.
/**
* Create a KeyManager and initialise variables
* @param config the Saxon configuration
*/
public KeyManager(Configuration config) {
keyMap = new HashMap(10);
docIndexes = new WeakHashMap>>(10);
// Create a key definition for the idref() function
registerIdrefKey(config);
}
/**
* An internal key definition is used to support the idref() function. The key definition
* is equivalent to xsl:key match="element(*, xs:IDREF) | element(*, IDREFS) |
* attribute(*, xs:IDREF) | attribute(*, IDREFS)" use="tokenize(string(.))". This method creates this
* key definition.
* @param config The configuration. This is needed because the patterns that are
* generated need access to schema information.
*/
private void registerIdrefKey(Configuration config) {
PatternFinder idref = IdrefTest.getInstance();
StringFn sf = (StringFn) SystemFunctionCall.makeSystemFunction(
"string", new Expression[]{new ContextItemExpression()});
StringLiteral regex = new StringLiteral("\\s+");
Tokenize use = (Tokenize) SystemFunctionCall.makeSystemFunction("tokenize", new Expression[]{sf, regex});
KeyDefinition key = new KeyDefinition(idref, use, null, null);
key.setIndexedItemType(BuiltInAtomicType.STRING);
try {
addKeyDefinition(StandardNames.getStructuredQName(StandardNames.XS_IDREFS), key, config);
} catch (XPathException err) {
throw new AssertionError(err); // shouldn't happen
}
}
/**
* Pre-register a key definition. This simply registers that a key with a given name exists,
* without providing any details.
* @param keyName the name of the key to be pre-registered
*/
public void preRegisterKeyDefinition(StructuredQName keyName) {
KeyDefinitionSet keySet = keyMap.get(keyName);
if (keySet==null) {
keySet = new KeyDefinitionSet(keyName, keyMap.size());
keyMap.put(keyName, keySet);
}
}
/**
* Register a key definition. Note that multiple key definitions with the same name are
* allowed
* @param keyName Structured QName representing the name of the key
* @param keydef The details of the key's definition
* @param config The configuration
* @throws XPathException if this key definition is inconsistent with existing key definitions having the same name
*/
public void addKeyDefinition(StructuredQName keyName, KeyDefinition keydef, Configuration config) throws XPathException {
KeyDefinitionSet keySet = keyMap.get(keyName);
if (keySet==null) {
keySet = new KeyDefinitionSet(keyName, keyMap.size());
keyMap.put(keyName, keySet);
}
keySet.addKeyDefinition(keydef);
boolean backwardsCompatible = keySet.isBackwardsCompatible();
if (backwardsCompatible) {
// In backwards compatibility mode, convert all the use-expression results to sequences of strings
List v = keySet.getKeyDefinitions();
for (KeyDefinition kd : v) {
kd.setBackwardsCompatible(true);
if (!kd.getBody().getItemType(config.getTypeHierarchy()).equals(BuiltInAtomicType.STRING)) {
Expression exp = new AtomicSequenceConverter(kd.getBody(), BuiltInAtomicType.STRING);
((AtomicSequenceConverter) exp).allocateConverter(config, false);
kd.setBody(exp);
}
}
}
}
/**
* Get all the key definitions that match a particular name
* @param qName The name of the required key
* @return The set of key definitions of the named key if there are any, or null otherwise.
*/
public KeyDefinitionSet getKeyDefinitionSet(StructuredQName qName) {
return keyMap.get(qName);
}
/**
* Build the index for a particular document for a named key
* @param keySet The set of key definitions with this name
* @param itemType the type of the values to be indexed.
* @param foundItemTypes Optional (may be null). If supplied, a set that is to be populated with
* the set of primitive types actually found among the "use" values.
* @param doc The source document in question
* @param context The dynamic context
* @return the index in question, as a Map mapping a key value onto a ArrayList of nodes
* @throws XPathException if a dynamic error is encountered
*/
private synchronized Map