package net.sf.saxon.sxpath;
import net.sf.saxon.Configuration;
import net.sf.saxon.expr.Container;
import net.sf.saxon.expr.VariableReference;
import net.sf.saxon.expr.Expression;
import net.sf.saxon.instruct.SlotManager;
import net.sf.saxon.om.*;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.Type;
import net.sf.saxon.value.QNameValue;
import java.io.Serializable;
import java.util.*;
/**
* An IndependentContext provides a context for parsing an XPath expression appearing
* in a context other than a stylesheet.
*
* <p>This class is used in a number of places where freestanding XPath expressions occur.
* These include the native Saxon XPath API, the .NET XPath API, XPath expressions used
* in XML Schema identity constraints, and XPath expressions supplied to saxon:evaluate().
* It is not used by the JAXP XPath API (though it shares code with that API through
* the common superclass AbstractStaticContext).</p>
*
* <p>This class currently provides no mechanism for binding user-defined functions.</p>
*/
public class IndependentContext extends AbstractStaticContext
implements XPathStaticContext, NamespaceResolver, Serializable, Container {
private HashMap<String, String> namespaces = new HashMap<String, String>(10);
private HashMap<StructuredQName, XPathVariable> variables = new HashMap<StructuredQName, XPathVariable>(20);
private NamespaceResolver externalResolver = null;
private Set importedSchemaNamespaces = new HashSet();
private boolean autoDeclare = false;
/**
* Create an IndependentContext along with a new (non-schema-aware) Saxon Configuration
*/
public IndependentContext() {
this(new Configuration());
}
/**
* Create an IndependentContext using a specific Configuration
* @param config the Saxon configuration to be used
*/
public IndependentContext(Configuration config) {
setConfiguration(config);
clearNamespaces();
setDefaultFunctionLibrary();
}
/**
* Get the granularity of the container.
* @return 0 for a temporary container created during parsing; 1 for a container
* that operates at the level of an XPath expression; 2 for a container at the level
* of a global function or template
*/
public int getContainerGranularity() {
return 1;
}
/**
* Create a copy of this IndependentContext. All aspects of the context are copied
* except for declared variables.
* @return the new copy
*/
public IndependentContext copy() {
IndependentContext ic = new IndependentContext(getConfiguration());
ic.setBaseURI(getBaseURI());
ic.setLocationMap(getLocationMap());
ic.setDefaultElementNamespace(getDefaultElementNamespace());
ic.setDefaultFunctionNamespace(getDefaultFunctionNamespace());
ic.namespaces = new HashMap<String, String>(namespaces);
ic.variables = new HashMap<StructuredQName, XPathVariable>(10);
ic.importedSchemaNamespaces = importedSchemaNamespaces;
ic.externalResolver = externalResolver;
ic.autoDeclare = autoDeclare;
return ic;
}
/**
* Declare a namespace whose prefix can be used in expressions
* @param prefix The namespace prefix. Must not be null. Supplying "" sets the
* default element namespace.
* @param uri The namespace URI. Must not be null.
*/
public void declareNamespace(String prefix, String uri) {
if (prefix==null) {
throw new NullPointerException("Null prefix supplied to declareNamespace()");
}
if (uri==null) {
throw new NullPointerException("Null namespace URI supplied to declareNamespace()");
}
if ("".equals(prefix)) {
setDefaultElementNamespace(uri);
} else {
namespaces.put(prefix, uri);
getNamePool().allocateNamespaceCode(prefix, uri);
}
}
/**
* Clear all the declared namespaces, except for the standard ones (xml, xslt, saxon, xdt).
* This also resets the default element namespace to the "null" namespace
*/
public void clearNamespaces() {
namespaces.clear();
declareNamespace("xml", NamespaceConstant.XML);
declareNamespace("xsl", NamespaceConstant.XSLT);
declareNamespace("saxon", NamespaceConstant.SAXON);
declareNamespace("xs", NamespaceConstant.SCHEMA);
declareNamespace("", "");
}
/**
* Clear all the declared namespaces, including the standard ones (xml, xslt, saxon).
* Leave only the XML namespace and the default namespace (xmlns="").
* This also resets the default element namespace to the "null" namespace.
*/
public void clearAllNamespaces() {
namespaces.clear();
declareNamespace("xml", NamespaceConstant.XML);
declareNamespace("", "");
}
/**
* Declares all the namespaces that are in-scope for a given node, removing all previous
* namespace declarations.
* In addition, the standard namespaces (xml, xslt, saxon) are declared. This method also
* sets the default element namespace to be the same as the default namespace for this node.
* @param node The node whose in-scope namespaces are to be used as the context namespaces.
* If the node is an attribute, text node, etc, then the namespaces of its parent element are used.
*/
public void setNamespaces(NodeInfo node) {
namespaces.clear();
int kind = node.getNodeKind();
if (kind == Type.ATTRIBUTE || kind == Type.TEXT ||
kind == Type.COMMENT || kind == Type.PROCESSING_INSTRUCTION ||
kind == Type.NAMESPACE) {
node = node.getParent();
}
if (node == null) {
return;
}
AxisIterator iter = node.iterateAxis(Axis.NAMESPACE);
while (true) {
NodeInfo ns = (NodeInfo)iter.next();
if (ns == null) {
return;
}
String prefix = ns.getLocalPart();
if ("".equals(prefix)) {
setDefaultElementNamespace(ns.getStringValue());
} else {
declareNamespace(ns.getLocalPart(), ns.getStringValue());
}
}
}
/**
* Set an external namespace resolver. If this is set, then all resolution of namespace
* prefixes is delegated to the external namespace resolver, and namespaces declared
* individually on this IndependentContext object are ignored.
* @param resolver the external NamespaceResolver
*/
public void setNamespaceResolver(NamespaceResolver resolver) {
externalResolver = resolver;
}
/**
* Say whether undeclared variables are allowed. By default, they are not allowed. When
* undeclared variables are allowed, it is not necessary to predeclare the variables that
* may be used in the XPath expression; instead, a variable is automatically declared when a reference
* to the variable is encountered within the expression.
* @param allow true if undeclared variables are allowed, false if they are not allowed.
* @since 9.2
*/
public void setAllowUndeclaredVariables(boolean allow) {
autoDeclare = allow;
}
/**
* Ask whether undeclared variables are allowed. By default, they are not allowed. When
* undeclared variables are allowed, it is not necessary to predeclare the variables that
* may be used in the XPath expression; instead, a variable is automatically declared when a reference
* to the variable is encountered within the expression.
* @return true if undeclared variables are allowed, false if they are not allowed.
* @since 9.2
*/
public boolean isAllowUndeclaredVariables() {
return autoDeclare;
}
/**
* Declare a variable. A variable must be declared before an expression referring
* to it is compiled. The initial value of the variable will be the empty sequence
* @param qname The name of the variable
* @return an XPathVariable object representing information about the variable that has been
* declared.
*/
public XPathVariable declareVariable(QNameValue qname) {
return declareVariable(qname.toStructuredQName());
}
/**
* Declare a variable. A variable must be declared before an expression referring
* to it is compiled. The initial value of the variable will be the empty sequence
* @param namespaceURI The namespace URI of the name of the variable. Supply "" to represent
* names in no namespace (null is also accepted)
* @param localName The local part of the name of the variable (an NCName)
* @return an XPathVariable object representing information about the variable that has been
* declared.
*/
public XPathVariable declareVariable(String namespaceURI, String localName) {
StructuredQName qName = new StructuredQName("", namespaceURI, localName);
return declareVariable(qName);
}
/**
* Declare a variable. A variable must be declared before an expression referring
* to it is compiled. The initial value of the variable will be the empty sequence
* @param qName the name of the variable.
* @return an XPathVariable object representing information about the variable that has been
* declared.
* @since 9.2
*/
public XPathVariable declareVariable(StructuredQName qName) {
XPathVariable var = variables.get(qName);
if (var != null) {
return var;
} else {
var = XPathVariable.make(qName);
int slot = variables.size();
var.setSlotNumber(slot);
variables.put(qName, var);
return var;
}
}
/**
* Get an iterator over all the variables that have been declared, either explicitly by an
* application call on declareVariable(), or implicitly if the option <code>allowUndeclaredVariables</code>
* is set.
* @return an iterator; the objects returned by this iterator will be instances of XPathVariable
* @since 9.2
*/
public Iterator<XPathVariable> iterateExternalVariables() {
return variables.values().iterator();
}
/**
* Get the declared variable with a given name, if there is one
* @param qName the name of the required variable
* @return the explicitly or implicitly declared variable with this name if it exists,
* or null otherwise
* @since 9.2
*/
public XPathVariable getExternalVariable(StructuredQName qName) {
return variables.get(qName);
}
/**
* Get the slot number allocated to a particular variable
* @param qname the name of the variable
* @return the slot number, or -1 if the variable has not been declared
*/
public int getSlotNumber(QNameValue qname) {
StructuredQName sq = qname.toStructuredQName();
XPathVariable var = variables.get(sq);
if (var == null) {
return -1;
}
return var.getLocalSlotNumber();
}
/**
* Get the URI for a prefix, using the declared namespaces as
* the context for namespace resolution. The default namespace is NOT used
* when the prefix is empty.
* This method is provided for use by the XPath parser.
* @param prefix The prefix
* @throws net.sf.saxon.trans.XPathException if the prefix is not declared
*/
public String getURIForPrefix(String prefix) throws XPathException {
String uri = getURIForPrefix(prefix, false);
if (uri==null) {
throw new XPathException("Prefix " + prefix + " has not been declared");
}
return uri;
}
public NamespaceResolver getNamespaceResolver() {
if (externalResolver != null) {
return externalResolver;
} else {
return this;
}
}
/**
* Get the namespace URI corresponding to a given prefix. Return null
* if the prefix is not in scope.
* @param prefix the namespace prefix
* @param useDefault true if the default namespace is to be used when the
* prefix is ""
* @return the uri for the namespace, or null if the prefix is not in scope.
* Return "" if the prefix maps to the null namespace.
*/
public String getURIForPrefix(String prefix, boolean useDefault) {
if (externalResolver != null) {
return externalResolver.getURIForPrefix(prefix, useDefault);
}
if (prefix.length() == 0) {
return useDefault ? getDefaultElementNamespace() : "";
} else {
return namespaces.get(prefix);
}
}
/**
* Get an iterator over all the prefixes declared in this namespace context. This will include
* the default namespace (prefix="") and the XML namespace where appropriate
*/
public Iterator iteratePrefixes() {
if (externalResolver != null) {
return externalResolver.iteratePrefixes();
} else {
return namespaces.keySet().iterator();
}
}
/**
* Bind a variable used in an XPath Expression to the XSLVariable element in which it is declared.
* This method is provided for use by the XPath parser, and it should not be called by the user of
* the API, or overridden, unless variables are to be declared using a mechanism other than the
* declareVariable method of this class.
* @param qName the name of the variable
* @return the resulting variable reference
*/
public Expression bindVariable(StructuredQName qName) throws XPathException {
XPathVariable var = variables.get(qName);
if (var==null) {
if (autoDeclare) {
return new VariableReference(declareVariable(qName));
} else {
throw new XPathException("Undeclared variable in XPath expression: $" + qName.getClarkName());
}
} else {
return new VariableReference(var);
}
}
/**
* Get a Stack Frame Map containing definitions of all the declared variables. This will return a newly
* created object that the caller is free to modify by adding additional variables, without affecting
* the static context itself.
*/
public SlotManager getStackFrameMap() {
SlotManager map = getConfiguration().makeSlotManager();
XPathVariable[] va = new XPathVariable[variables.size()];
for (Iterator<XPathVariable> v = variables.values().iterator(); v.hasNext();) {
XPathVariable var = v.next();
va[var.getLocalSlotNumber()] = var;
}
for (int i=0; i<va.length; i++) {
map.allocateSlotNumber(va[i].getVariableQName());
}
return map;
}
public boolean isImportedSchema(String namespace) {
return importedSchemaNamespaces.contains(namespace);
}
/**
* Get the set of imported schemas
* @return a Set, the set of URIs representing the names of imported schemas
*/
public Set getImportedSchemaNamespaces() {
return importedSchemaNamespaces;
}
/**
* Register the set of imported schema namespaces
* @param namespaces the set of namespaces for which schema components are available in the
* static context
*/
public void setImportedSchemaNamespaces(Set namespaces) {
importedSchemaNamespaces = namespaces;
if (!namespaces.isEmpty()) {
getExecutable().setSchemaAware(true);
}
}
}
//
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
// you may not use this file except in compliance with the License. You may obtain a copy of the
// License at http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
// See the License for the specific language governing rights and limitations under the License.
//
// The Original Code is: all this file.
//
// The Initial Developer of the Original Code is Michael H. Kay
//
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
//
// Contributor(s): none.
//