| /******************************************************************************* |
| * Copyright (c) 2005, 2010 Andrea Bittau, University College London, and others |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Andrea Bittau - initial API and implementation from the PsychoPath XPath 2.0 |
| * David Carver (STAR) - bug 277792 - add built in types to static context. |
| * Jesper Steen Moller - bug 297707 - Missing the empty-sequence() type |
| * Mukul Gandhi - bug 280798 - PsychoPath support for JDK 1.4 |
| * Mukul Gandhi - bug 325262 - providing ability to store an XPath2 sequence |
| * into an user-defined variable. |
| *******************************************************************************/ |
| |
| package org.eclipse.wst.xml.xpath2.processor.internal; |
| |
| import org.apache.xerces.xs.ItemPSVI; |
| import org.apache.xerces.xs.XSAttributeDeclaration; |
| import org.apache.xerces.xs.XSElementDeclaration; |
| import org.apache.xerces.xs.XSModel; |
| import org.apache.xerces.xs.XSTypeDefinition; |
| import org.eclipse.wst.xml.xpath2.processor.ResultSequence; |
| import org.eclipse.wst.xml.xpath2.processor.StaticContext; |
| import org.eclipse.wst.xml.xpath2.processor.function.FnFunctionLibrary; |
| import org.eclipse.wst.xml.xpath2.processor.function.XSCtrLibrary; |
| import org.eclipse.wst.xml.xpath2.processor.internal.function.ConstructorFL; |
| import org.eclipse.wst.xml.xpath2.processor.internal.function.Function; |
| import org.eclipse.wst.xml.xpath2.processor.internal.function.FunctionLibrary; |
| import org.eclipse.wst.xml.xpath2.processor.internal.types.AnyAtomicType; |
| import org.eclipse.wst.xml.xpath2.processor.internal.types.AnyType; |
| import org.eclipse.wst.xml.xpath2.processor.internal.types.NodeType; |
| import org.eclipse.wst.xml.xpath2.processor.internal.types.QName; |
| import org.eclipse.wst.xml.xpath2.processor.internal.types.XSAnyURI; |
| |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Stack; |
| |
| /** |
| * Default implementation of a static context as described by the XPath 2.0 |
| * specification. |
| */ |
| public class DefaultStaticContext implements StaticContext { |
| |
| private boolean _xpath1_compatible; |
| private String _default_namespace; |
| private String _default_function_namespace; |
| private XSModel _schema; |
| private XSCtrLibrary builtinTypes; |
| |
| // key: String prefix, contents: String namespace |
| private Map _namespaces; |
| |
| private String _cntxt_item_type; |
| private Map _functions; |
| |
| // XXX collations |
| |
| private XSAnyURI _base_uri; |
| private Map _documents; |
| private Map _collections; |
| |
| public String get_cntxt_item_type() { |
| return _cntxt_item_type; |
| } |
| |
| public void set_cntxt_item_type(String cntxtItemType) { |
| _cntxt_item_type = cntxtItemType; |
| } |
| |
| public Map get_collections() { |
| return _collections; |
| } |
| |
| public void set_collections(Map collections) { |
| _collections = collections; |
| } |
| |
| public String get_default_collection_type() { |
| return _default_collection_type; |
| } |
| |
| public void set_default_collection_type(String defaultCollectionType) { |
| _default_collection_type = defaultCollectionType; |
| } |
| |
| private String _default_collection_type; |
| |
| // Variables are held like this: |
| // A stack of maps of variables.... |
| // or in more human terms: |
| // a stack of scopes each containing a symbol table |
| // XXX vars contain AnyType... should they be ResultSequence ? |
| private Stack _scopes; |
| |
| /** |
| * Constructor. |
| * |
| * @param schema |
| * Schema information from document. May be null. |
| */ |
| public DefaultStaticContext(XSModel schema) { |
| _xpath1_compatible = false; |
| |
| _default_namespace = null; |
| _default_function_namespace = FnFunctionLibrary.XPATH_FUNCTIONS_NS; |
| _schema = schema; |
| builtinTypes = new XSCtrLibrary(); |
| |
| _functions = new HashMap(20); // allow null keys: null namespace |
| _namespaces = new HashMap(20); // ditto |
| |
| _cntxt_item_type = null; |
| |
| _scopes = new Stack(); |
| new_scope(); |
| |
| if (_schema != null) |
| init_schema(schema); |
| |
| _base_uri = new XSAnyURI(); |
| |
| // XXX wildcard prefix |
| add_namespace("*", "*"); |
| |
| } |
| |
| /** |
| * Constructor for schema-less documents. |
| * |
| */ |
| public DefaultStaticContext() { |
| this(null); |
| } |
| |
| private void init_schema(XSModel schema) { |
| } |
| |
| /** |
| * return the base URI |
| * |
| * @return XSAnyURI |
| */ |
| public XSAnyURI base_uri() { |
| return _base_uri; |
| } |
| |
| /** |
| * is it xpath1 compatible? |
| * |
| * @return boolean |
| */ |
| public boolean xpath1_compatible() { |
| return _xpath1_compatible; |
| } |
| |
| /** |
| * adds namespace |
| * |
| * @param prefix |
| * namespace prefix |
| * @param namespace |
| * namespace URI |
| * |
| */ |
| public void add_namespace(String prefix, String namespace) { |
| // XXX are these reserved ? |
| // refer to formal semantics section 2.5.1 |
| if ((prefix != null) && (prefix.equals("fs") || prefix.equals("op") || prefix.equals("dm"))) |
| return; |
| if (prefix == null) { |
| _default_namespace = namespace; |
| _namespaces.put("", namespace); |
| } else { |
| _namespaces.put(prefix, namespace); |
| } |
| } |
| |
| /** |
| * Retrieves the default namespace, when one is not allocated |
| * |
| * @return string |
| */ |
| public String default_namespace() { |
| return _default_namespace; |
| } |
| |
| /** |
| * Retrieves the defaul function namespace |
| * |
| * @return string |
| */ |
| public String default_function_namespace() { |
| return _default_function_namespace; |
| } |
| |
| /** |
| * Adds a function to the library. |
| * |
| * @param fl |
| * Function library to add. |
| */ |
| public void add_function_library(FunctionLibrary fl) { |
| fl.set_static_context(this); |
| _functions.put(fl.namespace(), fl); |
| } |
| |
| /** |
| * Check for existance of function. |
| * |
| * @param name |
| * function name. |
| * @param arity |
| * arity of function. |
| * @return true if function exists. False otherwise. |
| */ |
| public boolean function_exists(QName name, int arity) { |
| String ns = name.namespace(); |
| if (!_functions.containsKey(ns)) |
| return false; |
| |
| FunctionLibrary fl = (FunctionLibrary) _functions.get(ns); |
| |
| return fl.function_exists(name, arity); |
| } |
| |
| protected Function function(QName name, int arity) { |
| String ns = name.namespace(); |
| if (!_functions.containsKey(ns)) |
| return null; |
| |
| FunctionLibrary fl = (FunctionLibrary) _functions.get(ns); |
| |
| return fl.function(name, arity); |
| } |
| |
| /** |
| * |
| * Creates an atomic from a specific type name initialized with a default |
| * value. |
| * |
| * @param name |
| * name of type to create |
| * @return Atomic type of desired type. |
| */ |
| public AnyAtomicType make_atomic(QName name) { |
| String ns = name.namespace(); |
| |
| if (!_functions.containsKey(ns)) |
| return null; |
| |
| FunctionLibrary fl = (FunctionLibrary) _functions.get(ns); |
| |
| if (!(fl instanceof ConstructorFL)) |
| return null; |
| |
| ConstructorFL cfl = (ConstructorFL) fl; |
| |
| return cfl.atomic_type(name); |
| } |
| |
| private boolean expand_qname(QName name, String def) { |
| String prefix = name.prefix(); |
| |
| if (prefix == null) { |
| name.set_namespace(def); |
| return true; |
| } |
| |
| if (!prefix_exists(prefix)) |
| return false; |
| |
| name.set_namespace(resolve_prefix(prefix)); |
| return true; |
| |
| } |
| |
| /** |
| * Expands the qname's prefix into a namespace. |
| * |
| * @param name |
| * qname to expand. |
| * @return true on success. |
| */ |
| public boolean expand_qname(QName name) { |
| return expand_qname(name, null); |
| } |
| |
| /** |
| * Expands a qname and uses the default function namespace if unprefixed. |
| * |
| * @param name |
| * qname to expand. |
| * @return true on success. |
| */ |
| public boolean expand_function_qname(QName name) { |
| return expand_qname(name, default_function_namespace()); |
| } |
| |
| /** |
| * Expands a qname and uses the default type/element namespace if |
| * unprefixed. |
| * |
| * @param name |
| * qname to expand. |
| * @return true on success. |
| */ |
| public boolean expand_elem_type_qname(QName name) { |
| return expand_qname(name, default_namespace()); |
| } |
| |
| /** |
| * |
| * Checks whether the type is defined in the in scope schema definitions. |
| * |
| * @param qname |
| * type name. |
| * @return true if type is defined. |
| */ |
| public boolean type_defined(QName qname) { |
| |
| if (_schema == null) { |
| return builtinTypes.atomic_type(qname) != null; |
| } |
| |
| XSTypeDefinition td = _schema.getTypeDefinition(qname.local(), qname |
| .namespace()); |
| if (td == null) |
| return false; |
| |
| return true; |
| } |
| |
| /** |
| * Checks whether the type is defined in the in scope schema definitions. |
| * |
| * @param ns |
| * namespace of type. |
| * @param type |
| * name of type. |
| * @return true if type is defined. |
| * |
| */ |
| public boolean type_defined(String ns, String type) { |
| return type_defined(new QName(ns, type)); |
| } |
| |
| /** |
| * is element declared? |
| * |
| * @param elem |
| * name of element. |
| * @return true if element declared. |
| */ |
| public boolean element_declared(QName elem) { |
| if (_schema == null) |
| return false; |
| |
| XSElementDeclaration ed = _schema.getElementDeclaration(elem.local(), |
| elem.namespace()); |
| |
| if (ed == null) |
| return false; |
| |
| return true; |
| } |
| |
| /** |
| * Obtains schema definition of the type of an element. |
| * |
| * @param elem |
| * name of element who's type is desired. |
| * @return schema definition of type |
| */ |
| public XSTypeDefinition element_type_definition(QName elem) { |
| XSElementDeclaration ed = _schema.getElementDeclaration(elem.local(), |
| elem.namespace()); |
| |
| return ed.getTypeDefinition(); |
| } |
| |
| /** |
| * Checks if an attribute is in the in-scope schema definitions. |
| * |
| * @param attr |
| * name of attribute. |
| * @return true if attribute is declared. |
| */ |
| public boolean attribute_declared(QName attr) { |
| if (_schema == null) |
| return false; |
| |
| XSAttributeDeclaration ad = _schema.getAttributeDeclaration(attr |
| .local(), attr.namespace()); |
| |
| if (ad == null) |
| return false; |
| |
| return true; |
| } |
| |
| /** |
| * Retrieves type definition of the attribute in an element. |
| * |
| * @param elem |
| * element name |
| * @return schema definition of the type of the attribute |
| */ |
| public XSTypeDefinition attribute_type_definition(QName elem) { |
| XSAttributeDeclaration ad = _schema.getAttributeDeclaration(elem |
| .local(), elem.namespace()); |
| |
| return ad.getTypeDefinition(); |
| } |
| |
| /** |
| * does prefix exist? |
| * |
| * @param pref |
| * prefix name. |
| * @return true if it does. |
| */ |
| public boolean prefix_exists(String pref) { |
| return _namespaces.containsKey(pref); |
| } |
| |
| /** |
| * Resolves a prefix into a namespace URI. |
| * |
| * @param pref |
| * prefix name |
| * @return uri prefix is resolved to or null. |
| */ |
| public String resolve_prefix(String pref) { |
| return (String) _namespaces.get(pref); |
| } |
| |
| /** |
| * Checks if an XML node derives from a specified type. |
| * |
| * @param at |
| * node actual type |
| * @param et |
| * name of expected type |
| * @return true if a derivation exists |
| */ |
| // XXX fix this |
| public boolean derives_from(NodeType at, QName et) { |
| ItemPSVI psvi = (ItemPSVI) at.node_value(); |
| XSTypeDefinition td = psvi.getTypeDefinition(); |
| |
| short method = 0; |
| |
| // XXX |
| if (!et.expanded()) { |
| String pre = et.prefix(); |
| |
| if (pre != null) { |
| if (prefix_exists(pre)) { |
| et.set_namespace(resolve_prefix(pre)); |
| } else |
| assert false; |
| } else |
| et.set_namespace(default_namespace()); |
| } |
| |
| return td.derivedFrom(et.namespace(), et.local(), method); |
| } |
| |
| /** |
| * Checks if an XML node derives from a specified type definition. |
| * |
| * @param at |
| * node actual type. |
| * @param et |
| * type definition of expected type. |
| * @return true if a derivation exists. |
| */ |
| public boolean derives_from(NodeType at, XSTypeDefinition et) { |
| ItemPSVI psvi = (ItemPSVI) at.node_value(); |
| XSTypeDefinition td = psvi.getTypeDefinition(); |
| |
| short method = 0; |
| |
| return td.derivedFromType(et, method); |
| } |
| |
| /** |
| * Creates a new scope level. |
| */ |
| // variable stuff |
| public void new_scope() { |
| Map vars = new HashMap(); |
| |
| _scopes.push(vars); |
| } |
| |
| /** |
| * Destroys a scope. |
| */ |
| public void destroy_scope() { |
| _scopes.pop(); |
| } |
| |
| private Map current_scope() { |
| return (Map) _scopes.peek(); |
| } |
| |
| /** |
| * does variable exist in current scope ? |
| * |
| * @param var |
| * variable name. |
| * @return true if it does. |
| */ |
| public boolean variable_exists(QName var) { |
| Map scope = current_scope(); |
| |
| return scope.containsKey(var); |
| } |
| |
| /** |
| * checks to see if variable is in scope |
| * |
| * @param var |
| * variable name. |
| * @return true if variable is in current or above scope. |
| */ |
| public boolean variable_in_scope(QName var) { |
| // order doesn't matter.. |
| for (Iterator i = _scopes.iterator(); i.hasNext();) { |
| Map scope = (Map) i.next(); |
| |
| if (scope.containsKey(var)) |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Adds a variable to current scope. |
| * |
| * used for static checking.... i.e. presence of variables |
| * |
| * @param var |
| * variable name to add. |
| */ |
| public void add_variable(QName var) { |
| set_variable(var, (AnyType) null); |
| } |
| |
| // overwrites, or creates |
| protected void set_variable(QName var, AnyType val) { |
| Map scope = current_scope(); |
| |
| scope.put(var, val); |
| } |
| |
| /* |
| * Set a XPath2 sequence into a variable. |
| */ |
| protected void set_variable(QName var, ResultSequence val) { |
| Map scope = current_scope(); |
| |
| scope.put(var, val); |
| } |
| |
| |
| /** |
| * Deletes a variable from current scope. |
| * |
| * @param var |
| * variable name to delete. |
| * @return false if variable doesn't exist. |
| */ |
| public boolean del_variable(QName var) { |
| if (!variable_exists(var)) |
| return false; |
| |
| Map scope = current_scope(); |
| if (scope.remove(var) == null) |
| return false; |
| return true; |
| |
| } |
| |
| // return null if "not found" |
| protected Object get_var(QName var) { |
| // go through the stack in reverse order... reverse iterators |
| // would be nice here... |
| |
| int pos = _scopes.size(); |
| while (--pos >= 0) { |
| Map scope = (Map) _scopes.get(pos); |
| |
| // gotcha |
| if (scope.containsKey(var)) { |
| return scope.get(var); |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Debug function which will print current variable scopes and info. |
| */ |
| // debug functions |
| public void debug_print_vars() { |
| int level = 0; |
| |
| for (Iterator i = _scopes.iterator(); i.hasNext();) { |
| Map scope = (Map) i.next(); |
| |
| System.out.println("Scope level " + level); |
| // scope.entrySet().iterator(); |
| for (Iterator j = scope.entrySet().iterator(); j.hasNext();) { |
| QName varname = (QName) j.next(); |
| |
| AnyType val = (AnyType) scope.get(varname); |
| |
| String string_val = "null"; |
| |
| if (val != null) |
| string_val = val.string_value(); |
| |
| System.out.println("Varname: " + varname.string() |
| + " expanded=" + varname.expanded() + " Value: " |
| + string_val); |
| |
| } |
| |
| level++; |
| } |
| } |
| |
| /** |
| * Set the Base URI for the static context. |
| */ |
| public void set_base_uri(String baseuri) { |
| _base_uri = new XSAnyURI(baseuri); |
| } |
| |
| public void set_documents(Map _documents) { |
| this._documents = _documents; |
| } |
| |
| public Map get_documents() { |
| return _documents; |
| } |
| } |