diff --git a/bundles/org.eclipse.e4.emf.xpath/.settings/.api_filters b/bundles/org.eclipse.e4.emf.xpath/.settings/.api_filters
deleted file mode 100644
index 8c85f39506c..00000000000
--- a/bundles/org.eclipse.e4.emf.xpath/.settings/.api_filters
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/bundles/org.eclipse.e4.emf.xpath/META-INF/MANIFEST.MF b/bundles/org.eclipse.e4.emf.xpath/META-INF/MANIFEST.MF
index 74cc46b7049..36466660543 100644
--- a/bundles/org.eclipse.e4.emf.xpath/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.e4.emf.xpath/META-INF/MANIFEST.MF
@@ -2,17 +2,12 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Bundle-SymbolicName: org.eclipse.e4.emf.xpath
-Bundle-Version: 0.5.0.qualifier
+Bundle-Version: 0.5.1.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-17
Require-Bundle: org.eclipse.emf.ecore;bundle-version="2.35.0",
org.eclipse.core.runtime;bundle-version="3.29.0"
Export-Package: org.eclipse.e4.emf.internal.xpath;x-internal:=true,
org.eclipse.e4.emf.internal.xpath.helper;x-friends:="org.eclipse.e4.emf.xpath.test,org.eclipse.e4.ui.model.workbench,org.eclipse.e4.ui.workbench",
org.eclipse.e4.emf.xpath
-Import-Package: org.apache.commons.jxpath;version="[1.3.0,2.0.0)",
- org.apache.commons.jxpath.ri;version="[1.3.0,2.0.0)",
- org.apache.commons.jxpath.ri.compiler;version="[1.3.0,2.0.0)",
- org.apache.commons.jxpath.ri.model;version="[1.3.0,2.0.0)",
- org.apache.commons.jxpath.util;version="[1.3.0,2.0.0)"
Bundle-Vendor: %Bundle-Vendor
Automatic-Module-Name: org.eclipse.e4.emf.xpath
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/AbstractFactory.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/AbstractFactory.java
new file mode 100644
index 00000000000..05e2ec2f90f
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/AbstractFactory.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+/**
+ * The {@link JXPathContext#createPath JXPathContext.createPath()} method of
+ * JXPathContext can create missing objects as it traverses an XPath; it
+ * utilizes an AbstractFactory for that purpose. Install a factory on
+ * JXPathContext by calling {@link JXPathContext#setFactory JXPathContext.
+ * setFactory()}.
+ *
+ * All methods of this class return false. Override any of them to return true
+ * to indicate that the factory has successfully created the described object.
+ */
+public abstract class AbstractFactory {
+
+ /**
+ * The parameters may describe a collection element or an individual
+ * object. It is up to the factory to infer which one it is. If it is a
+ * collection, the factory should check if the collection exists. If not,
+ * it should create the collection. Then it should create the index'th
+ * element of the collection and return true.
+ *
+ *
+ * @param context can be used to evaluate other XPaths, get to variables
+ * etc.
+ * @param pointer describes the location of the node to be created
+ * @param parent is the object that will serve as a parent of the new
+ * object
+ * @param name is the name of the child of the parent that needs to be
+ * created. In the case of DOM may be qualified.
+ * @param index is used if the pointer represents a collection element. You
+ * may need to expand or even create the collection to accommodate the new
+ * element.
+ *
+ * @return true if the object was successfully created
+ */
+ public boolean createObject(final JXPathContext context, final Pointer pointer,
+ final Object parent, final String name, final int index) {
+ return false;
+ }
+
+ /**
+ * Declare the specified variable
+ *
+ * @param context hosts variable pools. See
+ * {@link JXPathContext#getVariables() JXPathContext.getVariables()}
+ * @param name is the name of the variable without the "$" sign
+ * @return true if the variable was successfully defined
+ */
+ public boolean declareVariable(final JXPathContext context, final String name) {
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/BasicNodeSet.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/BasicNodeSet.java
new file mode 100644
index 00000000000..23043d4aaa3
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/BasicNodeSet.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A simple implementation of {@link NodeSet} that behaves as a collection
+ * of pointers.
+ */
+public class BasicNodeSet implements NodeSet {
+ private final List pointers = new ArrayList();
+ private List readOnlyPointers;
+ private List nodes;
+ private List values;
+
+ /**
+ * Add a pointer to this NodeSet.
+ * @param pointer to add
+ */
+ public void add(final Pointer pointer) {
+ if (pointers.add(pointer)) {
+ clearCacheLists();
+ }
+ }
+
+ /**
+ * Add the specified NodeSet to this NodeSet.
+ * @param nodeSet to add
+ */
+ public void add(final NodeSet nodeSet) {
+ if (pointers.addAll(nodeSet.getPointers())) {
+ clearCacheLists();
+ }
+ }
+
+ @Override
+ public synchronized List getPointers() {
+ if (readOnlyPointers == null) {
+ readOnlyPointers = Collections.unmodifiableList(pointers);
+ }
+ return readOnlyPointers;
+ }
+
+ @Override
+ public synchronized List getNodes() {
+ if (nodes == null) {
+ nodes = new ArrayList();
+ for (int i = 0; i < pointers.size(); i++) {
+ final Pointer pointer = (Pointer) pointers.get(i);
+ nodes.add(pointer.getNode());
+ }
+ nodes = Collections.unmodifiableList(nodes);
+ }
+ return nodes;
+ }
+
+ @Override
+ public synchronized List getValues() {
+ if (values == null) {
+ values = new ArrayList();
+ for (int i = 0; i < pointers.size(); i++) {
+ final Pointer pointer = (Pointer) pointers.get(i);
+ values.add(pointer.getValue());
+ }
+ values = Collections.unmodifiableList(values);
+ }
+ return values;
+ }
+
+ @Override
+ public String toString() {
+ return pointers.toString();
+ }
+
+ /**
+ * Clear cache list members.
+ */
+ private synchronized void clearCacheLists() {
+ readOnlyPointers = null;
+ nodes = null;
+ values = null;
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/BasicVariables.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/BasicVariables.java
new file mode 100644
index 00000000000..04726dac24d
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/BasicVariables.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+import java.util.HashMap;
+
+/**
+ * A basic implementation of the Variables interface that uses a HashMap.
+ */
+public class BasicVariables implements Variables {
+ private static final long serialVersionUID = 2708263960832062725L;
+
+ /**
+ * Contains the values of declared variables
+ */
+ private final HashMap vars = new HashMap();
+
+ /**
+ * Returns true if the variable has been defined, even if the
+ * value of the variable is null.
+ *
+ * @param varName is a variable name without the "$" sign
+ * @return true if the variable is declared
+ */
+ @Override
+ public boolean isDeclaredVariable(final String varName) {
+ return vars.containsKey(varName);
+ }
+
+ /**
+ * Returns the value of the variable if it is defined,
+ * otherwise, throws IllegalArgumentException
+ *
+ * @param varName is a variable name without the "$" sign
+ * @return the value of the variable
+ */
+ @Override
+ public Object getVariable(final String varName) {
+ // Note that a variable may be defined with a null value
+
+ if (vars.containsKey(varName)) {
+ return vars.get(varName);
+ }
+
+ throw new IllegalArgumentException(
+ "No such variable: '" + varName + "'");
+ }
+
+ /**
+ * Defines a new variable with the specified value or modifies
+ * the value of an existing variable.
+ *
+ * @param varName is a variable name without the "$" sign
+ * @param value is the new value for the variable, which can be null
+ */
+ @Override
+ public void declareVariable(final String varName, final Object value) {
+ vars.put(varName, value);
+ }
+
+ /**
+ * Removes an existing variable. May throw UnsupportedOperationException.
+ *
+ * @param varName is a variable name without the "$" sign
+ */
+ @Override
+ public void undeclareVariable(final String varName) {
+ vars.remove(varName);
+ }
+
+ @Override
+ public String toString() {
+ return vars.toString();
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ClassFunctions.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ClassFunctions.java
new file mode 100644
index 00000000000..2180cc96298
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ClassFunctions.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.Set;
+
+import org.apache.commons.jxpath.functions.ConstructorFunction;
+import org.apache.commons.jxpath.functions.MethodFunction;
+import org.apache.commons.jxpath.util.MethodLookupUtils;
+
+/**
+ * Extension functions provided by a Java class.
+ *
+ * Let's say we declared a ClassFunction like this:
+ *
+ * new ClassFunctions(Integer.class, "int")
+ *
+ *
+ * We can now use XPaths like:
+ *
+ * - {@code "int:new(3)"}
+ * - Equivalent to {@code new Integer(3)}
+ * - {@code "int:getInteger('foo')"}
+ * - Equivalent to {@code Integer.getInteger("foo")}
+ * - {@code "int:floatValue(int:new(4))"}
+ * - Equivalent to {@code new Integer(4).floatValue()}
+ *
+ *
+ *
+ * If the first argument of a method is {@link ExpressionContext}, the
+ * expression context in which the function is evaluated is passed to
+ * the method.
+ */
+public class ClassFunctions implements Functions {
+ private static final Object[] EMPTY_ARRAY = {};
+
+ private final Class functionClass;
+ private final String namespace;
+
+ /**
+ * Create a new ClassFunctions.
+ * @param functionClass Class providing the functions
+ * @param namespace assigned ns
+ */
+ public ClassFunctions(final Class functionClass, final String namespace) {
+ this.functionClass = functionClass;
+ this.namespace = namespace;
+ }
+
+ /**
+ * Returns a set of one namespace - the one specified in the constructor.
+ *
+ * @return a singleton
+ */
+ @Override
+ public Set getUsedNamespaces() {
+ return Collections.singleton(namespace);
+ }
+
+ /**
+ * Returns a {@link Function}, if any, for the specified namespace,
+ * name and parameter types.
+ *
+ * @param namespace if it is not the namespace specified in the constructor,
+ * the method returns null
+ * @param name is a function name or "new" for a constructor.
+ * @param parameters Object[] of parameters
+ * @return a MethodFunction, a ConstructorFunction or null if there is no
+ * such function.
+ */
+ @Override
+ public Function getFunction(
+ final String namespace,
+ final String name,
+ Object[] parameters) {
+ if (namespace == null) {
+ if (this.namespace != null) {
+ return null;
+ }
+ }
+ else if (!namespace.equals(this.namespace)) {
+ return null;
+ }
+
+ if (parameters == null) {
+ parameters = EMPTY_ARRAY;
+ }
+
+ if (name.equals("new")) {
+ final Constructor constructor =
+ MethodLookupUtils.lookupConstructor(functionClass, parameters);
+ if (constructor != null) {
+ return new ConstructorFunction(constructor);
+ }
+ }
+ else {
+ Method method = MethodLookupUtils.
+ lookupStaticMethod(functionClass, name, parameters);
+ if (method != null) {
+ return new MethodFunction(method);
+ }
+
+ method = MethodLookupUtils.
+ lookupMethod(functionClass, name, parameters);
+ if (method != null) {
+ return new MethodFunction(method);
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/CompiledExpression.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/CompiledExpression.java
new file mode 100644
index 00000000000..dfa2284815c
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/CompiledExpression.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+import java.util.Iterator;
+
+/**
+ * Represents a compiled XPath. The interpretation of compiled XPaths
+ * may be faster, because it bypasses the compilation step. The reference
+ * implementation of {@link JXPathContext} also globally caches some of the
+ * results of compilation, so the direct use of JXPathContext is not
+ * always less efficient than the use of CompiledExpression.
+ *
+ * Use CompiledExpression only when there is a need to evaluate the
+ * same expression multiple times and the CompiledExpression can be
+ * conveniently cached.
+ *
+ * To acquire a CompiledExpression, call {@link JXPathContext#compile
+ * JXPathContext.compile}
+ */
+public interface CompiledExpression {
+
+ /**
+ * Evaluates the xpath and returns the resulting object. Primitive
+ * types are wrapped into objects.
+ * @param context to evaluate
+ * @return Object
+ */
+ Object getValue(JXPathContext context);
+
+ /**
+ * Evaluates the xpath, converts the result to the specified class and
+ * returns the resulting object.
+ * @param context to evaluate
+ * @param requiredType return type
+ * @return Object
+ */
+ Object getValue(JXPathContext context, Class requiredType);
+
+ /**
+ * Modifies the value of the property described by the supplied xpath.
+ * Will throw an exception if one of the following conditions occurs:
+ *
+ * - The xpath does not in fact describe an existing property
+ *
- The property is not writable (no public, non-static set method)
+ *
+ * @param context base
+ * @param value to set
+ */
+ void setValue(JXPathContext context, Object value);
+
+ /**
+ * Creates intermediate elements of
+ * the path by invoking an {@link AbstractFactory}, which should first be
+ * installed on the context by calling {@link JXPathContext#setFactory}.
+ * @param context base
+ * @return Pointer created
+ */
+ Pointer createPath(JXPathContext context);
+
+ /**
+ * The same as setValue, except it creates intermediate elements of
+ * the path by invoking an {@link AbstractFactory}, which should first be
+ * installed on the context by calling {@link JXPathContext#setFactory}.
+ *
+ * Will throw an exception if one of the following conditions occurs:
+ *
+ * - Elements of the xpath aleady exist, by the path does not in
+ * fact describe an existing property
+ *
- The AbstractFactory fails to create an instance for an intermediate
+ * element.
+ *
- The property is not writable (no public, non-static set method)
+ *
+ * @param context base
+ * @param value to set
+ * @return Pointer created
+ */
+ Pointer createPathAndSetValue(JXPathContext context, Object value);
+
+ /**
+ * Traverses the xpath and returns a Iterator of all results found
+ * for the path. If the xpath matches no properties
+ * in the graph, the Iterator will not be null.
+ * @param context base
+ * @return Iterator
+ */
+ Iterator iterate(JXPathContext context);
+
+ /**
+ * Traverses the xpath and returns a Pointer.
+ * A Pointer provides easy access to a property.
+ * If the xpath matches no properties
+ * in the graph, the pointer will be null.
+ * @param context base
+ * @param xpath string
+ * @return Pointer found
+ */
+ Pointer getPointer(JXPathContext context, String xpath);
+
+ /**
+ * Traverses the xpath and returns an Iterator of Pointers.
+ * A Pointer provides easy access to a property.
+ * If the xpath matches no properties
+ * in the graph, the Iterator be empty, but not null.
+ * @param context to iterate
+ * @return Iterator
+ */
+ Iterator iteratePointers(JXPathContext context);
+
+ /**
+ * Remove the graph element described by this expression.
+ * @param context base
+ */
+ void removePath(JXPathContext context);
+
+ /**
+ * Remove all graph elements described by this expression.
+ * @param context base
+ */
+ void removeAll(JXPathContext context);
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/Container.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/Container.java
new file mode 100644
index 00000000000..af82f71be95
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/Container.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+import java.io.Serializable;
+
+/**
+ * A Container is an object implementing an indirection
+ * mechanism transparent to JXPath. For example, if property
+ * "foo" of the context node has a Container as its value,
+ * the XPath "foo" will produce the contents of that Container,
+ * rather than the container itself.
+ */
+public interface Container extends Serializable {
+
+ /**
+ * Returns the contained value.
+ * @return Object value
+ */
+ Object getValue();
+
+ /**
+ * Modifies the value contained by this container. May throw
+ * UnsupportedOperationException.
+ * @param value Object value to set.
+ */
+ void setValue(Object value);
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/DynamicPropertyHandler.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/DynamicPropertyHandler.java
new file mode 100644
index 00000000000..397342c3231
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/DynamicPropertyHandler.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+/**
+ * A generic mechanism for accessing collections of name/value pairs.
+ * Examples of such collections are HashMap, Properties,
+ * ServletContext. In order to add support for a new such collection
+ * type to JXPath, perform the following two steps:
+ *
+ * - Build an implementation of the DynamicPropertyHandler interface
+ * for the desired collection type.
+ * - Invoke the static method {@link JXPathIntrospector#registerDynamicClass
+ * JXPathIntrospector.registerDynamicClass(class, handlerClass)}
+ *
+ * JXPath allows access to dynamic properties using these three formats:
+ *
+ * - {@code "myMap/myKey"}
+ * - {@code "myMap[@name = 'myKey']"}
+ * - {@code "myMap[name(.) = 'myKey']"}
+ *
+ */
+public interface DynamicPropertyHandler {
+
+ /**
+ * Returns a list of dynamic property names for the supplied object.
+ * @param object to inspect
+ * @return String[]
+ */
+ String[] getPropertyNames(Object object);
+
+ /**
+ * Returns the value of the specified dynamic property.
+ * @param object to search
+ * @param propertyName to retrieve
+ * @return Object
+ */
+ Object getProperty(Object object, String propertyName);
+
+ /**
+ * Modifies the value of the specified dynamic property.
+ * @param object to modify
+ * @param propertyName to modify
+ * @param value to set
+ */
+ void setProperty(Object object, String propertyName, Object value);
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ExceptionHandler.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ExceptionHandler.java
new file mode 100644
index 00000000000..b8908368e6d
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ExceptionHandler.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+/**
+ * Exception handler interface. Actually handles Throwables.
+ * @since 1.4
+ */
+public interface ExceptionHandler {
+ /**
+ * Handle an encountered Throwable.
+ * @param t to handle
+ * @param ptr specific context
+ */
+ void handle(Throwable t, Pointer ptr);
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ExpressionContext.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ExpressionContext.java
new file mode 100644
index 00000000000..3cd44ede7b8
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ExpressionContext.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+import java.util.List;
+
+/**
+ * If an extenstion function has an argument of type ExpressionContext,
+ * it can gain access to the current node of an XPath expression context.
+ *
+ * Example:
+ *
+ * public class MyExtenstionFunctions {
+ * public static String objectType(ExpressionContext context){
+ * Object value = context.getContextNodePointer().getValue();
+ * if (value == null){
+ * return "null";
+ * }
+ * return value.getClass().getName();
+ * }
+ * }
+ *
+ *
+ * You can then register this extension function using a {@link ClassFunctions
+ * ClassFunctions} object and call it like this:
+ *
+ * "/descendent-or-self::node()[ns:objectType() = 'java.util.Date']"
+ *
+ * This expression will find all nodes of the graph that are dates.
+ */
+public interface ExpressionContext {
+
+ /**
+ * Gets the JXPathContext in which this function is being evaluated.
+ *
+ * @return A list representing the current context nodes.
+ */
+ JXPathContext getJXPathContext();
+
+ /**
+ * Gets the current context node.
+ *
+ * @return The current context node pointer.
+ */
+ Pointer getContextNodePointer();
+
+ /**
+ * Gets the current context node list. Each element of the list is
+ * a Pointer.
+ *
+ * @return A list representing the current context nodes.
+ */
+ List getContextNodeList();
+
+ /**
+ * Returns the current context position.
+ * @return int
+ */
+ int getPosition();
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ExtendedKeyManager.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ExtendedKeyManager.java
new file mode 100644
index 00000000000..e39d89c8ef8
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ExtendedKeyManager.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+/**
+ * More complete implementation for the XPath {@code "key()"} function.
+ * Returns NodeSet results and allows Object values for better compatibility
+ * with non-XML graphs.
+ * @since JXPath 1.3
+ */
+public interface ExtendedKeyManager extends KeyManager {
+
+ /**
+ * Find a NodeSet by key/value.
+ * @param context base
+ * @param key String
+ * @param value Object
+ * @return NodeSet found
+ */
+ NodeSet getNodeSetByKey(JXPathContext context, String key, Object value);
+
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/Function.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/Function.java
new file mode 100644
index 00000000000..867ab4858e9
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/Function.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+/**
+ * Extension function interface. Extension functions are grouped into
+ * {@link Functions Functions} objects, which are installed on
+ * JXPathContexts using the
+ * {@link JXPathContext#setFunctions JXPathContext.setFunctions()}
+ * call.
+ *
+ * The Function interface can be implemented directly. However,
+ * most of the time JXPath's built-in implementations should suffice.
+ * See {@link ClassFunctions ClassFunctions} and
+ * {@link PackageFunctions PackageFunctions}.
+ */
+public interface Function {
+
+ /**
+ * Computes the value of the function. Each implementation of Function
+ * is responsible for conversion of supplied parameters to the required
+ * argument types.
+ *
+ * @param context can be used to acquire the context in which the
+ * function is being evaluted.
+ * @param parameters function arguments
+ * @return Object result
+ */
+ Object invoke(ExpressionContext context, Object[] parameters);
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/Functions.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/Functions.java
new file mode 100644
index 00000000000..d3788c5c08e
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/Functions.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+import java.util.Set;
+
+/**
+ * A group of Function objects sharing a common namespace or a set of
+ * common namespaces. Use the classes
+ * {@link ClassFunctions ClassFunctions} and
+ * {@link PackageFunctions PackageFunctions}
+ * to register extension functions implemented as Java methods.
+ */
+public interface Functions {
+
+ /**
+ * Returns all namespaces in which this function collection defines
+ * functions.
+ * @return Set
+ */
+ Set getUsedNamespaces();
+
+ /**
+ * Returns a Function, if any, for the specified namespace,
+ * name and parameter types.
+ * @param namespace ns
+ * @param name function name
+ * @param parameters Object[]
+ * @return Function
+ */
+ Function getFunction(String namespace, String name, Object[] parameters);
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/IdentityManager.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/IdentityManager.java
new file mode 100644
index 00000000000..77cec43000c
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/IdentityManager.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+/**
+ * A delegate of {@link JXPathContext} that implements the XPath {@code "id()"}
+ * function. This delegate is only used when there is no default implementation
+ * of the {@code id()} function. For example, it is not used
+ * when the root of the context is a DOM Node.
+ */
+public interface IdentityManager {
+
+ /**
+ * Finds a node by its ID.
+ * @param context JXPathContext
+ * @param id String
+ * @return Pointer
+ */
+ Pointer getPointerByID(JXPathContext context, String id);
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathAbstractFactoryException.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathAbstractFactoryException.java
new file mode 100644
index 00000000000..dadb6d1d265
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathAbstractFactoryException.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+/**
+ * Thrown when an {@link AbstractFactory} cannot generate an object for
+ * the requested path.
+ */
+
+public class JXPathAbstractFactoryException extends JXPathException {
+
+ private static final long serialVersionUID = -4403564377958943239L;
+
+ /**
+ * Create a new JXPathAbstractFactoryException.
+ * @param message exception message
+ */
+ public JXPathAbstractFactoryException(final String message) {
+ super(message);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathBasicBeanInfo.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathBasicBeanInfo.java
new file mode 100644
index 00000000000..dedd522e485
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathBasicBeanInfo.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+
+/**
+ * An implementation of JXPathBeanInfo based on JavaBeans' BeanInfo. Properties
+ * advertised by JXPathBasicBeanInfo are the same as those advertised by
+ * BeanInfo for the corresponding class.
+ *
+ * @see java.beans.BeanInfo
+ * @see java.beans.Introspector
+ */
+public class JXPathBasicBeanInfo implements JXPathBeanInfo {
+ private static final long serialVersionUID = -3863803443111484155L;
+
+ private static final Comparator PROPERTY_DESCRIPTOR_COMPARATOR = (left, right) -> ((PropertyDescriptor) left).getName().compareTo(
+ ((PropertyDescriptor) right).getName());
+
+ private boolean atomic = false;
+ private final Class clazz;
+ private Class dynamicPropertyHandlerClass;
+ private transient PropertyDescriptor[] propertyDescriptors;
+ private transient HashMap propertyDescriptorMap;
+
+ /**
+ * Create a new JXPathBasicBeanInfo.
+ * @param clazz bean class
+ */
+ public JXPathBasicBeanInfo(final Class clazz) {
+ this.clazz = clazz;
+ }
+
+ /**
+ * Create a new JXPathBasicBeanInfo.
+ * @param clazz bean class
+ * @param atomic whether objects of this class are treated as atomic
+ * objects which have no properties of their own.
+ */
+ public JXPathBasicBeanInfo(final Class clazz, final boolean atomic) {
+ this.clazz = clazz;
+ this.atomic = atomic;
+ }
+
+ /**
+ * Create a new JXPathBasicBeanInfo.
+ * @param clazz bean class
+ * @param dynamicPropertyHandlerClass dynamic property handler class
+ */
+ public JXPathBasicBeanInfo(final Class clazz, final Class dynamicPropertyHandlerClass) {
+ this.clazz = clazz;
+ this.atomic = false;
+ this.dynamicPropertyHandlerClass = dynamicPropertyHandlerClass;
+ }
+
+ /**
+ * Returns true if objects of this class are treated as atomic
+ * objects which have no properties of their own.
+ * @return boolean
+ */
+ @Override
+ public boolean isAtomic() {
+ return atomic;
+ }
+
+ /**
+ * Return true if the corresponding objects have dynamic properties.
+ * @return boolean
+ */
+ @Override
+ public boolean isDynamic() {
+ return dynamicPropertyHandlerClass != null;
+ }
+
+ @Override
+ public synchronized PropertyDescriptor[] getPropertyDescriptors() {
+ if (propertyDescriptors == null) {
+ if (clazz == Object.class) {
+ propertyDescriptors = new PropertyDescriptor[0];
+ }
+ else {
+ try {
+ BeanInfo bi;
+ if (clazz.isInterface()) {
+ bi = Introspector.getBeanInfo(clazz);
+ }
+ else {
+ bi = Introspector.getBeanInfo(clazz, Object.class);
+ }
+ final PropertyDescriptor[] pds = bi.getPropertyDescriptors();
+ final PropertyDescriptor[] descriptors = new PropertyDescriptor[pds.length];
+ System.arraycopy(pds, 0, descriptors, 0, pds.length);
+ Arrays.sort(descriptors, PROPERTY_DESCRIPTOR_COMPARATOR);
+ propertyDescriptors = descriptors;
+ }
+ catch (final IntrospectionException ex) {
+ ex.printStackTrace();
+ return new PropertyDescriptor[0];
+ }
+ }
+ }
+ if (propertyDescriptors.length == 0) {
+ return propertyDescriptors;
+ }
+ final PropertyDescriptor[] result = new PropertyDescriptor[propertyDescriptors.length];
+ System.arraycopy(propertyDescriptors, 0, result, 0, propertyDescriptors.length);
+ return result;
+ }
+
+ @Override
+ public synchronized PropertyDescriptor getPropertyDescriptor(final String propertyName) {
+ if (propertyDescriptorMap == null) {
+ propertyDescriptorMap = new HashMap();
+ final PropertyDescriptor[] pds = getPropertyDescriptors();
+ for (final PropertyDescriptor pd : pds) {
+ propertyDescriptorMap.put(pd.getName(), pd);
+ }
+ }
+ return (PropertyDescriptor) propertyDescriptorMap.get(propertyName);
+ }
+
+ /**
+ * For a dynamic class, returns the corresponding DynamicPropertyHandler
+ * class.
+ * @return Class
+ */
+ @Override
+ public Class getDynamicPropertyHandlerClass() {
+ return dynamicPropertyHandlerClass;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder buffer = new StringBuilder();
+ buffer.append("BeanInfo [class = ");
+ buffer.append(clazz.getName());
+ if (isDynamic()) {
+ buffer.append(", dynamic");
+ }
+ if (isAtomic()) {
+ buffer.append(", atomic");
+ }
+ buffer.append(", properties = ");
+ final PropertyDescriptor[] jpds = getPropertyDescriptors();
+ for (final PropertyDescriptor jpd : jpds) {
+ buffer.append("\n ");
+ buffer.append(jpd.getPropertyType());
+ buffer.append(": ");
+ buffer.append(jpd.getName());
+ }
+ buffer.append("]");
+ return buffer.toString();
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathBeanInfo.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathBeanInfo.java
new file mode 100644
index 00000000000..667bef1cbf1
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathBeanInfo.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+import java.beans.PropertyDescriptor;
+import java.io.Serializable;
+
+/**
+ * JXPathBeanInfo is similar to {@link java.beans.BeanInfo} in that it describes
+ * properties of a JavaBean class. By default, JXPathBeanInfo classes are
+ * automatically generated by {@link JXPathIntrospector JXPathIntrospector}
+ * based on the java.beans.BeanInfo. As with JavaBeans, the user can supply an
+ * alternative implementation of JXPathBeanInfo for a custom class. The
+ * alternative implementation is located by class name, which is the same as the
+ * name of the class it represents with the suffix "XBeanInfo". So, for
+ * example, if you need to provide an alternative JXPathBeanInfo class for class
+ * "com.foo.Bar", write a class "com.foo.BarXBeanInfo" and make it implement the
+ * JXPathBeanInfo interface.
+ */
+public interface JXPathBeanInfo extends Serializable {
+
+ /**
+ * Returns true if objects of this class are treated as atomic
+ * objects which have no properties of their own.
+ * For example, {@link String} and {@link Number} are atomic.
+ * @return boolean
+ */
+ boolean isAtomic();
+
+ /**
+ * Returns true if the objects of this class have dynamic properties
+ * (e.g. java.util.Map). If this method returns true, {@link #getPropertyDescriptors}
+ * should return null and {@link #getDynamicPropertyHandlerClass} should return
+ * a valid class name. An object cannot have both static and dynamic
+ * properties at the same time.
+ * @return boolean
+ */
+ boolean isDynamic();
+
+ /**
+ * Returns a list of property descriptors for the beans described by this
+ * bean info object. Returns null for atomic beans.
+ * @return PropertyDescriptor[]
+ */
+ PropertyDescriptor[] getPropertyDescriptors();
+
+ /**
+ * Returns a PropertyDescriptor for the specified name or null if there
+ * is no such property.
+ * @param propertyName property name
+ * @return PropertyDescriptor
+ */
+ PropertyDescriptor getPropertyDescriptor(String propertyName);
+
+ /**
+ * For dynamic objects, returns the class implementing
+ * the {@link DynamicPropertyHandler} interface. That class can
+ * be used to access dynamic properties.
+ * @return Class
+ */
+ Class getDynamicPropertyHandlerClass();
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathContext.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathContext.java
new file mode 100644
index 00000000000..b7459c71bd1
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathContext.java
@@ -0,0 +1,904 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+import java.text.DecimalFormatSymbols;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+
+import org.apache.commons.jxpath.util.KeyManagerUtils;
+
+/**
+ * JXPathContext provides APIs for the traversal of graphs of JavaBeans using
+ * the XPath syntax. Using JXPathContext, you can read and write properties of
+ * JavaBeans, arrays, collections and maps. JXPathContext uses JavaBeans
+ * introspection to enumerate and access JavaBeans properties.
+ *
+ * JXPathContext allows alternative implementations. This is why instead of
+ * allocating JXPathContext directly, you should call a static
+ * {@code newContext} method. This method will utilize the
+ * {@link JXPathContextFactory} API to locate a suitable implementation of
+ * JXPath. Bundled with JXPath comes a default implementation called Reference
+ * Implementation.
+ *
+ *
+ * JXPath Interprets XPath Syntax on Java Object Graphs
+ *
+ * JXPath uses an intuitive interpretation of the xpath syntax in the context
+ * of Java object graphs. Here are some examples:
+ *
+ * Example 1: JavaBean Property Access
+ *
+ * JXPath can be used to access properties of a JavaBean.
+ *
+ *
+ * public class Employee {
+ * public String getFirstName(){
+ * ...
+ * }
+ * }
+ *
+ * Employee emp = new Employee();
+ * ...
+ *
+ * JXPathContext context = JXPathContext.newContext(emp);
+ * String fName = (String)context.getValue("firstName");
+ *
+ *
+ * In this example, we are using JXPath to access a property of the
+ * {@code emp} bean. In this simple case the invocation of JXPath is
+ * equivalent to invocation of getFirstName() on the bean.
+ *
+ * Example 2: Nested Bean Property Access
+ * JXPath can traverse object graphs:
+ *
+ *
+ * public class Employee {
+ * public Address getHomeAddress(){
+ * ...
+ * }
+ * }
+ * public class Address {
+ * public String getStreetNumber(){
+ * ...
+ * }
+ * }
+ *
+ * Employee emp = new Employee();
+ * ...
+ *
+ * JXPathContext context = JXPathContext.newContext(emp);
+ * String sNumber = (String)context.getValue("homeAddress/streetNumber");
+ *
+ *
+ * In this case XPath is used to access a property of a nested bean.
+ *
+ * A property identified by the xpath does not have to be a "leaf" property.
+ * For instance, we can extract the whole Address object in above example:
+ *
+ *
+ * Address addr = (Address)context.getValue("homeAddress");
+ *
+ *
+ * Example 3: Collection Subscripts
+ * JXPath can extract elements from arrays and collections.
+ *
+ *
+ * public class Integers {
+ * public int[] getNumbers(){
+ * ...
+ * }
+ * }
+ *
+ * Integers ints = new Integers();
+ * ...
+ *
+ * JXPathContext context = JXPathContext.newContext(ints);
+ * Integer thirdInt = (Integer)context.getValue("numbers[3]");
+ *
+ * A collection can be an arbitrary array or an instance of java.util.
+ * Collection.
+ *
+ * Note: in XPath the first element of a collection has index 1, not 0.
+ *
+ *
Example 4: Map Element Access
+ *
+ * JXPath supports maps. To get a value use its key.
+ *
+ *
+ * public class Employee {
+ * public Map getAddresses(){
+ * return addressMap;
+ * }
+ *
+ * public void addAddress(String key, Address address){
+ * addressMap.put(key, address);
+ * }
+ * ...
+ * }
+ *
+ * Employee emp = new Employee();
+ * emp.addAddress("home", new Address(...));
+ * emp.addAddress("office", new Address(...));
+ * ...
+ *
+ * JXPathContext context = JXPathContext.newContext(emp);
+ * String homeZipCode = (String)context.getValue("addresses/home/zipCode");
+ *
+ *
+ * Often you will need to use the alternative syntax for accessing Map
+ * elements:
+ *
+ *
+ * String homeZipCode =
+ * (String) context.getValue("addresses[@name='home']/zipCode");
+ *
+ *
+ * In this case, the key can be an expression, e.g. a variable.
+ *
+ * Note: At this point JXPath only supports Maps that use strings for keys.
+ * Note: JXPath supports the extended notion of Map: any object with
+ * dynamic properties can be handled by JXPath provided that its
+ * class is registered with the {@link JXPathIntrospector}.
+ *
+ * Example 5: Retrieving Multiple Results
+ *
+ * JXPath can retrieve multiple objects from a graph. Note that the method
+ * called in this case is not {@code getValue}, but {@code iterate}.
+ *
+ * {@code
+ * public class Author {
+ * public Book[] getBooks(){
+ * ...
+ * }
+ * }
+ *
+ * Author auth = new Author();
+ * ...
+ *
+ * JXPathContext context = JXPathContext.newContext(auth);
+ * Iterator threeBooks = context.iterate("books[position() < 4]");
+ * }
+ *
+ * This returns a list of at most three books from the array of all books
+ * written by the author.
+ *
+ * Example 6: Setting Properties
+ * JXPath can be used to modify property values.
+ *
+ *
+ * public class Employee {
+ * public Address getAddress() {
+ * ...
+ * }
+ *
+ * public void setAddress(Address address) {
+ * ...
+ * }
+ * }
+ *
+ * Employee emp = new Employee();
+ * Address addr = new Address();
+ * ...
+ *
+ * JXPathContext context = JXPathContext.newContext(emp);
+ * context.setValue("address", addr);
+ * context.setValue("address/zipCode", "90190");
+ *
+ *
+ *
+ * Example 7: Creating objects
+ * JXPath can be used to create new objects. First, create a subclass of {@link
+ * AbstractFactory AbstractFactory} and install it on the JXPathContext. Then
+ * call {@link JXPathContext#createPath createPathAndSetValue()} instead of
+ * "setValue". JXPathContext will invoke your AbstractFactory when it discovers
+ * that an intermediate node of the path is null. It will not override
+ * existing nodes.
+ *
+ *
+ * public class AddressFactory extends AbstractFactory {
+ * public boolean createObject(JXPathContext context,
+ * Pointer pointer, Object parent, String name, int index){
+ * if ((parent instanceof Employee) && name.equals("address"){
+ * ((Employee)parent).setAddress(new Address());
+ * return true;
+ * }
+ * return false;
+ * }
+ * }
+ *
+ * JXPathContext context = JXPathContext.newContext(emp);
+ * context.setFactory(new AddressFactory());
+ * context.createPathAndSetValue("address/zipCode", "90190");
+ *
+ *
+ * Example 8: Using Variables
+ * JXPath supports the notion of variables. The XPath syntax for accessing
+ * variables is "$varName".
+ *
+ *
+ * public class Author {
+ * public Book[] getBooks(){
+ * ...
+ * }
+ * }
+ *
+ * Author auth = new Author();
+ * ...
+ *
+ * JXPathContext context = JXPathContext.newContext(auth);
+ * context.getVariables().declareVariable("index", new Integer(2));
+ *
+ * Book secondBook = (Book)context.getValue("books[$index]");
+ *
+ *
+ * You can also set variables using JXPath:
+ *
+ *
+ * context.setValue("$index", new Integer(3));
+ *
+ *
+ * Note: you can only change the value of an existing variable this
+ * way, you cannot define a new variable.
+ *
+ *
+ * When a variable contains a JavaBean or a collection, you can
+ * traverse the bean or collection as well:
+ *
+ * ...
+ * context.getVariables().declareVariable("book", myBook);
+ * String title = (String)context.getValue("$book/title);
+ *
+ * Book array[] = new Book[]{...};
+ *
+ * context.getVariables().declareVariable("books", array);
+ *
+ * String title = (String)context.getValue("$books[2]/title);
+ *
+ *
+ * Example 9: Using Nested Contexts
+ * If you need to use the same set of variable while interpreting XPaths with
+ * different beans, it makes sense to put the variables in a separate context
+ * and specify that context as a parent context every time you allocate a new
+ * JXPathContext for a JavaBean.
+ *
+ *
+ * JXPathContext varContext = JXPathContext.newContext(null);
+ * varContext.getVariables().declareVariable("title", "Java");
+ *
+ * JXPathContext context = JXPathContext.newContext(varContext, auth);
+ *
+ * Iterator javaBooks = context.iterate("books[title = $title]");
+ *
+ *
+ * Using Custom Variable Pools
+ * By default, JXPathContext creates a HashMap of variables. However,
+ * you can substitute a custom implementation of the Variables
+ * interface to make JXPath work with an alternative source of variables.
+ * For example, you can define implementations of Variables that
+ * cover a servlet context, HTTP request or any similar structure.
+ *
+ * Example 10: Using Standard Extension Functions
+ * Using the standard extension functions, you can call methods on objects,
+ * static methods on classes and create objects using any constructor.
+ * The class names should be fully qualified.
+ *
+ * Here's how you can create new objects:
+ *
+ * Book book =
+ * (Book) context.getValue(
+ * "org.apache.commons.jxpath.example.Book.new ('John Updike')");
+ *
+ *
+ * Here's how you can call static methods:
+ *
+ * Book book =
+ * (Book) context.getValue(
+ * "org. apache.commons.jxpath.example.Book.getBestBook('John Updike')");
+ *
+ *
+ * Here's how you can call regular methods:
+ *
+ * String firstName = (String)context.getValue("getAuthorsFirstName($book)");
+ *
+ * As you can see, the target of the method is specified as the first parameter
+ * of the function.
+ *
+ * Example 11: Using Custom Extension Functions
+ * Collections of custom extension functions can be implemented
+ * as {@link Functions Functions} objects or as Java classes, whose methods
+ * become extenstion functions.
+ *
+ * Let's say the following class implements various formatting operations:
+ *
+ * public class Formats {
+ * public static String date(Date d, String pattern){
+ * return new SimpleDateFormat(pattern).format(d);
+ * }
+ * ...
+ * }
+ *
+ *
+ * We can register this class with a JXPathContext:
+ *
+ *
+ * context.setFunctions(new ClassFunctions(Formats.class, "format"));
+ * ...
+ *
+ * context.getVariables().declareVariable("today", new Date());
+ * String today = (String)context.getValue("format:date($today, 'MM/dd/yyyy')");
+ *
+ *
+ * You can also register whole packages of Java classes using PackageFunctions.
+ *
+ * Also, see {@link FunctionLibrary FunctionLibrary}, which is a class
+ * that allows you to register multiple sets of extension functions with
+ * the same JXPathContext.
+ *
+ *
Configuring JXPath
+ *
+ * JXPath uses JavaBeans introspection to discover properties of JavaBeans.
+ * You can provide alternative property lists by supplying
+ * custom JXPathBeanInfo classes (see {@link JXPathBeanInfo JXPathBeanInfo}).
+ *
+ * Notes
+ *
+ * - JXPath does not support DOM attributes for non-DOM objects. Even though
+ * XPaths like "para[@type='warning']" are legitimate, they will always produce
+ * empty results. The only attribute supported for JavaBeans is "name". The
+ * XPath "foo/bar" is equivalent to "foo[@name='bar']".
+ *
+ *
- The term matches no property in
+ * the graph is used throughout the documentation. It describes a property or
+ * path that can be determined as not belonging to the graph. Determining
+ * whether a property or path belongs to the graph depends on the type of object
+ * being used as {@code cotextBean} (see {@link #newContext(Object)}).
+ * It is only possible strongly typed models where a specific Java model is used
+ * as context. It is not possible with dynamic models such Maps or DOM
+ * implementations.
+ *
When a XPath does not match a property in the graph, the methods of this
+ * class that retrieve a pointer will generally behave in the following way,
+ * depending on the last value configured with {@link #setLenient(boolean)}:
+ *
+ *
+ * - If {@code lenient} is {@code false} (default) - methods
+ * will throw {@link JXPathNotFoundException}.
+ *
- If {@code lenient} is {@code true} - methods will throw
+ * no exception and return a value appropriate for that method to
+ * express the absence: might be a Java {@code null} or a
+ * {@link Pointer} whose {@link Pointer#getValue()} returns
+ * {@code null}, depends on the method.
+ *
+ *
+ *
+ *
+ * See XPath Tutorial by
+ * W3Schools
. Also see XML Path
+ * Language (XPath) Version 1.0
+ *
+ * You will also find more information and examples in
+ *
+ * JXPath User's Guide
+ */
+public abstract class JXPathContext {
+ private static volatile JXPathContextFactory contextFactory;
+ private static volatile JXPathContext compilationContext;
+
+ private static final PackageFunctions GENERIC_FUNCTIONS =
+ new PackageFunctions("", null);
+
+ /** Parent context */
+ protected JXPathContext parentContext;
+ /** Context bean */
+ protected Object contextBean;
+ /** Variables */
+ protected Variables vars;
+ /** Functions */
+ protected Functions functions;
+ /** AbstractFactory */
+ protected AbstractFactory factory;
+ /** IdentityManager */
+ protected IdentityManager idManager;
+ /** KeyManager */
+ protected KeyManager keyManager;
+ /** Decimal format map */
+ protected HashMap decimalFormats;
+
+ private Locale locale;
+ private boolean lenientSet = false;
+ private boolean lenient = false;
+
+ /**
+ * Creates a new JXPathContext with the specified object as the root node.
+ * @param contextBean Object
+ * @return JXPathContext
+ */
+ public static JXPathContext newContext(final Object contextBean) {
+ return getContextFactory().newContext(null, contextBean);
+ }
+
+ /**
+ * Creates a new JXPathContext with the specified bean as the root node and
+ * the specified parent context. Variables defined in a parent context can
+ * be referenced in XPaths passed to the child context.
+ * @param parentContext parent context
+ * @param contextBean Object
+ * @return JXPathContext
+ */
+ public static JXPathContext newContext(
+ final JXPathContext parentContext,
+ final Object contextBean) {
+ return getContextFactory().newContext(parentContext, contextBean);
+ }
+
+ /**
+ * Acquires a context factory and caches it.
+ * @return JXPathContextFactory
+ */
+ private static JXPathContextFactory getContextFactory () {
+ if (contextFactory == null) {
+ contextFactory = JXPathContextFactory.newInstance();
+ }
+ return contextFactory;
+ }
+
+ /**
+ * This constructor should remain protected - it is to be overridden by
+ * subclasses, but never explicitly invoked by clients.
+ * @param parentContext parent context
+ * @param contextBean Object
+ */
+ protected JXPathContext(final JXPathContext parentContext, final Object contextBean) {
+ this.parentContext = parentContext;
+ this.contextBean = contextBean;
+ }
+
+ /**
+ * Returns the parent context of this context or null.
+ * @return JXPathContext
+ */
+ public JXPathContext getParentContext() {
+ return parentContext;
+ }
+
+ /**
+ * Returns the JavaBean associated with this context.
+ * @return Object
+ */
+ public Object getContextBean() {
+ return contextBean;
+ }
+
+ /**
+ * Returns a Pointer for the context bean.
+ * @return Pointer
+ */
+ public abstract Pointer getContextPointer();
+
+ /**
+ * Returns a JXPathContext that is relative to the current JXPathContext.
+ * The supplied pointer becomes the context pointer of the new context.
+ * The relative context inherits variables, extension functions, locale etc
+ * from the parent context.
+ * @param pointer Pointer
+ * @return JXPathContext
+ */
+ public abstract JXPathContext getRelativeContext(Pointer pointer);
+
+ /**
+ * Installs a custom implementation of the Variables interface.
+ * @param vars Variables
+ */
+ public void setVariables(final Variables vars) {
+ this.vars = vars;
+ }
+
+ /**
+ * Returns the variable pool associated with the context. If no such
+ * pool was specified with the {@link #setVariables} method,
+ * returns the default implementation of Variables,
+ * {@link BasicVariables BasicVariables}.
+ * @return Variables
+ */
+ public Variables getVariables() {
+ if (vars == null) {
+ vars = new BasicVariables();
+ }
+ return vars;
+ }
+
+ /**
+ * Install a library of extension functions.
+ * @param functions Functions
+ * @see FunctionLibrary
+ */
+ public void setFunctions(final Functions functions) {
+ this.functions = functions;
+ }
+
+ /**
+ * Returns the set of functions installed on the context.
+ * @return Functions
+ */
+ public Functions getFunctions() {
+ if (functions != null) {
+ return functions;
+ }
+ if (parentContext == null) {
+ return GENERIC_FUNCTIONS;
+ }
+ return null;
+ }
+
+ /**
+ * Install an abstract factory that should be used by the
+ * {@code createPath()} and {@code createPathAndSetValue()}
+ * methods.
+ * @param factory AbstractFactory
+ */
+ public void setFactory(final AbstractFactory factory) {
+ this.factory = factory;
+ }
+
+ /**
+ * Returns the AbstractFactory installed on this context.
+ * If none has been installed and this context has a parent context,
+ * returns the parent's factory. Otherwise returns null.
+ * @return AbstractFactory
+ */
+ public AbstractFactory getFactory() {
+ if (factory == null && parentContext != null) {
+ return parentContext.getFactory();
+ }
+ return factory;
+ }
+
+ /**
+ * Sets the locale for this context. The value of the "lang"
+ * attribute as well as the lang() function will be
+ * affected by the locale. By default, JXPath uses
+ * {@code Locale.getDefault()}
+ * @param locale Locale
+ */
+ public synchronized void setLocale(final Locale locale) {
+ this.locale = locale;
+ }
+
+ /**
+ * Returns the locale set with setLocale. If none was set and
+ * the context has a parent, returns the parent's locale.
+ * Otherwise, returns Locale.getDefault().
+ * @return Locale
+ */
+ public synchronized Locale getLocale() {
+ if (locale == null) {
+ if (parentContext != null) {
+ return parentContext.getLocale();
+ }
+ locale = Locale.getDefault();
+ }
+ return locale;
+ }
+
+ /**
+ * Gets the named DecimalFormatSymbols.
+ * @param name key
+ * @return DecimalFormatSymbols
+ * @see #setDecimalFormatSymbols(String, DecimalFormatSymbols)
+ */
+ public synchronized DecimalFormatSymbols getDecimalFormatSymbols(final String name) {
+ if (decimalFormats == null) {
+ return parentContext == null ? null : parentContext.getDecimalFormatSymbols(name);
+ }
+ return decimalFormats.get(name);
+ }
+
+ /**
+ * If the context is in the lenient mode, then getValue() returns null
+ * for inexistent paths. Otherwise, a path that does not map to
+ * an existing property will throw an exception. Note that if the
+ * property exists, but its value is null, the exception is not
+ * thrown.
+ *
+ * By default, lenient = false
+ * @param lenient flag
+ */
+ public synchronized void setLenient(final boolean lenient) {
+ this.lenient = lenient;
+ lenientSet = true;
+ }
+
+ /**
+ * Learn whether this JXPathContext is lenient.
+ * @return boolean
+ * @see #setLenient(boolean)
+ */
+ public synchronized boolean isLenient() {
+ if (!lenientSet && parentContext != null) {
+ return parentContext.isLenient();
+ }
+ return lenient;
+ }
+
+ /**
+ * Overridden by each concrete implementation of JXPathContext
+ * to perform compilation. Is called by {@code compile()}.
+ * @param xpath to compile
+ * @return CompiledExpression
+ */
+ protected abstract CompiledExpression compilePath(String xpath);
+
+ /**
+ * Evaluates the xpath and returns the resulting object. Primitive
+ * types are wrapped into objects.
+ * @param xpath to evaluate
+ * @return Object found
+ */
+ public abstract Object getValue(String xpath);
+
+ /**
+ * Evaluates the xpath, converts the result to the specified class and
+ * returns the resulting object.
+ * @param xpath to evaluate
+ * @param requiredType required type
+ * @return Object found
+ */
+ public abstract Object getValue(String xpath, Class requiredType);
+
+ /**
+ * Modifies the value of the property described by the supplied xpath.
+ * Will throw an exception if one of the following conditions occurs:
+ *
+ * - The xpath does not in fact describe an existing property
+ *
- The property is not writable (no public, non-static set method)
+ *
+ * @param xpath indicating position
+ * @param value to set
+ */
+ public abstract void setValue(String xpath, Object value);
+
+ /**
+ * Creates missing elements of the path by invoking an {@link AbstractFactory},
+ * which should first be installed on the context by calling {@link #setFactory}.
+ *
+ * Will throw an exception if the AbstractFactory fails to create
+ * an instance for a path element.
+ * @param xpath indicating destination to create
+ * @return pointer to new location
+ */
+ public abstract Pointer createPath(String xpath);
+
+ /**
+ * The same as setValue, except it creates intermediate elements of
+ * the path by invoking an {@link AbstractFactory}, which should first be
+ * installed on the context by calling {@link #setFactory}.
+ *
+ * Will throw an exception if one of the following conditions occurs:
+ *
+ * - Elements of the xpath aleady exist, but the path does not in
+ * fact describe an existing property
+ *
- The AbstractFactory fails to create an instance for an intermediate
+ * element.
+ *
- The property is not writable (no public, non-static set method)
+ *
+ * @param xpath indicating position to create
+ * @param value to set
+ * @return pointer to new location
+ */
+ public abstract Pointer createPathAndSetValue(String xpath, Object value);
+
+ /**
+ * Removes the element of the object graph described by the xpath.
+ * @param xpath indicating position to remove
+ */
+ public abstract void removePath(String xpath);
+
+ /**
+ * Removes all elements of the object graph described by the xpath.
+ * @param xpath indicating positions to remove
+ */
+ public abstract void removeAll(String xpath);
+
+ /**
+ * Traverses the xpath and returns an Iterator of all results found
+ * for the path. If the xpath matches no properties
+ * in the graph, the Iterator will be empty, but not null.
+ *
+ * @param the type of elements returned by the iterator.
+ * @param xpath to iterate
+ * @return Iterator
+ */
+ public abstract Iterator iterate(String xpath);
+
+ /**
+ * Traverses the xpath and returns a Pointer. A Pointer provides easy access
+ * to a property.
+ *
+ * If the xpath matches no
+ * properties in the graph the behavior depends on the value that has
+ * been configured with {@link #setLenient(boolean)}:
+ *
+ * - {@code false} (default) the method will throw a
+ * {@link JXPathNotFoundException}.
+ *
- {@code true} the method returns a pointer whose
+ * {@link Pointer#getValue()} method will always return null.
+ *
+ *
+ * @param xpath desired
+ * @return Pointer A {@link Pointer}, never {@code null}.
+ * @throws JXPathNotFoundException see method description.
+ */
+ public abstract Pointer getPointer(String xpath);
+
+ /**
+ * Traverses the xpath and returns an Iterator of Pointers.
+ * A Pointer provides easy access to a property.
+ * If the xpath matches no properties
+ * in the graph, the Iterator be empty, but not null.
+ * @param xpath to iterate
+ * @return Iterator
+ */
+ public abstract Iterator iteratePointers(String xpath);
+
+ /**
+ * Install an identity manager that will be used by the context
+ * to look up a node by its ID.
+ * @param idManager IdentityManager to set
+ */
+ public void setIdentityManager(final IdentityManager idManager) {
+ this.idManager = idManager;
+ }
+
+ /**
+ * Returns this context's identity manager. If none has been installed,
+ * returns the identity manager of the parent context.
+ * @return IdentityManager
+ */
+ public IdentityManager getIdentityManager() {
+ if (idManager == null && parentContext != null) {
+ return parentContext.getIdentityManager();
+ }
+ return idManager;
+ }
+
+ /**
+ * Locates a Node by its ID.
+ *
+ * @param id is the ID of the sought node.
+ * @return Pointer
+ */
+ public Pointer getPointerByID(final String id) {
+ final IdentityManager manager = getIdentityManager();
+ if (manager != null) {
+ return manager.getPointerByID(this, id);
+ }
+ throw new JXPathException(
+ "Cannot find an element by ID - "
+ + "no IdentityManager has been specified");
+ }
+
+ /**
+ * Install a key manager that will be used by the context
+ * to look up a node by a key value.
+ * @param keyManager KeyManager
+ */
+ public void setKeyManager(final KeyManager keyManager) {
+ this.keyManager = keyManager;
+ }
+
+ /**
+ * Returns this context's key manager. If none has been installed,
+ * returns the key manager of the parent context.
+ * @return KeyManager
+ */
+ public KeyManager getKeyManager() {
+ if (keyManager == null && parentContext != null) {
+ return parentContext.getKeyManager();
+ }
+ return keyManager;
+ }
+
+ /**
+ * Locates a NodeSet by key/value.
+ * @param key string
+ * @param value object
+ * @return NodeSet found
+ */
+ public NodeSet getNodeSetByKey(final String key, final Object value) {
+ final KeyManager manager = getKeyManager();
+ if (manager != null) {
+ return KeyManagerUtils.getExtendedKeyManager(manager)
+ .getNodeSetByKey(this, key, value);
+ }
+ throw new JXPathException("Cannot find an element by key - "
+ + "no KeyManager has been specified");
+ }
+
+ /**
+ * Registers a namespace prefix.
+ *
+ * @param prefix A namespace prefix
+ * @param namespaceURI A URI for that prefix
+ */
+ public void registerNamespace(final String prefix, final String namespaceURI) {
+ throw new UnsupportedOperationException(
+ "Namespace registration is not implemented by " + getClass());
+ }
+
+ /**
+ * Given a prefix, returns a registered namespace URI. If the requested
+ * prefix was not defined explicitly using the registerNamespace method,
+ * JXPathContext will then check the context node to see if the prefix is
+ * defined there. See
+ * {@link #setNamespaceContextPointer(Pointer) setNamespaceContextPointer}.
+ *
+ * @param prefix The namespace prefix to look up
+ * @return namespace URI or null if the prefix is undefined.
+ */
+ public String getNamespaceURI(final String prefix) {
+ throw new UnsupportedOperationException(
+ "Namespace registration is not implemented by " + getClass());
+ }
+
+ /**
+ * Gets the prefix associated with the specifed namespace URI.
+ * @param namespaceURI the ns URI to check.
+ * @return String prefix
+ * @since JXPath 1.3
+ */
+ public String getPrefix(final String namespaceURI) {
+ throw new UnsupportedOperationException(
+ "Namespace registration is not implemented by " + getClass());
+ }
+
+ /**
+ * Namespace prefixes can be defined implicitly by specifying a pointer to a
+ * context where the namespaces are defined. By default,
+ * NamespaceContextPointer is the same as the Context Pointer, see
+ * {@link #getContextPointer() getContextPointer()}
+ *
+ * @param namespaceContextPointer The pointer to the context where prefixes used in
+ * XPath expressions should be resolved.
+ */
+ public void setNamespaceContextPointer(final Pointer namespaceContextPointer) {
+ throw new UnsupportedOperationException(
+ "Namespace registration is not implemented by " + getClass());
+ }
+
+ /**
+ * Returns the namespace context pointer set with
+ * {@link #setNamespaceContextPointer(Pointer) setNamespaceContextPointer()}
+ * or, if none has been specified, the context pointer otherwise.
+ *
+ * @return The namespace context pointer.
+ */
+ public Pointer getNamespaceContextPointer() {
+ throw new UnsupportedOperationException(
+ "Namespace registration is not implemented by " + getClass());
+ }
+
+ /**
+ * Sets the ExceptionHandler used by this context, if any.
+ * @param exceptionHandler to set
+ * @since 1.4
+ */
+ public void setExceptionHandler(final ExceptionHandler exceptionHandler) {
+ throw new UnsupportedOperationException(
+ "ExceptionHandler registration is not implemented by " + getClass());
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathContextFactory.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathContextFactory.java
new file mode 100644
index 00000000000..d54135ff31a
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathContextFactory.java
@@ -0,0 +1,256 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Properties;
+
+import org.apache.commons.jxpath.util.ClassLoaderUtil;
+
+/**
+ * Defines a factory API that enables applications to obtain a
+ * {@link JXPathContext} instance. To acquire a JXPathContext, first call the
+ * static {@link #newInstance} method of JXPathContextFactory.
+ * This method returns a concrete JXPathContextFactory.
+ * Then call {@link #newContext} on that instance. You will rarely
+ * need to perform these steps explicitly: usually you can call one of the
+ * {@code JXPathContex.newContext} methods, which will perform these steps
+ * for you.
+ *
+ * @see JXPathContext#newContext(Object)
+ * @see JXPathContext#newContext(JXPathContext,Object)
+ */
+public abstract class JXPathContextFactory {
+
+ /** The default property */
+ public static final String FACTORY_NAME_PROPERTY =
+ "org.apache.commons.jxpath.JXPathContextFactory";
+
+ /** The default factory class */
+ private static final String DEFAULT_FACTORY_CLASS =
+ "org.apache.commons.jxpath.ri.JXPathContextFactoryReferenceImpl";
+
+ /** Avoid reading all the files when the findFactory
+ method is called the second time ( cache the result of
+ finding the default impl )
+ */
+ private static final String FACTORY_IMPL_NAME = findFactory( FACTORY_NAME_PROPERTY, DEFAULT_FACTORY_CLASS );
+
+ /**
+ * Create a new JXPathContextFactory.
+ */
+ protected JXPathContextFactory () {
+
+ }
+
+ /**
+ * Obtain a new instance of a {@code JXPathContextFactory}.
+ * This static method creates a new factory instance.
+ * This method uses the following ordered lookup procedure to determine
+ * the {@code JXPathContextFactory} implementation class to load:
+ *
+ * -
+ * Use the {@code org.apache.commons.jxpath.JXPathContextFactory}
+ * system property.
+ *
+ * -
+ * Alternatively, use the JAVA_HOME (the parent directory where jdk is
+ * installed)/lib/jxpath.properties for a property file that contains the
+ * name of the implementation class keyed on
+ * {@code org.apache.commons.jxpath.JXPathContextFactory}.
+ *
+ * -
+ * Use the Services API (as detailed in the JAR specification), if
+ * available, to determine the class name. The Services API will look
+ * for a class name in the file
+ * {@code META- INF/services/org.apache.commons.jxpath.
+ * JXPathContextFactory} in jars available to the runtime.
+ *
+ * -
+ * Platform default {@code JXPathContextFactory} instance.
+ *
+ *
+ *
+ * Once an application has obtained a reference to a
+ * {@code JXPathContextFactory} it can use the factory to
+ * obtain JXPathContext instances.
+ *
+ * @return JXPathContextFactory
+ * @throws JXPathContextFactoryConfigurationError if the implementation
+ * is not available or cannot be instantiated.
+ */
+ public static JXPathContextFactory newInstance() {
+ JXPathContextFactory factoryImpl;
+ try {
+ final Class clazz = ClassLoaderUtil.getClass( FACTORY_IMPL_NAME, true );
+ factoryImpl = (JXPathContextFactory) clazz.getConstructor().newInstance();
+ }
+ catch (final ReflectiveOperationException ie) {
+ throw new JXPathContextFactoryConfigurationError(ie);
+ }
+ return factoryImpl;
+ }
+
+ /**
+ * Creates a new instance of a JXPathContext using the
+ * currently configured parameters.
+ * @param parentContext parent context
+ * @param contextBean Object bean
+ * @return JXPathContext
+ * @throws JXPathContextFactoryConfigurationError if a JXPathContext
+ * cannot be created which satisfies the configuration requested
+ */
+
+ public abstract JXPathContext newContext(
+ JXPathContext parentContext,
+ Object contextBean);
+
+ // This code is duplicated in all factories.
+ // Keep it in sync or move it to a common place
+ // Because it's small probably it's easier to keep it here
+
+ /** Temp debug code - this will be removed after we test everything
+ */
+ private static boolean debug = false;
+ static {
+ try {
+ debug = System.getProperty("jxpath.debug") != null;
+ }
+ catch (final SecurityException ignore) { // NOPMD
+ // This is ok
+ }
+ }
+
+ /**
+ * Private implementation method - will find the implementation
+ * class in the specified order.
+ * @param property Property name
+ * @param defaultFactory Default implementation, if nothing else is found
+ * @return class name of the JXPathContextFactory
+ */
+ private static String findFactory(final String property, final String defaultFactory) {
+ // Use the factory ID system property first
+ try {
+ final String systemProp = System.getProperty(property);
+ if (systemProp != null) {
+ if (debug) {
+ System.err.println(
+ "JXPath: found system property" + systemProp);
+ }
+ return systemProp;
+ }
+
+ }
+ catch (final SecurityException ignore) { // NOPMD
+ // Ignore
+ }
+
+ // try to read from $java.home/lib/xml.properties
+ try {
+ final String javah = System.getProperty("java.home");
+ final String configFile =
+ javah
+ + File.separator
+ + "lib"
+ + File.separator
+ + "jxpath.properties";
+ final File f = new File(configFile);
+ if (f.exists()) {
+ final Properties props = new Properties();
+ final FileInputStream fis = new FileInputStream(f);
+ try {
+ props.load(fis);
+ }
+ finally {
+ if (fis != null) {
+ try {
+ fis.close();
+ }
+ catch (final IOException ignore) { // NOPMD
+ //swallow
+ }
+ }
+ }
+ final String factory = props.getProperty(property);
+ if (factory != null) {
+ if (debug) {
+ System.err.println(
+ "JXPath: found java.home property " + factory);
+ }
+ return factory;
+ }
+ }
+ }
+ catch (final IOException ex) {
+ if (debug) {
+ ex.printStackTrace();
+ }
+ }
+
+ final String serviceId = "META-INF/services/" + property;
+ // try to find services in CLASSPATH
+ try {
+ final ClassLoader cl = JXPathContextFactory.class.getClassLoader();
+ InputStream is;
+ if (cl == null) {
+ is = ClassLoader.getSystemResourceAsStream(serviceId);
+ }
+ else {
+ is = cl.getResourceAsStream(serviceId);
+ }
+
+ if (is != null) {
+ if (debug) {
+ System.err.println("JXPath: found " + serviceId);
+ }
+ final BufferedReader rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+
+ String factory = null;
+ try {
+ factory = rd.readLine();
+ }
+ finally {
+ try {
+ rd.close();
+ }
+ catch (final IOException ignore) { // NOPMD
+ // Ignore
+ }
+ }
+
+ if (factory != null && !"".equals(factory)) {
+ if (debug) {
+ System.err.println(
+ "JXPath: loaded from services: " + factory);
+ }
+ return factory;
+ }
+ }
+ }
+ catch (final Exception ex) {
+ if (debug) {
+ ex.printStackTrace();
+ }
+ }
+ return defaultFactory;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathContextFactoryConfigurationError.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathContextFactoryConfigurationError.java
new file mode 100644
index 00000000000..5a17fcd94fc
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathContextFactoryConfigurationError.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+/**
+ * Thrown when a problem with configuration with the {@link JXPathContextFactory JXPathContextFactories}
+ * exists. This error will typically be thrown when the class of a
+ * factory specified in the system properties cannot be found
+ * or instantiated.
+ */
+public class JXPathContextFactoryConfigurationError extends Error {
+
+ private static final long serialVersionUID = 1L;
+ /** @serial */
+ private final Exception exception;
+
+ /**
+ * Create a new {@code JXPathContextFactoryConfigurationError} with no
+ * detail mesage.
+ */
+ public JXPathContextFactoryConfigurationError() {
+ this.exception = null;
+ }
+
+ /**
+ * Create a new {@code JXPathContextFactoryConfigurationError} with a
+ * given {@code Exception} base cause of the error.
+ *
+ * @param e The exception to be encapsulated in a
+ * JXPathContextFactoryConfigurationError.
+ */
+ public JXPathContextFactoryConfigurationError(final Exception e) {
+ super(e.toString());
+ this.exception = e;
+ }
+
+ /**
+ * Gets the message (if any) for this error . If there is no
+ * message for the exception and there is an encapsulated
+ * exception then the message of that exception will be returned.
+ *
+ * @return The error message.
+ */
+ @Override
+ public String getMessage () {
+ final String message = super.getMessage();
+ if (message == null && exception != null) {
+ return exception.getMessage();
+ }
+ return message;
+ }
+
+ /**
+ * Gets the actual exception (if any) that caused this exception to
+ * be raised.
+ *
+ * @return The encapsulated exception, or null if there is none.
+ */
+ public Exception getException () {
+ return exception;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathException.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathException.java
new file mode 100644
index 00000000000..729d01d7d63
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathException.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+/**
+ * Thrown in various situations by JXPath; may contain a nested exception.
+ */
+
+public class JXPathException extends RuntimeException {
+ private static final long serialVersionUID = 4306409701468017766L;
+
+ /** @serial */
+ private final Throwable exception;
+
+ /**
+ * Create a new {@code JXPathException} with no
+ * detail mesage.
+ */
+
+ public JXPathException() {
+ this.exception = null;
+ }
+
+ /**
+ * Create a new {@code JXPathException} with
+ * the {@code String } specified as an error message.
+ *
+ * @param msg The error message for the exception.
+ */
+ public JXPathException(final String msg) {
+ super(msg);
+ this.exception = null;
+ }
+
+ /**
+ * Create a new {@code JXPathException} with the
+ * given {@code Exception} base cause and detail message.
+ *
+ * @param msg The detail message.
+ * @param e The exception to be encapsulated in a JXPathException
+ */
+ public JXPathException(final String msg, final Throwable e) {
+ super(msg);
+ this.exception = e;
+ }
+
+ /**
+ * Gets the message (if any) for this error . If there is no
+ * message for the exception and there is an encapsulated
+ * exception then the message of that exception will be returned.
+ *
+ * @return The error message.
+ */
+ @Override
+ public String getMessage() {
+ final String message = super.getMessage();
+ if (exception == null) {
+ return message;
+ }
+ final StringBuilder buf = new StringBuilder();
+ if (message != null) {
+ buf.append(message).append("; ");
+ }
+ final String eMsg = exception.getMessage();
+ buf.append(eMsg == null ? exception.getClass().getName() : eMsg);
+ return buf.toString();
+ }
+
+ /**
+ * Gets the actual exception (if any) that caused this exception to
+ * be raised.
+ *
+ * @return The encapsulated exception, or null if there is none.
+ */
+ public Throwable getException() {
+ return exception;
+ }
+
+ /**
+ * Same as {@link #getException() getException()}
+ * @return The encapsulated exception, or null if there is none.
+ */
+ @Override
+ public Throwable getCause() {
+ return exception;
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathFunctionNotFoundException.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathFunctionNotFoundException.java
new file mode 100644
index 00000000000..40ec35efdf7
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathFunctionNotFoundException.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+/**
+ * Thrown when JXPath encounters an unknown extension function.
+ */
+
+public class JXPathFunctionNotFoundException extends JXPathException {
+
+ private static final long serialVersionUID = -8875537628056117241L;
+
+ /**
+ * Create a new JXPathFunctionNotFoundException.
+ * @param message exception message
+ */
+ public JXPathFunctionNotFoundException(final String message) {
+ super(message);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathIntrospector.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathIntrospector.java
new file mode 100644
index 00000000000..d2bc9b1748a
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathIntrospector.java
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.jxpath.util.ClassLoaderUtil;
+
+/**
+ * JXPathIntrospector maintains a registry of {@link JXPathBeanInfo
+ * JXPathBeanInfo} objects for Java classes.
+ */
+public class JXPathIntrospector {
+
+ private static Map byClass = Collections.synchronizedMap(new HashMap());
+ private static Map byInterface = Collections.synchronizedMap(new HashMap());
+
+ static {
+ registerAtomicClass(Class.class);
+ registerAtomicClass(Boolean.TYPE);
+ registerAtomicClass(Boolean.class);
+ registerAtomicClass(Byte.TYPE);
+ registerAtomicClass(Byte.class);
+ registerAtomicClass(Character.TYPE);
+ registerAtomicClass(Character.class);
+ registerAtomicClass(Short.TYPE);
+ registerAtomicClass(Short.class);
+ registerAtomicClass(Integer.TYPE);
+ registerAtomicClass(Integer.class);
+ registerAtomicClass(Long.TYPE);
+ registerAtomicClass(Long.class);
+ registerAtomicClass(Float.TYPE);
+ registerAtomicClass(Float.class);
+ registerAtomicClass(Double.TYPE);
+ registerAtomicClass(Double.class);
+ registerAtomicClass(String.class);
+ registerAtomicClass(Date.class);
+ registerAtomicClass(java.sql.Date.class);
+ registerAtomicClass(java.sql.Time.class);
+ registerAtomicClass(java.sql.Timestamp.class);
+
+ registerDynamicClass(Map.class, MapDynamicPropertyHandler.class);
+ }
+
+ /**
+ * Automatically creates and registers a JXPathBeanInfo object
+ * for the specified class. That object returns true to isAtomic().
+ * @param beanClass to register
+ */
+ public static void registerAtomicClass(final Class beanClass) {
+ synchronized (byClass) {
+ byClass.put(beanClass, new JXPathBasicBeanInfo(beanClass, true));
+ }
+ }
+
+ /**
+ * Automatically creates and registers a {@link JXPathBeanInfo} object
+ * for the specified class. That object returns true to
+ * {@link JXPathBeanInfo#isDynamic()}.
+ *
+ * @param beanClass to register
+ * @param dynamicPropertyHandlerClass to handle beanClass
+ */
+ public static void registerDynamicClass(final Class beanClass,
+ final Class dynamicPropertyHandlerClass) {
+ final JXPathBasicBeanInfo bi =
+ new JXPathBasicBeanInfo(beanClass, dynamicPropertyHandlerClass);
+ if (beanClass.isInterface()) {
+ synchronized (byInterface) {
+ byInterface.put(beanClass, bi);
+ }
+ }
+ else {
+ synchronized (byClass) {
+ byClass.put(beanClass, bi);
+ }
+ }
+ }
+
+ /**
+ * Creates and registers a JXPathBeanInfo object for the supplied class. If
+ * the class has already been registered, returns the registered
+ * JXPathBeanInfo object.
+ *
+ * The process of creation of JXPathBeanInfo is as follows:
+ *
+ * - If class named {@code XBeanInfo} exists,
+ * an instance of that class is allocated.
+ *
- Otherwise, an instance of {@link JXPathBasicBeanInfo
+ * JXPathBasicBeanInfo} is allocated.
+ *
+ * @param beanClass whose info to get
+ * @return JXPathBeanInfo
+ */
+ public static JXPathBeanInfo getBeanInfo(final Class beanClass) {
+ JXPathBeanInfo beanInfo = (JXPathBeanInfo) byClass.get(beanClass);
+ if (beanInfo == null) {
+ beanInfo = findDynamicBeanInfo(beanClass);
+ if (beanInfo == null) {
+ beanInfo = findInformant(beanClass);
+ if (beanInfo == null) {
+ beanInfo = new JXPathBasicBeanInfo(beanClass);
+ }
+ }
+ synchronized (byClass) {
+ byClass.put(beanClass, beanInfo);
+ }
+ }
+ return beanInfo;
+ }
+
+ /**
+ * Find a dynamic bean info if available for any superclasses or
+ * interfaces.
+ * @param beanClass to search for
+ * @return JXPathBeanInfo
+ */
+ private static JXPathBeanInfo findDynamicBeanInfo(final Class beanClass) {
+ JXPathBeanInfo beanInfo;
+ if (beanClass.isInterface()) {
+ beanInfo = (JXPathBeanInfo) byInterface.get(beanClass);
+ if (beanInfo != null && beanInfo.isDynamic()) {
+ return beanInfo;
+ }
+ }
+
+ final Class[] interfaces = beanClass.getInterfaces();
+ if (interfaces != null) {
+ for (final Class element : interfaces) {
+ beanInfo = findDynamicBeanInfo(element);
+ if (beanInfo != null && beanInfo.isDynamic()) {
+ return beanInfo;
+ }
+ }
+ }
+
+ final Class sup = beanClass.getSuperclass();
+ if (sup != null) {
+ beanInfo = (JXPathBeanInfo) byClass.get(sup);
+ if (beanInfo != null && beanInfo.isDynamic()) {
+ return beanInfo;
+ }
+ return findDynamicBeanInfo(sup);
+ }
+ return null;
+ }
+
+ /**
+ * find a JXPathBeanInfo instance for the specified class.
+ * Similar to javax.beans property handler discovery; search for a
+ * class with "XBeanInfo" appended to beanClass.name, then check
+ * whether beanClass implements JXPathBeanInfo for itself.
+ * Invokes the default constructor for any class it finds.
+ * @param beanClass for which to look for an info provider
+ * @return JXPathBeanInfo instance or null if none found
+ */
+ private static synchronized JXPathBeanInfo findInformant(final Class beanClass) {
+ final String name = beanClass.getName() + "XBeanInfo";
+ try {
+ return (JXPathBeanInfo) instantiate(beanClass, name);
+ }
+ catch (final Exception ignore) { // NOPMD
+ // Just drop through
+ }
+
+ // Now try checking if the bean is its own JXPathBeanInfo.
+ try {
+ if (JXPathBeanInfo.class.isAssignableFrom(beanClass)) {
+ return (JXPathBeanInfo) beanClass.getConstructor().newInstance();
+ }
+ }
+ catch (final Exception ignore) { // NOPMD
+ // Just drop through
+ }
+
+ return null;
+ }
+
+ /**
+ * Try to create an instance of a named class.
+ * First try the classloader of "sibling", then try the system
+ * classloader.
+ * @param sibling Class
+ * @param className to instantiate
+ * @return new Object
+ * @throws Exception if instantiation fails
+ */
+ private static Object instantiate(final Class sibling, final String className)
+ throws Exception {
+
+ // First check with sibling's classloader (if any).
+ final ClassLoader cl = sibling.getClassLoader();
+ if (cl != null) {
+ try {
+ final Class cls = cl.loadClass(className);
+ return cls.getConstructor().newInstance();
+ }
+ catch (final Exception ex) { //NOPMD
+ // Just drop through and use the ClassLoaderUtil.
+ }
+ }
+
+ // Now try the ClassLoaderUtil.
+ final Class cls = ClassLoaderUtil.getClass(className);
+ return cls.newInstance();
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathInvalidAccessException.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathInvalidAccessException.java
new file mode 100644
index 00000000000..7042b2d3e95
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathInvalidAccessException.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+/**
+ * Similary to {@link java.lang.reflect.InvocationTargetException} in that
+ * it is thrown when JXPath cannot access properties, collection etc on the
+ * target object model.
+ */
+public class JXPathInvalidAccessException extends JXPathException {
+
+ private static final long serialVersionUID = -8875537628056117241L;
+
+ /**
+ * Create a new JXPathInvalidAccessException.
+ * @param message exception message
+ */
+ public JXPathInvalidAccessException(final String message) {
+ super(message);
+ }
+
+ /**
+ * Create a new JXPathInvalidAccessException.
+ * @param message exception message
+ * @param ex precipitating exception
+ */
+ public JXPathInvalidAccessException(final String message, final Throwable ex) {
+ super(message, ex);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathInvalidSyntaxException.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathInvalidSyntaxException.java
new file mode 100644
index 00000000000..a82a7522abd
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathInvalidSyntaxException.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+/**
+ * Thrown when JXPath cannot parse a supplied XPath.
+ */
+
+public class JXPathInvalidSyntaxException extends JXPathException {
+ private static final long serialVersionUID = 504555366032561816L;
+
+ /**
+ * Create a new JXPathInvalidSyntaxException.
+ * @param message relevant message
+ */
+ public JXPathInvalidSyntaxException(final String message) {
+ super(message);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathNotFoundException.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathNotFoundException.java
new file mode 100644
index 00000000000..56020eb996d
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathNotFoundException.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+/**
+ * Thrown when JXPath cannot find a requested path.
+ */
+public class JXPathNotFoundException extends JXPathException {
+
+ private static final long serialVersionUID = -8875537628056117241L;
+
+ /**
+ * Create a new JXPathNotFoundException.
+ * @param message exception detail
+ */
+ public JXPathNotFoundException(final String message) {
+ super(message);
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathTypeConversionException.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathTypeConversionException.java
new file mode 100644
index 00000000000..8af26d214e7
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/JXPathTypeConversionException.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+/**
+ * Thrown when JXPath cannot convert supplied value to the required type.
+ */
+
+public class JXPathTypeConversionException extends JXPathException {
+
+ private static final long serialVersionUID = -4403564377958943239L;
+
+ /**
+ * Create a new JXPathTypeConversionException.
+ * @param message exception text
+ */
+ public JXPathTypeConversionException(final String message) {
+ super(message);
+ }
+
+ /**
+ * Create a new JXPathTypeConversionException.
+ * @param message exception text
+ * @param ex underlying cause
+ */
+ public JXPathTypeConversionException(final String message, final Exception ex) {
+ super(message, ex);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/KeyManager.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/KeyManager.java
new file mode 100644
index 00000000000..3724a1928de
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/KeyManager.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+/**
+ * A delegate of {@link JXPathContext} that implements the XPath {@code "key()"}
+ * function.
+ */
+public interface KeyManager {
+
+ /**
+ * Find a node by key/value.
+ *
+ * @param context to search
+ * @param keyName String
+ * @param keyValue String
+ * @return Pointer
+ */
+ Pointer getPointerByKey(
+ JXPathContext context,
+ String keyName,
+ String keyValue);
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/MapDynamicPropertyHandler.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/MapDynamicPropertyHandler.java
new file mode 100644
index 00000000000..fb519529a33
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/MapDynamicPropertyHandler.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Implements the DynamicPropertyHandler interface for {@link java.util.Map}.
+ */
+public class MapDynamicPropertyHandler implements DynamicPropertyHandler {
+
+ @Override
+ public String[] getPropertyNames(final Object object) {
+ final Map map = (Map) object;
+ final Set set = map.keySet();
+ final String[] names = new String[set.size()];
+ final Iterator it = set.iterator();
+ for (int i = 0; i < names.length; i++) {
+ names[i] = String.valueOf(it.next());
+ }
+ return names;
+ }
+
+ @Override
+ public Object getProperty(final Object object, final String propertyName) {
+ return ((Map) object).get(propertyName);
+ }
+
+ @Override
+ public void setProperty(final Object object, final String propertyName, final Object value) {
+ ((Map) object).put(propertyName, value);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/NodeSet.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/NodeSet.java
new file mode 100644
index 00000000000..0581c1964f3
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/NodeSet.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+import java.util.List;
+
+/**
+ * NodeSet interface can be used as the type of an argument of an extension
+ * function. Alternatively, the function can declare the argument as
+ * a Collection (or List or Set), in which case it will be given a collection
+ * of values matching the path.
+ */
+public interface NodeSet {
+
+ /**
+ * Returns a list of nodes.
+ * @return List
+ */
+ List getNodes();
+
+ /**
+ * Returns a list of pointers for all nodes in the set.
+ * @return List
+ */
+ List getPointers();
+
+ /**
+ * Returns a list of values of all contained pointers.
+ * @return List
+ */
+ List getValues();
+
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/PackageFunctions.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/PackageFunctions.java
new file mode 100644
index 00000000000..c2e14b5ff88
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/PackageFunctions.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.Set;
+
+import org.apache.commons.jxpath.functions.ConstructorFunction;
+import org.apache.commons.jxpath.functions.MethodFunction;
+import org.apache.commons.jxpath.util.ClassLoaderUtil;
+import org.apache.commons.jxpath.util.MethodLookupUtils;
+import org.apache.commons.jxpath.util.TypeUtils;
+
+/**
+ * Extension functions provided by Java classes. The class prefix specified
+ * in the constructor is used when a constructor or a static method is called.
+ * Usually, a class prefix is a package name (hence the name of this class).
+ *
+ * Let's say, we declared a PackageFunction like this:
+ *
+ * new PackageFunctions("java.util.", "util")
+ *
+ *
+ * We can now use XPaths like:
+ *
+ * - {@code "util:Date.new()"}
+ * - Equivalent to {@code new java.util.Date()}
+ * - {@code "util:Collections.singleton('foo')"}
+ * - Equivalent to {@code java.util.Collections.singleton("foo")}
+ * - {@code "util:substring('foo', 1, 2)"}
+ * - Equivalent to {@code "foo".substring(1, 2)}. Note that in
+ * this case, the class prefix is not used. JXPath does not check that
+ * the first parameter of the function (the method target) is in fact
+ * a member of the package described by this PackageFunctions object.
+ *
+ *
+ *
+ * If the first argument of a method or constructor is {@link ExpressionContext},
+ * the expression context in which the function is evaluated is passed to
+ * the method.
+ *
+ *
+ * There is one PackageFunctions object registered by default with each
+ * JXPathContext. It does not have a namespace and uses no class prefix.
+ * The existence of this object allows us to use XPaths like:
+ * {@code "java.util.Date.new()"} and {@code "length('foo')"}
+ * without the explicit registration of any extension functions.
+ *
+ */
+public class PackageFunctions implements Functions {
+ private final String classPrefix;
+ private final String namespace;
+ private static final Object[] EMPTY_ARRAY = {};
+
+ /**
+ * Create a new PackageFunctions.
+ * @param classPrefix class prefix
+ * @param namespace namespace String
+ */
+ public PackageFunctions(final String classPrefix, final String namespace) {
+ this.classPrefix = classPrefix;
+ this.namespace = namespace;
+ }
+
+ /**
+ * Returns the namespace specified in the constructor
+ * @return (singleton) namespace Set
+ */
+ @Override
+ public Set getUsedNamespaces() {
+ return Collections.singleton(namespace);
+ }
+
+ /**
+ * Returns a {@link Function}, if found, for the specified namespace,
+ * name and parameter types.
+ *
+ * @param namespace - if it is not the same as specified in the
+ * construction, this method returns null
+ * @param name - name of the method, which can one these forms:
+ *
+ * - methodname, if invoking a method on an object passed as the
+ * first parameter
+ * - Classname.new, if looking for a constructor
+ * - subpackage.subpackage.Classname.new, if looking for a
+ * constructor in a subpackage
+ * - Classname.methodname, if looking for a static method
+ * - subpackage.subpackage.Classname.methodname, if looking for a
+ * static method of a class in a subpackage
+ *
+ * @param parameters Object[] of parameters
+ * @return a MethodFunction, a ConstructorFunction or null if no function
+ * is found
+ */
+ @Override
+ public Function getFunction(
+ final String namespace,
+ final String name,
+ Object[] parameters) {
+ if (!Objects.equals(this.namespace, namespace)) {
+ return null;
+ }
+
+ if (parameters == null) {
+ parameters = EMPTY_ARRAY;
+ }
+
+ if (parameters.length >= 1) {
+ Object target = TypeUtils.convert(parameters[0], Object.class);
+ if (target != null) {
+ Method method =
+ MethodLookupUtils.lookupMethod(
+ target.getClass(),
+ name,
+ parameters);
+ if (method != null) {
+ return new MethodFunction(method);
+ }
+
+ if (target instanceof NodeSet) {
+ target = ((NodeSet) target).getPointers();
+ }
+
+ method =
+ MethodLookupUtils.lookupMethod(
+ target.getClass(),
+ name,
+ parameters);
+ if (method != null) {
+ return new MethodFunction(method);
+ }
+
+ if (target instanceof Collection) {
+ final Iterator iter = ((Collection) target).iterator();
+ if (iter.hasNext()) {
+ target = iter.next();
+ if (target instanceof Pointer) {
+ target = ((Pointer) target).getValue();
+ }
+ }
+ else {
+ target = null;
+ }
+ }
+ }
+ if (target != null) {
+ final Method method =
+ MethodLookupUtils.lookupMethod(
+ target.getClass(),
+ name,
+ parameters);
+ if (method != null) {
+ return new MethodFunction(method);
+ }
+ }
+ }
+
+ final String fullName = classPrefix + name;
+ final int inx = fullName.lastIndexOf('.');
+ if (inx == -1) {
+ return null;
+ }
+
+ final String className = fullName.substring(0, inx);
+ final String methodName = fullName.substring(inx + 1);
+
+ Class functionClass;
+ try {
+ functionClass = ClassLoaderUtil.getClass(className, true);
+ }
+ catch (final ClassNotFoundException ex) {
+ throw new JXPathException(
+ "Cannot invoke extension function "
+ + (namespace != null ? namespace + ":" + name : name),
+ ex);
+ }
+
+ if (methodName.equals("new")) {
+ final Constructor constructor =
+ MethodLookupUtils.lookupConstructor(functionClass, parameters);
+ if (constructor != null) {
+ return new ConstructorFunction(constructor);
+ }
+ }
+ else {
+ final Method method =
+ MethodLookupUtils.lookupStaticMethod(
+ functionClass,
+ methodName,
+ parameters);
+ if (method != null) {
+ return new MethodFunction(method);
+ }
+ }
+ return null;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/Pointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/Pointer.java
new file mode 100644
index 00000000000..a467c07492d
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/Pointer.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+import java.io.Serializable;
+
+/**
+ * Pointers represent locations of objects and their properties
+ * in Java object graphs. JXPathContext has methods
+ * ({@link JXPathContext#getPointer(java.lang.String) getPointer()}
+ * and ({@link JXPathContext#iteratePointers(java.lang.String)
+ * iteratePointers()}, which, given an XPath, produce Pointers for the objects
+ * or properties described the path. For example, {@code ctx.getPointer
+ * ("foo/bar")} will produce a Pointer that can get and set the property
+ * "bar" of the object which is the value of the property "foo" of the root
+ * object. The value of {@code ctx.getPointer("aMap/aKey[3]")} will be a
+ * pointer to the 3'rd element of the array, which is the value for the key
+ * "aKey" of the map, which is the value of the property "aMap" of the root
+ * object.
+ */
+public interface Pointer extends Cloneable, Comparable, Serializable {
+
+ /**
+ * Returns the value of the object, property or collection element
+ * this pointer represents. May convert the value to one of the
+ * canonical InfoSet types: String, Number, Boolean, Set.
+ *
+ * For example, in the case of an XML element, getValue() will
+ * return the text contained by the element rather than
+ * the element itself.
+ * @return Object value
+ */
+ Object getValue();
+
+ /**
+ * Returns the raw value of the object, property or collection element
+ * this pointer represents. Never converts the object to a
+ * canonical type: returns it as is.
+ *
+ * For example, for an XML element, getNode() will
+ * return the element itself rather than the text it contains.
+ * @return Object node
+ */
+ Object getNode();
+
+ /**
+ * Modifies the value of the object, property or collection element
+ * this pointer represents.
+ * @param value value to set
+ */
+ void setValue(Object value);
+
+ /**
+ * Returns the node this pointer is based on.
+ * @return Object
+ */
+ Object getRootNode();
+
+ /**
+ * Returns a string that is a proper "canonical" XPath that corresponds to
+ * this pointer. Consider this example:
+ * {@code Pointer ptr = ctx.getPointer("//employees[firstName = 'John']")
+ * }
+ *
The value of {@code ptr.asPath()} will look something like
+ * {@code "/departments[2]/employees[3]"}, so, basically, it represents
+ * the concrete location(s) of the result of a search performed by JXPath.
+ * If an object in the pointer's path is a Dynamic Property object (like a
+ * Map), the asPath method generates an XPath that looks like this: {@code "
+ * /departments[@name = 'HR']/employees[3]"}.
+ * @return String path
+ */
+ String asPath();
+
+ /**
+ * Pointers are cloneable.
+ * @return cloned Object
+ */
+ Object clone();
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/Variables.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/Variables.java
new file mode 100644
index 00000000000..6f0280e2863
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/Variables.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath;
+
+import java.io.Serializable;
+
+/**
+ * Variables provide access to a global set of values accessible via XPath.
+ * XPath can reference variables using the {@code "$varname"} syntax.
+ * To use a custom implementation of this interface, pass it to
+ * {@link JXPathContext#setVariables JXPathContext.setVariables()}
+ */
+public interface Variables extends Serializable {
+
+ /**
+ * Returns true if the specified variable is declared.
+ * @param varName variable name
+ * @return boolean
+ */
+ boolean isDeclaredVariable(String varName);
+
+ /**
+ * Returns the value of the specified variable.
+ * @param varName variable name
+ * @return Object value
+ * @throws IllegalArgumentException if there is no such variable.
+ */
+ Object getVariable(String varName);
+
+ /**
+ * Defines a new variable with the specified value or modifies
+ * the value of an existing variable.
+ * May throw UnsupportedOperationException.
+ * @param varName variable name
+ * @param value to declare
+ */
+ void declareVariable(String varName, Object value);
+
+ /**
+ * Removes an existing variable. May throw UnsupportedOperationException.
+ *
+ * @param varName is a variable name without the "$" sign
+ */
+ void undeclareVariable(String varName);
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/functions/ConstructorFunction.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/functions/ConstructorFunction.java
new file mode 100644
index 00000000000..9c07f45fcfc
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/functions/ConstructorFunction.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.functions;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import org.apache.commons.jxpath.ExpressionContext;
+import org.apache.commons.jxpath.Function;
+import org.apache.commons.jxpath.JXPathInvalidAccessException;
+import org.apache.commons.jxpath.util.TypeUtils;
+
+/**
+ * An extension function that creates an instance using a constructor.
+ */
+public class ConstructorFunction implements Function {
+ private static final Object[] EMPTY_ARRAY = {};
+
+ private final Constructor constructor;
+
+ /**
+ * Create a new ConstructorFunction.
+ * @param constructor the constructor to call.
+ */
+ public ConstructorFunction(final Constructor constructor) {
+ this.constructor = constructor;
+ }
+
+ /**
+ * Converts parameters to suitable types and invokes the constructor.
+ * @param context evaluation context
+ * @param parameters constructor args
+ * @return new instance
+ */
+ @Override
+ public Object invoke(final ExpressionContext context, Object[] parameters) {
+ try {
+ Object[] args;
+ if (parameters == null) {
+ parameters = EMPTY_ARRAY;
+ }
+ int pi = 0;
+ final Class[] types = constructor.getParameterTypes();
+ if (types.length > 0
+ && ExpressionContext.class.isAssignableFrom(types[0])) {
+ pi = 1;
+ }
+ args = new Object[parameters.length + pi];
+ if (pi == 1) {
+ args[0] = context;
+ }
+ for (int i = 0; i < parameters.length; i++) {
+ args[i + pi] = TypeUtils.convert(parameters[i], types[i + pi]);
+ }
+ return constructor.newInstance(args);
+ }
+ catch (Throwable ex) {
+ if (ex instanceof InvocationTargetException) {
+ ex = ((InvocationTargetException) ex).getTargetException();
+ }
+ throw new JXPathInvalidAccessException(
+ "Cannot invoke constructor " + constructor,
+ ex);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/functions/MethodFunction.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/functions/MethodFunction.java
new file mode 100644
index 00000000000..28dd87d3162
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/functions/MethodFunction.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.functions;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import org.apache.commons.jxpath.ExpressionContext;
+import org.apache.commons.jxpath.Function;
+import org.apache.commons.jxpath.JXPathInvalidAccessException;
+import org.apache.commons.jxpath.util.TypeUtils;
+import org.apache.commons.jxpath.util.ValueUtils;
+
+/**
+ * An XPath extension function implemented as an individual Java method.
+ */
+public class MethodFunction implements Function {
+
+ private final Method method;
+ private static final Object[] EMPTY_ARRAY = {};
+
+ /**
+ * Create a new MethodFunction.
+ * @param method implementing Method
+ */
+ public MethodFunction(final Method method) {
+ this.method = ValueUtils.getAccessibleMethod(method);
+ }
+
+ @Override
+ public Object invoke(final ExpressionContext context, Object[] parameters) {
+ try {
+ Object target;
+ Object[] args;
+ if (Modifier.isStatic(method.getModifiers())) {
+ target = null;
+ if (parameters == null) {
+ parameters = EMPTY_ARRAY;
+ }
+ int pi = 0;
+ final Class[] types = method.getParameterTypes();
+ if (types.length >= 1
+ && ExpressionContext.class.isAssignableFrom(types[0])) {
+ pi = 1;
+ }
+ args = new Object[parameters.length + pi];
+ if (pi == 1) {
+ args[0] = context;
+ }
+ for (int i = 0; i < parameters.length; i++) {
+ args[i + pi] =
+ TypeUtils.convert(parameters[i], types[i + pi]);
+ }
+ }
+ else {
+ int pi = 0;
+ final Class[] types = method.getParameterTypes();
+ if (types.length >= 1
+ && ExpressionContext.class.isAssignableFrom(types[0])) {
+ pi = 1;
+ }
+ target =
+ TypeUtils.convert(
+ parameters[0],
+ method.getDeclaringClass());
+ args = new Object[parameters.length - 1 + pi];
+ if (pi == 1) {
+ args[0] = context;
+ }
+ for (int i = 1; i < parameters.length; i++) {
+ args[pi + i - 1] =
+ TypeUtils.convert(parameters[i], types[i + pi - 1]);
+ }
+ }
+
+ return method.invoke(target, args);
+ }
+ catch (Throwable ex) {
+ if (ex instanceof InvocationTargetException) {
+ ex = ((InvocationTargetException) ex).getTargetException();
+ }
+ throw new JXPathInvalidAccessException("Cannot invoke " + method,
+ ex);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return method.toString();
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/Compiler.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/Compiler.java
new file mode 100644
index 00000000000..bfe2868d318
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/Compiler.java
@@ -0,0 +1,346 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri;
+
+/**
+ * The Compiler APIs are completely agnostic to the actual types of objects
+ * produced and consumed by the APIs. Arguments and return values are
+ * declared as java.lang.Object.
+ *
+ * Since objects returned by Compiler methods are passed as arguments to other
+ * Compiler methods, the descriptions of these methods use virtual types. There
+ * are four virtual object types: EXPRESSION, QNAME, STEP and NODE_TEST.
+ *
+ * The following example illustrates this notion. This sequence compiles
+ * the xpath "foo[round(1 div 2)]/text()":
+ *
+ * Object qname1 = compiler.qname(null, "foo")
+ * Object expr1 = compiler.number("1");
+ * Object expr2 = compiler.number("2");
+ * Object expr3 = compiler.div(expr1, expr2);
+ * Object expr4 = compiler.
+ * coreFunction(Compiler.FUNCTION_ROUND, new Object[]{expr3});
+ * Object test1 = compiler.nodeNameTest(qname1);
+ * Object step1 = compiler.
+ * step(Compiler.AXIS_CHILD, test1, new Object[]{expr4});
+ * Object test2 = compiler.nodeTypeTest(Compiler.NODE_TYPE_TEXT);
+ * Object step2 = compiler.nodeTypeTest(Compiler.AXIS_CHILD, test2, null);
+ * Object expr5 = compiler.locationPath(false, new Object[]{step1, step2});
+ *
+ */
+public interface Compiler {
+
+ int NODE_TYPE_NODE = 1;
+ int NODE_TYPE_TEXT = 2;
+ int NODE_TYPE_COMMENT = 3;
+ int NODE_TYPE_PI = 4;
+
+ int AXIS_SELF = 1;
+ int AXIS_CHILD = 2;
+ int AXIS_PARENT = 3;
+ int AXIS_ANCESTOR = 4;
+ int AXIS_ATTRIBUTE = 5;
+ int AXIS_NAMESPACE = 6;
+ int AXIS_PRECEDING = 7;
+ int AXIS_FOLLOWING = 8;
+ int AXIS_DESCENDANT = 9;
+ int AXIS_ANCESTOR_OR_SELF = 10;
+ int AXIS_FOLLOWING_SIBLING = 11;
+ int AXIS_PRECEDING_SIBLING = 12;
+ int AXIS_DESCENDANT_OR_SELF = 13;
+
+ int FUNCTION_LAST = 1;
+ int FUNCTION_POSITION = 2;
+ int FUNCTION_COUNT = 3;
+ int FUNCTION_ID = 4;
+ int FUNCTION_LOCAL_NAME = 5;
+ int FUNCTION_NAMESPACE_URI = 6;
+ int FUNCTION_NAME = 7;
+ int FUNCTION_STRING = 8;
+ int FUNCTION_CONCAT = 9;
+ int FUNCTION_STARTS_WITH = 10;
+ int FUNCTION_CONTAINS = 11;
+ int FUNCTION_SUBSTRING_BEFORE = 12;
+ int FUNCTION_SUBSTRING_AFTER = 13;
+ int FUNCTION_SUBSTRING = 14;
+ int FUNCTION_STRING_LENGTH = 15;
+ int FUNCTION_NORMALIZE_SPACE = 16;
+ int FUNCTION_TRANSLATE = 17;
+ int FUNCTION_BOOLEAN = 18;
+ int FUNCTION_NOT = 19;
+ int FUNCTION_TRUE = 20;
+ int FUNCTION_FALSE = 21;
+ int FUNCTION_LANG = 22;
+ int FUNCTION_NUMBER = 23;
+ int FUNCTION_SUM = 24;
+ int FUNCTION_FLOOR = 25;
+ int FUNCTION_CEILING = 26;
+ int FUNCTION_ROUND = 27;
+ int FUNCTION_NULL = 28;
+ int FUNCTION_KEY = 29;
+ int FUNCTION_FORMAT_NUMBER = 30;
+
+ int FUNCTION_ENDS_WITH = 31;
+
+ /**
+ * Produces an EXPRESSION object that represents a numeric constant.
+ * @param value numeric String
+ * @return Object
+ */
+ Object number(String value);
+
+ /**
+ * Produces an EXPRESSION object that represents a string constant.
+ * @param value String literal
+ * @return Object
+ */
+ Object literal(String value);
+
+ /**
+ * Produces an QNAME that represents a name with an optional prefix.
+ * @param prefix String prefix
+ * @param name String name
+ * @return Object
+ */
+ Object qname(String prefix, String name);
+
+ /**
+ * Produces an EXPRESSION object representing the sum of all argumens
+ *
+ * @param arguments are EXPRESSION objects
+ * @return Object
+ */
+ Object sum(Object[] arguments);
+
+ /**
+ * Produces an EXPRESSION object representing left minus right
+ *
+ * @param left is an EXPRESSION object
+ * @param right is an EXPRESSION object
+ * @return Object
+ */
+ Object minus(Object left, Object right);
+
+ /**
+ * Produces an EXPRESSION object representing left multiplied by
+ * right
+ *
+ * @param left is an EXPRESSION object
+ * @param right is an EXPRESSION object
+ * @return Object
+ */
+ Object multiply(Object left, Object right);
+
+ /**
+ * Produces an EXPRESSION object representing left divided by
+ * right
+ *
+ * @param left is an EXPRESSION object
+ * @param right is an EXPRESSION object
+ * @return Object
+ */
+ Object divide(Object left, Object right);
+
+ /**
+ * Produces an EXPRESSION object representing left modulo
+ * right
+ *
+ * @param left is an EXPRESSION object
+ * @param right is an EXPRESSION object
+ * @return Object
+ */
+ Object mod(Object left, Object right);
+
+ /**
+ * Produces an EXPRESSION object representing the comparison:
+ * left less than right
+ *
+ * @param left is an EXPRESSION object
+ * @param right is an EXPRESSION object
+ * @return Object
+ */
+ Object lessThan(Object left, Object right);
+
+ /**
+ * Produces an EXPRESSION object representing the comparison:
+ * left less than or equal to right
+ *
+ * @param left is an EXPRESSION object
+ * @param right is an EXPRESSION object
+ * @return Object
+ */
+ Object lessThanOrEqual(Object left, Object right);
+
+ /**
+ * Produces an EXPRESSION object representing the comparison:
+ * left greater than right
+ *
+ * @param left is an EXPRESSION object
+ * @param right is an EXPRESSION object
+ * @return Object
+ */
+ Object greaterThan(Object left, Object right);
+
+ /**
+ * Produces an EXPRESSION object representing the comparison:
+ * left greater than or equal to right
+ *
+ * @param left is an EXPRESSION object
+ * @param right is an EXPRESSION object
+ * @return Object
+ */
+ Object greaterThanOrEqual(Object left, Object right);
+
+ /**
+ * Produces an EXPRESSION object representing the comparison:
+ * left equals to right
+ *
+ * @param left is an EXPRESSION object
+ * @param right is an EXPRESSION object
+ * @return Object
+ */
+ Object equal(Object left, Object right);
+
+ /**
+ * Produces an EXPRESSION object representing the comparison:
+ * left is not equal to right
+ *
+ * @param left is an EXPRESSION object
+ * @param right is an EXPRESSION object
+ * @return Object
+ */
+ Object notEqual(Object left, Object right);
+
+ /**
+ * Produces an EXPRESSION object representing unary negation of the argument
+ *
+ * @param argument is an EXPRESSION object
+ * @return Object
+ */
+ Object minus(Object argument);
+
+ /**
+ * Produces an EXPRESSION object representing variable reference
+ *
+ * @param qname is a QNAME object
+ * @return Object
+ */
+ Object variableReference(Object qname);
+
+ /**
+ * Produces an EXPRESSION object representing the computation of
+ * a core function with the supplied arguments.
+ *
+ * @param code is one of FUNCTION_... constants
+ * @param args are EXPRESSION objects
+ * @return Object
+ */
+ Object function(int code, Object[] args);
+
+ /**
+ * Produces an EXPRESSION object representing the computation of
+ * a library function with the supplied arguments.
+ *
+ * @param name is a QNAME object (function name)
+ * @param args are EXPRESSION objects
+ * @return Object
+ */
+ Object function(Object name, Object[] args);
+
+ /**
+ * Produces an EXPRESSION object representing logical conjunction of
+ * all arguments
+ *
+ * @param arguments are EXPRESSION objects
+ * @return Object
+ */
+ Object and(Object[] arguments);
+
+ /**
+ * Produces an EXPRESSION object representing logical disjunction of
+ * all arguments
+ *
+ * @param arguments are EXPRESSION objects
+ * @return Object
+ */
+ Object or(Object[] arguments);
+
+ /**
+ * Produces an EXPRESSION object representing union of all node sets
+ *
+ * @param arguments are EXPRESSION objects
+ * @return Object
+ */
+ Object union(Object[] arguments);
+
+ /**
+ * Produces a NODE_TEST object that represents a node name test.
+ *
+ * @param qname is a QNAME object
+ * @return Object
+ */
+ Object nodeNameTest(Object qname);
+
+ /**
+ * Produces a NODE_TEST object that represents a node type test.
+ *
+ * @param nodeType is a NODE_TEST object
+ * @return Object
+ */
+ Object nodeTypeTest(int nodeType);
+
+ /**
+ * Produces a NODE_TEST object that represents a processing instruction
+ * test.
+ *
+ * @param instruction is a NODE_TEST object
+ * @return Object
+ */
+ Object processingInstructionTest(String instruction);
+
+ /**
+ * Produces a STEP object that represents a node test.
+ *
+ * @param axis is one of the AXIS_... constants
+ * @param nodeTest is a NODE_TEST object
+ * @param predicates are EXPRESSION objects
+ * @return Object
+ */
+ Object step(int axis, Object nodeTest, Object[] predicates);
+
+ /**
+ * Produces an EXPRESSION object representing a location path
+ *
+ * @param absolute indicates whether the path is absolute
+ * @param steps are STEP objects
+ * @return Object
+ */
+ Object locationPath(boolean absolute, Object[] steps);
+
+ /**
+ * Produces an EXPRESSION object representing a filter expression
+ *
+ * @param expression is an EXPRESSION object
+ * @param predicates are EXPRESSION objects
+ * @param steps are STEP objects
+ * @return Object
+ */
+ Object expressionPath(
+ Object expression,
+ Object[] predicates,
+ Object[] steps);
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/EvalContext.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/EvalContext.java
new file mode 100644
index 00000000000..49ce88410be
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/EvalContext.java
@@ -0,0 +1,388 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.apache.commons.jxpath.BasicNodeSet;
+import org.apache.commons.jxpath.ExpressionContext;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.JXPathException;
+import org.apache.commons.jxpath.NodeSet;
+import org.apache.commons.jxpath.Pointer;
+import org.apache.commons.jxpath.ri.axes.RootContext;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.util.ReverseComparator;
+
+/**
+ * An XPath evaluation context.
+ *
+ * When evaluating a path, a chain of EvalContexts is created, each context in
+ * the chain representing a step of the path. Subclasses of EvalContext
+ * implement behavior of various XPath axes: "child::", "parent::" etc.
+ */
+public abstract class EvalContext implements ExpressionContext, Iterator {
+ /** Parent context */
+ protected EvalContext parentContext;
+
+ /** Root context */
+ protected RootContext rootContext;
+
+ /** Position */
+ protected int position = 0;
+
+ private boolean startedSetIteration = false;
+ private boolean done = false;
+ private boolean hasPerformedIteratorStep = false;
+ private Iterator pointerIterator;
+
+ /**
+ * Create a new EvalContext.
+ * @param parentContext parent context
+ */
+ public EvalContext(final EvalContext parentContext) {
+ this.parentContext = parentContext;
+ }
+
+ @Override
+ public Pointer getContextNodePointer() {
+ return getCurrentNodePointer();
+ }
+
+ @Override
+ public JXPathContext getJXPathContext() {
+ return getRootContext().getJXPathContext();
+ }
+
+ @Override
+ public int getPosition() {
+ return position;
+ }
+
+ /**
+ * Determines the document order for this context.
+ *
+ * @return 1 ascending order, -1 descending order,
+ * 0 - does not require ordering
+ */
+ public int getDocumentOrder() {
+ return parentContext != null && parentContext.isChildOrderingRequired() ? 1 : 0;
+ }
+
+ /**
+ * Even if this context has the natural ordering and therefore does
+ * not require collecting and sorting all nodes prior to returning them,
+ * such operation may be required for any child context.
+ * @return boolean
+ */
+ public boolean isChildOrderingRequired() {
+ // Default behavior: if this context needs to be ordered,
+ // the children need to be ordered too
+ return getDocumentOrder() != 0;
+ }
+
+ /**
+ * Returns true if there are mode nodes matching the context's constraints.
+ * @return boolean
+ */
+ @Override
+ public boolean hasNext() {
+ if (pointerIterator != null) {
+ return pointerIterator.hasNext();
+ }
+ if (getDocumentOrder() != 0) {
+ return constructIterator();
+ }
+ if (!done && !hasPerformedIteratorStep) {
+ performIteratorStep();
+ }
+ return !done;
+ }
+
+ /**
+ * Returns the next node pointer in the context
+ * @return Object
+ */
+ @Override
+ public Object next() {
+ if (pointerIterator != null) {
+ return pointerIterator.next();
+ }
+
+ if (getDocumentOrder() != 0) {
+ if (!constructIterator()) {
+ throw new NoSuchElementException();
+ }
+ return pointerIterator.next();
+ }
+ if (!done && !hasPerformedIteratorStep) {
+ performIteratorStep();
+ }
+ if (done) {
+ throw new NoSuchElementException();
+ }
+ hasPerformedIteratorStep = false;
+ return getCurrentNodePointer();
+ }
+
+ /**
+ * Moves the iterator forward by one position
+ */
+ private void performIteratorStep() {
+ done = true;
+ if (position != 0 && nextNode()) {
+ done = false;
+ }
+ else {
+ while (nextSet()) {
+ if (nextNode()) {
+ done = false;
+ break;
+ }
+ }
+ }
+ hasPerformedIteratorStep = true;
+ }
+
+ /**
+ * Operation is not supported
+ * @throws UnsupportedOperationException Always thrown.
+ */
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException(
+ "JXPath iterators cannot remove nodes");
+ }
+
+ /**
+ * Constructs an iterator.
+ * @return whether the Iterator was constructed
+ */
+ private boolean constructIterator() {
+ final HashSet set = new HashSet<>();
+ final ArrayList list = new ArrayList<>();
+ while (nextSet()) {
+ while (nextNode()) {
+ final NodePointer pointer = getCurrentNodePointer();
+ if (!set.contains(pointer)) {
+ set.add(pointer);
+ list.add(pointer);
+ }
+ }
+ }
+ if (list.isEmpty()) {
+ return false;
+ }
+
+ sortPointers(list);
+
+ pointerIterator = list.iterator();
+ return true;
+ }
+
+ /**
+ * Sort a list of pointers based on document order.
+ * @param l the list to sort.
+ */
+ protected void sortPointers(final List l) {
+ switch (getDocumentOrder()) {
+ case 1:
+ Collections.sort(l);
+ break;
+ case -1:
+ Collections.sort(l, ReverseComparator.INSTANCE);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Returns the list of all Pointers in this context for the current
+ * position of the parent context.
+ * @return List
+ */
+ @Override
+ public List getContextNodeList() {
+ final int pos = position;
+ if (pos != 0) {
+ reset();
+ }
+ final List list = new ArrayList<>();
+ while (nextNode()) {
+ list.add(getCurrentNodePointer());
+ }
+ if (pos != 0) {
+ setPosition(pos);
+ }
+ else {
+ reset();
+ }
+ return list;
+ }
+
+ /**
+ * Returns the list of all Pointers in this context for all positions
+ * of the parent contexts. If there was an ongoing iteration over
+ * this context, the method should not be called.
+ * @return NodeSet
+ */
+ public NodeSet getNodeSet() {
+ if (position != 0) {
+ throw new JXPathException(
+ "Simultaneous operations: "
+ + "should not request pointer list while "
+ + "iterating over an EvalContext");
+ }
+ final BasicNodeSet set = new BasicNodeSet();
+ while (nextSet()) {
+ while (nextNode()) {
+ set.add((Pointer) getCurrentNodePointer().clone());
+ }
+ }
+
+ return set;
+ }
+
+ /**
+ * Typically returns the NodeSet by calling getNodeSet(),
+ * but will be overridden for contexts that more naturally produce
+ * individual values, e.g. VariableContext
+ * @return Object
+ */
+ public Object getValue() {
+ return getNodeSet();
+ }
+
+ @Override
+ public String toString() {
+ final Pointer ptr = getContextNodePointer();
+ return ptr == null ? "Empty expression context" : "Expression context [" + getPosition()
+ + "] " + ptr.asPath();
+ }
+
+ /**
+ * Returns the root context of the path, which provides easy
+ * access to variables and functions.
+ * @return RootContext
+ */
+ public RootContext getRootContext() {
+ if (rootContext == null) {
+ rootContext = parentContext.getRootContext();
+ }
+ return rootContext;
+ }
+
+ /**
+ * Sets current position = 0, which is the pre-iteration state.
+ */
+ public void reset() {
+ position = 0;
+ }
+
+ /**
+ * Gets the current position.
+ * @return int position.
+ */
+ public int getCurrentPosition() {
+ return position;
+ }
+
+ /**
+ * Returns the first encountered Pointer that matches the current
+ * context's criteria.
+ * @return Pointer
+ */
+ public Pointer getSingleNodePointer() {
+ reset();
+ while (nextSet()) {
+ if (nextNode()) {
+ return getCurrentNodePointer();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the current context node. Undefined before the beginning
+ * of the iteration.
+ * @return NodePoiner
+ */
+ public abstract NodePointer getCurrentNodePointer();
+
+ /**
+ * Returns true if there is another sets of objects to interate over.
+ * Resets the current position and node.
+ * @return boolean
+ */
+ public boolean nextSet() {
+ reset(); // Restart iteration within the set
+
+ // Most of the time you have one set per parent node
+ // First time this method is called, we should look for
+ // the first parent set that contains at least one node.
+ if (!startedSetIteration) {
+ startedSetIteration = true;
+ while (parentContext.nextSet()) {
+ if (parentContext.nextNode()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // In subsequent calls, we see if the parent context
+ // has any nodes left in the current set
+ if (parentContext.nextNode()) {
+ return true;
+ }
+
+ // If not, we look for the next set that contains
+ // at least one node
+ while (parentContext.nextSet()) {
+ if (parentContext.nextNode()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if there is another object in the current set.
+ * Switches the current position and node to the next object.
+ * @return boolean
+ */
+ public abstract boolean nextNode();
+
+ /**
+ * Moves the current position to the specified index. Used with integer
+ * predicates to quickly get to the n'th element of the node set.
+ * Returns false if the position is out of the node set range.
+ * You can call it with 0 as the position argument to restart the iteration.
+ * @param position to set
+ * @return boolean
+ */
+ public boolean setPosition(final int position) {
+ this.position = position;
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/InfoSetUtil.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/InfoSetUtil.java
new file mode 100644
index 00000000000..b50e05109d5
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/InfoSetUtil.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri;
+
+import org.apache.commons.jxpath.Pointer;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.ri.model.VariablePointer;
+
+/**
+ * Type conversions, XPath style.
+ */
+public class InfoSetUtil {
+
+ private static final Double ZERO = Double.valueOf(0);
+ private static final Double ONE = Double.valueOf(1);
+ private static final Double NOT_A_NUMBER = Double.valueOf(Double.NaN);
+
+ /**
+ * Converts the supplied object to String.
+ * @param object to convert
+ * @return String value
+ */
+ public static String stringValue(final Object object) {
+ if (object instanceof String) {
+ return (String) object;
+ }
+ if (object instanceof Number) {
+ final double d = ((Number) object).doubleValue();
+ final long l = ((Number) object).longValue();
+ return d == l ? String.valueOf(l) : String.valueOf(d);
+ }
+ if (object instanceof Boolean) {
+ return ((Boolean) object).booleanValue() ? "true" : "false";
+ }
+ if (object == null) {
+ return "";
+ }
+ if (object instanceof NodePointer) {
+ return stringValue(((NodePointer) object).getValue());
+ }
+ if (object instanceof EvalContext) {
+ final EvalContext ctx = (EvalContext) object;
+ final Pointer ptr = ctx.getSingleNodePointer();
+ return ptr == null ? "" : stringValue(ptr);
+ }
+ return String.valueOf(object);
+ }
+
+ /**
+ * Converts the supplied object to Number.
+ * @param object to convert
+ * @return Number result
+ */
+ public static Number number(final Object object) {
+ if (object instanceof Number) {
+ return (Number) object;
+ }
+ if (object instanceof Boolean) {
+ return ((Boolean) object).booleanValue() ? ONE : ZERO;
+ }
+ if (object instanceof String) {
+ try {
+ return Double.valueOf((String) object);
+ }
+ catch (final NumberFormatException ex) {
+ return NOT_A_NUMBER;
+ }
+ }
+ if (object instanceof EvalContext) {
+ final EvalContext ctx = (EvalContext) object;
+ final Pointer ptr = ctx.getSingleNodePointer();
+ return ptr == null ? NOT_A_NUMBER : number(ptr);
+ }
+ if (object instanceof NodePointer) {
+ return number(((NodePointer) object).getValue());
+ }
+ return number(stringValue(object));
+ }
+
+ /**
+ * Converts the supplied object to double.
+ * @param object to convert
+ * @return double
+ */
+ public static double doubleValue(final Object object) {
+ if (object instanceof Number) {
+ return ((Number) object).doubleValue();
+ }
+ if (object instanceof Boolean) {
+ return ((Boolean) object).booleanValue() ? 0.0 : 1.0;
+ }
+ if (object instanceof String) {
+ if (object.equals("")) {
+ return 0.0;
+ }
+ try {
+ return Double.parseDouble((String) object);
+ }
+ catch (final NumberFormatException ex) {
+ return Double.NaN;
+ }
+ }
+ if (object instanceof NodePointer) {
+ return doubleValue(((NodePointer) object).getValue());
+ }
+ if (object instanceof EvalContext) {
+ final EvalContext ctx = (EvalContext) object;
+ final Pointer ptr = ctx.getSingleNodePointer();
+ return ptr == null ? Double.NaN : doubleValue(ptr);
+ }
+ return doubleValue(stringValue(object));
+ }
+
+ /**
+ * Converts the supplied object to boolean.
+ * @param object to convert
+ * @return boolean
+ */
+ public static boolean booleanValue(final Object object) {
+ if (object instanceof Number) {
+ final double value = ((Number) object).doubleValue();
+ final int negZero = -0;
+ return value != 0 && value != negZero && !Double.isNaN(value);
+ }
+ if (object instanceof Boolean) {
+ return ((Boolean) object).booleanValue();
+ }
+ if (object instanceof EvalContext) {
+ final EvalContext ctx = (EvalContext) object;
+ final Pointer ptr = ctx.getSingleNodePointer();
+ return ptr != null && booleanValue(ptr);
+ }
+ if (object instanceof String) {
+ return ((String) object).length() != 0;
+ }
+ if (object instanceof NodePointer) {
+ NodePointer pointer = (NodePointer) object;
+ if (pointer instanceof VariablePointer) {
+ return booleanValue(pointer.getNode());
+ }
+ pointer = pointer.getValuePointer();
+ return pointer.isActual();
+ }
+ return object != null;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/JXPathCompiledExpression.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/JXPathCompiledExpression.java
new file mode 100644
index 00000000000..9c7d32ec32b
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/JXPathCompiledExpression.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri;
+
+import java.util.Iterator;
+
+import org.apache.commons.jxpath.CompiledExpression;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.Pointer;
+import org.apache.commons.jxpath.ri.compiler.Expression;
+
+/**
+ * RI of CompiledExpression.
+ */
+public class JXPathCompiledExpression implements CompiledExpression {
+
+ private final String xpath;
+ private final Expression expression;
+
+ /**
+ * Create a new JXPathCompiledExpression.
+ * @param xpath source
+ * @param expression compiled
+ */
+ public JXPathCompiledExpression(final String xpath, final Expression expression) {
+ this.xpath = xpath;
+ this.expression = expression;
+ }
+
+ @Override
+ public String toString() {
+ return xpath;
+ }
+
+ @Override
+ public Object getValue(final JXPathContext context) {
+ return ((JXPathContextReferenceImpl) context).
+ getValue(xpath, expression);
+ }
+
+ @Override
+ public Object getValue(final JXPathContext context, final Class requiredType) {
+ return ((JXPathContextReferenceImpl) context).
+ getValue(xpath, expression, requiredType);
+ }
+
+ @Override
+ public void setValue(final JXPathContext context, final Object value) {
+ ((JXPathContextReferenceImpl) context).
+ setValue(xpath, expression, value);
+ }
+
+ @Override
+ public Pointer createPath(final JXPathContext context) {
+ return ((JXPathContextReferenceImpl) context).
+ createPath(xpath, expression);
+ }
+
+ @Override
+ public Pointer createPathAndSetValue(final JXPathContext context, final Object value) {
+ return ((JXPathContextReferenceImpl) context).
+ createPathAndSetValue(xpath, expression, value);
+ }
+
+ @Override
+ public Iterator iterate(final JXPathContext context) {
+ return ((JXPathContextReferenceImpl) context).
+ iterate(xpath, expression);
+ }
+
+ @Override
+ public Pointer getPointer(final JXPathContext context, final String xpath) {
+ return ((JXPathContextReferenceImpl) context).
+ getPointer(xpath, expression);
+ }
+
+ @Override
+ public Iterator iteratePointers(final JXPathContext context) {
+ return ((JXPathContextReferenceImpl) context).
+ iteratePointers(xpath, expression);
+ }
+
+ @Override
+ public void removePath(final JXPathContext context) {
+ ((JXPathContextReferenceImpl) context).removePath(xpath, expression);
+ }
+
+ @Override
+ public void removeAll(final JXPathContext context) {
+ ((JXPathContextReferenceImpl) context).removeAll(xpath, expression);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/JXPathContextReferenceImpl.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/JXPathContextReferenceImpl.java
new file mode 100644
index 00000000000..3dc78de7c1b
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/JXPathContextReferenceImpl.java
@@ -0,0 +1,809 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri;
+
+import java.lang.ref.SoftReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Vector;
+
+import org.apache.commons.jxpath.CompiledExpression;
+import org.apache.commons.jxpath.ExceptionHandler;
+import org.apache.commons.jxpath.Function;
+import org.apache.commons.jxpath.Functions;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.JXPathException;
+import org.apache.commons.jxpath.JXPathFunctionNotFoundException;
+import org.apache.commons.jxpath.JXPathInvalidSyntaxException;
+import org.apache.commons.jxpath.JXPathNotFoundException;
+import org.apache.commons.jxpath.JXPathTypeConversionException;
+import org.apache.commons.jxpath.Pointer;
+import org.apache.commons.jxpath.ri.axes.InitialContext;
+import org.apache.commons.jxpath.ri.axes.RootContext;
+import org.apache.commons.jxpath.ri.compiler.Expression;
+import org.apache.commons.jxpath.ri.compiler.LocationPath;
+import org.apache.commons.jxpath.ri.compiler.Path;
+import org.apache.commons.jxpath.ri.compiler.TreeCompiler;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.ri.model.NodePointerFactory;
+import org.apache.commons.jxpath.ri.model.VariablePointerFactory;
+import org.apache.commons.jxpath.ri.model.beans.BeanPointerFactory;
+import org.apache.commons.jxpath.ri.model.beans.CollectionPointerFactory;
+import org.apache.commons.jxpath.ri.model.container.ContainerPointerFactory;
+import org.apache.commons.jxpath.ri.model.dynamic.DynamicPointerFactory;
+import org.apache.commons.jxpath.util.ClassLoaderUtil;
+import org.apache.commons.jxpath.util.ReverseComparator;
+import org.apache.commons.jxpath.util.TypeUtils;
+
+/**
+ * The reference implementation of JXPathContext.
+ */
+public class JXPathContextReferenceImpl extends JXPathContext {
+
+ /**
+ * Change this to {@code false} to disable soft caching of
+ * CompiledExpressions.
+ */
+ public static final boolean USE_SOFT_CACHE = true;
+
+ private static final Compiler COMPILER = new TreeCompiler();
+ private static Map compiled = new HashMap();
+ private static int cleanupCount = 0;
+
+ private static NodePointerFactory[] nodeFactoryArray = null;
+ // The frequency of the cache cleanup
+ private static final int CLEANUP_THRESHOLD = 500;
+ private static final Vector nodeFactories = new Vector();
+
+ static {
+ nodeFactories.add(new CollectionPointerFactory());
+ nodeFactories.add(new BeanPointerFactory());
+ nodeFactories.add(new DynamicPointerFactory());
+ nodeFactories.add(new VariablePointerFactory());
+
+ // DOM factory is only registered if DOM support is on the classpath
+ final Object domFactory = allocateConditionally(
+ "org.apache.commons.jxpath.ri.model.dom.DOMPointerFactory",
+ "org.w3c.dom.Node");
+ if (domFactory != null) {
+ nodeFactories.add(domFactory);
+ }
+
+ nodeFactories.add(new ContainerPointerFactory());
+ createNodeFactoryArray();
+ }
+
+ /**
+ * Create the default node factory array.
+ */
+ private static synchronized void createNodeFactoryArray() {
+ if (nodeFactoryArray == null) {
+ nodeFactoryArray =
+ (NodePointerFactory[]) nodeFactories.
+ toArray(new NodePointerFactory[nodeFactories.size()]);
+ Arrays.sort(nodeFactoryArray, (a, b) -> {
+ final int orderA = a.getOrder();
+ final int orderB = b.getOrder();
+ return orderA - orderB;
+ });
+ }
+ }
+
+ /**
+ * Call this with a custom NodePointerFactory to add support for
+ * additional types of objects. Make sure the factory returns
+ * a name that puts it in the right position on the list of factories.
+ * @param factory NodePointerFactory to add
+ */
+ public static void addNodePointerFactory(final NodePointerFactory factory) {
+ synchronized (nodeFactories) {
+ nodeFactories.add(factory);
+ nodeFactoryArray = null;
+ }
+ }
+
+ /**
+ * Gets the registered NodePointerFactories.
+ * @return NodePointerFactory[]
+ */
+ public static NodePointerFactory[] getNodePointerFactories() {
+ return nodeFactoryArray;
+ }
+
+ /** Namespace resolver */
+ protected NamespaceResolver namespaceResolver;
+
+ private Pointer rootPointer;
+ private Pointer contextPointer;
+
+ /**
+ * Create a new JXPathContextReferenceImpl.
+ * @param parentContext parent context
+ * @param contextBean Object
+ * @param contextPointer context pointer
+ */
+ public JXPathContextReferenceImpl(final JXPathContext parentContext,
+ final Object contextBean, final Pointer contextPointer) {
+ super(parentContext, contextBean);
+
+ synchronized (nodeFactories) {
+ createNodeFactoryArray();
+ }
+
+ if (contextPointer != null) {
+ this.contextPointer = contextPointer;
+ this.rootPointer =
+ NodePointer.newNodePointer(
+ new QName(null, "root"),
+ contextPointer.getRootNode(),
+ getLocale());
+ }
+ else {
+ this.contextPointer =
+ NodePointer.newNodePointer(
+ new QName(null, "root"),
+ contextBean,
+ getLocale());
+ this.rootPointer = this.contextPointer;
+ }
+
+ NamespaceResolver parentNR = null;
+ if (parentContext instanceof JXPathContextReferenceImpl) {
+ parentNR = ((JXPathContextReferenceImpl) parentContext).getNamespaceResolver();
+ }
+ namespaceResolver = new NamespaceResolver(parentNR);
+ namespaceResolver
+ .setNamespaceContextPointer((NodePointer) this.contextPointer);
+ }
+
+ /**
+ * Returns a static instance of TreeCompiler.
+ *
+ * Override this to return an alternate compiler.
+ * @return Compiler
+ */
+ protected Compiler getCompiler() {
+ return COMPILER;
+ }
+
+ @Override
+ protected CompiledExpression compilePath(final String xpath) {
+ return new JXPathCompiledExpression(xpath, compileExpression(xpath));
+ }
+
+ /**
+ * Compile the given expression.
+ * @param xpath to compile
+ * @return Expression
+ */
+ private Expression compileExpression(final String xpath) {
+ Expression expr;
+
+ synchronized (compiled) {
+ if (USE_SOFT_CACHE) {
+ expr = null;
+ final SoftReference ref = (SoftReference) compiled.get(xpath);
+ if (ref != null) {
+ expr = (Expression) ref.get();
+ }
+ }
+ else {
+ expr = (Expression) compiled.get(xpath);
+ }
+ }
+
+ if (expr != null) {
+ return expr;
+ }
+
+ expr = (Expression) Parser.parseExpression(xpath, getCompiler());
+
+ synchronized (compiled) {
+ if (USE_SOFT_CACHE) {
+ if (cleanupCount++ >= CLEANUP_THRESHOLD) {
+ final Iterator it = compiled.entrySet().iterator();
+ while (it.hasNext()) {
+ final Entry me = (Entry) it.next();
+ if (((SoftReference) me.getValue()).get() == null) {
+ it.remove();
+ }
+ }
+ cleanupCount = 0;
+ }
+ compiled.put(xpath, new SoftReference(expr));
+ }
+ else {
+ compiled.put(xpath, expr);
+ }
+ }
+
+ return expr;
+ }
+
+ /**
+ * Traverses the xpath and returns the resulting object. Primitive
+ * types are wrapped into objects.
+ * @param xpath expression
+ * @return Object found
+ */
+ @Override
+ public Object getValue(final String xpath) {
+ final Expression expression = compileExpression(xpath);
+// TODO: (work in progress) - trying to integrate with Xalan
+// Object ctxNode = getNativeContextNode(expression);
+// if (ctxNode != null) {
+// System.err.println("WILL USE XALAN: " + xpath);
+// CachedXPathAPI api = new CachedXPathAPI();
+// try {
+// if (expression instanceof Path) {
+// Node node = api.selectSingleNode((Node)ctxNode, xpath);
+// System.err.println("NODE: " + node);
+// if (node == null) {
+// return null;
+// }
+// return new DOMNodePointer(node, null).getValue();
+// }
+// else {
+// XObject object = api.eval((Node)ctxNode, xpath);
+// switch (object.getType()) {
+// case XObject.CLASS_STRING: return object.str();
+// case XObject.CLASS_NUMBER: return new Double(object.num());
+// case XObject.CLASS_BOOLEAN: return new Boolean(object.bool());
+// default:
+// System.err.println("OTHER TYPE: " + object.getTypeString());
+// }
+// }
+// }
+// catch (TransformerException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+// return
+// }
+
+ return getValue(xpath, expression);
+ }
+
+// private Object getNativeContextNode(Expression expression) {
+// Object node = getNativeContextNode(getContextBean());
+// if (node == null) {
+// return null;
+// }
+//
+// List vars = expression.getUsedVariables();
+// if (vars != null) {
+// return null;
+// }
+//
+// return node;
+// }
+
+// private Object getNativeContextNode(Object bean) {
+// if (bean instanceof Number || bean instanceof String || bean instanceof Boolean) {
+// return bean;
+// }
+// if (bean instanceof Node) {
+// return (Node)bean;
+// }
+//
+// if (bean instanceof Container) {
+// bean = ((Container)bean).getValue();
+// return getNativeContextNode(bean);
+// }
+//
+// return null;
+// }
+
+ /**
+ * Gets the value indicated.
+ * @param xpath String
+ * @param expr Expression
+ * @return Object
+ */
+ public Object getValue(final String xpath, final Expression expr) {
+ Object result = expr.computeValue(getEvalContext());
+ if (result == null) {
+ if (expr instanceof Path && !isLenient()) {
+ throw new JXPathNotFoundException("No value for xpath: "
+ + xpath);
+ }
+ return null;
+ }
+ if (result instanceof EvalContext) {
+ final EvalContext ctx = (EvalContext) result;
+ result = ctx.getSingleNodePointer();
+ if (!isLenient() && result == null) {
+ throw new JXPathNotFoundException("No value for xpath: "
+ + xpath);
+ }
+ }
+ if (result instanceof NodePointer) {
+ result = ((NodePointer) result).getValuePointer();
+ if (!isLenient()) {
+ NodePointer.verify((NodePointer) result);
+ }
+ result = ((NodePointer) result).getValue();
+ }
+ return result;
+ }
+
+ /**
+ * Calls getValue(xpath), converts the result to the required type
+ * and returns the result of the conversion.
+ * @param xpath expression
+ * @param requiredType Class
+ * @return Object
+ */
+ @Override
+ public Object getValue(final String xpath, final Class requiredType) {
+ final Expression expr = compileExpression(xpath);
+ return getValue(xpath, expr, requiredType);
+ }
+
+ /**
+ * Gets the value indicated.
+ * @param xpath expression
+ * @param expr compiled Expression
+ * @param requiredType Class
+ * @return Object
+ */
+ public Object getValue(final String xpath, final Expression expr, final Class requiredType) {
+ Object value = getValue(xpath, expr);
+ if (value != null && requiredType != null) {
+ if (!TypeUtils.canConvert(value, requiredType)) {
+ throw new JXPathTypeConversionException(
+ "Invalid expression type. '"
+ + xpath
+ + "' returns "
+ + value.getClass().getName()
+ + ". It cannot be converted to "
+ + requiredType.getName());
+ }
+ value = TypeUtils.convert(value, requiredType);
+ }
+ return value;
+ }
+
+ /**
+ * Traverses the xpath and returns a Iterator of all results found
+ * for the path. If the xpath matches no properties
+ * in the graph, the Iterator will not be null.
+ * @param xpath expression
+ * @return Iterator
+ */
+ @Override
+ public Iterator iterate(final String xpath) {
+ return iterate(xpath, compileExpression(xpath));
+ }
+
+ /**
+ * Traverses the xpath and returns a Iterator of all results found
+ * for the path. If the xpath matches no properties
+ * in the graph, the Iterator will not be null.
+ * @param xpath expression
+ * @param expr compiled Expression
+ * @return Iterator
+ */
+ public Iterator iterate(final String xpath, final Expression expr) {
+ return expr.iterate(getEvalContext());
+ }
+
+ @Override
+ public Pointer getPointer(final String xpath) {
+ return getPointer(xpath, compileExpression(xpath));
+ }
+
+ /**
+ * Gets a pointer to the specified path/expression.
+ * @param xpath String
+ * @param expr compiled Expression
+ * @return Pointer
+ */
+ public Pointer getPointer(final String xpath, final Expression expr) {
+ Object result = expr.computeValue(getEvalContext());
+ if (result instanceof EvalContext) {
+ result = ((EvalContext) result).getSingleNodePointer();
+ }
+ if (result instanceof Pointer) {
+ if (!isLenient() && !((NodePointer) result).isActual()) {
+ throw new JXPathNotFoundException("No pointer for xpath: "
+ + xpath);
+ }
+ return (Pointer) result;
+ }
+ return NodePointer.newNodePointer(null, result, getLocale());
+ }
+
+ @Override
+ public void setValue(final String xpath, final Object value) {
+ setValue(xpath, compileExpression(xpath), value);
+ }
+
+ /**
+ * Sets the value of xpath to value.
+ * @param xpath path
+ * @param expr compiled Expression
+ * @param value Object
+ */
+ public void setValue(final String xpath, final Expression expr, final Object value) {
+ try {
+ setValue(xpath, expr, value, false);
+ }
+ catch (final Throwable ex) {
+ throw new JXPathException(
+ "Exception trying to set value with xpath " + xpath, ex);
+ }
+ }
+
+ @Override
+ public Pointer createPath(final String xpath) {
+ return createPath(xpath, compileExpression(xpath));
+ }
+
+ /**
+ * Create the given path.
+ * @param xpath String
+ * @param expr compiled Expression
+ * @return resulting Pointer
+ */
+ public Pointer createPath(final String xpath, final Expression expr) {
+ try {
+ final Object result = expr.computeValue(getEvalContext());
+ Pointer pointer;
+
+ if (result instanceof Pointer) {
+ pointer = (Pointer) result;
+ }
+ else if (result instanceof EvalContext) {
+ final EvalContext ctx = (EvalContext) result;
+ pointer = ctx.getSingleNodePointer();
+ }
+ else {
+ checkSimplePath(expr);
+ // This should never happen
+ throw new JXPathException("Cannot create path:" + xpath);
+ }
+ return ((NodePointer) pointer).createPath(this);
+ }
+ catch (final Throwable ex) {
+ throw new JXPathException(
+ "Exception trying to create xpath " + xpath,
+ ex);
+ }
+ }
+
+ @Override
+ public Pointer createPathAndSetValue(final String xpath, final Object value) {
+ return createPathAndSetValue(xpath, compileExpression(xpath), value);
+ }
+
+ /**
+ * Create the given path setting its value to value.
+ * @param xpath String
+ * @param expr compiled Expression
+ * @param value Object
+ * @return resulting Pointer
+ */
+ public Pointer createPathAndSetValue(final String xpath, final Expression expr,
+ final Object value) {
+ try {
+ return setValue(xpath, expr, value, true);
+ }
+ catch (final Throwable ex) {
+ throw new JXPathException(
+ "Exception trying to create xpath " + xpath,
+ ex);
+ }
+ }
+
+ /**
+ * Sets the specified value.
+ * @param xpath path
+ * @param expr compiled Expression
+ * @param value destination value
+ * @param create whether to create missing node(s)
+ * @return Pointer created
+ */
+ private Pointer setValue(final String xpath, final Expression expr, final Object value,
+ final boolean create) {
+ final Object result = expr.computeValue(getEvalContext());
+ Pointer pointer;
+
+ if (result instanceof Pointer) {
+ pointer = (Pointer) result;
+ }
+ else if (result instanceof EvalContext) {
+ final EvalContext ctx = (EvalContext) result;
+ pointer = ctx.getSingleNodePointer();
+ }
+ else {
+ if (create) {
+ checkSimplePath(expr);
+ }
+
+ // This should never happen
+ throw new JXPathException("Cannot set value for xpath: " + xpath);
+ }
+ if (create) {
+ pointer = ((NodePointer) pointer).createPath(this, value);
+ }
+ else {
+ pointer.setValue(value);
+ }
+ return pointer;
+ }
+
+ /**
+ * Checks if the path follows the JXPath restrictions on the type
+ * of path that can be passed to create... methods.
+ * @param expr Expression to check
+ */
+ private void checkSimplePath(final Expression expr) {
+ if (!(expr instanceof LocationPath)
+ || !((LocationPath) expr).isSimplePath()) {
+ throw new JXPathInvalidSyntaxException(
+ "JXPath can only create a path if it uses exclusively "
+ + "the child:: and attribute:: axes and has "
+ + "no context-dependent predicates");
+ }
+ }
+
+ /**
+ * Traverses the xpath and returns an Iterator of Pointers.
+ * A Pointer provides easy access to a property.
+ * If the xpath matches no properties
+ * in the graph, the Iterator be empty, but not null.
+ * @param xpath expression
+ * @return Iterator
+ */
+ @Override
+ public Iterator iteratePointers(final String xpath) {
+ return iteratePointers(xpath, compileExpression(xpath));
+ }
+
+ /**
+ * Traverses the xpath and returns an Iterator of Pointers.
+ * A Pointer provides easy access to a property.
+ * If the xpath matches no properties
+ * in the graph, the Iterator be empty, but not null.
+ * @param xpath expression
+ * @param expr compiled Expression
+ * @return Iterator
+ */
+ public Iterator iteratePointers(final String xpath, final Expression expr) {
+ return expr.iteratePointers(getEvalContext());
+ }
+
+ @Override
+ public void removePath(final String xpath) {
+ removePath(xpath, compileExpression(xpath));
+ }
+
+ /**
+ * Remove the specified path.
+ * @param xpath expression
+ * @param expr compiled Expression
+ */
+ public void removePath(final String xpath, final Expression expr) {
+ try {
+ final NodePointer pointer = (NodePointer) getPointer(xpath, expr);
+ if (pointer != null) {
+ pointer.remove();
+ }
+ }
+ catch (final Throwable ex) {
+ throw new JXPathException(
+ "Exception trying to remove xpath " + xpath,
+ ex);
+ }
+ }
+
+ @Override
+ public void removeAll(final String xpath) {
+ removeAll(xpath, compileExpression(xpath));
+ }
+
+ /**
+ * Remove all matching nodes.
+ * @param xpath expression
+ * @param expr compiled Expression
+ */
+ public void removeAll(final String xpath, final Expression expr) {
+ try {
+ final ArrayList list = new ArrayList();
+ Iterator it = expr.iteratePointers(getEvalContext());
+ while (it.hasNext()) {
+ list.add(it.next());
+ }
+ Collections.sort(list, ReverseComparator.INSTANCE);
+ it = list.iterator();
+ if (it.hasNext()) {
+ final NodePointer pointer = (NodePointer) it.next();
+ pointer.remove();
+ while (it.hasNext()) {
+ removePath(((NodePointer) it.next()).asPath());
+ }
+ }
+ }
+ catch (final Throwable ex) {
+ throw new JXPathException(
+ "Exception trying to remove all for xpath " + xpath,
+ ex);
+ }
+ }
+
+ @Override
+ public JXPathContext getRelativeContext(final Pointer pointer) {
+ final Object contextBean = pointer.getNode();
+ if (contextBean == null) {
+ throw new JXPathException(
+ "Cannot create a relative context for a non-existent node: "
+ + pointer);
+ }
+ return new JXPathContextReferenceImpl(this, contextBean, pointer);
+ }
+
+ @Override
+ public Pointer getContextPointer() {
+ return contextPointer;
+ }
+
+ /**
+ * Gets absolute root pointer.
+ * @return NodePointer
+ */
+ private NodePointer getAbsoluteRootPointer() {
+ return (NodePointer) rootPointer;
+ }
+
+ /**
+ * Gets the evaluation context.
+ * @return EvalContext
+ */
+ private EvalContext getEvalContext() {
+ return new InitialContext(new RootContext(this,
+ (NodePointer) getContextPointer()));
+ }
+
+ /**
+ * Gets the absolute root context.
+ * @return EvalContext
+ */
+ public EvalContext getAbsoluteRootContext() {
+ return new InitialContext(new RootContext(this,
+ getAbsoluteRootPointer()));
+ }
+
+ /**
+ * Gets a VariablePointer for the given variable name.
+ * @param name variable name
+ * @return NodePointer
+ */
+ public NodePointer getVariablePointer(final QName name) {
+ return NodePointer.newNodePointer(name, VariablePointerFactory
+ .contextWrapper(this), getLocale());
+ }
+
+ /**
+ * Gets the named Function.
+ * @param functionName name
+ * @param parameters function args
+ * @return Function
+ */
+ public Function getFunction(final QName functionName, final Object[] parameters) {
+ final String namespace = functionName.getPrefix();
+ final String name = functionName.getName();
+ JXPathContext funcCtx = this;
+ Function func;
+ Functions funcs;
+ while (funcCtx != null) {
+ funcs = funcCtx.getFunctions();
+ if (funcs != null) {
+ func = funcs.getFunction(namespace, name, parameters);
+ if (func != null) {
+ return func;
+ }
+ }
+ funcCtx = funcCtx.getParentContext();
+ }
+ throw new JXPathFunctionNotFoundException(
+ "Undefined function: " + functionName.toString());
+ }
+
+ @Override
+ public void registerNamespace(final String prefix, final String namespaceURI) {
+ if (namespaceResolver.isSealed()) {
+ namespaceResolver = (NamespaceResolver) namespaceResolver.clone();
+ }
+ namespaceResolver.registerNamespace(prefix, namespaceURI);
+ }
+
+ @Override
+ public String getNamespaceURI(final String prefix) {
+ return namespaceResolver.getNamespaceURI(prefix);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @see org.apache.commons.jxpath.JXPathContext#getPrefix(java.lang.String)
+ */
+ @Override
+ public String getPrefix(final String namespaceURI) {
+ return namespaceResolver.getPrefix(namespaceURI);
+ }
+
+ @Override
+ public void setNamespaceContextPointer(final Pointer pointer) {
+ if (namespaceResolver.isSealed()) {
+ namespaceResolver = (NamespaceResolver) namespaceResolver.clone();
+ }
+ namespaceResolver.setNamespaceContextPointer((NodePointer) pointer);
+ }
+
+ @Override
+ public Pointer getNamespaceContextPointer() {
+ return namespaceResolver.getNamespaceContextPointer();
+ }
+
+ /**
+ * Gets the namespace resolver.
+ * @return NamespaceResolver
+ */
+ public NamespaceResolver getNamespaceResolver() {
+ namespaceResolver.seal();
+ return namespaceResolver;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setExceptionHandler(final ExceptionHandler exceptionHandler) {
+ if (rootPointer instanceof NodePointer) {
+ ((NodePointer) rootPointer).setExceptionHandler(exceptionHandler);
+ }
+ }
+
+ /**
+ * Checks if existenceCheckClass exists on the class path. If so, allocates
+ * an instance of the specified class, otherwise returns null.
+ * @param className to instantiate
+ * @param existenceCheckClassName guard class
+ * @return className instance
+ */
+ public static Object allocateConditionally(final String className,
+ final String existenceCheckClassName) {
+ try {
+ try {
+ ClassLoaderUtil.getClass(existenceCheckClassName, true);
+ }
+ catch (final ClassNotFoundException ex) {
+ return null;
+ }
+ final Class cls = ClassLoaderUtil.getClass(className, true);
+ return cls.getConstructor().newInstance();
+ }
+ catch (final Exception ex) {
+ throw new JXPathException("Cannot allocate " + className, ex);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/NamespaceResolver.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/NamespaceResolver.java
new file mode 100644
index 00000000000..371745b8d90
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/NamespaceResolver.java
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri;
+
+import java.io.Serializable;
+import java.util.HashMap;
+
+import org.apache.commons.jxpath.Pointer;
+import org.apache.commons.jxpath.ri.model.NodeIterator;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * Namespace resolver for {@link JXPathContextReferenceImpl}.
+ */
+public class NamespaceResolver implements Cloneable, Serializable {
+ private static final long serialVersionUID = 1085590057838651311L;
+
+ /** Parent NamespaceResolver */
+ protected final NamespaceResolver parent;
+ /** Namespace map */
+ protected HashMap namespaceMap = new HashMap();
+ /** Reverse lookup map */
+ protected HashMap reverseMap = new HashMap();
+ /** Pointer */
+ protected NodePointer pointer;
+ private boolean sealed;
+
+ /**
+ * Find the namespace prefix for the specified namespace URI and NodePointer.
+ * @param pointer location
+ * @param namespaceURI to check
+ * @return prefix if found
+ * @since JXPath 1.3
+ */
+ protected static String getPrefix(final NodePointer pointer, final String namespaceURI) {
+ NodePointer currentPointer = pointer;
+ while (currentPointer != null) {
+ final NodeIterator ni = currentPointer.namespaceIterator();
+ for (int position = 1; ni != null && ni.setPosition(position); position++) {
+ final NodePointer nsPointer = ni.getNodePointer();
+ final String uri = nsPointer.getNamespaceURI();
+ if (uri.equals(namespaceURI)) {
+ final String prefix = nsPointer.getName().getName();
+ if (!"".equals(prefix)) {
+ return prefix;
+ }
+ }
+ }
+ currentPointer = currentPointer.getParent();
+ }
+ return null;
+ }
+
+ /**
+ * Create a new NamespaceResolver.
+ */
+ public NamespaceResolver() {
+ this(null);
+ }
+
+ /**
+ * Create a new NamespaceResolver.
+ * @param parent NamespaceResolver
+ */
+ public NamespaceResolver(final NamespaceResolver parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * Registers a namespace prefix.
+ *
+ * @param prefix A namespace prefix
+ * @param namespaceURI A URI for that prefix
+ */
+ public synchronized void registerNamespace(final String prefix, final String namespaceURI) {
+ if (isSealed()) {
+ throw new IllegalStateException(
+ "Cannot register namespaces on a sealed NamespaceResolver");
+ }
+ namespaceMap.put(prefix, namespaceURI);
+ reverseMap.put(namespaceURI, prefix);
+ }
+
+ /**
+ * Register a namespace for the expression context.
+ * @param pointer the Pointer to set.
+ */
+ public void setNamespaceContextPointer(final NodePointer pointer) {
+ this.pointer = pointer;
+ }
+
+ /**
+ * Gets the namespace context pointer.
+ * @return Pointer
+ */
+ public Pointer getNamespaceContextPointer() {
+ if (pointer == null && parent != null) {
+ return parent.getNamespaceContextPointer();
+ }
+ return pointer;
+ }
+
+ /**
+ * Given a prefix, returns a registered namespace URI. If the requested
+ * prefix was not defined explicitly using the registerNamespace method,
+ * JXPathContext will then check the context node to see if the prefix is
+ * defined there. See
+ * {@link #setNamespaceContextPointer(NodePointer) setNamespaceContextPointer}.
+ *
+ * @param prefix The namespace prefix to look up
+ * @return namespace URI or null if the prefix is undefined.
+ */
+ public synchronized String getNamespaceURI(final String prefix) {
+ final String uri = getExternallyRegisteredNamespaceURI(prefix);
+ return uri == null && pointer != null ? pointer.getNamespaceURI(prefix)
+ : uri;
+ }
+
+ /**
+ * Given a prefix, returns an externally registered namespace URI.
+ *
+ * @param prefix The namespace prefix to look up
+ * @return namespace URI or null if the prefix is undefined.
+ * @since JXPath 1.3
+ */
+ protected synchronized String getExternallyRegisteredNamespaceURI(
+ final String prefix) {
+ final String uri = (String) namespaceMap.get(prefix);
+ return uri == null && parent != null ? parent
+ .getExternallyRegisteredNamespaceURI(prefix) : uri;
+ }
+
+ /**
+ * Gets the prefix associated with the specifed namespace URI.
+ * @param namespaceURI the ns URI to check.
+ * @return String prefix
+ */
+ public synchronized String getPrefix(final String namespaceURI) {
+ final String prefix = getExternallyRegisteredPrefix(namespaceURI);
+ return prefix == null && pointer != null ? getPrefix(pointer,
+ namespaceURI) : prefix;
+ }
+
+ /**
+ * Gets the nearest prefix found that matches an externally-registered namespace.
+ * @param namespaceURI the ns URI to check.
+ * @return String prefix if found.
+ * @since JXPath 1.3
+ */
+ protected synchronized String getExternallyRegisteredPrefix(final String namespaceURI) {
+ final String prefix = (String) reverseMap.get(namespaceURI);
+ return prefix == null && parent != null ? parent
+ .getExternallyRegisteredPrefix(namespaceURI) : prefix;
+ }
+
+ /**
+ * Learn whether this NamespaceResolver has been sealed.
+ * @return boolean
+ */
+ public boolean isSealed() {
+ return sealed;
+ }
+
+ /**
+ * Seal this {@link NamespaceResolver}.
+ */
+ public void seal() {
+ sealed = true;
+ if (parent != null) {
+ parent.seal();
+ }
+ }
+
+ @Override
+ public Object clone() {
+ try {
+ final NamespaceResolver result = (NamespaceResolver) super.clone();
+ result.sealed = false;
+ return result;
+ }
+ catch (final CloneNotSupportedException e) {
+ // Of course, it's supported.
+ e.printStackTrace();
+ return null;
+ }
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/Parser.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/Parser.java
new file mode 100644
index 00000000000..58fe7cae56a
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/Parser.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri;
+
+import java.io.StringReader;
+
+import org.apache.commons.jxpath.JXPathInvalidSyntaxException;
+import org.apache.commons.jxpath.ri.parser.ParseException;
+import org.apache.commons.jxpath.ri.parser.TokenMgrError;
+import org.apache.commons.jxpath.ri.parser.XPathParser;
+
+/**
+ * XPath parser
+ */
+public class Parser {
+
+ private static final XPathParser PARSER = new XPathParser(new StringReader(""));
+
+ /**
+ * Parses the XPath expression. Throws a JXPathException in case
+ * of a syntax error.
+ * @param expression to parse
+ * @param compiler the compiler
+ * @return parsed Object
+ */
+ public static Object parseExpression(
+ final String expression,
+ final Compiler compiler) {
+ synchronized (PARSER) {
+ PARSER.setCompiler(compiler);
+ Object expr;
+ try {
+ PARSER.ReInit(new StringReader(expression));
+ expr = PARSER.parseExpression();
+ }
+ catch (final TokenMgrError e) {
+ throw new JXPathInvalidSyntaxException(
+ "Invalid XPath: '"
+ + addEscapes(expression)
+ + "'. Invalid symbol '"
+ + addEscapes(String.valueOf(e.getCharacter()))
+ + "' "
+ + describePosition(expression, e.getPosition()));
+ }
+ catch (final ParseException e) {
+ throw new JXPathInvalidSyntaxException(
+ "Invalid XPath: '"
+ + addEscapes(expression)
+ + "'. Syntax error "
+ + describePosition(
+ expression,
+ e.currentToken.beginColumn));
+ }
+ return expr;
+ }
+ }
+
+ /**
+ * Describe a parse position.
+ * @param expression to parse
+ * @param position parse position
+ * @return String
+ */
+ private static String describePosition(final String expression, final int position) {
+ if (position <= 0) {
+ return "at the beginning of the expression";
+ }
+ if (position >= expression.length()) {
+ return "- expression incomplete";
+ }
+ return "after: '"
+ + addEscapes(expression.substring(0, position)) + "'";
+ }
+
+ /**
+ * Add escapes to the specified String.
+ * @param string incoming String
+ * @return String
+ */
+ private static String addEscapes(final String string) {
+ // Piggy-back on the code generated by JavaCC
+ return TokenMgrError.addEscapes(string);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/QName.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/QName.java
new file mode 100644
index 00000000000..ccad0141a70
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/QName.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri;
+
+import java.io.Serializable;
+
+/**
+ * A qualified name: a combination of an optional namespace prefix
+ * and an local name.
+ */
+public class QName implements Serializable {
+ private static final long serialVersionUID = 7616199282015091496L;
+
+ private final String prefix;
+ private final String name;
+ private final String qualifiedName;
+
+ /**
+ * Create a new QName.
+ * @param qualifiedName value
+ */
+ public QName(final String qualifiedName) {
+ this.qualifiedName = qualifiedName;
+ final int index = qualifiedName.indexOf(':');
+ prefix = index < 0 ? null : qualifiedName.substring(0, index);
+ name = index < 0 ? qualifiedName : qualifiedName.substring(index + 1);
+ }
+
+ /**
+ * Create a new QName.
+ * @param prefix ns
+ * @param localName String
+ */
+ public QName(final String prefix, final String localName) {
+ this.prefix = prefix;
+ this.name = localName;
+ this.qualifiedName = prefix == null ? localName : prefix + ':' + localName;
+ }
+
+ /**
+ * Gets the prefix of this QName.
+ * @return String
+ */
+ public String getPrefix() {
+ return prefix;
+ }
+
+ /**
+ * Gets the local name.
+ * @return String
+ */
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String toString() {
+ return qualifiedName;
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (!(object instanceof QName)) {
+ return false;
+ }
+ return qualifiedName.equals(((QName) object).qualifiedName);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/AncestorContext.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/AncestorContext.java
new file mode 100644
index 00000000000..1230779fb0b
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/AncestorContext.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.axes;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * EvalContext that walks the "ancestor::" and "ancestor-or-self::" axes.
+ */
+public class AncestorContext extends EvalContext {
+ private final NodeTest nodeTest;
+ private boolean setStarted = false;
+ private NodePointer currentNodePointer;
+ private final boolean includeSelf;
+
+ /**
+ * Create a new AncestorContext.
+ * @param parentContext represents the previous step on the path
+ * @param includeSelf differentiates between "ancestor::" and
+ * "ancestor-or-self::" axes
+ * @param nodeTest is the name of the element(s) we are looking for
+ */
+ public AncestorContext(
+ final EvalContext parentContext,
+ final boolean includeSelf,
+ final NodeTest nodeTest) {
+ super(parentContext);
+ this.includeSelf = includeSelf;
+ this.nodeTest = nodeTest;
+ }
+
+ @Override
+ public NodePointer getCurrentNodePointer() {
+ return currentNodePointer;
+ }
+
+ @Override
+ public int getDocumentOrder() {
+ return -1;
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ setStarted = false;
+ }
+
+ @Override
+ public boolean setPosition(final int position) {
+ if (position < getCurrentPosition()) {
+ reset();
+ }
+
+ while (getCurrentPosition() < position) {
+ if (!nextNode()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean nextNode() {
+ if (!setStarted) {
+ setStarted = true;
+ currentNodePointer = parentContext.getCurrentNodePointer();
+ if (includeSelf && currentNodePointer.testNode(nodeTest)) {
+ position++;
+ return true;
+ }
+ }
+
+ while (true) {
+ currentNodePointer = currentNodePointer.getImmediateParentPointer();
+
+ if (currentNodePointer == null) {
+ return false;
+ }
+
+ if (currentNodePointer.testNode(nodeTest)) {
+ position++;
+ return true;
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/AttributeContext.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/AttributeContext.java
new file mode 100644
index 00000000000..be08c0e5687
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/AttributeContext.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.axes;
+
+import org.apache.commons.jxpath.ri.Compiler;
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
+import org.apache.commons.jxpath.ri.model.NodeIterator;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * EvalContext that walks the "attribute::" axis.
+ */
+public class AttributeContext extends EvalContext {
+ private static final QName WILDCARD = new QName(null, "*");
+
+ private final NodeTest nodeTest;
+ private boolean setStarted = false;
+ private NodeIterator iterator;
+ private NodePointer currentNodePointer;
+
+ /**
+ * Create a new AttributeContext.
+ * @param parentContext represents the previous step on the path
+ * @param nodeTest is the name of the attribute we are looking for
+ */
+ public AttributeContext(final EvalContext parentContext, final NodeTest nodeTest) {
+ super(parentContext);
+ this.nodeTest = nodeTest;
+ }
+
+ @Override
+ public NodePointer getCurrentNodePointer() {
+ return currentNodePointer;
+ }
+
+ @Override
+ public void reset() {
+ setStarted = false;
+ iterator = null;
+ super.reset();
+ }
+
+ @Override
+ public boolean setPosition(final int position) {
+ if (position < getCurrentPosition()) {
+ reset();
+ }
+
+ while (getCurrentPosition() < position) {
+ if (!nextNode()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean nextNode() {
+ super.setPosition(getCurrentPosition() + 1);
+ if (!setStarted) {
+ setStarted = true;
+ QName name;
+ if (nodeTest instanceof NodeNameTest) {
+ name = ((NodeNameTest) nodeTest).getNodeName();
+ }
+ else if (nodeTest instanceof NodeTypeTest && ((NodeTypeTest) nodeTest).getNodeType() == Compiler.NODE_TYPE_NODE) {
+ name = WILDCARD;
+ }
+ else {
+ iterator = null;
+ return false;
+ }
+ iterator = parentContext.getCurrentNodePointer().attributeIterator(name);
+ }
+ if (iterator == null) {
+ return false;
+ }
+ if (!iterator.setPosition(iterator.getPosition() + 1)) {
+ return false;
+ }
+ currentNodePointer = iterator.getNodePointer();
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/ChildContext.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/ChildContext.java
new file mode 100644
index 00000000000..bfcc0213d88
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/ChildContext.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.axes;
+
+import org.apache.commons.jxpath.Pointer;
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.model.NodeIterator;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * EvalContext that can walk the "child::", "following-sibling::" and
+ * "preceding-sibling::" axes.
+ */
+public class ChildContext extends EvalContext {
+ private final NodeTest nodeTest;
+ private final boolean startFromParentLocation;
+ private final boolean reverse;
+ private NodeIterator iterator;
+
+ /**
+ * Create a new ChildContext.
+ * @param parentContext parent EvalContext
+ * @param nodeTest NodeTest
+ * @param startFromParentLocation whether to start from parent location
+ * @param reverse whether to iterate in reverse
+ */
+ public ChildContext(final EvalContext parentContext, final NodeTest nodeTest,
+ final boolean startFromParentLocation, final boolean reverse) {
+ super(parentContext);
+ this.nodeTest = nodeTest;
+ this.startFromParentLocation = startFromParentLocation;
+ this.reverse = reverse;
+ }
+
+ @Override
+ public NodePointer getCurrentNodePointer() {
+ if (position == 0 && !setPosition(1)) {
+ return null;
+ }
+ return iterator == null ? null : iterator.getNodePointer();
+ }
+
+ /**
+ * This method is called on the last context on the path when only
+ * one value is needed. Note that this will return the whole property,
+ * even if it is a collection. It will not extract the first element
+ * of the collection. For example, "books" will return the collection
+ * of books rather than the first book from that collection.
+ * @return Pointer
+ */
+ @Override
+ public Pointer getSingleNodePointer() {
+ if (position == 0) {
+ while (nextSet()) {
+ prepare();
+ if (iterator == null) {
+ return null;
+ }
+ // See if there is a property there, singular or collection
+ final NodePointer pointer = iterator.getNodePointer();
+ if (pointer != null) {
+ return pointer;
+ }
+ }
+ return null;
+ }
+ return getCurrentNodePointer();
+ }
+
+ @Override
+ public boolean nextNode() {
+ return setPosition(getCurrentPosition() + 1);
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ iterator = null;
+ }
+
+ @Override
+ public boolean setPosition(final int position) {
+ final int oldPosition = getCurrentPosition();
+ super.setPosition(position);
+ if (oldPosition == 0) {
+ prepare();
+ }
+ return iterator != null && iterator.setPosition(position);
+ }
+
+ /**
+ * Allocates a PropertyIterator.
+ */
+ private void prepare() {
+ final NodePointer parent = parentContext.getCurrentNodePointer();
+ if (parent == null) {
+ return;
+ }
+ final NodePointer useParent = startFromParentLocation ? parent.getParent() : parent;
+ iterator = useParent == null ? null : useParent.childIterator(nodeTest, reverse,
+ startFromParentLocation ? parent : null);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/DescendantContext.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/DescendantContext.java
new file mode 100644
index 00000000000..1610dd3c79e
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/DescendantContext.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.axes;
+
+import java.util.Stack;
+
+import org.apache.commons.jxpath.Pointer;
+import org.apache.commons.jxpath.ri.Compiler;
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
+import org.apache.commons.jxpath.ri.model.NodeIterator;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * An EvalContext that walks the "descendant::" and "descendant-or-self::"
+ * axes.
+ */
+public class DescendantContext extends EvalContext {
+ private final NodeTest nodeTest;
+ private boolean setStarted = false;
+ private Stack stack = null;
+ private NodePointer currentNodePointer = null;
+ private final boolean includeSelf;
+ private static final NodeTest ELEMENT_NODE_TEST =
+ new NodeTypeTest(Compiler.NODE_TYPE_NODE);
+
+ /**
+ * Create a new DescendantContext.
+ * @param parentContext parent context
+ * @param includeSelf whether to include this node
+ * @param nodeTest test
+ */
+ public DescendantContext(final EvalContext parentContext, final boolean includeSelf,
+ final NodeTest nodeTest) {
+ super(parentContext);
+ this.includeSelf = includeSelf;
+ this.nodeTest = nodeTest;
+ }
+
+ @Override
+ public boolean isChildOrderingRequired() {
+ return true;
+ }
+
+ @Override
+ public NodePointer getCurrentNodePointer() {
+ if (position == 0 && !setPosition(1)) {
+ return null;
+ }
+ return currentNodePointer;
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ setStarted = false;
+ }
+
+ @Override
+ public boolean setPosition(final int position) {
+ if (position < this.position) {
+ reset();
+ }
+
+ while (this.position < position) {
+ if (!nextNode()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean nextNode() {
+ if (!setStarted) {
+ setStarted = true;
+ if (stack == null) {
+ stack = new Stack();
+ }
+ else {
+ stack.clear();
+ }
+ currentNodePointer = parentContext.getCurrentNodePointer();
+ if (currentNodePointer != null) {
+ if (!currentNodePointer.isLeaf()) {
+ stack.push(
+ currentNodePointer.childIterator(
+ ELEMENT_NODE_TEST,
+ false,
+ null));
+ }
+ if (includeSelf && currentNodePointer.testNode(nodeTest)) {
+ position++;
+ return true;
+ }
+ }
+ }
+
+ while (!stack.isEmpty()) {
+ final NodeIterator it = (NodeIterator) stack.peek();
+ if (it.setPosition(it.getPosition() + 1)) {
+ currentNodePointer = it.getNodePointer();
+ if (!isRecursive()) {
+ if (!currentNodePointer.isLeaf()) {
+ stack.push(
+ currentNodePointer.childIterator(
+ ELEMENT_NODE_TEST,
+ false,
+ null));
+ }
+ if (currentNodePointer.testNode(nodeTest)) {
+ position++;
+ return true;
+ }
+ }
+ }
+ else {
+ // We get here only if the name test failed
+ // and the iterator ended
+ stack.pop();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks if we are reentering a bean we have already seen and if so
+ * returns true to prevent infinite recursion.
+ * @return boolean
+ */
+ private boolean isRecursive() {
+ final Object node = currentNodePointer.getNode();
+ for (int i = stack.size() - 1; --i >= 0;) {
+ final NodeIterator it = (NodeIterator) stack.get(i);
+ final Pointer pointer = it.getNodePointer();
+ if (pointer != null && pointer.getNode() == node) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/InitialContext.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/InitialContext.java
new file mode 100644
index 00000000000..f2c0335b247
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/InitialContext.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.axes;
+
+import org.apache.commons.jxpath.Pointer;
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * A single-set EvalContext that provides access to the current node of
+ * the parent context and nothing else. It does not pass the iteration
+ * on to the parent context.
+ */
+public class InitialContext extends EvalContext {
+ private boolean started = false;
+ private boolean collection;
+ private final NodePointer nodePointer;
+
+ /**
+ * Create a new InitialContext.
+ * @param parentContext parent context
+ */
+ public InitialContext(final EvalContext parentContext) {
+ super(parentContext);
+ nodePointer =
+ (NodePointer) parentContext.getCurrentNodePointer().clone();
+ if (nodePointer != null) {
+ collection =
+ nodePointer.getIndex() == NodePointer.WHOLE_COLLECTION;
+ }
+ }
+
+ @Override
+ public Pointer getSingleNodePointer() {
+ return nodePointer;
+ }
+
+ @Override
+ public NodePointer getCurrentNodePointer() {
+ return nodePointer;
+ }
+
+ @Override
+ public Object getValue() {
+ return nodePointer.getValue();
+ }
+
+ @Override
+ public boolean nextNode() {
+ return setPosition(position + 1);
+ }
+
+ @Override
+ public boolean setPosition(final int position) {
+ this.position = position;
+ if (collection) {
+ if (position >= 1 && position <= nodePointer.getLength()) {
+ nodePointer.setIndex(position - 1);
+ return true;
+ }
+ return false;
+ }
+ return position == 1;
+ }
+
+ @Override
+ public boolean nextSet() {
+ if (started) {
+ return false;
+ }
+ started = true;
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/NamespaceContext.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/NamespaceContext.java
new file mode 100644
index 00000000000..ffcf7b1c25d
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/NamespaceContext.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.axes;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.model.NodeIterator;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * EvalContext that walks the "namespace::" axis.
+ */
+public class NamespaceContext extends EvalContext {
+ private final NodeTest nodeTest;
+ private boolean setStarted = false;
+ private NodeIterator iterator;
+ private NodePointer currentNodePointer;
+
+ /**
+ * @param parentContext represents the previous step on the path
+ * @param nodeTest is the name of the namespace we are looking for
+ */
+ public NamespaceContext(final EvalContext parentContext, final NodeTest nodeTest) {
+ super(parentContext);
+ this.nodeTest = nodeTest;
+ }
+
+ @Override
+ public NodePointer getCurrentNodePointer() {
+ return currentNodePointer;
+ }
+
+ @Override
+ public void reset() {
+ setStarted = false;
+ iterator = null;
+ super.reset();
+ }
+
+ @Override
+ public boolean setPosition(final int position) {
+ if (position < getCurrentPosition()) {
+ reset();
+ }
+
+ while (getCurrentPosition() < position) {
+ if (!nextNode()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean nextNode() {
+ super.setPosition(getCurrentPosition() + 1);
+ if (!setStarted) {
+ setStarted = true;
+ if (!(nodeTest instanceof NodeNameTest)) {
+ return false;
+ }
+
+ final NodeNameTest nodeNameTest = (NodeNameTest) nodeTest;
+ final QName testName = nodeNameTest.getNodeName();
+ if (testName.getPrefix() != null) {
+ return false;
+ }
+ if (nodeNameTest.isWildcard()) {
+ iterator =
+ parentContext.getCurrentNodePointer().namespaceIterator();
+ }
+ else {
+ currentNodePointer =
+ parentContext.getCurrentNodePointer().namespacePointer(
+ testName.getName());
+ return currentNodePointer != null;
+ }
+ }
+
+ if (iterator == null) {
+ return false;
+ }
+ if (!iterator.setPosition(iterator.getPosition() + 1)) {
+ return false;
+ }
+ currentNodePointer = iterator.getNodePointer();
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/NodeSetContext.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/NodeSetContext.java
new file mode 100644
index 00000000000..9099fd7843a
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/NodeSetContext.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.axes;
+
+import org.apache.commons.jxpath.NodeSet;
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * A simple context that is based on a {@link NodeSet}.
+ */
+public class NodeSetContext extends EvalContext {
+ private boolean startedSet = false;
+ private final NodeSet nodeSet;
+
+ /**
+ * Create a new NodeSetContext.
+ * @param parentContext parent context
+ * @param nodeSet associated NodeSet
+ */
+ public NodeSetContext(final EvalContext parentContext, final NodeSet nodeSet) {
+ super(parentContext);
+ this.nodeSet = nodeSet;
+ }
+
+ @Override
+ public NodeSet getNodeSet() {
+ return nodeSet;
+ }
+
+ @Override
+ public NodePointer getCurrentNodePointer() {
+ if (position == 0 && !setPosition(1)) {
+ return null;
+ }
+ return (NodePointer) nodeSet.getPointers().get(position - 1);
+ }
+
+ @Override
+ public boolean setPosition(final int position) {
+ super.setPosition(position);
+ return position >= 1 && position <= nodeSet.getPointers().size();
+ }
+
+ @Override
+ public boolean nextSet() {
+ if (startedSet) {
+ return false;
+ }
+ startedSet = true;
+ return true;
+ }
+
+ @Override
+ public boolean nextNode() {
+ return setPosition(position + 1);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/ParentContext.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/ParentContext.java
new file mode 100644
index 00000000000..d11b134ca9a
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/ParentContext.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.axes;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * EvalContext that walks the "parent::" axis.
+ */
+public class ParentContext extends EvalContext {
+ private final NodeTest nodeTest;
+ private boolean setStarted = false;
+ private NodePointer currentNodePointer;
+
+ /**
+ * Create a new ParentContext.
+ * @param parentContext parent context
+ * @param nodeTest test
+ */
+ public ParentContext(final EvalContext parentContext, final NodeTest nodeTest) {
+ super(parentContext);
+ this.nodeTest = nodeTest;
+ }
+
+ @Override
+ public NodePointer getCurrentNodePointer() {
+ return currentNodePointer;
+ }
+
+ @Override
+ public int getCurrentPosition() {
+ return 1;
+ }
+
+ @Override
+ public int getDocumentOrder() {
+ return -1;
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ setStarted = false;
+ }
+
+ @Override
+ public boolean setPosition(final int position) {
+ super.setPosition(position);
+ return position == 1;
+ }
+
+ @Override
+ public boolean nextNode() {
+ // Each set contains exactly one node: the parent
+ if (setStarted) {
+ return false;
+ }
+ setStarted = true;
+ final NodePointer thisLocation = parentContext.getCurrentNodePointer();
+ currentNodePointer = thisLocation.getImmediateParentPointer();
+ while (currentNodePointer != null
+ && currentNodePointer.isContainer()) {
+ currentNodePointer = currentNodePointer.getImmediateParentPointer();
+ }
+ if (currentNodePointer != null
+ && currentNodePointer.testNode(nodeTest)) {
+ position++;
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/PrecedingOrFollowingContext.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/PrecedingOrFollowingContext.java
new file mode 100644
index 00000000000..b3b84aa2abd
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/PrecedingOrFollowingContext.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.axes;
+
+import java.util.Stack;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.model.NodeIterator;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * EvalContext that walks the "preceding::" and "following::" axes.
+ */
+public class PrecedingOrFollowingContext extends EvalContext {
+ private final NodeTest nodeTest;
+ private boolean setStarted = false;
+ private Stack stack = null;
+ private NodePointer currentNodePointer;
+ private NodePointer currentRootLocation;
+ private final boolean reverse;
+
+ /**
+ * Create a new PrecedingOrFollowingContext.
+ * @param parentContext parent context
+ * @param nodeTest test
+ * @param reverse whether to iterate in reverse order
+ */
+ public PrecedingOrFollowingContext(
+ final EvalContext parentContext,
+ final NodeTest nodeTest,
+ final boolean reverse) {
+ super(parentContext);
+ this.nodeTest = nodeTest;
+ this.reverse = reverse;
+ }
+
+ @Override
+ public NodePointer getCurrentNodePointer() {
+ return currentNodePointer;
+ }
+
+ @Override
+ public int getDocumentOrder() {
+ return reverse ? -1 : 1;
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ setStarted = false;
+ }
+
+ @Override
+ public boolean setPosition(final int position) {
+ if (position < this.position) {
+ reset();
+ }
+
+ while (this.position < position) {
+ if (!nextNode()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean nextNode() {
+ if (!setStarted) {
+ setStarted = true;
+ if (stack == null) {
+ stack = new Stack();
+ }
+ else {
+ stack.clear();
+ }
+ currentRootLocation = parentContext.getCurrentNodePointer();
+ final NodePointer parent = currentRootLocation.getParent();
+ if (parent != null) {
+ // TBD: check type
+ stack.push(
+ parent.childIterator(null, reverse, currentRootLocation));
+ }
+ }
+
+ while (true) {
+ if (stack.isEmpty()) {
+ currentRootLocation = currentRootLocation.getParent();
+
+ if (currentRootLocation == null
+ || currentRootLocation.isRoot()) {
+ break;
+ }
+
+ final NodePointer parent = currentRootLocation.getParent();
+ if (parent != null) {
+ stack.push(
+ parent.childIterator(
+ null,
+ reverse,
+ currentRootLocation));
+ }
+ }
+
+ while (!stack.isEmpty()) {
+ if (!reverse) {
+ final NodeIterator it = (NodeIterator) stack.peek();
+ if (it.setPosition(it.getPosition() + 1)) {
+ currentNodePointer = it.getNodePointer();
+ if (!currentNodePointer.isLeaf()) {
+ stack.push(
+ currentNodePointer.childIterator(
+ null,
+ reverse,
+ null));
+ }
+ if (currentNodePointer.testNode(nodeTest)) {
+ super.setPosition(getCurrentPosition() + 1);
+ return true;
+ }
+ }
+ else {
+ // We get here only if the name test failed
+ // and the iterator ended
+ stack.pop();
+ }
+ }
+ else {
+ NodeIterator it = (NodeIterator) stack.peek();
+ if (it.setPosition(it.getPosition() + 1)) {
+ currentNodePointer = it.getNodePointer();
+ if (!currentNodePointer.isLeaf()) {
+ stack.push(
+ currentNodePointer.childIterator(
+ null,
+ reverse,
+ null));
+ }
+ else if (currentNodePointer.testNode(nodeTest)) {
+ super.setPosition(getCurrentPosition() + 1);
+ return true;
+ }
+ }
+ else {
+ stack.pop();
+ if (!stack.isEmpty()) {
+ it = (NodeIterator) stack.peek();
+ currentNodePointer = it.getNodePointer();
+ if (currentNodePointer.testNode(nodeTest)) {
+ super.setPosition(getCurrentPosition() + 1);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/PredicateContext.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/PredicateContext.java
new file mode 100644
index 00000000000..645fdf5390a
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/PredicateContext.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.axes;
+
+import java.util.Iterator;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.InfoSetUtil;
+import org.apache.commons.jxpath.ri.compiler.Expression;
+import org.apache.commons.jxpath.ri.compiler.NameAttributeTest;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.ri.model.beans.PropertyOwnerPointer;
+import org.apache.commons.jxpath.ri.model.beans.PropertyPointer;
+
+/**
+ * EvalContext that checks predicates.
+ */
+public class PredicateContext extends EvalContext {
+ private final Expression expression;
+ private boolean done = false;
+ private Expression nameTestExpression;
+ private PropertyPointer dynamicPropertyPointer;
+
+ /**
+ * Create a new PredicateContext.
+ * @param parentContext parent context
+ * @param expression compiled Expression
+ */
+ public PredicateContext(final EvalContext parentContext, final Expression expression) {
+ super(parentContext);
+ this.expression = expression;
+ if (expression instanceof NameAttributeTest) {
+ nameTestExpression =
+ ((NameAttributeTest) expression).getNameTestExpression();
+ }
+ }
+
+ @Override
+ public boolean nextNode() {
+ if (done) {
+ return false;
+ }
+ while (parentContext.nextNode()) {
+ if (setupDynamicPropertyPointer()) {
+ final Object pred = nameTestExpression.computeValue(parentContext);
+ final String propertyName = InfoSetUtil.stringValue(pred);
+
+ // At this point it would be nice to say:
+ // dynamicPropertyPointer.setPropertyName(propertyName)
+ // and then: dynamicPropertyPointer.isActual().
+ // However some PropertyPointers, e.g. DynamicPropertyPointer
+ // will declare that any property you ask for is actual.
+ // That's not acceptable for us: we really need to know
+ // if the property is currently declared. Thus,
+ // we'll need to perform a search.
+ boolean ok = false;
+ final String[] names = dynamicPropertyPointer.getPropertyNames();
+ for (final String name : names) {
+ if (name.equals(propertyName)) {
+ ok = true;
+ break;
+ }
+ }
+ if (ok) {
+ dynamicPropertyPointer.setPropertyName(propertyName);
+ position++;
+ return true;
+ }
+ }
+ else {
+ Object pred = expression.computeValue(parentContext);
+ if (pred instanceof Iterator) {
+ if (!((Iterator) pred).hasNext()) {
+ return false;
+ }
+ pred = ((Iterator) pred).next();
+ }
+
+ if (pred instanceof NodePointer) {
+ pred = ((NodePointer) pred).getNode();
+ }
+
+ if (pred instanceof Number) {
+ final int pos = (int) InfoSetUtil.doubleValue(pred);
+ position++;
+ done = true;
+ return parentContext.setPosition(pos);
+ }
+ if (InfoSetUtil.booleanValue(pred)) {
+ position++;
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Used for an optimized access to dynamic properties using the
+ * "map[@name = 'name']" syntax
+ * @return whether valid
+ */
+ private boolean setupDynamicPropertyPointer() {
+ if (nameTestExpression == null) {
+ return false;
+ }
+
+ NodePointer parent = parentContext.getCurrentNodePointer();
+ if (parent == null) {
+ return false;
+ }
+ parent = parent.getValuePointer();
+ if (!(parent instanceof PropertyOwnerPointer)) {
+ return false;
+ }
+ dynamicPropertyPointer =
+ (PropertyPointer) ((PropertyOwnerPointer) parent)
+ .getPropertyPointer()
+ .clone();
+ return true;
+ }
+
+ @Override
+ public boolean setPosition(final int position) {
+ if (nameTestExpression == null) {
+ return setPositionStandard(position);
+ }
+ if (dynamicPropertyPointer == null && !setupDynamicPropertyPointer()) {
+ return setPositionStandard(position);
+ }
+ if (position < 1
+ || position > dynamicPropertyPointer.getLength()) {
+ return false;
+ }
+ dynamicPropertyPointer.setIndex(position - 1);
+ return true;
+ }
+
+ @Override
+ public NodePointer getCurrentNodePointer() {
+ if (position == 0 && !setPosition(1)) {
+ return null;
+ }
+ if (dynamicPropertyPointer != null) {
+ return dynamicPropertyPointer.getValuePointer();
+ }
+ return parentContext.getCurrentNodePointer();
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ parentContext.reset();
+ done = false;
+ }
+
+ @Override
+ public boolean nextSet() {
+ reset();
+ return parentContext.nextSet();
+ }
+
+ /**
+ * Basic setPosition
+ * @param position to set
+ * @return whether valid
+ */
+ private boolean setPositionStandard(final int position) {
+ if (this.position > position) {
+ reset();
+ }
+
+ while (this.position < position) {
+ if (!nextNode()) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/RootContext.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/RootContext.java
new file mode 100644
index 00000000000..0907b9e4fad
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/RootContext.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.axes;
+
+import org.apache.commons.jxpath.Function;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.NodeSet;
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.JXPathContextReferenceImpl;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * EvalContext that is used to hold the root node for the path traversal.
+ */
+public class RootContext extends EvalContext {
+ private final JXPathContextReferenceImpl jxpathContext;
+ private final NodePointer pointer;
+ private Object[] registers;
+ private int availableRegister = 0;
+ private static final int MAX_REGISTER = 4;
+
+ /**
+ * Create a new RootContext.
+ * @param jxpathContext context
+ * @param pointer pointer
+ */
+ public RootContext(final JXPathContextReferenceImpl jxpathContext,
+ final NodePointer pointer) {
+ super(null);
+ this.jxpathContext = jxpathContext;
+ this.pointer = pointer;
+ if (pointer != null) {
+ pointer.setNamespaceResolver(jxpathContext.getNamespaceResolver());
+ }
+ }
+
+ @Override
+ public JXPathContext getJXPathContext() {
+ return jxpathContext;
+ }
+
+ @Override
+ public RootContext getRootContext() {
+ return this;
+ }
+
+ /**
+ * Gets absolute root context
+ * @return EvalContext
+ */
+ public EvalContext getAbsoluteRootContext() {
+ return jxpathContext.getAbsoluteRootContext();
+ }
+
+ @Override
+ public NodePointer getCurrentNodePointer() {
+ return pointer;
+ }
+
+ @Override
+ public Object getValue() {
+ return pointer;
+ }
+
+ @Override
+ public int getCurrentPosition() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean nextNode() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean nextSet() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean setPosition(final int position) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Gets a context that points to the specified object.
+ * @param constant object
+ * @return EvalContext
+ */
+ public EvalContext getConstantContext(final Object constant) {
+ if (constant instanceof NodeSet) {
+ return new NodeSetContext(
+ new RootContext(jxpathContext, null),
+ (NodeSet) constant);
+ }
+
+ NodePointer pointer;
+ if (constant instanceof NodePointer) {
+ pointer = (NodePointer) constant;
+ }
+ else {
+ pointer = NodePointer.newNodePointer(
+ new QName(null, ""),
+ constant,
+ null);
+ }
+ return new InitialContext(new RootContext(jxpathContext, pointer));
+ }
+
+ /**
+ * Gets variable context.
+ * @param variableName variable name
+ * @return EvalContext
+ */
+ public EvalContext getVariableContext(final QName variableName) {
+ return new InitialContext(
+ new RootContext(
+ jxpathContext,
+ jxpathContext.getVariablePointer(variableName)));
+ }
+
+ /**
+ * Gets the specified function from the context.
+ * @param functionName QName
+ * @param parameters Object[]
+ * @return Function
+ */
+ public Function getFunction(final QName functionName, final Object[] parameters) {
+ return jxpathContext.getFunction(functionName, parameters);
+ }
+
+
+ @Override
+ public String toString() {
+ return super.toString() + ":" + pointer.asPath();
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/SelfContext.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/SelfContext.java
new file mode 100644
index 00000000000..2a45a526d3a
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/SelfContext.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.axes;
+
+import org.apache.commons.jxpath.Pointer;
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * EvalContext that returns the current node from the parent context if the
+ * test succeeds.
+ */
+public class SelfContext extends EvalContext {
+ private final NodeTest nodeTest;
+ private boolean startedSet = false;
+ private NodePointer nodePointer;
+
+ /**
+ * Create a new SelfContext.
+ * @param parentContext EvalContext
+ * @param nodeTest guard
+ */
+ public SelfContext(final EvalContext parentContext, final NodeTest nodeTest) {
+ super(parentContext);
+ this.nodeTest = nodeTest;
+ }
+
+ @Override
+ public Pointer getSingleNodePointer() {
+ return parentContext.getSingleNodePointer();
+ }
+
+ @Override
+ public NodePointer getCurrentNodePointer() {
+ if (position == 0 && !setPosition(1)) {
+ return null;
+ }
+ return nodePointer;
+ }
+
+ @Override
+ public boolean nextNode() {
+ return setPosition(getCurrentPosition() + 1);
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ startedSet = false;
+ }
+
+ @Override
+ public boolean setPosition(final int position) {
+ if (position != 1) {
+ return false;
+ }
+ super.setPosition(position);
+ if (!startedSet) {
+ startedSet = true;
+ nodePointer = parentContext.getCurrentNodePointer();
+ }
+
+ if (nodePointer == null) {
+ return false;
+ }
+
+ return nodeTest == null || nodePointer.testNode(nodeTest);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/SimplePathInterpreter.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/SimplePathInterpreter.java
new file mode 100644
index 00000000000..8ef88ed2384
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/SimplePathInterpreter.java
@@ -0,0 +1,913 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.axes;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.jxpath.JXPathException;
+import org.apache.commons.jxpath.ri.Compiler;
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.InfoSetUtil;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.compiler.Expression;
+import org.apache.commons.jxpath.ri.compiler.NameAttributeTest;
+import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.compiler.Step;
+import org.apache.commons.jxpath.ri.model.NodeIterator;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.ri.model.beans.LangAttributePointer;
+import org.apache.commons.jxpath.ri.model.beans.NullElementPointer;
+import org.apache.commons.jxpath.ri.model.beans.NullPropertyPointer;
+import org.apache.commons.jxpath.ri.model.beans.PropertyOwnerPointer;
+import org.apache.commons.jxpath.ri.model.beans.PropertyPointer;
+
+/**
+ * An evaluation mechanism for simple XPaths, which
+ * is much faster than the usual process. It is only used for
+ * xpaths which have no context-dependent parts, consist entirely of
+ * {@code child::name} and {@code self::node()} steps with
+ * predicates that either integer or have the form {@code [@name = ...]}.
+ */
+public class SimplePathInterpreter {
+
+ // Because of the complexity caused by the variety of situations
+ // that need to be addressed by this class, we attempt to break up
+ // the class into individual methods addressing those situations
+ // individually. The names of the methods are supposed to
+ // give brief descriptions of those situations.
+
+ private static final QName QNAME_NAME = new QName(null, "name");
+ private static final int PERFECT_MATCH = 1000;
+
+ // Uncomment this variable and the PATH = ... lines in
+ // the two following methods in order to be able to print the
+ // currently evaluated path for debugging of this class
+// private static String PATH; // Debugging
+
+ /**
+ * Interpret a simple path that starts with the given root and
+ * follows the given steps. All steps must have the axis "child::"
+ * and a name test. They can also optionally have predicates
+ * of type [@name=expression] or simply [expression] interpreted
+ * as an index.
+ * @param context evaluation context
+ * @param root root pointer
+ * @param steps path steps
+ * @return NodePointer
+ */
+ public static NodePointer interpretSimpleLocationPath(
+ final EvalContext context, final NodePointer root, final Step[] steps) {
+// PATH = createNullPointer(context, root, steps, 0).toString(); // Dbg
+ final NodePointer pointer = doStep(context, root, steps, 0);
+// return valuePointer(pointer);
+ return pointer;
+ }
+
+ /**
+ * Interpret the steps of a simple expression path that
+ * starts with the given root, which is the result of evaluation
+ * of the root expression of the expression path, applies the
+ * given predicates to it and then follows the given steps.
+ * All steps must have the axis "child::" or "attribute::"
+ * and a name test. They can also optionally have predicates
+ * of type [@name=...] or simply [...] interpreted as an index.
+ * @param context evaluation context
+ * @param root root pointer
+ * @param predicates predicates corresponding to {@code steps}
+ * @param steps path steps
+ * @return NodePointer
+ */
+ public static NodePointer interpretSimpleExpressionPath(
+ final EvalContext context, final NodePointer root,
+ final Expression[] predicates, final Step[] steps) {
+// PATH = createNullPointerForPredicates(context, root,
+// steps, -1, predicates, 0).toString(); // Debugging
+ final NodePointer pointer =
+ doPredicate(context, root, steps, -1, predicates, 0);
+// return valuePointer(pointer);
+ return pointer;
+ }
+
+ /**
+ * Recursive evaluation of a path. The general plan is:
+ * Look at the current step,
+ * find nodes that match it,
+ * iterate over those nodes and
+ * for each of them call doStep again for subsequent steps.
+ * @param context evaluation context
+ * @param parent parent pointer
+ * @param steps path steps
+ * @param currentStep step number
+ * @return NodePointer
+ */
+ private static NodePointer doStep(
+ final EvalContext context, NodePointer parent,
+ final Step[] steps, final int currentStep) {
+ if (parent == null) {
+ return null;
+ }
+
+ if (currentStep == steps.length) {
+ // We have reached the end of the list of steps
+ return parent;
+ }
+
+ // Open all containers
+ parent = valuePointer(parent);
+
+ final Step step = steps[currentStep];
+ final Expression[] predicates = step.getPredicates();
+
+ // Divide and conquer: the process is broken out into
+ // four major use cases.
+ // 1. Current step has no predicates and
+ // the root is a property owner (e.g. bean or map)
+ // 2. Current step has predicates and
+ // the root is a property owner (e.g. bean or map)
+ // 3. Current step has no predicates and
+ // the root is an InfoSet standard node (e.g. DOM Node)
+ // 4. Current step has predicates and
+ // the root is an InfoSet standard node (e.g. DOM Node)
+
+ if (parent instanceof PropertyOwnerPointer) {
+ if (predicates == null || predicates.length == 0) {
+ return doStepNoPredicatesPropertyOwner(
+ context,
+ (PropertyOwnerPointer) parent,
+ steps,
+ currentStep);
+ }
+ return doStepPredicatesPropertyOwner(
+ context,
+ (PropertyOwnerPointer) parent,
+ steps,
+ currentStep);
+ }
+ if (predicates == null || predicates.length == 0) {
+ return doStepNoPredicatesStandard(
+ context,
+ parent,
+ steps,
+ currentStep);
+ }
+ return doStepPredicatesStandard(
+ context,
+ parent,
+ steps,
+ currentStep);
+ }
+
+ /**
+ * We have a step that starts with a property owner (bean, map, etc) and has
+ * no predicates. The name test of the step may map to a scalar property
+ * or to a collection. If it is a collection, we should apply the tail of
+ * the path to each element until we find a match. If we don't find
+ * a perfect match, we should return the "best quality" pointer, which
+ * has the longest chain of steps mapping to existing nodes and the shortes
+ * tail of Null* pointers.
+ * @param context evaluation context
+ * @param parentPointer property owner pointer
+ * @param steps path steps
+ * @param currentStep step number
+ * @return NodePointer
+ */
+ private static NodePointer doStepNoPredicatesPropertyOwner(
+ final EvalContext context, final PropertyOwnerPointer parentPointer,
+ final Step[] steps, final int currentStep) {
+ final Step step = steps[currentStep];
+ NodePointer childPointer =
+ createChildPointerForStep(parentPointer, step);
+
+ if (childPointer == null) {
+ return null;
+ }
+ if (!childPointer.isActual()) {
+ // The property does not exist - create a null pointer.
+ return createNullPointer(
+ context,
+ parentPointer,
+ steps,
+ currentStep);
+ }
+ if (currentStep == steps.length - 1) {
+ // If this is the last step - we are done, we found it
+ return childPointer;
+ }
+ if (childPointer.isCollection()) {
+ // Iterate over all values and
+ // execute remaining steps for each node,
+ // looking for the best quality match
+ int bestQuality = 0;
+ childPointer = (NodePointer) childPointer.clone();
+ NodePointer bestMatch = null;
+ final int count = childPointer.getLength();
+ for (int i = 0; i < count; i++) {
+ childPointer.setIndex(i);
+ final NodePointer pointer =
+ doStep(context, childPointer, steps, currentStep + 1);
+ final int quality = computeQuality(pointer);
+ if (quality == PERFECT_MATCH) {
+ return pointer;
+ }
+ else if (quality > bestQuality) {
+ bestQuality = quality;
+ bestMatch = (NodePointer) pointer.clone();
+ }
+ }
+ if (bestMatch != null) {
+ return bestMatch;
+ }
+ // This step did not find anything - return a null pointer
+ return createNullPointer(context, childPointer, steps, currentStep);
+ }
+ // Evaluate subsequent steps
+ return doStep(context, childPointer, steps, currentStep + 1);
+ }
+
+ /**
+ * A path that starts with a standard InfoSet node (e.g. DOM Node) and
+ * has no predicates. Get a child iterator and apply the tail of
+ * the path to each element until we find a match. If we don't find
+ * a perfect match, we should return the "best quality" pointer, which
+ * has the longest chain of steps mapping to existing nodes and the shortes
+ * tail of Null* pointers.
+ * @param context evaluation context
+ * @param parentPointer parent pointer
+ * @param steps path steps
+ * @param currentStep step number
+ * @return NodePointer
+ */
+ private static NodePointer doStepNoPredicatesStandard(
+ final EvalContext context, final NodePointer parentPointer,
+ final Step[] steps, final int currentStep) {
+ final Step step = steps[currentStep];
+
+ if (step.getAxis() == Compiler.AXIS_SELF) {
+ return doStep(context, parentPointer, steps, currentStep + 1);
+ }
+
+ int bestQuality = 0;
+ NodePointer bestMatch = null;
+ final NodeIterator it = getNodeIterator(context, parentPointer, step);
+ if (it != null) {
+ for (int i = 1; it.setPosition(i); i++) {
+ final NodePointer childPointer = it.getNodePointer();
+ if (steps.length == currentStep + 1) {
+ // If this is the last step - we are done, we found it
+ return childPointer;
+ }
+ final NodePointer pointer = doStep(
+ context, childPointer, steps, currentStep + 1);
+ final int quality = computeQuality(pointer);
+ if (quality == PERFECT_MATCH) {
+ return pointer;
+ }
+ if (quality > bestQuality) {
+ bestQuality = quality;
+ bestMatch = (NodePointer) pointer.clone();
+ }
+ }
+ }
+ return bestMatch != null ? bestMatch
+ : createNullPointer(context, parentPointer, steps, currentStep);
+ }
+
+ /**
+ * A path that starts with a property owner. The method evaluates
+ * the first predicate in a special way and then forwards to
+ * a general predicate processing method.
+ * @param context evaluation context
+ * @param parentPointer parent pointer
+ * @param steps path steps
+ * @param currentStep step number
+ * @return NodePointer
+ */
+ private static NodePointer doStepPredicatesPropertyOwner(
+ final EvalContext context, final PropertyOwnerPointer parentPointer,
+ final Step[] steps, final int currentStep) {
+ final Step step = steps[currentStep];
+ final Expression[] predicates = step.getPredicates();
+
+ final NodePointer childPointer =
+ createChildPointerForStep(parentPointer, step);
+ if (!childPointer.isActual()) {
+ // Property does not exist - return a null pointer
+ return createNullPointer(
+ context,
+ parentPointer,
+ steps,
+ currentStep);
+ }
+
+ // Evaluate predicates
+ return doPredicate(
+ context,
+ childPointer,
+ steps,
+ currentStep,
+ predicates,
+ 0);
+ }
+
+ /**
+ * Create the child pointer for a given step.
+ * @param parentPointer parent pointer
+ * @param step associated step
+ * @return NodePointer
+ */
+ private static NodePointer createChildPointerForStep(
+ final PropertyOwnerPointer parentPointer, final Step step) {
+ final int axis = step.getAxis();
+ if (axis == Compiler.AXIS_CHILD || axis == Compiler.AXIS_ATTRIBUTE) {
+ final QName name = ((NodeNameTest) step.getNodeTest()).getNodeName();
+ if (axis == Compiler.AXIS_ATTRIBUTE && isLangAttribute(name)) {
+ return new LangAttributePointer(parentPointer);
+ }
+ if (parentPointer.isValidProperty(name)) {
+ final NodePointer childPointer = parentPointer.getPropertyPointer();
+ ((PropertyPointer) childPointer).setPropertyName(
+ name.toString());
+ childPointer.setAttribute(axis == Compiler.AXIS_ATTRIBUTE);
+ return childPointer;
+ }
+ //invalid property gets nothing, not even a NullPointer
+ return null;
+ }
+ return parentPointer;
+ }
+
+ /**
+ * A path that starts with a standard InfoSet node, e.g. a DOM Node.
+ * The method evaluates the first predicate in a special way and
+ * then forwards to a general predicate processing method.
+ * @param context evaluation context
+ * @param parent parent pointer
+ * @param steps path steps
+ * @param currentStep step number
+ * @return NodePointer
+ */
+ private static NodePointer doStepPredicatesStandard(
+ final EvalContext context, final NodePointer parent,
+ final Step[] steps, final int currentStep) {
+ final Step step = steps[currentStep];
+ final Expression[] predicates = step.getPredicates();
+
+ final int axis = step.getAxis();
+ if (axis == Compiler.AXIS_SELF) {
+ return doPredicate(
+ context,
+ parent,
+ steps,
+ currentStep,
+ predicates,
+ 0);
+ }
+
+ final Expression predicate = predicates[0];
+
+ // Optimize for a single predicate to avoid building a list
+ // and to allow the direct access to the index'th element
+ // in the case of a simple subscript predecate
+ // It is a very common use case, so it deserves individual
+ // attention
+ if (predicates.length == 1) {
+ final NodeIterator it = getNodeIterator(context, parent, step);
+ NodePointer pointer = null;
+ if (it != null) {
+ if (predicate instanceof NameAttributeTest) { // [@name = key]
+ final String key = keyFromPredicate(context, predicate);
+ for (int i = 1; it.setPosition(i); i++) {
+ final NodePointer ptr = it.getNodePointer();
+ if (isNameAttributeEqual(ptr, key)) {
+ pointer = ptr;
+ break;
+ }
+ }
+ }
+ else {
+ final int index = indexFromPredicate(context, predicate);
+ if (it.setPosition(index + 1)) {
+ pointer = it.getNodePointer();
+ }
+ }
+ }
+ if (pointer != null) {
+ return doStep(context, pointer, steps, currentStep + 1);
+ }
+ }
+ else {
+ final NodeIterator it = getNodeIterator(context, parent, step);
+ if (it != null) {
+ final List list = new ArrayList();
+ for (int i = 1; it.setPosition(i); i++) {
+ list.add(it.getNodePointer());
+ }
+ final NodePointer pointer =
+ doPredicatesStandard(
+ context,
+ list,
+ steps,
+ currentStep,
+ predicates,
+ 0);
+ if (pointer != null) {
+ return pointer;
+ }
+ }
+ }
+ return createNullPointer(context, parent, steps, currentStep);
+ }
+
+ /**
+ * Evaluates predicates and proceeds with the subsequent steps
+ * of the path.
+ * @param context evaluation context
+ * @param parent parent pointer
+ * @param steps path steps
+ * @param currentStep step number
+ * @param predicates predicate expressions
+ * @param currentPredicate int predicate number
+ * @return NodePointer
+ */
+ private static NodePointer doPredicate(
+ final EvalContext context, final NodePointer parent,
+ final Step[] steps, final int currentStep,
+ final Expression[] predicates, final int currentPredicate) {
+ if (currentPredicate == predicates.length) {
+ return doStep(context, parent, steps, currentStep + 1);
+ }
+
+ final Expression predicate = predicates[currentPredicate];
+ if (predicate instanceof NameAttributeTest) { // [@name = key1]
+ return doPredicateName(
+ context,
+ parent,
+ steps,
+ currentStep,
+ predicates,
+ currentPredicate);
+ }
+ // else [index]
+ return doPredicateIndex(
+ context,
+ parent,
+ steps,
+ currentStep,
+ predicates,
+ currentPredicate);
+ }
+
+ /**
+ * Execute a NameAttributeTest predicate
+ * @param context evaluation context
+ * @param parent parent pointer
+ * @param steps path steps
+ * @param currentStep int step number
+ * @param predicates predicates
+ * @param currentPredicate int predicate number
+ * @return NodePointer
+ */
+ private static NodePointer doPredicateName(
+ final EvalContext context, final NodePointer parent,
+ final Step[] steps, final int currentStep,
+ final Expression[] predicates, final int currentPredicate) {
+ final Expression predicate = predicates[currentPredicate];
+ final String key = keyFromPredicate(context, predicate);
+ NodePointer child = valuePointer(parent);
+ if (child instanceof PropertyOwnerPointer) {
+ final PropertyPointer pointer =
+ ((PropertyOwnerPointer) child).getPropertyPointer();
+ pointer.setPropertyName(key);
+ if (pointer.isActual()) {
+ return doPredicate(
+ context,
+ pointer,
+ steps,
+ currentStep,
+ predicates,
+ currentPredicate + 1);
+ }
+ }
+ else if (child.isCollection()) {
+ // For each node in the collection, perform the following:
+ // if the node is a property owner, apply this predicate to it;
+ // if the node is a collection, apply this predicate to each elem.;
+ // if the node is not a prop owner or a collection,
+ // see if it has the attribute "name" with the right value,
+ // if so - proceed to the next predicate
+ NodePointer bestMatch = null;
+ int bestQuality = 0;
+ child = (NodePointer) child.clone();
+ final int count = child.getLength();
+ for (int i = 0; i < count; i++) {
+ child.setIndex(i);
+ final NodePointer valuePointer = valuePointer(child);
+ NodePointer pointer;
+ if (valuePointer instanceof PropertyOwnerPointer
+ || valuePointer.isCollection()) {
+ pointer =
+ doPredicateName(
+ context,
+ valuePointer,
+ steps,
+ currentStep,
+ predicates,
+ currentPredicate);
+ }
+ else if (isNameAttributeEqual(valuePointer, key)) {
+ pointer =
+ doPredicate(
+ context,
+ valuePointer,
+ steps,
+ currentStep,
+ predicates,
+ currentPredicate + 1);
+ }
+ else {
+ pointer = null;
+ }
+ if (pointer != null) {
+ final int quality = computeQuality(pointer);
+ if (quality == PERFECT_MATCH) {
+ return pointer;
+ }
+ if (quality > bestQuality) {
+ bestMatch = (NodePointer) pointer.clone();
+ bestQuality = quality;
+ }
+ }
+ }
+ if (bestMatch != null) {
+ return bestMatch;
+ }
+ }
+ else {
+ // If the node is a standard InfoSet node (e.g. DOM Node),
+ // employ doPredicates_standard, which will iterate through
+ // the node's children and apply all predicates
+ final NodePointer found =
+ doPredicatesStandard(
+ context,
+ Collections.singletonList(child),
+ steps,
+ currentStep,
+ predicates,
+ currentPredicate);
+ if (found != null) {
+ return found;
+ }
+ }
+ // If nothing worked - return a null pointer
+ return createNullPointerForPredicates(
+ context,
+ child,
+ steps,
+ currentStep,
+ predicates,
+ currentPredicate);
+ }
+
+ /**
+ * Called exclusively for standard InfoSet nodes, e.g. DOM nodes
+ * to evaluate predicate sequences like [@name=...][@name=...][index].
+ * @param context evaluation context
+ * @param parents List of parent pointers
+ * @param steps path steps
+ * @param currentStep step number
+ * @param predicates predicates
+ * @param currentPredicate int predicate number
+ * @return NodePointer
+ */
+ private static NodePointer doPredicatesStandard(
+ final EvalContext context, final List parents,
+ final Step[] steps, final int currentStep,
+ final Expression[] predicates, final int currentPredicate) {
+ if (parents.isEmpty()) {
+ return null;
+ }
+
+ // If all predicates have been processed, take the first
+ // element from the list of results and proceed to the
+ // remaining steps with that element.
+ if (currentPredicate == predicates.length) {
+ final NodePointer pointer = (NodePointer) parents.get(0);
+ return doStep(context, pointer, steps, currentStep + 1);
+ }
+
+ final Expression predicate = predicates[currentPredicate];
+ if (predicate instanceof NameAttributeTest) {
+ final String key = keyFromPredicate(context, predicate);
+ final List newList = new ArrayList();
+ for (int i = 0; i < parents.size(); i++) {
+ final NodePointer pointer = (NodePointer) parents.get(i);
+ if (isNameAttributeEqual(pointer, key)) {
+ newList.add(pointer);
+ }
+ }
+ if (newList.isEmpty()) {
+ return null;
+ }
+ return doPredicatesStandard(
+ context,
+ newList,
+ steps,
+ currentStep,
+ predicates,
+ currentPredicate + 1);
+ }
+ // For a subscript, simply take the corresponding
+ // element from the list of results and
+ // proceed to the remaining predicates with that element
+ final int index = indexFromPredicate(context, predicate);
+ if (index < 0 || index >= parents.size()) {
+ return null;
+ }
+ final NodePointer ptr = (NodePointer) parents.get(index);
+ return doPredicate(
+ context,
+ ptr,
+ steps,
+ currentStep,
+ predicates,
+ currentPredicate + 1);
+ }
+
+ /**
+ * Evaluate a subscript predicate: see if the node is a collection and
+ * if the index is inside the collection.
+ * @param context evaluation context
+ * @param parent parent pointer
+ * @param steps path steps
+ * @param currentStep step number
+ * @param predicates predicates
+ * @param currentPredicate int predicate number
+ * @return NodePointer
+ */
+ private static NodePointer doPredicateIndex(
+ final EvalContext context, final NodePointer parent,
+ final Step[] steps, final int currentStep,
+ final Expression[] predicates, final int currentPredicate) {
+ final Expression predicate = predicates[currentPredicate];
+ final int index = indexFromPredicate(context, predicate);
+ NodePointer pointer = parent;
+ if (isCollectionElement(pointer, index)) {
+ pointer = (NodePointer) pointer.clone();
+ pointer.setIndex(index);
+ return doPredicate(
+ context,
+ pointer,
+ steps,
+ currentStep,
+ predicates,
+ currentPredicate + 1);
+ }
+ return createNullPointerForPredicates(
+ context,
+ parent,
+ steps,
+ currentStep,
+ predicates,
+ currentPredicate);
+ }
+
+ /**
+ * Extract an integer from a subscript predicate. The returned index
+ * starts with 0, even though the subscript starts with 1.
+ * @param context evaluation context
+ * @param predicate to evaluate
+ * @return calculated index
+ */
+ private static int indexFromPredicate(
+ final EvalContext context,
+ final Expression predicate) {
+ Object value = predicate.computeValue(context);
+ if (value instanceof EvalContext) {
+ value = ((EvalContext) value).getSingleNodePointer();
+ }
+ if (value instanceof NodePointer) {
+ value = ((NodePointer) value).getValue();
+ }
+ if (value == null) {
+ throw new JXPathException("Predicate value is null: " + predicate);
+ }
+
+ if (value instanceof Number) {
+ final double round = 0.5;
+ return (int) (InfoSetUtil.doubleValue(value) + round) - 1;
+ }
+ return InfoSetUtil.booleanValue(value) ? 0 : -1;
+ }
+
+ /**
+ * Extracts the string value of the expression from a predicate like
+ * [@name=expression].
+ * @param context evaluation context
+ * @param predicate predicate to evaluate
+ * @return String key extracted
+ */
+ private static String keyFromPredicate(final EvalContext context,
+ final Expression predicate) {
+ final Expression expr =
+ ((NameAttributeTest) predicate).getNameTestExpression();
+ return InfoSetUtil.stringValue(expr.computeValue(context));
+ }
+
+ /**
+ * For a pointer that matches an actual node, returns 0.
+ * For a pointer that does not match an actual node, but whose
+ * parent pointer does returns -1, etc.
+ * @param pointer input pointer
+ * @return int match quality code
+ */
+ private static int computeQuality(NodePointer pointer) {
+ int quality = PERFECT_MATCH;
+ while (pointer != null && !pointer.isActual()) {
+ quality--;
+ pointer = pointer.getImmediateParentPointer();
+ }
+ return quality;
+ }
+
+ /**
+ * Returns true if the pointer has an attribute called "name" and
+ * its value is equal to the supplied string.
+ * @param pointer input pointer
+ * @param name name to check
+ * @return boolean
+ */
+ private static boolean isNameAttributeEqual(
+ final NodePointer pointer,
+ final String name) {
+ final NodeIterator it = pointer.attributeIterator(QNAME_NAME);
+ return it != null
+ && it.setPosition(1)
+ && name.equals(it.getNodePointer().getValue());
+ }
+
+ /**
+ * Returns true if the pointer is a collection and the index is
+ * withing the bounds of the collection.
+ * @param pointer input pointer
+ * @param index to check
+ * @return boolean
+ */
+ private static boolean isCollectionElement(
+ final NodePointer pointer,
+ final int index) {
+ return pointer.isActual()
+ && (index == 0
+ || pointer.isCollection()
+ && index >= 0
+ && index < pointer.getLength());
+ }
+
+ /**
+ * For an intermediate pointer (e.g. PropertyPointer, ContainerPointer)
+ * returns a pointer for the contained value.
+ * @param pointer input pointer
+ * @return NodePointer
+ */
+ private static NodePointer valuePointer(final NodePointer pointer) {
+ return pointer == null ? null : pointer.getValuePointer();
+ }
+
+ /**
+ * Creates a "null pointer" that
+ * a) represents the requested path and
+ * b) can be used for creation of missing nodes in the path.
+ * @param context evaluation context
+ * @param parent parent pointer
+ * @param steps path steps
+ * @param currentStep step number
+ * @return NodePointer
+ */
+ public static NodePointer createNullPointer(
+ final EvalContext context, NodePointer parent, final Step[] steps,
+ final int currentStep) {
+ if (currentStep == steps.length) {
+ return parent;
+ }
+
+ parent = valuePointer(parent);
+
+ final Step step = steps[currentStep];
+
+ final int axis = step.getAxis();
+ if (axis == Compiler.AXIS_CHILD || axis == Compiler.AXIS_ATTRIBUTE) {
+ final NullPropertyPointer pointer = new NullPropertyPointer(parent);
+ final QName name = ((NodeNameTest) step.getNodeTest()).getNodeName();
+ pointer.setPropertyName(name.toString());
+ pointer.setAttribute(axis == Compiler.AXIS_ATTRIBUTE);
+ parent = pointer;
+ }
+ // else { it is self::node() }
+
+ final Expression[] predicates = step.getPredicates();
+ return createNullPointerForPredicates(
+ context,
+ parent,
+ steps,
+ currentStep,
+ predicates,
+ 0);
+ }
+
+ /**
+ * Creates a "null pointer" that starts with predicates.
+ * @param context evaluation context
+ * @param parent parent pointer
+ * @param steps path steps
+ * @param currentStep step number
+ * @param predicates predicates
+ * @param currentPredicate int predicate number
+ * @return NodePointer
+ */
+ private static NodePointer createNullPointerForPredicates(
+ final EvalContext context, NodePointer parent,
+ final Step[] steps, final int currentStep,
+ final Expression[] predicates, final int currentPredicate) {
+ for (int i = currentPredicate; i < predicates.length; i++) {
+ final Expression predicate = predicates[i];
+ if (predicate instanceof NameAttributeTest) {
+ final String key = keyFromPredicate(context, predicate);
+ parent = valuePointer(parent);
+ final NullPropertyPointer pointer = new NullPropertyPointer(parent);
+ pointer.setNameAttributeValue(key);
+ parent = pointer;
+ }
+ else {
+ final int index = indexFromPredicate(context, predicate);
+ if (parent instanceof NullPropertyPointer) {
+ parent.setIndex(index);
+ }
+ else {
+ parent = new NullElementPointer(parent, index);
+ }
+ }
+ }
+ // Proceed with the remaining steps
+ return createNullPointer(
+ context, parent, steps, currentStep + 1);
+ }
+
+ /**
+ * Gets a NodeIterator.
+ * @param context evaluation context
+ * @param pointer owning pointer
+ * @param step triggering step
+ * @return NodeIterator
+ */
+ private static NodeIterator getNodeIterator(
+ final EvalContext context,
+ final NodePointer pointer,
+ final Step step) {
+ if (step.getAxis() == Compiler.AXIS_CHILD) {
+ NodeTest nodeTest = step.getNodeTest();
+ final QName qname = ((NodeNameTest) nodeTest).getNodeName();
+ final String prefix = qname.getPrefix();
+ if (prefix != null) {
+ final String namespaceURI = context.getJXPathContext()
+ .getNamespaceURI(prefix);
+ nodeTest = new NodeNameTest(qname, namespaceURI);
+ }
+ return pointer.childIterator(nodeTest, false, null);
+ }
+ // else Compiler.AXIS_ATTRIBUTE
+ if (!(step.getNodeTest() instanceof NodeNameTest)) {
+ throw new UnsupportedOperationException(
+ "Not supported node test for attributes: "
+ + step.getNodeTest());
+ }
+ return pointer.attributeIterator(
+ ((NodeNameTest) step.getNodeTest()).getNodeName());
+ }
+
+ /**
+ * Learn whether {@code name} is a lang attribute.
+ * @param name to compare
+ * @return boolean
+ */
+ private static boolean isLangAttribute(final QName name) {
+ return name.getPrefix() != null
+ && name.getPrefix().equals("xml")
+ && name.getName().equals("lang");
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/UnionContext.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/UnionContext.java
new file mode 100644
index 00000000000..c454e643016
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/axes/UnionContext.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.axes;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.apache.commons.jxpath.BasicNodeSet;
+import org.apache.commons.jxpath.Pointer;
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * EvalContext that represents a union between other contexts - result
+ * of a union operation like (a | b)
+ */
+public class UnionContext extends NodeSetContext {
+ private final EvalContext[] contexts;
+ private boolean prepared;
+
+ /**
+ * Create a new UnionContext.
+ * @param parentContext parent context
+ * @param contexts child contexts
+ */
+ public UnionContext(final EvalContext parentContext, final EvalContext[] contexts) {
+ super(parentContext, new BasicNodeSet());
+ this.contexts = contexts;
+ }
+
+ @Override
+ public int getDocumentOrder() {
+ return contexts.length > 1 ? 1 : super.getDocumentOrder();
+ }
+
+ @Override
+ public boolean setPosition(final int position) {
+ if (!prepared) {
+ prepared = true;
+ final BasicNodeSet nodeSet = (BasicNodeSet) getNodeSet();
+ final ArrayList pointers = new ArrayList();
+ for (final EvalContext ctx : contexts) {
+ while (ctx.nextSet()) {
+ while (ctx.nextNode()) {
+ final NodePointer ptr = ctx.getCurrentNodePointer();
+ if (!pointers.contains(ptr)) {
+ pointers.add(ptr);
+ }
+ }
+ }
+ }
+ sortPointers(pointers);
+
+ for (final Iterator it = pointers.iterator(); it.hasNext();) {
+ nodeSet.add((Pointer) it.next());
+ }
+ }
+ return super.setPosition(position);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/Constant.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/Constant.java
new file mode 100644
index 00000000000..6fe6d3651b7
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/Constant.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.InfoSetUtil;
+
+/**
+ * A compile tree element containing a constant number or string.
+ */
+public class Constant extends Expression {
+
+ private final Object value;
+
+ /**
+ * Create a new Constant.
+ * @param number constant
+ */
+ public Constant(final Number number) {
+ this.value = number;
+ }
+
+ /**
+ * Create a new Constant.
+ * @param string constant
+ */
+ public Constant(final String string) {
+ this.value = string;
+ }
+
+ @Override
+ public Object compute(final EvalContext context) {
+ return value;
+ }
+
+ @Override
+ public Object computeValue(final EvalContext context) {
+ return value;
+ }
+
+ /**
+ * Returns false
+ * @return false
+ */
+ @Override
+ public boolean isContextDependent() {
+ return false;
+ }
+
+ /**
+ * Returns false
+ * @return false
+ */
+ @Override
+ public boolean computeContextDependent() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ if (value instanceof Number) {
+ return InfoSetUtil.stringValue(value);
+ }
+ return "'" + value + "'";
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreFunction.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreFunction.java
new file mode 100644
index 00000000000..3eda5714188
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreFunction.java
@@ -0,0 +1,931 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.util.Collection;
+import java.util.Locale;
+
+import org.apache.commons.jxpath.BasicNodeSet;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.JXPathException;
+import org.apache.commons.jxpath.JXPathInvalidSyntaxException;
+import org.apache.commons.jxpath.NodeSet;
+import org.apache.commons.jxpath.ri.Compiler;
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.InfoSetUtil;
+import org.apache.commons.jxpath.ri.axes.NodeSetContext;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * An element of the compile tree representing one of built-in functions
+ * like "position()" or "number()".
+ */
+public class CoreFunction extends Operation {
+
+ private static final Double ZERO = Double.valueOf(0);
+ private final int functionCode;
+
+ /**
+ * Create a new CoreFunction.
+ * @param functionCode int function code
+ * @param args argument Expressions
+ */
+ public CoreFunction(final int functionCode, final Expression[] args) {
+ super(args);
+ this.functionCode = functionCode;
+ }
+
+ /**
+ * Gets the function code.
+ * @return int function code
+ */
+ public int getFunctionCode() {
+ return functionCode;
+ }
+
+ /**
+ * Gets the name of this function.
+ * @return String function name
+ */
+ protected String getFunctionName() {
+ switch (functionCode) {
+ case Compiler.FUNCTION_LAST :
+ return "last";
+ case Compiler.FUNCTION_POSITION :
+ return "position";
+ case Compiler.FUNCTION_COUNT :
+ return "count";
+ case Compiler.FUNCTION_ID :
+ return "id";
+ case Compiler.FUNCTION_LOCAL_NAME :
+ return "local-name";
+ case Compiler.FUNCTION_NAMESPACE_URI :
+ return "namespace-uri";
+ case Compiler.FUNCTION_NAME :
+ return "name";
+ case Compiler.FUNCTION_STRING :
+ return "string";
+ case Compiler.FUNCTION_CONCAT :
+ return "concat";
+ case Compiler.FUNCTION_STARTS_WITH :
+ return "starts-with";
+ case Compiler.FUNCTION_ENDS_WITH :
+ return "ends-with";
+ case Compiler.FUNCTION_CONTAINS :
+ return "contains";
+ case Compiler.FUNCTION_SUBSTRING_BEFORE :
+ return "substring-before";
+ case Compiler.FUNCTION_SUBSTRING_AFTER :
+ return "substring-after";
+ case Compiler.FUNCTION_SUBSTRING :
+ return "substring";
+ case Compiler.FUNCTION_STRING_LENGTH :
+ return "string-length";
+ case Compiler.FUNCTION_NORMALIZE_SPACE :
+ return "normalize-space";
+ case Compiler.FUNCTION_TRANSLATE :
+ return "translate";
+ case Compiler.FUNCTION_BOOLEAN :
+ return "boolean";
+ case Compiler.FUNCTION_NOT :
+ return "not";
+ case Compiler.FUNCTION_TRUE :
+ return "true";
+ case Compiler.FUNCTION_FALSE :
+ return "false";
+ case Compiler.FUNCTION_LANG :
+ return "lang";
+ case Compiler.FUNCTION_NUMBER :
+ return "number";
+ case Compiler.FUNCTION_SUM :
+ return "sum";
+ case Compiler.FUNCTION_FLOOR :
+ return "floor";
+ case Compiler.FUNCTION_CEILING :
+ return "ceiling";
+ case Compiler.FUNCTION_ROUND :
+ return "round";
+ case Compiler.FUNCTION_KEY :
+ return "key";
+ case Compiler.FUNCTION_FORMAT_NUMBER:
+ return "format-number";
+ default:
+ return "unknownFunction" + functionCode + "()";
+ }
+ }
+
+ /**
+ * Convenience method to return the first argument.
+ * @return Expression
+ */
+ public Expression getArg1() {
+ return args[0];
+ }
+
+ /**
+ * Convenience method to return the second argument.
+ * @return Expression
+ */
+ public Expression getArg2() {
+ return args[1];
+ }
+
+ /**
+ * Convenience method to return the third argument.
+ * @return Expression
+ */
+ public Expression getArg3() {
+ return args[2];
+ }
+
+ /**
+ * Gets the number of argument Expressions.
+ * @return int count
+ */
+ public int getArgumentCount() {
+ if (args == null) {
+ return 0;
+ }
+ return args.length;
+ }
+
+ /**
+ * Returns true if any argument is context dependent or if
+ * the function is last(), position(), boolean(), local-name(),
+ * name(), string(), lang(), number().
+ * @return boolean
+ */
+ @Override
+ public boolean computeContextDependent() {
+ if (super.computeContextDependent()) {
+ return true;
+ }
+
+ switch (functionCode) {
+ case Compiler.FUNCTION_LAST:
+ case Compiler.FUNCTION_POSITION:
+ return true;
+
+ case Compiler.FUNCTION_BOOLEAN:
+ case Compiler.FUNCTION_LOCAL_NAME:
+ case Compiler.FUNCTION_NAME:
+ case Compiler.FUNCTION_NAMESPACE_URI:
+ case Compiler.FUNCTION_STRING:
+ case Compiler.FUNCTION_LANG:
+ case Compiler.FUNCTION_NUMBER:
+ return args == null || args.length == 0;
+
+ case Compiler.FUNCTION_FORMAT_NUMBER:
+ return args != null && args.length == 2;
+
+ case Compiler.FUNCTION_COUNT:
+ case Compiler.FUNCTION_ID:
+ case Compiler.FUNCTION_CONCAT:
+ case Compiler.FUNCTION_STARTS_WITH:
+ case Compiler.FUNCTION_ENDS_WITH:
+ case Compiler.FUNCTION_CONTAINS:
+ case Compiler.FUNCTION_SUBSTRING_BEFORE:
+ case Compiler.FUNCTION_SUBSTRING_AFTER:
+ case Compiler.FUNCTION_SUBSTRING:
+ case Compiler.FUNCTION_STRING_LENGTH:
+ case Compiler.FUNCTION_NORMALIZE_SPACE:
+ case Compiler.FUNCTION_TRANSLATE:
+ case Compiler.FUNCTION_NOT:
+ case Compiler.FUNCTION_TRUE:
+ case Compiler.FUNCTION_FALSE:
+ case Compiler.FUNCTION_SUM:
+ case Compiler.FUNCTION_FLOOR:
+ case Compiler.FUNCTION_CEILING:
+ case Compiler.FUNCTION_ROUND:
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder buffer = new StringBuilder();
+ buffer.append(getFunctionName());
+ buffer.append('(');
+ final Expression[] args = getArguments();
+ if (args != null) {
+ for (int i = 0; i < args.length; i++) {
+ if (i > 0) {
+ buffer.append(", ");
+ }
+ buffer.append(args[i]);
+ }
+ }
+ buffer.append(')');
+ return buffer.toString();
+ }
+
+ @Override
+ public Object compute(final EvalContext context) {
+ return computeValue(context);
+ }
+
+ @Override
+ public Object computeValue(final EvalContext context) {
+ switch (functionCode) {
+ case Compiler.FUNCTION_LAST :
+ return functionLast(context);
+ case Compiler.FUNCTION_POSITION :
+ return functionPosition(context);
+ case Compiler.FUNCTION_COUNT :
+ return functionCount(context);
+ case Compiler.FUNCTION_LANG :
+ return functionLang(context);
+ case Compiler.FUNCTION_ID :
+ return functionID(context);
+ case Compiler.FUNCTION_LOCAL_NAME :
+ return functionLocalName(context);
+ case Compiler.FUNCTION_NAMESPACE_URI :
+ return functionNamespaceURI(context);
+ case Compiler.FUNCTION_NAME :
+ return functionName(context);
+ case Compiler.FUNCTION_STRING :
+ return functionString(context);
+ case Compiler.FUNCTION_CONCAT :
+ return functionConcat(context);
+ case Compiler.FUNCTION_STARTS_WITH :
+ return functionStartsWith(context);
+ case Compiler.FUNCTION_ENDS_WITH :
+ return functionEndsWith(context);
+ case Compiler.FUNCTION_CONTAINS :
+ return functionContains(context);
+ case Compiler.FUNCTION_SUBSTRING_BEFORE :
+ return functionSubstringBefore(context);
+ case Compiler.FUNCTION_SUBSTRING_AFTER :
+ return functionSubstringAfter(context);
+ case Compiler.FUNCTION_SUBSTRING :
+ return functionSubstring(context);
+ case Compiler.FUNCTION_STRING_LENGTH :
+ return functionStringLength(context);
+ case Compiler.FUNCTION_NORMALIZE_SPACE :
+ return functionNormalizeSpace(context);
+ case Compiler.FUNCTION_TRANSLATE :
+ return functionTranslate(context);
+ case Compiler.FUNCTION_BOOLEAN :
+ return functionBoolean(context);
+ case Compiler.FUNCTION_NOT :
+ return functionNot(context);
+ case Compiler.FUNCTION_TRUE :
+ return functionTrue(context);
+ case Compiler.FUNCTION_FALSE :
+ return functionFalse(context);
+ case Compiler.FUNCTION_NULL :
+ return functionNull(context);
+ case Compiler.FUNCTION_NUMBER :
+ return functionNumber(context);
+ case Compiler.FUNCTION_SUM :
+ return functionSum(context);
+ case Compiler.FUNCTION_FLOOR :
+ return functionFloor(context);
+ case Compiler.FUNCTION_CEILING :
+ return functionCeiling(context);
+ case Compiler.FUNCTION_ROUND :
+ return functionRound(context);
+ case Compiler.FUNCTION_KEY :
+ return functionKey(context);
+ case Compiler.FUNCTION_FORMAT_NUMBER :
+ return functionFormatNumber(context);
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * last() implementation.
+ * @param context evaluation context
+ * @return Number
+ */
+ protected Object functionLast(final EvalContext context) {
+ assertArgCount(0);
+ // Move the position to the beginning and iterate through
+ // the context to count nodes.
+ final int old = context.getCurrentPosition();
+ context.reset();
+ int count = 0;
+ while (context.nextNode()) {
+ count++;
+ }
+
+ // Restore the current position.
+ if (old != 0) {
+ context.setPosition(old);
+ }
+ return Double.valueOf(count);
+ }
+
+ /**
+ * position() implementation.
+ * @param context evaluation context
+ * @return Number
+ */
+ protected Object functionPosition(final EvalContext context) {
+ assertArgCount(0);
+ return Integer.valueOf(context.getCurrentPosition());
+ }
+
+ /**
+ * count() implementation.
+ * @param context evaluation context
+ * @return Number
+ */
+ protected Object functionCount(final EvalContext context) {
+ assertArgCount(1);
+ final Expression arg1 = getArg1();
+ int count = 0;
+ Object value = arg1.compute(context);
+ if (value instanceof NodePointer) {
+ value = ((NodePointer) value).getValue();
+ }
+ if (value instanceof EvalContext) {
+ final EvalContext ctx = (EvalContext) value;
+ while (ctx.hasNext()) {
+ ctx.next();
+ count++;
+ }
+ }
+ else if (value instanceof Collection) {
+ count = ((Collection) value).size();
+ }
+ else if (value == null) {
+ count = 0;
+ }
+ else {
+ count = 1;
+ }
+ return Double.valueOf(count);
+ }
+
+ /**
+ * lang() implementation.
+ * @param context evaluation context
+ * @return Boolean
+ */
+ protected Object functionLang(final EvalContext context) {
+ assertArgCount(1);
+ final String lang = InfoSetUtil.stringValue(getArg1().computeValue(context));
+ final NodePointer pointer = (NodePointer) context.getSingleNodePointer();
+ if (pointer == null) {
+ return Boolean.FALSE;
+ }
+ return pointer.isLanguage(lang) ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ /**
+ * id() implementation.
+ * @param context evaluation context
+ * @return Pointer
+ */
+ protected Object functionID(final EvalContext context) {
+ assertArgCount(1);
+ final String id = InfoSetUtil.stringValue(getArg1().computeValue(context));
+ final JXPathContext jxpathContext = context.getJXPathContext();
+ final NodePointer pointer = (NodePointer) jxpathContext.getContextPointer();
+ return pointer.getPointerByID(jxpathContext, id);
+ }
+
+ /**
+ * key() implementation.
+ * @param context evaluation context
+ * @return various Object
+ */
+ protected Object functionKey(final EvalContext context) {
+ assertArgCount(2);
+ final String key = InfoSetUtil.stringValue(getArg1().computeValue(context));
+ Object value = getArg2().compute(context);
+ EvalContext ec = null;
+ if (value instanceof EvalContext) {
+ ec = (EvalContext) value;
+ if (ec.hasNext()) {
+ value = ((NodePointer) ec.next()).getValue();
+ }
+ else { // empty context -> empty results
+ return new NodeSetContext(context, new BasicNodeSet());
+ }
+ }
+ final JXPathContext jxpathContext = context.getJXPathContext();
+ NodeSet nodeSet = jxpathContext.getNodeSetByKey(key, value);
+ if (ec != null && ec.hasNext()) {
+ final BasicNodeSet accum = new BasicNodeSet();
+ accum.add(nodeSet);
+ while (ec.hasNext()) {
+ value = ((NodePointer) ec.next()).getValue();
+ accum.add(jxpathContext.getNodeSetByKey(key, value));
+ }
+ nodeSet = accum;
+ }
+ return new NodeSetContext(context, nodeSet);
+ }
+
+ /**
+ * namespace-uri() implementation.
+ * @param context evaluation context
+ * @return String
+ */
+ protected Object functionNamespaceURI(final EvalContext context) {
+ if (getArgumentCount() == 0) {
+ final NodePointer ptr = context.getCurrentNodePointer();
+ final String str = ptr.getNamespaceURI();
+ return str == null ? "" : str;
+ }
+ assertArgCount(1);
+ final Object set = getArg1().compute(context);
+ if (set instanceof EvalContext) {
+ final EvalContext ctx = (EvalContext) set;
+ if (ctx.hasNext()) {
+ final NodePointer ptr = (NodePointer) ctx.next();
+ final String str = ptr.getNamespaceURI();
+ return str == null ? "" : str;
+ }
+ }
+ return "";
+ }
+
+ /**
+ * local-name() implementation.
+ * @param context evaluation context
+ * @return String
+ */
+ protected Object functionLocalName(final EvalContext context) {
+ if (getArgumentCount() == 0) {
+ final NodePointer ptr = context.getCurrentNodePointer();
+ return ptr.getName().getName();
+ }
+ assertArgCount(1);
+ final Object set = getArg1().compute(context);
+ if (set instanceof EvalContext) {
+ final EvalContext ctx = (EvalContext) set;
+ if (ctx.hasNext()) {
+ final NodePointer ptr = (NodePointer) ctx.next();
+ return ptr.getName().getName();
+ }
+ }
+ return "";
+ }
+
+ /**
+ * name() implementation.
+ * @param context evaluation context
+ * @return String
+ */
+ protected Object functionName(final EvalContext context) {
+ if (getArgumentCount() == 0) {
+ final NodePointer ptr = context.getCurrentNodePointer();
+ return ptr.getName().toString();
+ }
+ assertArgCount(1);
+ final Object set = getArg1().compute(context);
+ if (set instanceof EvalContext) {
+ final EvalContext ctx = (EvalContext) set;
+ if (ctx.hasNext()) {
+ final NodePointer ptr = (NodePointer) ctx.next();
+ return ptr.getName().toString();
+ }
+ }
+ return "";
+ }
+
+ /**
+ * string() implementation.
+ * @param context evaluation context
+ * @return String
+ */
+ protected Object functionString(final EvalContext context) {
+ if (getArgumentCount() == 0) {
+ return InfoSetUtil.stringValue(context.getCurrentNodePointer());
+ }
+ assertArgCount(1);
+ return InfoSetUtil.stringValue(getArg1().computeValue(context));
+ }
+
+ /**
+ * concat() implementation.
+ * @param context evaluation context
+ * @return String
+ */
+ protected Object functionConcat(final EvalContext context) {
+ if (getArgumentCount() < 2) {
+ assertArgCount(2);
+ }
+ final StringBuilder buffer = new StringBuilder();
+ final Expression[] args = getArguments();
+ for (final Expression arg : args) {
+ buffer.append(InfoSetUtil.stringValue(arg.compute(context)));
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * starts-with() implementation.
+ * @param context evaluation context
+ * @return Boolean
+ */
+ protected Object functionStartsWith(final EvalContext context) {
+ assertArgCount(2);
+ final String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
+ final String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
+ return s1.startsWith(s2) ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ /**
+ * ends-with() implementation.
+ * @param context evaluation context
+ * @return Boolean
+ * @since 1.4
+ */
+ protected Object functionEndsWith(final EvalContext context) {
+ assertArgCount(2);
+ final String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
+ final String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
+ return s1.endsWith(s2) ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ /**
+ * contains() implementation.
+ * @param context evaluation context
+ * @return Boolean
+ */
+ protected Object functionContains(final EvalContext context) {
+ assertArgCount(2);
+ final String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
+ final String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
+ return Boolean.valueOf(s1.contains(s2));
+ }
+
+ /**
+ * substring-before() implementation.
+ * @param context evaluation context
+ * @return String
+ */
+ protected Object functionSubstringBefore(final EvalContext context) {
+ assertArgCount(2);
+ final String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
+ final String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
+ final int index = s1.indexOf(s2);
+ if (index == -1) {
+ return "";
+ }
+ return s1.substring(0, index);
+ }
+
+ /**
+ * substring-after() implementation.
+ * @param context evaluation context
+ * @return String
+ */
+ protected Object functionSubstringAfter(final EvalContext context) {
+ assertArgCount(2);
+ final String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
+ final String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
+ final int index = s1.indexOf(s2);
+ if (index == -1) {
+ return "";
+ }
+ return s1.substring(index + s2.length());
+ }
+
+ /**
+ * substring() implementation.
+ * @param context evaluation context
+ * @return String
+ */
+ protected Object functionSubstring(final EvalContext context) {
+ final int minArgs = 2;
+ final int maxArgs = 3;
+ assertArgRange(minArgs, maxArgs);
+ final int ac = getArgumentCount();
+
+ final String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
+ double from = InfoSetUtil.doubleValue(getArg2().computeValue(context));
+ if (Double.isNaN(from)) {
+ return "";
+ }
+
+ from = Math.round(from);
+ if (from > s1.length() + 1) {
+ return "";
+ }
+ if (ac == 2) {
+ if (from < 1) {
+ from = 1;
+ }
+ return s1.substring((int) from - 1);
+ }
+ double length =
+ InfoSetUtil.doubleValue(getArg3().computeValue(context));
+ length = Math.round(length);
+ if (length < 0) {
+ return "";
+ }
+
+ final double to = from + length;
+ if (to < 1) {
+ return "";
+ }
+
+ if (to > s1.length() + 1) {
+ if (from < 1) {
+ from = 1;
+ }
+ return s1.substring((int) from - 1);
+ }
+
+ if (from < 1) {
+ from = 1;
+ }
+ return s1.substring((int) from - 1, (int) (to - 1));
+ }
+
+ /**
+ * string-length() implementation.
+ * @param context evaluation context
+ * @return Number
+ */
+ protected Object functionStringLength(final EvalContext context) {
+ String s;
+ if (getArgumentCount() == 0) {
+ s = InfoSetUtil.stringValue(context.getCurrentNodePointer());
+ }
+ else {
+ assertArgCount(1);
+ s = InfoSetUtil.stringValue(getArg1().computeValue(context));
+ }
+ return Double.valueOf(s.length());
+ }
+
+ /**
+ * normalize-space() implementation.
+ * @param context evaluation context
+ * @return String
+ */
+ protected Object functionNormalizeSpace(final EvalContext context) {
+ assertArgCount(1);
+ final String s = InfoSetUtil.stringValue(getArg1().computeValue(context));
+ final char[] chars = s.toCharArray();
+ int out = 0;
+ int phase = 0;
+ for (int in = 0; in < chars.length; in++) {
+ switch (chars[in]) {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ if (phase == 1) { // non-space
+ phase = 2;
+ chars[out++] = ' ';
+ }
+ break;
+ default:
+ chars[out++] = chars[in];
+ phase = 1;
+ }
+ }
+ if (phase == 2) { // trailing-space
+ out--;
+ }
+ return new String(chars, 0, out);
+ }
+
+ /**
+ * translate() implementation.
+ * @param context evaluation context
+ * @return String
+ */
+ protected Object functionTranslate(final EvalContext context) {
+ final int argCount = 3;
+ assertArgCount(argCount);
+ final String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
+ final String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
+ final String s3 = InfoSetUtil.stringValue(getArg3().computeValue(context));
+ final char[] chars = s1.toCharArray();
+ int out = 0;
+ for (int in = 0; in < chars.length; in++) {
+ final char c = chars[in];
+ final int inx = s2.indexOf(c);
+ if (inx != -1) {
+ if (inx < s3.length()) {
+ chars[out++] = s3.charAt(inx);
+ }
+ }
+ else {
+ chars[out++] = c;
+ }
+ }
+ return new String(chars, 0, out);
+ }
+
+ /**
+ * boolean() implementation.
+ * @param context evaluation context
+ * @return Boolean
+ */
+ protected Object functionBoolean(final EvalContext context) {
+ assertArgCount(1);
+ return InfoSetUtil.booleanValue(getArg1().computeValue(context))
+ ? Boolean.TRUE
+ : Boolean.FALSE;
+ }
+
+ /**
+ * not() implementation.
+ * @param context evaluation context
+ * @return Boolean
+ */
+ protected Object functionNot(final EvalContext context) {
+ assertArgCount(1);
+ return InfoSetUtil.booleanValue(getArg1().computeValue(context))
+ ? Boolean.FALSE
+ : Boolean.TRUE;
+ }
+
+ /**
+ * true() implementation.
+ * @param context evaluation context
+ * @return Boolean.TRUE
+ */
+ protected Object functionTrue(final EvalContext context) {
+ assertArgCount(0);
+ return Boolean.TRUE;
+ }
+
+ /**
+ * false() implementation.
+ * @param context evaluation context
+ * @return Boolean.FALSE
+ */
+ protected Object functionFalse(final EvalContext context) {
+ assertArgCount(0);
+ return Boolean.FALSE;
+ }
+
+ /**
+ * null() implementation.
+ * @param context evaluation context
+ * @return null
+ */
+ protected Object functionNull(final EvalContext context) {
+ assertArgCount(0);
+ return null;
+ }
+
+ /**
+ * number() implementation.
+ * @param context evaluation context
+ * @return Number
+ */
+ protected Object functionNumber(final EvalContext context) {
+ if (getArgumentCount() == 0) {
+ return InfoSetUtil.number(context.getCurrentNodePointer());
+ }
+ assertArgCount(1);
+ return InfoSetUtil.number(getArg1().computeValue(context));
+ }
+
+ /**
+ * sum() implementation.
+ * @param context evaluation context
+ * @return Number
+ */
+ protected Object functionSum(final EvalContext context) {
+ assertArgCount(1);
+ final Object v = getArg1().compute(context);
+ if (v == null) {
+ return ZERO;
+ }
+ if (v instanceof EvalContext) {
+ double sum = 0.0;
+ final EvalContext ctx = (EvalContext) v;
+ while (ctx.hasNext()) {
+ final NodePointer ptr = (NodePointer) ctx.next();
+ sum += InfoSetUtil.doubleValue(ptr);
+ }
+ return Double.valueOf(sum);
+ }
+ throw new JXPathException(
+ "Invalid argument type for 'sum': " + v.getClass().getName());
+ }
+
+ /**
+ * floor() implementation.
+ * @param context evaluation context
+ * @return Number
+ */
+ protected Object functionFloor(final EvalContext context) {
+ assertArgCount(1);
+ final double v = InfoSetUtil.doubleValue(getArg1().computeValue(context));
+ if (Double.isNaN(v) || Double.isInfinite(v)) {
+ return Double.valueOf(v);
+ }
+ return Double.valueOf(Math.floor(v));
+ }
+
+ /**
+ * ceiling() implementation.
+ * @param context evaluation context
+ * @return Number
+ */
+ protected Object functionCeiling(final EvalContext context) {
+ assertArgCount(1);
+ final double v = InfoSetUtil.doubleValue(getArg1().computeValue(context));
+ if (Double.isNaN(v) || Double.isInfinite(v)) {
+ return Double.valueOf(v);
+ }
+ return Double.valueOf(Math.ceil(v));
+ }
+
+ /**
+ * round() implementation.
+ * @param context evaluation context
+ * @return Number
+ */
+ protected Object functionRound(final EvalContext context) {
+ assertArgCount(1);
+ final double v = InfoSetUtil.doubleValue(getArg1().computeValue(context));
+ if (Double.isNaN(v) || Double.isInfinite(v)) {
+ return Double.valueOf(v);
+ }
+ return Double.valueOf(Math.round(v));
+ }
+
+ /**
+ * format-number() implementation.
+ * @param context evaluation context
+ * @return String
+ */
+ private Object functionFormatNumber(final EvalContext context) {
+ final int minArgs = 2;
+ final int maxArgs = 3;
+ assertArgRange(minArgs, maxArgs);
+
+ final double number =
+ InfoSetUtil.doubleValue(getArg1().computeValue(context));
+ final String pattern =
+ InfoSetUtil.stringValue(getArg2().computeValue(context));
+
+ DecimalFormatSymbols symbols;
+ if (getArgumentCount() == maxArgs) {
+ final String symbolsName =
+ InfoSetUtil.stringValue(getArg3().computeValue(context));
+ symbols =
+ context.getJXPathContext().getDecimalFormatSymbols(symbolsName);
+ }
+ else {
+ final NodePointer pointer = context.getCurrentNodePointer();
+ Locale locale;
+ if (pointer != null) {
+ locale = pointer.getLocale();
+ }
+ else {
+ locale = context.getJXPathContext().getLocale();
+ }
+ symbols = new DecimalFormatSymbols(locale);
+ }
+
+ final DecimalFormat format = (DecimalFormat) NumberFormat.getInstance();
+ format.setDecimalFormatSymbols(symbols);
+ format.applyLocalizedPattern(pattern);
+ return format.format(number);
+ }
+
+ /**
+ * Assert {@code count} args.
+ * @param count int
+ */
+ private void assertArgCount(final int count) {
+ assertArgRange(count, count);
+ }
+
+ /**
+ * Assert at least {@code min}/at most {@code max} args.
+ * @param min int
+ * @param max int
+ */
+ private void assertArgRange(final int min, final int max) {
+ final int ct = getArgumentCount();
+ if (ct < min || ct > max) {
+ throw new JXPathInvalidSyntaxException(
+ "Incorrect number of arguments: " + this);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperation.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperation.java
new file mode 100644
index 00000000000..7ae49db1ebd
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperation.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+
+/**
+ * The common subclass for tree elements representing core operations like "+",
+ * "- ", "*" etc.
+ */
+public abstract class CoreOperation extends Operation {
+
+ /** Or precedence */
+ protected static final int OR_PRECEDENCE = 0;
+ /** And precedence */
+ protected static final int AND_PRECEDENCE = 1;
+ /** Compare precedence */
+ protected static final int COMPARE_PRECEDENCE = 2;
+ /** Relational expression precedence */
+ protected static final int RELATIONAL_EXPR_PRECEDENCE = 3;
+ /** Add/subtract precedence */
+ protected static final int ADD_PRECEDENCE = 4;
+ /** Multiply/divide/mod precedence */
+ protected static final int MULTIPLY_PRECEDENCE = 5;
+ /** Negate precedence */
+ protected static final int NEGATE_PRECEDENCE = 6;
+ /** Union precedence */
+ protected static final int UNION_PRECEDENCE = 7;
+
+ /**
+ * Create a new CoreOperation.
+ * @param args Expression[]
+ */
+ public CoreOperation(final Expression[] args) {
+ super(args);
+ }
+
+ @Override
+ public Object compute(final EvalContext context) {
+ return computeValue(context);
+ }
+
+ @Override
+ public abstract Object computeValue(EvalContext context);
+
+ /**
+ * Returns the XPath symbol for this operation, e.g. "+", "div", etc.
+ * @return String symbol
+ */
+ public abstract String getSymbol();
+
+ /**
+ * Returns true if the operation is not sensitive to the order of arguments,
+ * e.g. "=", "and" etc, and false if it is, e.g. "<=", "div".
+ * @return boolean
+ */
+ protected abstract boolean isSymmetric();
+
+ /**
+ * Computes the precedence of the operation.
+ * @return int precedence
+ */
+ protected abstract int getPrecedence();
+
+ @Override
+ public String toString() {
+ if (args.length == 1) {
+ return getSymbol() + parenthesize(args[0], false);
+ }
+ final StringBuilder buffer = new StringBuilder();
+ for (int i = 0; i < args.length; i++) {
+ if (i > 0) {
+ buffer.append(' ');
+ buffer.append(getSymbol());
+ buffer.append(' ');
+ }
+ buffer.append(parenthesize(args[i], i == 0));
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Wrap an expression in parens if necessary.
+ * @param expression other Expression
+ * @param left whether {@code expression} is left of this one.
+ * @return String
+ */
+ private String parenthesize(final Expression expression, final boolean left) {
+ final String s = expression.toString();
+ if (!(expression instanceof CoreOperation)) {
+ return s;
+ }
+ final int compared = getPrecedence() - ((CoreOperation) expression).getPrecedence();
+
+ if (compared < 0) {
+ return s;
+ }
+ if (compared == 0 && (isSymmetric() || left)) {
+ return s;
+ }
+ return '(' + s + ')';
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationAdd.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationAdd.java
new file mode 100644
index 00000000000..4245030afd8
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationAdd.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.InfoSetUtil;
+
+/**
+ * Implementation of {@link Expression} for the operation "+".
+ */
+public class CoreOperationAdd extends CoreOperation {
+
+ /**
+ * Create a new CoreOperationAdd.
+ * @param args Expression arguments to add together.
+ */
+ public CoreOperationAdd(final Expression[] args) {
+ super(args);
+ }
+
+ @Override
+ public Object computeValue(final EvalContext context) {
+ double s = 0.0;
+ for (final Expression arg : args) {
+ s += InfoSetUtil.doubleValue(arg.computeValue(context));
+ }
+ return Double.valueOf(s);
+ }
+
+ @Override
+ protected int getPrecedence() {
+ return ADD_PRECEDENCE;
+ }
+
+ @Override
+ protected boolean isSymmetric() {
+ return true;
+ }
+
+ @Override
+ public String getSymbol() {
+ return "+";
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationAnd.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationAnd.java
new file mode 100644
index 00000000000..113c15e6b39
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationAnd.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.InfoSetUtil;
+
+/**
+ * Implementation of {@link Expression} for the operation "and".
+ */
+public class CoreOperationAnd extends CoreOperation {
+
+ /**
+ * Create a new CoreOperationAnd.
+ * @param args to combine
+ */
+ public CoreOperationAnd(final Expression[] args) {
+ super(args);
+ }
+
+ @Override
+ public Object computeValue(final EvalContext context) {
+ for (final Expression arg : args) {
+ if (!InfoSetUtil.booleanValue(arg.computeValue(context))) {
+ return Boolean.FALSE;
+ }
+ }
+ return Boolean.TRUE;
+ }
+
+ @Override
+ protected int getPrecedence() {
+ return AND_PRECEDENCE;
+ }
+
+ @Override
+ protected boolean isSymmetric() {
+ return true;
+ }
+
+ @Override
+ public String getSymbol() {
+ return "and";
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationCompare.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationCompare.java
new file mode 100644
index 00000000000..5e54b915e24
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationCompare.java
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+
+import org.apache.commons.jxpath.Pointer;
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.InfoSetUtil;
+import org.apache.commons.jxpath.ri.axes.InitialContext;
+import org.apache.commons.jxpath.ri.axes.SelfContext;
+
+/**
+ * Common superclass for the implementations of Expression for the operations
+ * "=" and "!=".
+ */
+public abstract class CoreOperationCompare extends CoreOperation {
+ private final boolean invert;
+
+ /**
+ * Create a new CoreOperationCompare.
+ * @param arg1 left operand
+ * @param arg2 right operand
+ */
+ public CoreOperationCompare(final Expression arg1, final Expression arg2) {
+ this(arg1, arg2, false);
+ }
+
+ /**
+ * Create a new CoreOperationCompare.
+ * @param arg1 left operand
+ * @param arg2 right operand
+ * @param invert whether to invert (not) the comparison
+ */
+ protected CoreOperationCompare(final Expression arg1, final Expression arg2, final boolean invert) {
+ super(new Expression[] { arg1, arg2 });
+ this.invert = invert;
+ }
+
+ @Override
+ public Object computeValue(final EvalContext context) {
+ return equal(context, args[0], args[1]) ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ @Override
+ protected int getPrecedence() {
+ return COMPARE_PRECEDENCE;
+ }
+
+ @Override
+ protected boolean isSymmetric() {
+ return true;
+ }
+
+ /**
+ * Compares two values.
+ * @param context evaluation context
+ * @param left operand
+ * @param right operand
+ * @return whether left = right in XPath terms
+ */
+ protected boolean equal(final EvalContext context, final Expression left,
+ final Expression right) {
+ Object l = left.compute(context);
+ Object r = right.compute(context);
+
+ if (l instanceof InitialContext) {
+ ((EvalContext) l).reset();
+ }
+
+ if (l instanceof SelfContext) {
+ l = ((EvalContext) l).getSingleNodePointer();
+ }
+
+ if (r instanceof InitialContext) {
+ ((EvalContext) r).reset();
+ }
+
+ if (r instanceof SelfContext) {
+ r = ((EvalContext) r).getSingleNodePointer();
+ }
+
+ if (l instanceof Collection) {
+ l = ((Collection) l).iterator();
+ }
+
+ if (r instanceof Collection) {
+ r = ((Collection) r).iterator();
+ }
+
+ if (l instanceof Iterator && r instanceof Iterator) {
+ return findMatch((Iterator) l, (Iterator) r);
+ }
+ if (l instanceof Iterator) {
+ return contains((Iterator) l, r);
+ }
+ if (r instanceof Iterator) {
+ return contains((Iterator) r, l);
+ }
+ return equal(l, r);
+ }
+
+ /**
+ * Learn whether it contains value.
+ * @param it Iterator to check
+ * @param value for which to look
+ * @return whether value was found
+ */
+ protected boolean contains(final Iterator it, final Object value) {
+ while (it.hasNext()) {
+ final Object element = it.next();
+ if (equal(element, value)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Learn whether lit intersects rit.
+ * @param lit left Iterator
+ * @param rit right Iterator
+ * @return boolean
+ */
+ protected boolean findMatch(final Iterator lit, final Iterator rit) {
+ final HashSet left = new HashSet();
+ while (lit.hasNext()) {
+ left.add(lit.next());
+ }
+ while (rit.hasNext()) {
+ if (contains(left.iterator(), rit.next())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Learn whether l equals r in XPath terms.
+ * @param l left operand
+ * @param r right operand
+ * @return whether l = r
+ */
+ protected boolean equal(Object l, Object r) {
+ if (l instanceof Pointer) {
+ l = ((Pointer) l).getValue();
+ }
+
+ if (r instanceof Pointer) {
+ r = ((Pointer) r).getValue();
+ }
+
+ boolean result;
+ if (l instanceof Boolean || r instanceof Boolean) {
+ result = l == r || InfoSetUtil.booleanValue(l) == InfoSetUtil.booleanValue(r);
+ }
+ else if (l instanceof Number || r instanceof Number) {
+ //if either side is NaN, no comparison returns true:
+ final double ld = InfoSetUtil.doubleValue(l);
+ if (Double.isNaN(ld)) {
+ return false;
+ }
+ final double rd = InfoSetUtil.doubleValue(r);
+ if (Double.isNaN(rd)) {
+ return false;
+ }
+ result = ld == rd;
+ }
+ else {
+ if (l instanceof String || r instanceof String) {
+ l = InfoSetUtil.stringValue(l);
+ r = InfoSetUtil.stringValue(r);
+ }
+ result = l == r || l != null && l.equals(r);
+ }
+ return result ^ invert;
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationDivide.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationDivide.java
new file mode 100644
index 00000000000..ff5933cea5f
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationDivide.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.InfoSetUtil;
+
+/**
+ * Implementation of {@link Expression} for the operation "div".
+ */
+public class CoreOperationDivide extends CoreOperation {
+
+ /**
+ * Create a new CoreOperationDivide.
+ * @param arg1 dividend
+ * @param arg2 divisor
+ */
+ public CoreOperationDivide(final Expression arg1, final Expression arg2) {
+ super(new Expression[] { arg1, arg2 });
+ }
+
+ @Override
+ public Object computeValue(final EvalContext context) {
+ final double l = InfoSetUtil.doubleValue(args[0].computeValue(context));
+ final double r = InfoSetUtil.doubleValue(args[1].computeValue(context));
+ return Double.valueOf(l / r);
+ }
+
+ @Override
+ protected int getPrecedence() {
+ return MULTIPLY_PRECEDENCE;
+ }
+
+ @Override
+ protected boolean isSymmetric() {
+ return false;
+ }
+
+ @Override
+ public String getSymbol() {
+ return "div";
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationEqual.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationEqual.java
new file mode 100644
index 00000000000..0455664f3f5
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationEqual.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+/**
+ * Implementation of {@link Expression} for the operation "=".
+ */
+public class CoreOperationEqual extends CoreOperationCompare {
+
+ /**
+ * Create a new CoreOperationEqual
+ * @param arg1 first comparison Expression
+ * @param arg2 second comparison Expression
+ */
+ public CoreOperationEqual(final Expression arg1, final Expression arg2) {
+ super(arg1, arg2);
+ }
+
+ @Override
+ public String getSymbol() {
+ return "=";
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationGreaterThan.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationGreaterThan.java
new file mode 100644
index 00000000000..084ee0f8373
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationGreaterThan.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+/**
+ * Implementation of {@link Expression} for the operation ">".
+ */
+public class CoreOperationGreaterThan extends CoreOperationRelationalExpression {
+
+ /**
+ * Create a new CoreOperationGreaterThan.
+ * @param arg1 left operand
+ * @param arg2 right operand
+ */
+ public CoreOperationGreaterThan(final Expression arg1, final Expression arg2) {
+ super(new Expression[] { arg1, arg2 });
+ }
+
+ @Override
+ protected boolean evaluateCompare(final int compare) {
+ return compare > 0;
+ }
+
+ @Override
+ public String getSymbol() {
+ return ">";
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationGreaterThanOrEqual.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationGreaterThanOrEqual.java
new file mode 100644
index 00000000000..f47f79f9aea
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationGreaterThanOrEqual.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+/**
+ * Implementation of {@link Expression} for the operation ">=".
+ */
+public class CoreOperationGreaterThanOrEqual extends
+ CoreOperationRelationalExpression {
+
+ /**
+ * Create a new CoreOperationGreaterThanOrEqual.
+ * @param arg1 operand 1
+ * @param arg2 operand 2
+ */
+ public CoreOperationGreaterThanOrEqual(final Expression arg1, final Expression arg2) {
+ super(new Expression[] { arg1, arg2 });
+ }
+
+ @Override
+ protected boolean evaluateCompare(final int compare) {
+ return compare >= 0;
+ }
+
+ @Override
+ public String getSymbol() {
+ return ">=";
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationLessThan.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationLessThan.java
new file mode 100644
index 00000000000..d4d1f3acc68
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationLessThan.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+/**
+ * Implementation of {@link Expression} for the operation "<".
+ */
+public class CoreOperationLessThan extends CoreOperationRelationalExpression {
+
+ /**
+ * Create a new CoreOperationLessThan.
+ * @param arg1 left Expression
+ * @param arg2 right Expression
+ */
+ public CoreOperationLessThan(final Expression arg1, final Expression arg2) {
+ super(new Expression[] { arg1, arg2 });
+ }
+
+ @Override
+ protected boolean evaluateCompare(final int compare) {
+ return compare < 0;
+ }
+
+ @Override
+ public String getSymbol() {
+ return "<";
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationLessThanOrEqual.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationLessThanOrEqual.java
new file mode 100644
index 00000000000..f140d4f0e9f
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationLessThanOrEqual.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+/**
+ * Implementation of {@link Expression} for the operation "<=".
+ */
+public class CoreOperationLessThanOrEqual extends
+ CoreOperationRelationalExpression {
+
+ /**
+ * Create a new CoreOperationLessThanOrEqual.
+ * @param arg1 left Expression
+ * @param arg2 right Expression
+ */
+ public CoreOperationLessThanOrEqual(final Expression arg1, final Expression arg2) {
+ super(new Expression[] { arg1, arg2 });
+ }
+
+ @Override
+ protected boolean evaluateCompare(final int compare) {
+ return compare <= 0;
+ }
+
+ @Override
+ public String getSymbol() {
+ return "<=";
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationMod.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationMod.java
new file mode 100644
index 00000000000..f04a8fd4e8d
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationMod.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.InfoSetUtil;
+
+/**
+ * Implementation of {@link Expression} for the operation "mod".
+ */
+public class CoreOperationMod extends CoreOperation {
+
+ /**
+ * Create a new CoreOperationMod.
+ * @param arg1 dividend
+ * @param arg2 divisor
+ */
+ public CoreOperationMod(final Expression arg1, final Expression arg2) {
+ super(new Expression[] { arg1, arg2 });
+ }
+
+ @Override
+ public Object computeValue(final EvalContext context) {
+ final long l = (long) InfoSetUtil.doubleValue(args[0].computeValue(context));
+ final long r = (long) InfoSetUtil.doubleValue(args[1].computeValue(context));
+ return Double.valueOf(l % r);
+ }
+
+ @Override
+ protected int getPrecedence() {
+ return MULTIPLY_PRECEDENCE;
+ }
+
+ @Override
+ protected boolean isSymmetric() {
+ return false;
+ }
+
+ @Override
+ public String getSymbol() {
+ return "mod";
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationMultiply.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationMultiply.java
new file mode 100644
index 00000000000..df35849d95a
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationMultiply.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.InfoSetUtil;
+
+/**
+ * Implementation of {@link Expression} for the operation "*".
+ */
+public class CoreOperationMultiply extends CoreOperation {
+
+ /**
+ * Create a new CoreOperationMultiply.
+ * @param arg1 factor 1
+ * @param arg2 factor 2
+ */
+ public CoreOperationMultiply(final Expression arg1, final Expression arg2) {
+ super(new Expression[] { arg1, arg2 });
+ }
+
+ @Override
+ public Object computeValue(final EvalContext context) {
+ final double l = InfoSetUtil.doubleValue(args[0].computeValue(context));
+ final double r = InfoSetUtil.doubleValue(args[1].computeValue(context));
+ return Double.valueOf(l * r);
+ }
+
+ @Override
+ protected int getPrecedence() {
+ return MULTIPLY_PRECEDENCE;
+ }
+
+ @Override
+ protected boolean isSymmetric() {
+ return true;
+ }
+
+ @Override
+ public String getSymbol() {
+ return "*";
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationNegate.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationNegate.java
new file mode 100644
index 00000000000..afeeed9dee1
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationNegate.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.InfoSetUtil;
+
+/**
+ * Implementation of {@link Expression} for the operation unary "-".
+ */
+public class CoreOperationNegate extends CoreOperation {
+
+ /**
+ * Create a new CoreOperationNegate.
+ * @param arg the Expression to negate
+ */
+ public CoreOperationNegate(final Expression arg) {
+ super(new Expression[] { arg });
+ }
+
+ @Override
+ public Object computeValue(final EvalContext context) {
+ final double a = InfoSetUtil.doubleValue(args[0].computeValue(context));
+ return Double.valueOf(-a);
+ }
+
+ @Override
+ protected int getPrecedence() {
+ return NEGATE_PRECEDENCE;
+ }
+
+ @Override
+ protected boolean isSymmetric() {
+ return false;
+ }
+
+ @Override
+ public String getSymbol() {
+ return "-";
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationNotEqual.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationNotEqual.java
new file mode 100644
index 00000000000..d2a679e9cc3
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationNotEqual.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+/**
+ * Implementation of {@link Expression} for the operation "!=".
+ */
+public class CoreOperationNotEqual extends CoreOperationCompare {
+
+ /**
+ * Create a new CoreOperationNotEqual.
+ * @param arg1 left operand
+ * @param arg2 right operand
+ */
+ public CoreOperationNotEqual(final Expression arg1, final Expression arg2) {
+ super(arg1, arg2, true);
+ }
+
+ @Override
+ public String getSymbol() {
+ return "!=";
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationOr.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationOr.java
new file mode 100644
index 00000000000..4b6cbf2771a
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationOr.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.InfoSetUtil;
+
+/**
+ * Implementation of {@link Expression} for the operation "or".
+ */
+public class CoreOperationOr extends CoreOperation {
+
+ /**
+ * Create a new CoreOperationOr.
+ * @param args or'd Expression components
+ */
+ public CoreOperationOr(final Expression[] args) {
+ super(args);
+ }
+
+ @Override
+ public Object computeValue(final EvalContext context) {
+ for (final Expression arg : args) {
+ if (InfoSetUtil.booleanValue(arg.computeValue(context))) {
+ return Boolean.TRUE;
+ }
+ }
+ return Boolean.FALSE;
+ }
+
+ @Override
+ protected int getPrecedence() {
+ return OR_PRECEDENCE;
+ }
+
+ @Override
+ protected boolean isSymmetric() {
+ return true;
+ }
+
+ @Override
+ public String getSymbol() {
+ return "or";
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationRelationalExpression.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationRelationalExpression.java
new file mode 100644
index 00000000000..0ea95b9ff08
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationRelationalExpression.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.InfoSetUtil;
+import org.apache.commons.jxpath.ri.axes.InitialContext;
+import org.apache.commons.jxpath.ri.axes.SelfContext;
+
+/**
+ * Base implementation of Expression for the operations ">", ">=", "<", "<=".
+ * @since JXPath 1.3
+ */
+public abstract class CoreOperationRelationalExpression extends CoreOperation {
+
+ /**
+ * Create a new CoreOperationRelationalExpression.
+ * @param args arguments
+ */
+ protected CoreOperationRelationalExpression(final Expression[] args) {
+ super(args);
+ }
+
+ @Override
+ public final Object computeValue(final EvalContext context) {
+ return compute(args[0].compute(context), args[1].compute(context))
+ ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ @Override
+ protected final int getPrecedence() {
+ return RELATIONAL_EXPR_PRECEDENCE;
+ }
+
+ @Override
+ protected final boolean isSymmetric() {
+ return false;
+ }
+
+ /**
+ * Template method for subclasses to evaluate the result of a comparison.
+ * @param compare result of comparison to evaluate
+ * @return ultimate operation success/failure
+ */
+ protected abstract boolean evaluateCompare(int compare);
+
+ /**
+ * Compare left to right.
+ * @param left left operand
+ * @param right right operand
+ * @return operation success/failure
+ */
+ private boolean compute(Object left, Object right) {
+ left = reduce(left);
+ right = reduce(right);
+
+ if (left instanceof InitialContext) {
+ ((InitialContext) left).reset();
+ }
+ if (right instanceof InitialContext) {
+ ((InitialContext) right).reset();
+ }
+ if (left instanceof Iterator && right instanceof Iterator) {
+ return findMatch((Iterator) left, (Iterator) right);
+ }
+ if (left instanceof Iterator) {
+ return containsMatch((Iterator) left, right);
+ }
+ if (right instanceof Iterator) {
+ return containsMatch(left, (Iterator) right);
+ }
+ final double ld = InfoSetUtil.doubleValue(left);
+ if (Double.isNaN(ld)) {
+ return false;
+ }
+ final double rd = InfoSetUtil.doubleValue(right);
+ if (Double.isNaN(rd)) {
+ return false;
+ }
+ return evaluateCompare(ld == rd ? 0 : ld < rd ? -1 : 1);
+ }
+
+ /**
+ * Reduce an operand for comparison.
+ * @param o Object to reduce
+ * @return reduced operand
+ */
+ private Object reduce(Object o) {
+ if (o instanceof SelfContext) {
+ o = ((EvalContext) o).getSingleNodePointer();
+ }
+ if (o instanceof Collection) {
+ o = ((Collection) o).iterator();
+ }
+ return o;
+ }
+
+ /**
+ * Learn whether any element returned from an Iterator matches a given value.
+ * @param it Iterator
+ * @param value to look for
+ * @return whether a match was found
+ */
+ private boolean containsMatch(final Iterator it, final Object value) {
+ while (it.hasNext()) {
+ final Object element = it.next();
+ if (compute(element, value)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Learn whether any element returned from an Iterator matches a given value.
+ * @param it Iterator
+ * @param value to look for
+ * @return whether a match was found
+ */
+ private boolean containsMatch(final Object value, final Iterator it) {
+ while (it.hasNext()) {
+ final Object element = it.next();
+ if (compute(value, element)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Learn whether there is an intersection between two Iterators.
+ * @param lit left Iterator
+ * @param rit right Iterator
+ * @return whether a match was found
+ */
+ private boolean findMatch(final Iterator lit, final Iterator rit) {
+ final HashSet left = new HashSet();
+ while (lit.hasNext()) {
+ left.add(lit.next());
+ }
+ while (rit.hasNext()) {
+ if (containsMatch(left.iterator(), rit.next())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationSubtract.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationSubtract.java
new file mode 100644
index 00000000000..27347b3d06b
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationSubtract.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.InfoSetUtil;
+
+/**
+ * Implementation of {@link Expression} for the operation "-".
+ */
+public class CoreOperationSubtract extends CoreOperation {
+
+ /**
+ * Create a new CoreOperationSubtract.
+ * @param arg1 minuend
+ * @param arg2 subtrahend
+ */
+ public CoreOperationSubtract(final Expression arg1, final Expression arg2) {
+ super(new Expression[] { arg1, arg2 });
+ }
+
+ @Override
+ public Object computeValue(final EvalContext context) {
+ final double l = InfoSetUtil.doubleValue(args[0].computeValue(context));
+ final double r = InfoSetUtil.doubleValue(args[1].computeValue(context));
+ return Double.valueOf(l - r);
+ }
+
+ @Override
+ protected int getPrecedence() {
+ return ADD_PRECEDENCE;
+ }
+
+ @Override
+ protected boolean isSymmetric() {
+ return false;
+ }
+
+ @Override
+ public String getSymbol() {
+ return "-";
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationUnion.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationUnion.java
new file mode 100644
index 00000000000..9718bfba725
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/CoreOperationUnion.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.axes.UnionContext;
+
+/**
+ * Implementation of {@link Expression} for the operation "|".
+ */
+public class CoreOperationUnion extends CoreOperation {
+
+ /**
+ * Create a new CoreOperationUnion.
+ * @param args Expression[]
+ */
+ public CoreOperationUnion(final Expression[] args) {
+ super(args);
+ }
+
+ @Override
+ public Object computeValue(final EvalContext context) {
+ final EvalContext[] argCtxs = new EvalContext[args.length];
+ for (int i = 0; i < args.length; i++) {
+ final Object value = args[i].compute(context);
+ if (value instanceof EvalContext) {
+ argCtxs[i] = (EvalContext) value;
+ }
+ else {
+ argCtxs[i] = context.getRootContext().getConstantContext(value);
+ }
+ }
+ return new UnionContext(context.getRootContext(), argCtxs);
+ }
+
+ @Override
+ protected int getPrecedence() {
+ return UNION_PRECEDENCE;
+ }
+
+ @Override
+ protected boolean isSymmetric() {
+ return true;
+ }
+
+ @Override
+ public String getSymbol() {
+ return "|";
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/Expression.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/Expression.java
new file mode 100644
index 00000000000..bd79a7c91c5
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/Expression.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Locale;
+
+import org.apache.commons.jxpath.NodeSet;
+import org.apache.commons.jxpath.Pointer;
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.util.ValueUtils;
+
+/**
+ * Common superclass for several types of nodes in the parse tree. Provides
+ * APIs for optimization of evaluation of expressions. Specifically, an
+ * expression only needs to executed once during the evaluation of an xpath
+ * if that expression is context-independent. Expression.isContextDependent()
+ * provides that hint.
+ */
+public abstract class Expression {
+
+ private boolean contextDependencyKnown = false;
+ private boolean contextDependent;
+
+ /**
+ * Returns true if this expression should be re-evaluated
+ * each time the current position in the context changes.
+ * @return boolean
+ */
+ public synchronized boolean isContextDependent() {
+ if (!contextDependencyKnown) {
+ contextDependent = computeContextDependent();
+ contextDependencyKnown = true;
+ }
+ return contextDependent;
+ }
+
+ /**
+ * Implemented by subclasses and result is cached by isContextDependent()
+ * @return calculated context-dependentness as boolean
+ */
+ public abstract boolean computeContextDependent();
+
+ /**
+ * Evaluates the expression. If the result is a node set, returns
+ * the first element of the node set.
+ * @param context evaluation context
+ * @return Object
+ */
+ public abstract Object computeValue(EvalContext context);
+
+ /**
+ * Evaluates the expression. If the result is a node set, returns
+ * the first element of the node set.
+ * @param context evaluation context
+ * @return Object
+ */
+ public abstract Object compute(EvalContext context);
+
+ /**
+ * Iterate over the values from the specified context.
+ * @param context evaluation context
+ * @return value Iterator
+ */
+ public Iterator iterate(final EvalContext context) {
+ final Object result = compute(context);
+ if (result instanceof EvalContext) {
+ return new ValueIterator((EvalContext) result);
+ }
+ if (result instanceof NodeSet) {
+ return new ValueIterator(((NodeSet) result).getPointers().iterator());
+ }
+ return ValueUtils.iterate(result);
+ }
+
+ /**
+ * Iterate over the pointers from the specified context.
+ * @param context evaluation context
+ * @return pointer Iterator
+ */
+ public Iterator iteratePointers(final EvalContext context) {
+ final Object result = compute(context);
+ if (result == null) {
+ return Collections.EMPTY_LIST.iterator();
+ }
+ if (result instanceof EvalContext) {
+ return (EvalContext) result;
+ }
+ if (result instanceof NodeSet) {
+ return new PointerIterator(((NodeSet) result).getPointers().iterator(),
+ new QName(null, "value"),
+ context.getRootContext().getCurrentNodePointer().getLocale());
+ }
+ return new PointerIterator(ValueUtils.iterate(result),
+ new QName(null, "value"),
+ context.getRootContext().getCurrentNodePointer().getLocale());
+ }
+
+ /**
+ * Pointer iterator
+ */
+ public static class PointerIterator implements Iterator {
+ private final Iterator iterator;
+ private final QName qname;
+ private final Locale locale;
+
+ //to what method does the following comment refer?
+ /**
+ * Create a new PointerIterator
+ * @param it underlying Iterator
+ * @param qname name
+ * @param locale Locale
+ * @deprecated Use the method that takes a NamespaceManager
+ */
+ @Deprecated
+ public PointerIterator(final Iterator it, final QName qname, final Locale locale) {
+ this.iterator = it;
+ this.qname = qname;
+ this.locale = locale;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public Object next() {
+ final Object o = iterator.next();
+ return o instanceof Pointer ? o : NodePointer.newNodePointer(qname, o, locale);
+ }
+
+ /**
+ * Unsupported.
+ */
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Value Iterator
+ */
+ public static class ValueIterator implements Iterator {
+ private final Iterator iterator;
+
+ /**
+ * Create a new ValueIterator.
+ * @param it underlying Iterator, may contain pointers
+ */
+ public ValueIterator(final Iterator it) {
+ this.iterator = it;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public Object next() {
+ final Object o = iterator.next();
+ return o instanceof Pointer ? ((Pointer) o).getValue() : o;
+ }
+
+ /**
+ * Unsupported.
+ */
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/ExpressionPath.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/ExpressionPath.java
new file mode 100644
index 00000000000..3dbf8911abc
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/ExpressionPath.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.axes.InitialContext;
+import org.apache.commons.jxpath.ri.axes.NodeSetContext;
+import org.apache.commons.jxpath.ri.axes.PredicateContext;
+import org.apache.commons.jxpath.ri.axes.SimplePathInterpreter;
+import org.apache.commons.jxpath.ri.axes.UnionContext;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * An element of the parse tree that represents an expression path, which is a
+ * path that starts with an expression like a function call: {@code getFoo(.)
+ * /bar}.
+ */
+public class ExpressionPath extends Path {
+
+ private final Expression expression;
+ private final Expression[] predicates;
+
+ private boolean basicKnown = false;
+ private boolean basic;
+
+ /**
+ * Create a new ExpressionPath.
+ * @param expression Expression
+ * @param predicates to execute
+ * @param steps navigation
+ */
+ public ExpressionPath(final Expression expression, final Expression[] predicates,
+ final Step[] steps) {
+ super(steps);
+ this.expression = expression;
+ this.predicates = predicates;
+ }
+
+ /**
+ * Gets the expression.
+ * @return Expression
+ */
+ public Expression getExpression() {
+ return expression;
+ }
+
+ /**
+ * Predicates are the expressions in brackets that may follow
+ * the root expression of the path.
+ * @return Expression[]
+ */
+ public Expression[] getPredicates() {
+ return predicates;
+ }
+
+ /**
+ * Returns true if the root expression or any of the
+ * predicates or the path steps are context dependent.
+ * @return boolean
+ */
+ @Override
+ public boolean computeContextDependent() {
+ if (expression.isContextDependent()) {
+ return true;
+ }
+ if (predicates != null) {
+ for (final Expression predicate : predicates) {
+ if (predicate.isContextDependent()) {
+ return true;
+ }
+ }
+ }
+ return super.computeContextDependent();
+ }
+
+ /**
+ * Recognized paths formatted as {@code $x[3]/foo[2]}. The
+ * evaluation of such "simple" paths is optimized and streamlined.
+ * @return boolean
+ */
+ public synchronized boolean isSimpleExpressionPath() {
+ if (!basicKnown) {
+ basicKnown = true;
+ basic = isSimplePath() && areBasicPredicates(getPredicates());
+ }
+ return basic;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder buffer = new StringBuilder();
+ if (expression instanceof CoreOperation
+ || expression instanceof ExpressionPath
+ || expression instanceof LocationPath) {
+ buffer.append('(');
+ buffer.append(expression);
+ buffer.append(')');
+ }
+ else {
+ buffer.append(expression);
+ }
+ if (predicates != null) {
+ for (final Expression predicate : predicates) {
+ buffer.append('[');
+ buffer.append(predicate);
+ buffer.append(']');
+ }
+ }
+
+ final Step[] steps = getSteps();
+ if (steps != null) {
+ for (final Step step : steps) {
+ buffer.append("/");
+ buffer.append(step);
+ }
+ }
+ return buffer.toString();
+ }
+
+ @Override
+ public Object compute(final EvalContext context) {
+ return expressionPath(context, false);
+ }
+
+ @Override
+ public Object computeValue(final EvalContext context) {
+ return expressionPath(context, true);
+ }
+
+ /**
+ * Walks an expression path (a path that starts with an expression)
+ * @param evalContext base context
+ * @param firstMatch whether to return the first match found
+ * @return Object found
+ */
+ protected Object expressionPath(final EvalContext evalContext, final boolean firstMatch) {
+ final Object value = expression.compute(evalContext);
+ EvalContext context;
+ if (value instanceof InitialContext) {
+ // This is an optimization. We can avoid iterating through a
+ // collection if the context bean is in fact one.
+ context = (InitialContext) value;
+ }
+ else if (value instanceof EvalContext) {
+ // UnionContext will collect all values from the "value" context
+ // and treat the whole thing as a big collection.
+ context =
+ new UnionContext(
+ evalContext,
+ new EvalContext[] {(EvalContext) value });
+ }
+ else {
+ context = evalContext.getRootContext().getConstantContext(value);
+ }
+
+ if (firstMatch
+ && isSimpleExpressionPath()
+ && !(context instanceof NodeSetContext)) {
+ final EvalContext ctx = context;
+ final NodePointer ptr = (NodePointer) ctx.getSingleNodePointer();
+ if (ptr != null
+ && (ptr.getIndex() == NodePointer.WHOLE_COLLECTION
+ || predicates == null
+ || predicates.length == 0)) {
+ return SimplePathInterpreter.interpretSimpleExpressionPath(
+ evalContext,
+ ptr,
+ predicates,
+ getSteps());
+ }
+ }
+ if (predicates != null) {
+ for (int j = 0; j < predicates.length; j++) {
+ if (j != 0) {
+ context = new UnionContext(context, new EvalContext[]{context});
+ }
+ context = new PredicateContext(context, predicates[j]);
+ }
+ }
+ return firstMatch ? (Object) getSingleNodePointerForSteps(context)
+ : evalSteps(context);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/ExtensionFunction.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/ExtensionFunction.java
new file mode 100644
index 00000000000..c45614e19e7
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/ExtensionFunction.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import java.util.Arrays;
+
+import org.apache.commons.jxpath.Function;
+import org.apache.commons.jxpath.JXPathFunctionNotFoundException;
+import org.apache.commons.jxpath.NodeSet;
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.axes.NodeSetContext;
+
+/**
+ * Represents an element of the parse tree representing an extension function
+ * call.
+ */
+public class ExtensionFunction extends Operation {
+
+ private final QName functionName;
+
+ /**
+ * Create a new ExtensionFunction.
+ * @param functionName name of the function
+ * @param args Expression[] of function args
+ */
+ public ExtensionFunction(final QName functionName, final Expression[] args) {
+ super(args);
+ this.functionName = functionName;
+ }
+
+ /**
+ * Gets the function name
+ * @return QName
+ */
+ public QName getFunctionName() {
+ return functionName;
+ }
+
+ /**
+ * An extension function gets the current context, therefore it MAY be
+ * context dependent.
+ * @return true
+ */
+ @Override
+ public boolean computeContextDependent() {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder buffer = new StringBuilder();
+ buffer.append(functionName);
+ buffer.append('(');
+ final Expression[] args = getArguments();
+ if (args != null) {
+ for (int i = 0; i < args.length; i++) {
+ if (i > 0) {
+ buffer.append(", ");
+ }
+ buffer.append(args[i]);
+ }
+ }
+ buffer.append(')');
+ return buffer.toString();
+ }
+
+ @Override
+ public Object compute(final EvalContext context) {
+ return computeValue(context);
+ }
+
+ @Override
+ public Object computeValue(final EvalContext context) {
+ Object[] parameters = null;
+ if (args != null) {
+ parameters = new Object[args.length];
+ for (int i = 0; i < args.length; i++) {
+ parameters[i] = convert(args[i].compute(context));
+ }
+ }
+
+ final Function function =
+ context.getRootContext().getFunction(functionName, parameters);
+ if (function == null) {
+ throw new JXPathFunctionNotFoundException("No such function: "
+ + functionName + Arrays.asList(parameters));
+ }
+ final Object result = function.invoke(context, parameters);
+ return result instanceof NodeSet ? new NodeSetContext(context,
+ (NodeSet) result) : result;
+ }
+
+ /**
+ * Convert any incoming context to a value.
+ * @param object Object to convert
+ * @return context value or {@code object} unscathed.
+ */
+ private Object convert(final Object object) {
+ return object instanceof EvalContext ? ((EvalContext) object).getValue() : object;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/LocationPath.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/LocationPath.java
new file mode 100644
index 00000000000..98afe8a4f95
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/LocationPath.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.axes.InitialContext;
+
+/**
+ */
+public class LocationPath extends Path {
+
+ private final boolean absolute;
+
+ /**
+ * Create a new LocationPath.
+ * @param absolute whether this is an absolute path
+ * @param steps to evaluate
+ */
+ public LocationPath(final boolean absolute, final Step[] steps) {
+ super(steps);
+ this.absolute = absolute;
+ }
+
+ /**
+ * Learn whether this LocationPath is absolute.
+ * @return boolean
+ */
+ public boolean isAbsolute() {
+ return absolute;
+ }
+
+ @Override
+ public boolean computeContextDependent() {
+ return !absolute || super.computeContextDependent();
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder buffer = new StringBuilder();
+ final Step[] steps = getSteps();
+ if (steps != null) {
+ for (int i = 0; i < steps.length; i++) {
+ if (i > 0 || absolute) {
+ buffer.append('/');
+ }
+ buffer.append(steps[i]);
+ }
+ }
+ return buffer.toString();
+ }
+
+ @Override
+ public Object compute(final EvalContext context) {
+ // Create a chain of contexts
+ EvalContext rootContext;
+ if (isAbsolute()) {
+ rootContext = context.getRootContext().getAbsoluteRootContext();
+ }
+ else {
+ rootContext = new InitialContext(context);
+ }
+ return evalSteps(rootContext);
+ }
+
+ @Override
+ public Object computeValue(final EvalContext context) {
+ // Create a chain of contexts
+ EvalContext rootContext;
+ if (isAbsolute()) {
+ rootContext = context.getRootContext().getAbsoluteRootContext();
+ }
+ else {
+ rootContext = new InitialContext(context);
+ }
+ return getSingleNodePointerForSteps(rootContext);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/NameAttributeTest.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/NameAttributeTest.java
new file mode 100644
index 00000000000..629b4dfab8e
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/NameAttributeTest.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+/**
+ * Captures the {@code foo[@name=expr]} expression. These
+ * expressions are handled in a special way when applied to beans
+ * or maps.
+ */
+public class NameAttributeTest extends CoreOperationEqual {
+
+ /**
+ * Create a new NameAttributeTest.
+ * @param namePath Expression
+ * @param nameValue Expression
+ */
+ public NameAttributeTest(final Expression namePath, final Expression nameValue) {
+ super(namePath, nameValue);
+ }
+
+ /**
+ * Gets the name test expression.
+ * @return Expression
+ */
+ public Expression getNameTestExpression() {
+ return args[1];
+ }
+
+ @Override
+ public boolean computeContextDependent() {
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/NodeNameTest.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/NodeNameTest.java
new file mode 100644
index 00000000000..118f700dec8
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/NodeNameTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.ri.QName;
+
+/**
+ */
+public class NodeNameTest extends NodeTest {
+ private final QName qname;
+ private String namespaceURI;
+
+ /**
+ * Create a new NodeNameTest.
+ * @param qname name to match
+ */
+ public NodeNameTest(final QName qname) {
+ this.qname = qname;
+ }
+
+ /**
+ * Create a new NodeNameTest.
+ * @param qname name to match
+ * @param namespaceURI uri to match
+ */
+ public NodeNameTest(final QName qname, final String namespaceURI) {
+ this.qname = qname;
+ this.namespaceURI = namespaceURI;
+ }
+
+ /**
+ * Gets the node name.
+ * @return QName
+ */
+ public QName getNodeName() {
+ return qname;
+ }
+
+ /**
+ * Gets the ns URI.
+ * @return String
+ */
+ public String getNamespaceURI() {
+ return namespaceURI;
+ }
+
+ /**
+ * Learn whether this is a wildcard test.
+ * @return {@code true} if the node name is "*".
+ */
+ public boolean isWildcard() {
+ return qname.getName().equals("*");
+ }
+
+ @Override
+ public String toString() {
+ return qname.toString();
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/NodeTest.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/NodeTest.java
new file mode 100644
index 00000000000..345fc024b40
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/NodeTest.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+/**
+ */
+public abstract class NodeTest {
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/NodeTypeTest.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/NodeTypeTest.java
new file mode 100644
index 00000000000..545c37331b3
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/NodeTypeTest.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.ri.Compiler;
+
+/**
+ */
+public class NodeTypeTest extends NodeTest {
+ private final int nodeType;
+
+ /**
+ * Create a new NodeTypeTest.
+ * @param nodeType to match
+ */
+ public NodeTypeTest(final int nodeType) {
+ this.nodeType = nodeType;
+ }
+
+ /**
+ * Gets the nodeType.
+ * @return int
+ */
+ public int getNodeType() {
+ return nodeType;
+ }
+
+ @Override
+ public String toString() {
+ return nodeTypeToString(nodeType) + "()";
+ }
+
+ /**
+ * Render the given node type as a String.
+ * @param code int
+ * @return String
+ */
+ public static String nodeTypeToString(final int code) {
+ switch (code) {
+ case Compiler.NODE_TYPE_NODE :
+ return "node";
+ case Compiler.NODE_TYPE_TEXT :
+ return "text";
+ case Compiler.NODE_TYPE_COMMENT :
+ return "comment";
+ case Compiler.NODE_TYPE_PI :
+ return "processing-instruction";
+ default:
+ return "UNKNOWN";
+ }
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/Operation.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/Operation.java
new file mode 100644
index 00000000000..b8c36b5c2af
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/Operation.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+/**
+ */
+public abstract class Operation extends Expression {
+
+ /** Expression[] of arguments */
+ protected Expression[] args;
+
+ /**
+ * Create a new Operation.
+ * @param args Expression[]
+ */
+ public Operation(final Expression[] args) {
+ this.args = args;
+ }
+
+ /**
+ * Gets the arguments.
+ * @return Expression[]
+ */
+ public Expression[] getArguments() {
+ return args;
+ }
+
+ @Override
+ public boolean computeContextDependent() {
+ if (args != null) {
+ for (final Expression arg : args) {
+ if (arg.isContextDependent()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/Path.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/Path.java
new file mode 100644
index 00000000000..2e712ea4741
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/Path.java
@@ -0,0 +1,329 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.Pointer;
+import org.apache.commons.jxpath.ri.Compiler;
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.axes.AncestorContext;
+import org.apache.commons.jxpath.ri.axes.AttributeContext;
+import org.apache.commons.jxpath.ri.axes.ChildContext;
+import org.apache.commons.jxpath.ri.axes.DescendantContext;
+import org.apache.commons.jxpath.ri.axes.InitialContext;
+import org.apache.commons.jxpath.ri.axes.NamespaceContext;
+import org.apache.commons.jxpath.ri.axes.ParentContext;
+import org.apache.commons.jxpath.ri.axes.PrecedingOrFollowingContext;
+import org.apache.commons.jxpath.ri.axes.PredicateContext;
+import org.apache.commons.jxpath.ri.axes.SelfContext;
+import org.apache.commons.jxpath.ri.axes.SimplePathInterpreter;
+import org.apache.commons.jxpath.ri.axes.UnionContext;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ */
+public abstract class Path extends Expression {
+
+ private final Step[] steps;
+ private boolean basicKnown = false;
+ private boolean basic;
+
+ /**
+ * Create a new Path.
+ * @param steps that compose the Path
+ */
+ public Path(final Step[] steps) {
+ this.steps = steps;
+ }
+
+ /**
+ * Gets the steps.
+ * @return Step[]
+ */
+ public Step[] getSteps() {
+ return steps;
+ }
+
+ @Override
+ public boolean computeContextDependent() {
+ if (steps != null) {
+ for (final Step step : steps) {
+ if (step.isContextDependent()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Recognizes paths formatted as {@code foo/bar[3]/baz[@name = 'biz']}.
+ * The evaluation of such "simple" paths is optimized and
+ * streamlined.
+ * @return {@code true} if this path is simple
+ */
+ public synchronized boolean isSimplePath() {
+ if (!basicKnown) {
+ basicKnown = true;
+ basic = true;
+ final Step[] steps = getSteps();
+ for (final Step step : steps) {
+ if (!isSimpleStep(step)) {
+ basic = false;
+ break;
+ }
+ }
+ }
+ return basic;
+ }
+
+ /**
+ * A Step is "simple" if it takes one of these forms: ".", "/foo",
+ * "@bar", "/foo[3]". If there are predicates, they should be
+ * context independent for the step to still be considered simple.
+ * @param step the step to check
+ * @return boolean
+ */
+ protected boolean isSimpleStep(final Step step) {
+ if (step.getAxis() == Compiler.AXIS_SELF) {
+ final NodeTest nodeTest = step.getNodeTest();
+ if (!(nodeTest instanceof NodeTypeTest)) {
+ return false;
+ }
+ final int nodeType = ((NodeTypeTest) nodeTest).getNodeType();
+ if (nodeType != Compiler.NODE_TYPE_NODE) {
+ return false;
+ }
+ return areBasicPredicates(step.getPredicates());
+ }
+ if (step.getAxis() == Compiler.AXIS_CHILD
+ || step.getAxis() == Compiler.AXIS_ATTRIBUTE) {
+ final NodeTest nodeTest = step.getNodeTest();
+ if (!(nodeTest instanceof NodeNameTest)) {
+ return false;
+ }
+ if (((NodeNameTest) nodeTest).isWildcard()) {
+ return false;
+ }
+ return areBasicPredicates(step.getPredicates());
+ }
+ return false;
+ }
+
+ /**
+ * Learn whether the elements of the specified array are "basic" predicates.
+ * @param predicates the Expression[] to check
+ * @return boolean
+ */
+ protected boolean areBasicPredicates(final Expression[] predicates) {
+ if (predicates != null && predicates.length != 0) {
+ boolean firstIndex = true;
+ for (final Expression predicate : predicates) {
+ if (predicate instanceof NameAttributeTest) {
+ if (((NameAttributeTest) predicate)
+ .getNameTestExpression()
+ .isContextDependent()) {
+ return false;
+ }
+ }
+ else if (predicate.isContextDependent()) {
+ return false;
+ }
+ else {
+ if (!firstIndex) {
+ return false;
+ }
+ firstIndex = false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Given a root context, walks a path therefrom and finds the
+ * pointer to the first element matching the path.
+ * @param context evaluation context
+ * @return Pointer
+ */
+ protected Pointer getSingleNodePointerForSteps(final EvalContext context) {
+ if (steps.length == 0) {
+ return context.getSingleNodePointer();
+ }
+
+ if (isSimplePath()) {
+ final NodePointer ptr = (NodePointer) context.getSingleNodePointer();
+ return SimplePathInterpreter.interpretSimpleLocationPath(
+ context,
+ ptr,
+ steps);
+ }
+ return searchForPath(context);
+ }
+
+ /**
+ * The idea here is to return a NullPointer rather than null if that's at
+ * all possible. Take for example this path: "//map/key". Let's say, "map"
+ * is an existing node, but "key" is not there. We will create a
+ * NullPointer that can be used to set/create the "key" property.
+ *
+ * However, a path like "//key" would still produce null, because we have
+ * no way of knowing where "key" would be if it existed.
+ *
+ *
+ * To accomplish this, we first try the path itself. If it does not find
+ * anything, we chop off last step of the path, as long as it is a simple
+ * one like child:: or attribute:: and try to evaluate the truncated path.
+ * If it finds exactly one node - create a NullPointer and return. If it
+ * fails, chop off another step and repeat. If it finds more than one
+ * location - return null.
+ *
+ * @param context evaluation context
+ * @return Pointer
+ */
+ protected Pointer searchForPath(final EvalContext context) {
+ EvalContext ctx = buildContextChain(context, steps.length, true);
+ final Pointer pointer = ctx.getSingleNodePointer();
+
+ if (pointer != null) {
+ return pointer;
+ }
+
+ for (int i = steps.length; --i > 0;) {
+ if (!isSimpleStep(steps[i])) {
+ return null;
+ }
+ ctx = buildContextChain(context, i, true);
+ if (ctx.hasNext()) {
+ final Pointer partial = (Pointer) ctx.next();
+ if (ctx.hasNext()) {
+ // If we find another location - the search is
+ // ambiguous, so we report failure
+ return null;
+ }
+ if (partial instanceof NodePointer) {
+ return SimplePathInterpreter.createNullPointer(
+ context,
+ (NodePointer) partial,
+ steps,
+ i);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Given a root context, walks a path therefrom and builds a context
+ * that contains all nodes matching the path.
+ * @param context evaluation context
+ * @return EvaluationContext
+ */
+ protected EvalContext evalSteps(final EvalContext context) {
+ return buildContextChain(context, steps.length, false);
+ }
+
+ /**
+ * Build a context from a chain of contexts.
+ * @param context evaluation context
+ * @param stepCount number of steps to descend
+ * @param createInitialContext whether to create the initial context
+ * @return created context
+ */
+ protected EvalContext buildContextChain(
+ EvalContext context,
+ final int stepCount,
+ final boolean createInitialContext) {
+ if (createInitialContext) {
+ context = new InitialContext(context);
+ }
+ if (steps.length == 0) {
+ return context;
+ }
+ for (int i = 0; i < stepCount; i++) {
+ context =
+ createContextForStep(
+ context,
+ steps[i].getAxis(),
+ steps[i].getNodeTest());
+ final Expression[] predicates = steps[i].getPredicates();
+ if (predicates != null) {
+ for (int j = 0; j < predicates.length; j++) {
+ if (j != 0) {
+ context = new UnionContext(context, new EvalContext[]{context});
+ }
+ context = new PredicateContext(context, predicates[j]);
+ }
+ }
+ }
+ return context;
+ }
+
+ /**
+ * Different axes are serviced by different contexts. This method
+ * allocates the right context for the supplied step.
+ * @param context evaluation context
+ * @param axis code
+ * @param nodeTest node test
+ * @return EvalContext
+ */
+ protected EvalContext createContextForStep(
+ final EvalContext context,
+ final int axis,
+ NodeTest nodeTest) {
+ if (nodeTest instanceof NodeNameTest) {
+ final QName qname = ((NodeNameTest) nodeTest).getNodeName();
+ final String prefix = qname.getPrefix();
+ if (prefix != null) {
+ final String namespaceURI = context.getJXPathContext()
+ .getNamespaceURI(prefix);
+ nodeTest = new NodeNameTest(qname, namespaceURI);
+ }
+ }
+
+ switch (axis) {
+ case Compiler.AXIS_ANCESTOR :
+ return new AncestorContext(context, false, nodeTest);
+ case Compiler.AXIS_ANCESTOR_OR_SELF :
+ return new AncestorContext(context, true, nodeTest);
+ case Compiler.AXIS_ATTRIBUTE :
+ return new AttributeContext(context, nodeTest);
+ case Compiler.AXIS_CHILD :
+ return new ChildContext(context, nodeTest, false, false);
+ case Compiler.AXIS_DESCENDANT :
+ return new DescendantContext(context, false, nodeTest);
+ case Compiler.AXIS_DESCENDANT_OR_SELF :
+ return new DescendantContext(context, true, nodeTest);
+ case Compiler.AXIS_FOLLOWING :
+ return new PrecedingOrFollowingContext(context, nodeTest, false);
+ case Compiler.AXIS_FOLLOWING_SIBLING :
+ return new ChildContext(context, nodeTest, true, false);
+ case Compiler.AXIS_NAMESPACE :
+ return new NamespaceContext(context, nodeTest);
+ case Compiler.AXIS_PARENT :
+ return new ParentContext(context, nodeTest);
+ case Compiler.AXIS_PRECEDING :
+ return new PrecedingOrFollowingContext(context, nodeTest, true);
+ case Compiler.AXIS_PRECEDING_SIBLING :
+ return new ChildContext(context, nodeTest, true, true);
+ case Compiler.AXIS_SELF :
+ return new SelfContext(context, nodeTest);
+ default:
+ return null; // Never happens
+ }
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/ProcessingInstructionTest.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/ProcessingInstructionTest.java
new file mode 100644
index 00000000000..14106201c57
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/ProcessingInstructionTest.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+/**
+ */
+public class ProcessingInstructionTest extends NodeTest {
+ private final String target;
+
+ /**
+ * Create a new ProcessingInstructionTest.
+ * @param target string
+ */
+ public ProcessingInstructionTest(final String target) {
+ this.target = target;
+ }
+
+ /**
+ * Gets the target.
+ * @return String
+ */
+ public String getTarget() {
+ return target;
+ }
+
+ @Override
+ public String toString() {
+ return "processing-instruction('" + target + "')";
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/Step.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/Step.java
new file mode 100644
index 00000000000..09c23b3e163
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/Step.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.ri.Compiler;
+
+/**
+ */
+public class Step {
+ private final int axis;
+ private final NodeTest nodeTest;
+ private final Expression[] predicates;
+
+ /**
+ * Create a new Step.
+ * @param axis axis code
+ * @param nodeTest step test
+ * @param predicates predicate expressions
+ */
+ protected Step(final int axis, final NodeTest nodeTest, final Expression[] predicates) {
+ this.axis = axis;
+ this.nodeTest = nodeTest;
+ this.predicates = predicates;
+ }
+
+ /**
+ * Gets the axis code.
+ * @return int
+ */
+ public int getAxis() {
+ return axis;
+ }
+
+ /**
+ * Gets the step test.
+ * @return NodeTest
+ */
+ public NodeTest getNodeTest() {
+ return nodeTest;
+ }
+
+ /**
+ * Gets the predicates.
+ * @return Expression[]
+ */
+ public Expression[] getPredicates() {
+ return predicates;
+ }
+
+ /**
+ * Learn whether this step contains any predicate that is context dependent.
+ * @return boolean
+ */
+ public boolean isContextDependent() {
+ if (predicates != null) {
+ for (final Expression predicate : predicates) {
+ if (predicate.isContextDependent()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder buffer = new StringBuilder();
+ final int axis = getAxis();
+ if (axis == Compiler.AXIS_CHILD) {
+ buffer.append(nodeTest);
+ }
+ else if (axis == Compiler.AXIS_ATTRIBUTE) {
+ buffer.append('@');
+ buffer.append(nodeTest);
+ }
+ else if (axis == Compiler.AXIS_SELF
+ && nodeTest instanceof NodeTypeTest
+ && ((NodeTypeTest) nodeTest).getNodeType()
+ == Compiler.NODE_TYPE_NODE) {
+ buffer.append(".");
+ }
+ else if (axis == Compiler.AXIS_PARENT
+ && nodeTest instanceof NodeTypeTest
+ && ((NodeTypeTest) nodeTest).getNodeType()
+ == Compiler.NODE_TYPE_NODE) {
+ buffer.append("..");
+ }
+ else if (axis == Compiler.AXIS_DESCENDANT_OR_SELF
+ && nodeTest instanceof NodeTypeTest
+ && ((NodeTypeTest) nodeTest).getNodeType()
+ == Compiler.NODE_TYPE_NODE
+ && (predicates == null || predicates.length == 0)) {
+ buffer.append("");
+ }
+ else {
+ buffer.append(axisToString(axis));
+ buffer.append("::");
+ buffer.append(nodeTest);
+ }
+ final Expression[] predicates = getPredicates();
+ if (predicates != null) {
+ for (final Expression predicate : predicates) {
+ buffer.append('[');
+ buffer.append(predicate);
+ buffer.append(']');
+ }
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Decode an axis code to its name.
+ * @param axis int code
+ * @return String name.
+ * @see Compiler
+ * @see "http://www.w3.org/TR/xpath#axes"
+ */
+ public static String axisToString(final int axis) {
+ switch (axis) {
+ case Compiler.AXIS_SELF :
+ return "self";
+ case Compiler.AXIS_CHILD :
+ return "child";
+ case Compiler.AXIS_PARENT :
+ return "parent";
+ case Compiler.AXIS_ANCESTOR :
+ return "ancestor";
+ case Compiler.AXIS_ATTRIBUTE :
+ return "attribute";
+ case Compiler.AXIS_NAMESPACE :
+ return "namespace";
+ case Compiler.AXIS_PRECEDING :
+ return "preceding";
+ case Compiler.AXIS_FOLLOWING :
+ return "following";
+ case Compiler.AXIS_DESCENDANT :
+ return "descendant";
+ case Compiler.AXIS_ANCESTOR_OR_SELF :
+ return "ancestor-or-self";
+ case Compiler.AXIS_FOLLOWING_SIBLING :
+ return "following-sibling";
+ case Compiler.AXIS_PRECEDING_SIBLING :
+ return "preceding-sibling";
+ case Compiler.AXIS_DESCENDANT_OR_SELF :
+ return "descendant-or-self";
+ default:
+ return "UNKNOWN";
+ }
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/TreeCompiler.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/TreeCompiler.java
new file mode 100644
index 00000000000..2fb074d3607
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/TreeCompiler.java
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.ri.Compiler;
+import org.apache.commons.jxpath.ri.QName;
+
+/**
+ */
+public class TreeCompiler implements Compiler {
+
+ private static final QName QNAME_NAME = new QName(null, "name");
+
+ @Override
+ public Object number(final String value) {
+ return new Constant(Double.valueOf(value));
+ }
+
+ @Override
+ public Object literal(final String value) {
+ return new Constant(value);
+ }
+
+ @Override
+ public Object qname(final String prefix, final String name) {
+ return new QName(prefix, name);
+ }
+
+ @Override
+ public Object sum(final Object[] arguments) {
+ return new CoreOperationAdd(toExpressionArray(arguments));
+ }
+
+ @Override
+ public Object minus(final Object left, final Object right) {
+ return new CoreOperationSubtract(
+ (Expression) left,
+ (Expression) right);
+ }
+
+ @Override
+ public Object multiply(final Object left, final Object right) {
+ return new CoreOperationMultiply((Expression) left, (Expression) right);
+ }
+
+ @Override
+ public Object divide(final Object left, final Object right) {
+ return new CoreOperationDivide((Expression) left, (Expression) right);
+ }
+
+ @Override
+ public Object mod(final Object left, final Object right) {
+ return new CoreOperationMod((Expression) left, (Expression) right);
+ }
+
+ @Override
+ public Object lessThan(final Object left, final Object right) {
+ return new CoreOperationLessThan((Expression) left, (Expression) right);
+ }
+
+ @Override
+ public Object lessThanOrEqual(final Object left, final Object right) {
+ return new CoreOperationLessThanOrEqual(
+ (Expression) left,
+ (Expression) right);
+ }
+
+ @Override
+ public Object greaterThan(final Object left, final Object right) {
+ return new CoreOperationGreaterThan(
+ (Expression) left,
+ (Expression) right);
+ }
+
+ @Override
+ public Object greaterThanOrEqual(final Object left, final Object right) {
+ return new CoreOperationGreaterThanOrEqual(
+ (Expression) left,
+ (Expression) right);
+ }
+
+ @Override
+ public Object equal(final Object left, final Object right) {
+ return isNameAttributeTest((Expression) left)
+ ? new NameAttributeTest((Expression) left, (Expression) right)
+ : new CoreOperationEqual((Expression) left, (Expression) right);
+ }
+
+ @Override
+ public Object notEqual(final Object left, final Object right) {
+ return new CoreOperationNotEqual((Expression) left, (Expression) right);
+ }
+
+ @Override
+ public Object minus(final Object argument) {
+ return new CoreOperationNegate((Expression) argument);
+ }
+
+ @Override
+ public Object variableReference(final Object qName) {
+ return new VariableReference((QName) qName);
+ }
+
+ @Override
+ public Object function(final int code, final Object[] args) {
+ return new CoreFunction(code, toExpressionArray(args));
+ }
+
+ @Override
+ public Object function(final Object name, final Object[] args) {
+ return new ExtensionFunction((QName) name, toExpressionArray(args));
+ }
+
+ @Override
+ public Object and(final Object[] arguments) {
+ return new CoreOperationAnd(toExpressionArray(arguments));
+ }
+
+ @Override
+ public Object or(final Object[] arguments) {
+ return new CoreOperationOr(toExpressionArray(arguments));
+ }
+
+ @Override
+ public Object union(final Object[] arguments) {
+ return new CoreOperationUnion(toExpressionArray(arguments));
+ }
+
+ @Override
+ public Object locationPath(final boolean absolute, final Object[] steps) {
+ return new LocationPath(absolute, toStepArray(steps));
+ }
+
+ @Override
+ public Object expressionPath(final Object expression, final Object[] predicates,
+ final Object[] steps) {
+ return new ExpressionPath(
+ (Expression) expression,
+ toExpressionArray(predicates),
+ toStepArray(steps));
+ }
+
+ @Override
+ public Object nodeNameTest(final Object qname) {
+ return new NodeNameTest((QName) qname);
+ }
+
+ @Override
+ public Object nodeTypeTest(final int nodeType) {
+ return new NodeTypeTest(nodeType);
+ }
+
+ @Override
+ public Object processingInstructionTest(final String instruction) {
+ return new ProcessingInstructionTest(instruction);
+ }
+
+ @Override
+ public Object step(final int axis, final Object nodeTest, final Object[] predicates) {
+ return new Step(
+ axis,
+ (NodeTest) nodeTest,
+ toExpressionArray(predicates));
+ }
+
+ /**
+ * Gets an Object[] as an Expression[].
+ * @param array Object[]
+ * @return Expression[]
+ */
+ private Expression[] toExpressionArray(final Object[] array) {
+ Expression[] expArray = null;
+ if (array != null) {
+ expArray = new Expression[array.length];
+ for (int i = 0; i < expArray.length; i++) {
+ expArray[i] = (Expression) array[i];
+ }
+ }
+ return expArray;
+ }
+
+ /**
+ * Gets an Object[] as a Step[].
+ * @param array Object[]
+ * @return Step[]
+ */
+ private Step[] toStepArray(final Object[] array) {
+ Step[] stepArray = null;
+ if (array != null) {
+ stepArray = new Step[array.length];
+ for (int i = 0; i < stepArray.length; i++) {
+ stepArray[i] = (Step) array[i];
+ }
+ }
+ return stepArray;
+ }
+
+ /**
+ * Learn whether arg is a name attribute test.
+ * @param arg Expression to test
+ * @return boolean
+ */
+ private boolean isNameAttributeTest(final Expression arg) {
+ if (!(arg instanceof LocationPath)) {
+ return false;
+ }
+
+ final Step[] steps = ((LocationPath) arg).getSteps();
+ if (steps.length != 1) {
+ return false;
+ }
+ if (steps[0].getAxis() != AXIS_ATTRIBUTE) {
+ return false;
+ }
+ final NodeTest test = steps[0].getNodeTest();
+ if (!(test instanceof NodeNameTest)) {
+ return false;
+ }
+ if (!((NodeNameTest) test).getNodeName().equals(QNAME_NAME)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/VariableReference.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/VariableReference.java
new file mode 100644
index 00000000000..c0f664c9079
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/compiler/VariableReference.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.ri.EvalContext;
+import org.apache.commons.jxpath.ri.QName;
+
+/**
+ * An element of the compile tree holding a variable reference.
+ */
+public class VariableReference extends Expression {
+
+ private final QName varName;
+
+ /**
+ * Create a new VariableReference.
+ * @param varName variable name
+ */
+ public VariableReference(final QName varName) {
+ this.varName = varName;
+ }
+
+ /**
+ * Gets the variable name.
+ * @return QName
+ */
+ public QName getVariableName() {
+ return varName;
+ }
+
+ @Override
+ public String toString() {
+ return "$" + varName;
+ }
+
+ @Override
+ public boolean isContextDependent() {
+ return false;
+ }
+
+ @Override
+ public boolean computeContextDependent() {
+ return false;
+ }
+
+ @Override
+ public Object compute(final EvalContext context) {
+ return computeValue(context);
+ }
+
+ /**
+ * Returns the value of the variable.
+ * @param context EvalContext against which to compute the variable's value.
+ * @return Object
+ */
+ @Override
+ public Object computeValue(final EvalContext context) {
+ return context.getRootContext().getVariableContext(varName);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/NodeIterator.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/NodeIterator.java
new file mode 100644
index 00000000000..4205226883d
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/NodeIterator.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model;
+
+/**
+ * Definition for an iterator for all kinds of Nodes.
+ */
+public interface NodeIterator {
+
+ /**
+ * Gets the current iterator position.
+ * @return int position
+ */
+ int getPosition();
+
+ /**
+ * Sets the new current position.
+ * @param position the position to set
+ * @return {@code true} if there is a node at {@code position}.
+ */
+ boolean setPosition(int position);
+
+ /**
+ * Gets the NodePointer at the current position.
+ * @return NodePointer
+ */
+ NodePointer getNodePointer();
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/NodePointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/NodePointer.java
new file mode 100644
index 00000000000..4188a092c30
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/NodePointer.java
@@ -0,0 +1,915 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model;
+
+import java.util.HashSet;
+import java.util.Locale;
+
+import org.apache.commons.jxpath.AbstractFactory;
+import org.apache.commons.jxpath.ExceptionHandler;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.JXPathException;
+import org.apache.commons.jxpath.JXPathNotFoundException;
+import org.apache.commons.jxpath.Pointer;
+import org.apache.commons.jxpath.ri.Compiler;
+import org.apache.commons.jxpath.ri.JXPathContextReferenceImpl;
+import org.apache.commons.jxpath.ri.NamespaceResolver;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
+import org.apache.commons.jxpath.ri.model.beans.NullPointer;
+
+/**
+ * Common superclass for Pointers of all kinds. A NodePointer maps to
+ * a deterministic XPath that represents the location of a node in an
+ * object graph. This XPath uses only simple axes: child, namespace and
+ * attribute and only simple, context-independent predicates.
+ */
+public abstract class NodePointer implements Pointer {
+
+ /** Serialization version */
+ private static final long serialVersionUID = 8117201322861007777L;
+
+ /** Whole collection index. */
+ public static final int WHOLE_COLLECTION = Integer.MIN_VALUE;
+
+ /** Constant to indicate unknown namespace */
+ public static final String UNKNOWN_NAMESPACE = "<>";
+
+ /** Index for this NodePointer */
+ protected int index = WHOLE_COLLECTION;
+
+ private boolean attribute = false;
+ private NamespaceResolver namespaceResolver;
+ private ExceptionHandler exceptionHandler;
+ private transient Object rootNode;
+
+ /**
+ * Allocates an entirely new NodePointer by iterating through all installed
+ * NodePointerFactories until it finds one that can create a pointer.
+ * @param name QName
+ * @param bean Object
+ * @param locale Locale
+ * @return NodePointer
+ */
+ public static NodePointer newNodePointer(
+ final QName name,
+ final Object bean,
+ final Locale locale) {
+ NodePointer pointer;
+ if (bean == null) {
+ pointer = new NullPointer(name, locale);
+ return pointer;
+ }
+
+ final NodePointerFactory[] factories =
+ JXPathContextReferenceImpl.getNodePointerFactories();
+ for (final NodePointerFactory element : factories) {
+ pointer = element.createNodePointer(name, bean, locale);
+ if (pointer != null) {
+ return pointer;
+ }
+ }
+ throw new JXPathException(
+ "Could not allocate a NodePointer for object of "
+ + bean.getClass());
+ }
+
+ /**
+ * Allocates an new child NodePointer by iterating through all installed
+ * NodePointerFactories until it finds one that can create a pointer.
+ * @param parent pointer
+ * @param name QName
+ * @param bean Object
+ * @return NodePointer
+ */
+ public static NodePointer newChildNodePointer(
+ final NodePointer parent,
+ final QName name,
+ final Object bean) {
+ final NodePointerFactory[] factories =
+ JXPathContextReferenceImpl.getNodePointerFactories();
+ for (final NodePointerFactory element : factories) {
+ final NodePointer pointer =
+ element.createNodePointer(parent, name, bean);
+ if (pointer != null) {
+ return pointer;
+ }
+ }
+ throw new JXPathException(
+ "Could not allocate a NodePointer for object of "
+ + bean.getClass());
+ }
+
+ /** Parent pointer */
+ protected NodePointer parent;
+
+ /** Locale */
+ protected Locale locale;
+
+ /**
+ * Create a new NodePointer.
+ * @param parent Pointer
+ */
+ protected NodePointer(final NodePointer parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * Create a new NodePointer.
+ * @param parent Pointer
+ * @param locale Locale
+ */
+ protected NodePointer(final NodePointer parent, final Locale locale) {
+ this.parent = parent;
+ this.locale = locale;
+ }
+
+ /**
+ * Gets the NamespaceResolver associated with this NodePointer.
+ * @return NamespaceResolver
+ */
+ public NamespaceResolver getNamespaceResolver() {
+ if (namespaceResolver == null && parent != null) {
+ namespaceResolver = parent.getNamespaceResolver();
+ }
+ return namespaceResolver;
+ }
+
+ /**
+ * Sets the NamespaceResolver for this NodePointer.
+ * @param namespaceResolver NamespaceResolver
+ */
+ public void setNamespaceResolver(final NamespaceResolver namespaceResolver) {
+ this.namespaceResolver = namespaceResolver;
+ }
+
+ /**
+ * Gets the parent pointer.
+ * @return NodePointer
+ */
+ public NodePointer getParent() {
+ NodePointer pointer = parent;
+ while (pointer != null && pointer.isContainer()) {
+ pointer = pointer.getImmediateParentPointer();
+ }
+ return pointer;
+ }
+
+ /**
+ * Gets the immediate parent pointer.
+ * @return NodePointer
+ */
+ public NodePointer getImmediateParentPointer() {
+ return parent;
+ }
+
+ /**
+ * Sets to true if the pointer represents the "attribute::" axis.
+ * @param attribute boolean
+ */
+ public void setAttribute(final boolean attribute) {
+ this.attribute = attribute;
+ }
+
+ /**
+ * Returns true if the pointer represents the "attribute::" axis.
+ * @return boolean
+ */
+ public boolean isAttribute() {
+ return attribute;
+ }
+
+ /**
+ * Returns true if this Pointer has no parent.
+ * @return boolean
+ */
+ public boolean isRoot() {
+ return parent == null;
+ }
+
+ /**
+ * If true, this node does not have children
+ * @return boolean
+ */
+ public abstract boolean isLeaf();
+
+ /**
+ * Learn whether this pointer is considered to be a node.
+ * @return boolean
+ * @deprecated Please use !isContainer()
+ */
+ @Deprecated
+ public boolean isNode() {
+ return !isContainer();
+ }
+
+ /**
+ * If true, this node is auxiliary and can only be used as an intermediate in
+ * the chain of pointers.
+ * @return boolean
+ */
+ public boolean isContainer() {
+ return false;
+ }
+
+ /**
+ * If the pointer represents a collection, the index identifies
+ * an element of that collection. The default value of {@code index}
+ * is {@code WHOLE_COLLECTION}, which just means that the pointer
+ * is not indexed at all.
+ * Note: the index on NodePointer starts with 0, not 1.
+ * @return int
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * Sets the index of this NodePointer.
+ * @param index int
+ */
+ public void setIndex(final int index) {
+ this.index = index;
+ }
+
+ /**
+ * Returns {@code true} if the value of the pointer is an array or
+ * a Collection.
+ * @return boolean
+ */
+ public abstract boolean isCollection();
+
+ /**
+ * If the pointer represents a collection (or collection element),
+ * returns the length of the collection.
+ * Otherwise returns 1 (even if the value is null).
+ * @return int
+ */
+ public abstract int getLength();
+
+ /**
+ * By default, returns {@code getNode()}, can be overridden to
+ * return a "canonical" value, like for instance a DOM element should
+ * return its string value.
+ * @return Object value
+ */
+ @Override
+ public Object getValue() {
+ final NodePointer valuePointer = getValuePointer();
+ if (valuePointer != this) {
+ return valuePointer.getValue();
+ }
+ // Default behavior is to return the same as getNode()
+ return getNode();
+ }
+
+ /**
+ * If this pointer manages a transparent container, like a variable,
+ * this method returns the pointer to the contents.
+ * Only an auxiliary (non-node) pointer can (and should) return a
+ * value pointer other than itself.
+ * Note that you probably don't want to override
+ * {@code getValuePointer()} directly. Override the
+ * {@code getImmediateValuePointer()} method instead. The
+ * {@code getValuePointer()} method is calls
+ * {@code getImmediateValuePointer()} and, if the result is not
+ * {@code this}, invokes {@code getValuePointer()} recursively.
+ * The idea here is to open all nested containers. Let's say we have a
+ * container within a container within a container. The
+ * {@code getValuePointer()} method should then open all those
+ * containers and return the pointer to the ultimate contents. It does so
+ * with the above recursion.
+ * @return NodePointer
+ */
+ public NodePointer getValuePointer() {
+ final NodePointer ivp = getImmediateValuePointer();
+ return ivp == this ? this : ivp.getValuePointer();
+ }
+
+ /**
+ * @see #getValuePointer()
+ * @return NodePointer is either {@code this} or a pointer
+ * for the immediately contained value.
+ */
+ public NodePointer getImmediateValuePointer() {
+ return this;
+ }
+
+ /**
+ * An actual pointer points to an existing part of an object graph, even
+ * if it is null. A non-actual pointer represents a part that does not exist
+ * at all.
+ * For instance consider the pointer "/address/street".
+ * If both address and street are not null,
+ * the pointer is actual.
+ * If address is not null, but street is null,
+ * the pointer is still actual.
+ * If address is null, the pointer is not actual.
+ * (In JavaBeans) if address is not a property of the root bean,
+ * a Pointer for this path cannot be obtained at all - actual or otherwise.
+ * @return boolean
+ */
+ public boolean isActual() {
+ return index == WHOLE_COLLECTION || index >= 0 && index < getLength();
+ }
+
+ /**
+ * Returns the name of this node. Can be null.
+ * @return QName
+ */
+ public abstract QName getName();
+
+ /**
+ * Returns the value represented by the pointer before indexing.
+ * So, if the node represents an element of a collection, this
+ * method returns the collection itself.
+ * @return Object value
+ */
+ public abstract Object getBaseValue();
+
+ /**
+ * Returns the object the pointer points to; does not convert it
+ * to a "canonical" type.
+ * @return Object node value
+ * @deprecated 1.1 Please use getNode()
+ */
+ @Deprecated
+ public Object getNodeValue() {
+ return getNode();
+ }
+
+ /**
+ * Returns the object the pointer points to; does not convert it
+ * to a "canonical" type. Opens containers, properties etc and returns
+ * the ultimate contents.
+ * @return Object node
+ */
+ @Override
+ public Object getNode() {
+ return getValuePointer().getImmediateNode();
+ }
+
+ /**
+ * Gets the root node.
+ * @return Object value of this pointer's root (top parent).
+ */
+ @Override
+ public synchronized Object getRootNode() {
+ if (rootNode == null) {
+ rootNode = parent == null ? getImmediateNode() : parent.getRootNode();
+ }
+ return rootNode;
+ }
+
+ /**
+ * Returns the object the pointer points to; does not convert it
+ * to a "canonical" type.
+ * @return Object node
+ */
+ public abstract Object getImmediateNode();
+
+ /**
+ * Converts the value to the required type and changes the corresponding
+ * object to that value.
+ * @param value the value to set
+ */
+ @Override
+ public abstract void setValue(Object value);
+
+ /**
+ * Compares two child NodePointers and returns a positive number,
+ * zero or a positive number according to the order of the pointers.
+ * @param pointer1 first pointer to be compared
+ * @param pointer2 second pointer to be compared
+ * @return int per Java comparison conventions
+ */
+ public abstract int compareChildNodePointers(
+ NodePointer pointer1, NodePointer pointer2);
+
+ /**
+ * Checks if this Pointer matches the supplied NodeTest.
+ * @param test the NodeTest to execute
+ * @return true if a match
+ */
+ public boolean testNode(final NodeTest test) {
+ if (test == null) {
+ return true;
+ }
+ if (test instanceof NodeNameTest) {
+ if (isContainer()) {
+ return false;
+ }
+ final NodeNameTest nodeNameTest = (NodeNameTest) test;
+ final QName testName = nodeNameTest.getNodeName();
+ final QName nodeName = getName();
+ if (nodeName == null) {
+ return false;
+ }
+
+ final String testPrefix = testName.getPrefix();
+ final String nodePrefix = nodeName.getPrefix();
+ if (!safeEquals(testPrefix, nodePrefix)) {
+ final String testNS = getNamespaceURI(testPrefix);
+ final String nodeNS = getNamespaceURI(nodePrefix);
+ if (!safeEquals(testNS, nodeNS)) {
+ return false;
+ }
+ }
+ if (nodeNameTest.isWildcard()) {
+ return true;
+ }
+ return testName.getName().equals(nodeName.getName());
+ }
+ return test instanceof NodeTypeTest
+ && ((NodeTypeTest) test).getNodeType() == Compiler.NODE_TYPE_NODE && isNode();
+ }
+
+ /**
+ * Called directly by JXPathContext. Must create path and
+ * set value.
+ * @param context the owning JXPathContext
+ * @param value the new value to set
+ * @return created NodePointer
+ */
+ public NodePointer createPath(final JXPathContext context, final Object value) {
+ setValue(value);
+ return this;
+ }
+
+ /**
+ * Remove the node of the object graph this pointer points to.
+ */
+ public void remove() {
+ // It is a no-op
+
+// System.err.println("REMOVING: " + asPath() + " " + getClass());
+// printPointerChain();
+ }
+
+ /**
+ * Called by a child pointer when it needs to create a parent object.
+ * Must create an object described by this pointer and return
+ * a new pointer that properly describes the new object.
+ * @param context the owning JXPathContext
+ * @return created NodePointer
+ */
+ public NodePointer createPath(final JXPathContext context) {
+ return this;
+ }
+
+ /**
+ * Called by a child pointer if that child needs to assign the value
+ * supplied in the createPath(context, value) call to a non-existent
+ * node. This method may have to expand the collection in order to assign
+ * the element.
+ * @param context the owning JXPathCOntext
+ * @param name the QName at which a child should be created
+ * @param index child index.
+ * @param value node value to set
+ * @return created NodePointer
+ */
+ public NodePointer createChild(
+ final JXPathContext context,
+ final QName name,
+ final int index,
+ final Object value) {
+ throw new JXPathException("Cannot create an object for path "
+ + asPath() + "/" + name + "[" + (index + 1) + "]"
+ + ", operation is not allowed for this type of node");
+ }
+
+ /**
+ * Called by a child pointer when it needs to create a parent object for a
+ * non-existent collection element. It may have to expand the collection,
+ * then create an element object and return a new pointer describing the
+ * newly created element.
+ * @param context the owning JXPathCOntext
+ * @param name the QName at which a child should be created
+ * @param index child index.
+ * @return created NodePointer
+ */
+ public NodePointer createChild(final JXPathContext context, final QName name, final int index) {
+ throw new JXPathException("Cannot create an object for path "
+ + asPath() + "/" + name + "[" + (index + 1) + "]"
+ + ", operation is not allowed for this type of node");
+ }
+
+ /**
+ * Called to create a non-existing attribute
+ * @param context the owning JXPathCOntext
+ * @param name the QName at which an attribute should be created
+ * @return created NodePointer
+ */
+ public NodePointer createAttribute(final JXPathContext context, final QName name) {
+ throw new JXPathException("Cannot create an attribute for path "
+ + asPath() + "/@" + name
+ + ", operation is not allowed for this type of node");
+ }
+
+ /**
+ * If the Pointer has a parent, returns the parent's locale; otherwise
+ * returns the locale specified when this Pointer was created.
+ * @return Locale for this NodePointer
+ */
+ public Locale getLocale() {
+ if (locale == null && parent != null) {
+ locale = parent.getLocale();
+ }
+ return locale;
+ }
+
+ /**
+ * Check whether our locale matches the specified language.
+ * @param lang String language to check
+ * @return true if the selected locale name starts
+ * with the specified prefix lang, case-insensitive.
+ */
+ public boolean isLanguage(final String lang) {
+ final Locale loc = getLocale();
+ final String name = loc.toString().replace('_', '-');
+ return name.toUpperCase(Locale.ENGLISH).startsWith(lang.toUpperCase(Locale.ENGLISH));
+ }
+
+ /**
+ * Returns a NodeIterator that iterates over all children or all children
+ * that match the given NodeTest, starting with the specified one.
+ * @param test NodeTest to filter children
+ * @param reverse specified iteration direction
+ * @param startWith the NodePointer to start with
+ * @return NodeIterator
+ */
+ public NodeIterator childIterator(
+ final NodeTest test,
+ final boolean reverse,
+ final NodePointer startWith) {
+ final NodePointer valuePointer = getValuePointer();
+ return valuePointer == null || valuePointer == this ? null
+ : valuePointer.childIterator(test, reverse, startWith);
+ }
+
+ /**
+ * Returns a NodeIterator that iterates over all attributes of the current
+ * node matching the supplied node name (could have a wildcard).
+ * May return null if the object does not support the attributes.
+ * @param qname the attribute name to test
+ * @return NodeIterator
+ */
+ public NodeIterator attributeIterator(final QName qname) {
+ final NodePointer valuePointer = getValuePointer();
+ return valuePointer == null || valuePointer == this ? null
+ : valuePointer.attributeIterator(qname);
+ }
+
+ /**
+ * Returns a NodeIterator that iterates over all namespaces of the value
+ * currently pointed at.
+ * May return null if the object does not support the namespaces.
+ * @return NodeIterator
+ */
+ public NodeIterator namespaceIterator() {
+ return null;
+ }
+
+ /**
+ * Returns a NodePointer for the specified namespace. Will return null
+ * if namespaces are not supported.
+ * Will return UNKNOWN_NAMESPACE if there is no such namespace.
+ * @param namespace incoming namespace
+ * @return NodePointer for {@code namespace}
+ */
+ public NodePointer namespacePointer(final String namespace) {
+ return null;
+ }
+
+ /**
+ * Decodes a namespace prefix to the corresponding URI.
+ * @param prefix prefix to decode
+ * @return String uri
+ */
+ public String getNamespaceURI(final String prefix) {
+ return null;
+ }
+
+ /**
+ * Returns the namespace URI associated with this Pointer.
+ * @return String uri
+ */
+ public String getNamespaceURI() {
+ return null;
+ }
+
+ /**
+ * Returns true if the supplied prefix represents the
+ * default namespace in the context of the current node.
+ * @param prefix the prefix to check
+ * @return {@code true} if prefix is default
+ */
+ protected boolean isDefaultNamespace(final String prefix) {
+ if (prefix == null) {
+ return true;
+ }
+
+ final String namespace = getNamespaceURI(prefix);
+ return namespace != null && namespace.equals(getDefaultNamespaceURI());
+ }
+
+ /**
+ * Gets the default ns uri
+ * @return String uri
+ */
+ protected String getDefaultNamespaceURI() {
+ return null;
+ }
+
+ /**
+ * Locates a node by ID.
+ * @param context JXPathContext owning context
+ * @param id String id
+ * @return Pointer found
+ */
+ public Pointer getPointerByID(final JXPathContext context, final String id) {
+ return context.getPointerByID(id);
+ }
+
+ /**
+ * Returns an XPath that maps to this Pointer.
+ * @return String xpath expression
+ */
+ @Override
+ public String asPath() {
+ // If the parent of this node is a container, it is responsible
+ // for appended this node's part of the path.
+ if (parent != null && parent.isContainer()) {
+ return parent.asPath();
+ }
+
+ final StringBuilder buffer = new StringBuilder();
+ if (parent != null) {
+ buffer.append(parent.asPath());
+ }
+
+ if (buffer.length() == 0
+ || buffer.charAt(buffer.length() - 1) != '/') {
+ buffer.append('/');
+ }
+ if (attribute) {
+ buffer.append('@');
+ }
+ buffer.append(getName());
+
+ if (index != WHOLE_COLLECTION && isCollection()) {
+ buffer.append('[').append(index + 1).append(']');
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Clone this NodePointer.
+ * @return cloned NodePointer
+ */
+ @Override
+ public Object clone() {
+ try {
+ final NodePointer ptr = (NodePointer) super.clone();
+ if (parent != null) {
+ ptr.parent = (NodePointer) parent.clone();
+ }
+ return ptr;
+ }
+ catch (final CloneNotSupportedException ex) {
+ // Of course it is supported
+ ex.printStackTrace();
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return asPath();
+ }
+
+ @Override
+ public int compareTo(final Object object) {
+ if (object == this) {
+ return 0;
+ }
+ // Let it throw a ClassCastException
+ final NodePointer pointer = (NodePointer) object;
+ if (safeEquals(parent, pointer.parent)) {
+ return parent == null ? 0 : parent.compareChildNodePointers(this, pointer);
+ }
+
+ // Task 1: find the common parent
+ int depth1 = 0;
+ NodePointer p1 = this;
+ final HashSet parents1 = new HashSet();
+ while (p1 != null) {
+ depth1++;
+ p1 = p1.parent;
+ if (p1 != null) {
+ parents1.add(p1);
+ }
+ }
+ boolean commonParentFound = false;
+ int depth2 = 0;
+ NodePointer p2 = pointer;
+ while (p2 != null) {
+ depth2++;
+ p2 = p2.parent;
+ if (parents1.contains(p2)) {
+ commonParentFound = true;
+ }
+ }
+ //nodes from different graphs are equal, else continue comparison:
+ return commonParentFound ? compareNodePointers(this, depth1, pointer, depth2) : 0;
+ }
+
+ /**
+ * Compare node pointers.
+ * @param p1 pointer 1
+ * @param depth1 depth 1
+ * @param p2 pointer 2
+ * @param depth2 depth 2
+ * @return comparison result: (< 0) -> (p1 lt p2); (0) -> (p1 eq p2); (> 0) -> (p1 gt p2)
+ */
+ private int compareNodePointers(
+ final NodePointer p1,
+ final int depth1,
+ final NodePointer p2,
+ final int depth2) {
+ if (depth1 < depth2) {
+ final int r = compareNodePointers(p1, depth1, p2.parent, depth2 - 1);
+ return r == 0 ? -1 : r;
+ }
+ if (depth1 > depth2) {
+ final int r = compareNodePointers(p1.parent, depth1 - 1, p2, depth2);
+ return r == 0 ? 1 : r;
+ }
+ //henceforth depth1 == depth2:
+ if (safeEquals(p1, p2)) {
+ return 0;
+ }
+ if (depth1 == 1) {
+ throw new JXPathException(
+ "Cannot compare pointers that do not belong to the same tree: '"
+ + p1 + "' and '" + p2 + "'");
+ }
+ final int r = compareNodePointers(p1.parent, depth1 - 1, p2.parent, depth2 - 1);
+ return r == 0 ? p1.parent.compareChildNodePointers(p1, p2) : r;
+ }
+
+ /**
+ * Sets the exceptionHandler of this NodePointer.
+ * @param exceptionHandler the ExceptionHandler to set
+ */
+ public void setExceptionHandler(final ExceptionHandler exceptionHandler) {
+ this.exceptionHandler = exceptionHandler;
+ }
+
+ /**
+ * Handle a Throwable using an installed ExceptionHandler, if available.
+ * Public to facilitate calling for RI support; not truly intended for public consumption.
+ * @param t to handle
+ * @param originator context
+ */
+ public void handle(final Throwable t, final NodePointer originator) {
+ if (exceptionHandler != null) {
+ exceptionHandler.handle(t, originator);
+ return;
+ }
+ if (parent != null) {
+ parent.handle(t, originator);
+ }
+ }
+
+ /**
+ * Handle a Throwable using an installed ExceptionHandler, if available.
+ * Public to facilitate calling for RI support; not truly intended for public consumption.
+ * @param t to handle
+ */
+ public void handle(final Throwable t) {
+ handle(t, this);
+ }
+
+ /**
+ * Return a string escaping single and double quotes.
+ * @param string string to treat
+ * @return string with any necessary changes made.
+ */
+ protected String escape(final String string) {
+ final char[] c = { '\'', '"' };
+ final String[] esc = { "'", """ };
+ StringBuilder sb = null;
+ for (int i = 0; sb == null && i < c.length; i++) {
+ if (string.indexOf(c[i]) >= 0) {
+ sb = new StringBuilder(string);
+ }
+ }
+ if (sb == null) {
+ return string;
+ }
+ for (int i = 0; i < c.length; i++) {
+ if (string.indexOf(c[i]) < 0) {
+ continue;
+ }
+ int pos = 0;
+ while (pos < sb.length()) {
+ if (sb.charAt(pos) == c[i]) {
+ sb.replace(pos, pos + 1, esc[i]);
+ pos += esc[i].length();
+ }
+ else {
+ pos++;
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Gets the AbstractFactory associated with the specified JXPathContext.
+ * @param context JXPathContext
+ * @return AbstractFactory
+ */
+ protected AbstractFactory getAbstractFactory(final JXPathContext context) {
+ final AbstractFactory factory = context.getFactory();
+ if (factory == null) {
+ throw new JXPathException(
+ "Factory is not set on the JXPathContext - cannot create path: "
+ + asPath());
+ }
+ return factory;
+ }
+
+ /**
+ * Print deep
+ * @param pointer to print
+ * @param indent indentation level
+ */
+ private static void printDeep(final NodePointer pointer, final String indent) {
+ if (indent.length() == 0) {
+ System.err.println(
+ "POINTER: "
+ + pointer
+ + "("
+ + pointer.getClass().getName()
+ + ")");
+ }
+ else {
+ System.err.println(
+ indent
+ + " of "
+ + pointer
+ + "("
+ + pointer.getClass().getName()
+ + ")");
+ }
+ if (pointer.getImmediateParentPointer() != null) {
+ printDeep(pointer.getImmediateParentPointer(), indent + " ");
+ }
+ }
+
+ private static boolean safeEquals(final Object o1, final Object o2) {
+ return o1 == o2 || o1 != null && o1.equals(o2);
+ }
+
+ /**
+ * Verify the structure of a given NodePointer.
+ * @param nodePointer to check
+ * @return nodePointer
+ * @throws JXPathNotFoundException Thrown when there is no value at the NodePointer.
+ */
+ public static NodePointer verify(final NodePointer nodePointer) {
+ if (!nodePointer.isActual()) {
+ // We need to differentiate between pointers representing
+ // a non-existing property and ones representing a property
+ // whose value is null. In the latter case, the pointer
+ // is going to have isActual == false, but its parent,
+ // which is a non-node pointer identifying the bean property,
+ // will return isActual() == true.
+ final NodePointer parent = nodePointer.getImmediateParentPointer();
+ if (parent == null
+ || !parent.isContainer()
+ || !parent.isActual()) {
+ throw new JXPathNotFoundException("No value for xpath: " + nodePointer);
+ }
+ }
+ return nodePointer;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/NodePointerFactory.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/NodePointerFactory.java
new file mode 100644
index 00000000000..9c4174cab31
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/NodePointerFactory.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model;
+
+import java.util.Locale;
+
+import org.apache.commons.jxpath.ri.QName;
+
+/**
+ * Creates NodePointers for objects of a certain type.
+ * NodePointerFactories are ordered according to the values returned
+ * by the "getOrder" method and always queried in that order.
+ */
+public interface NodePointerFactory {
+
+ /**
+ * The factory order number determines its position between other factories.
+ * @return int order
+ */
+ int getOrder();
+
+ /**
+ * Create a NodePointer for the supplied object. The node will represent
+ * the "root" object for a path.
+ *
+ * @param name String node name
+ * @param object child object
+ * @param locale Locale
+ * @return null if this factory does not recognize objects of the supplied
+ * type.
+ */
+ NodePointer createNodePointer(QName name, Object object, Locale locale);
+
+ /**
+ * Create a NodePointer for the supplied child object.
+ *
+ * @param parent parent node
+ * @param name String node name
+ * @param object child object
+ * @return null if this factory does not recognize objects of the supplied
+ * type.
+ */
+ NodePointer createNodePointer(
+ NodePointer parent,
+ QName name,
+ Object object);
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/VariablePointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/VariablePointer.java
new file mode 100644
index 00000000000..d01dd439edf
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/VariablePointer.java
@@ -0,0 +1,372 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model;
+
+import org.apache.commons.jxpath.AbstractFactory;
+import org.apache.commons.jxpath.JXPathAbstractFactoryException;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.JXPathException;
+import org.apache.commons.jxpath.JXPathIntrospector;
+import org.apache.commons.jxpath.JXPathInvalidAccessException;
+import org.apache.commons.jxpath.Variables;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.model.beans.NullPointer;
+import org.apache.commons.jxpath.util.ValueUtils;
+
+/**
+ * Pointer to a context variable.
+ */
+public class VariablePointer extends NodePointer {
+ private Variables variables;
+ private final QName name;
+ private NodePointer valuePointer;
+ private boolean actual;
+
+ private static final long serialVersionUID = -454731297397189293L;
+
+ /**
+ * Create a new VariablePointer.
+ * @param variables Variables instance
+ * @param name variable name
+ */
+ public VariablePointer(final Variables variables, final QName name) {
+ super(null);
+ this.variables = variables;
+ this.name = name;
+ actual = true;
+ }
+
+ /**
+ * Create a new (non-actual) VariablePointer.
+ * @param name variable name
+ */
+ public VariablePointer(final QName name) {
+ super(null);
+ this.name = name;
+ actual = false;
+ }
+
+ @Override
+ public boolean isContainer() {
+ return true;
+ }
+
+ @Override
+ public QName getName() {
+ return name;
+ }
+
+ @Override
+ public Object getBaseValue() {
+ if (!actual) {
+ throw new JXPathException("Undefined variable: " + name);
+ }
+ return variables.getVariable(name.toString());
+ }
+
+ @Override
+ public boolean isLeaf() {
+ final Object value = getNode();
+ return value == null || JXPathIntrospector.getBeanInfo(value.getClass()).isAtomic();
+ }
+
+ @Override
+ public boolean isCollection() {
+ final Object value = getBaseValue();
+ return value != null && ValueUtils.isCollection(value);
+ }
+
+ @Override
+ public Object getImmediateNode() {
+ final Object value = getBaseValue();
+ return index == WHOLE_COLLECTION ? ValueUtils.getValue(value)
+ : ValueUtils.getValue(value, index);
+ }
+
+ @Override
+ public void setValue(final Object value) {
+ if (!actual) {
+ throw new JXPathException("Cannot set undefined variable: " + name);
+ }
+ valuePointer = null;
+ if (index != WHOLE_COLLECTION) {
+ final Object collection = getBaseValue();
+ ValueUtils.setValue(collection, index, value);
+ }
+ else {
+ variables.declareVariable(name.toString(), value);
+ }
+ }
+
+ @Override
+ public boolean isActual() {
+ return actual;
+ }
+
+ @Override
+ public void setIndex(final int index) {
+ super.setIndex(index);
+ valuePointer = null;
+ }
+
+ @Override
+ public NodePointer getImmediateValuePointer() {
+ if (valuePointer == null) {
+ Object value;
+ if (actual) {
+ value = getImmediateNode();
+ valuePointer = newChildNodePointer(this, null, value);
+ }
+ else {
+ return new NullPointer(this, getName()) {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public Object getImmediateNode() {
+ throw new JXPathException(
+ "Undefined variable: " + name);
+ }
+ };
+ }
+ }
+ return valuePointer;
+ }
+
+ @Override
+ public int getLength() {
+ if (actual) {
+ final Object value = getBaseValue();
+ return value == null ? 1 : ValueUtils.getLength(value);
+ }
+ return 0;
+ }
+
+ @Override
+ public NodePointer createPath(final JXPathContext context, final Object value) {
+ if (actual) {
+ setValue(value);
+ return this;
+ }
+ final NodePointer ptr = createPath(context);
+ ptr.setValue(value);
+ return ptr;
+ }
+
+ @Override
+ public NodePointer createPath(final JXPathContext context) {
+ if (!actual) {
+ final AbstractFactory factory = getAbstractFactory(context);
+ if (!factory.declareVariable(context, name.toString())) {
+ throw new JXPathAbstractFactoryException(
+ "Factory cannot define variable '" + name
+ + "' for path: " + asPath());
+ }
+ findVariables(context);
+ // Assert: actual == true
+ }
+ return this;
+ }
+
+ @Override
+ public NodePointer createChild(
+ final JXPathContext context,
+ final QName name,
+ final int index) {
+ final Object collection = createCollection(context, index);
+ if (!isActual() || index != 0 && index != WHOLE_COLLECTION) {
+ final AbstractFactory factory = getAbstractFactory(context);
+ final boolean success =
+ factory.createObject(
+ context,
+ this,
+ collection,
+ getName().toString(),
+ index);
+ if (!success) {
+ throw new JXPathAbstractFactoryException(
+ "Factory could not create object path: " + asPath());
+ }
+ final NodePointer cln = (NodePointer) clone();
+ cln.setIndex(index);
+ return cln;
+ }
+ return this;
+ }
+
+ @Override
+ public NodePointer createChild(
+ final JXPathContext context,
+ final QName name,
+ final int index,
+ final Object value) {
+ final Object collection = createCollection(context, index);
+ ValueUtils.setValue(collection, index, value);
+ final NodePointer cl = (NodePointer) clone();
+ cl.setIndex(index);
+ return cl;
+ }
+
+ /**
+ * Create a collection.
+ * @param context JXPathContext
+ * @param index collection index
+ * @return Object
+ */
+ private Object createCollection(final JXPathContext context, int index) {
+ createPath(context);
+
+ Object collection = getBaseValue();
+ if (collection == null) {
+ throw new JXPathAbstractFactoryException(
+ "Factory did not assign a collection to variable '"
+ + name
+ + "' for path: "
+ + asPath());
+ }
+
+ if (index == WHOLE_COLLECTION) {
+ index = 0;
+ }
+ else if (index < 0) {
+ throw new JXPathInvalidAccessException("Index is less than 1: "
+ + asPath());
+ }
+
+ if (index >= getLength()) {
+ collection = ValueUtils.expandCollection(collection, index + 1);
+ variables.declareVariable(name.toString(), collection);
+ }
+
+ return collection;
+ }
+
+ @Override
+ public void remove() {
+ if (actual) {
+ if (index == WHOLE_COLLECTION) {
+ variables.undeclareVariable(name.toString());
+ }
+ else {
+ if (index < 0) {
+ throw new JXPathInvalidAccessException(
+ "Index is less than 1: " + asPath());
+ }
+
+ Object collection = getBaseValue();
+ if (collection != null && index < getLength()) {
+ collection = ValueUtils.remove(collection, index);
+ variables.declareVariable(name.toString(), collection);
+ }
+ }
+ }
+ }
+
+ /**
+ * Assimilate the Variables instance associated with the specified context.
+ * @param context JXPathContext to search
+ */
+ protected void findVariables(final JXPathContext context) {
+ valuePointer = null;
+ JXPathContext varCtx = context;
+ while (varCtx != null) {
+ variables = varCtx.getVariables();
+ if (variables.isDeclaredVariable(name.toString())) {
+ actual = true;
+ break;
+ }
+ varCtx = varCtx.getParentContext();
+ variables = null;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return (actual ? System.identityHashCode(variables) : 0)
+ + name.hashCode()
+ + index;
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ if (object == this) {
+ return true;
+ }
+
+ if (!(object instanceof VariablePointer)) {
+ return false;
+ }
+
+ final VariablePointer other = (VariablePointer) object;
+ return variables == other.variables
+ && name.equals(other.name)
+ && index == other.index;
+ }
+
+ @Override
+ public String asPath() {
+ final StringBuilder buffer = new StringBuilder();
+ buffer.append('$');
+ buffer.append(name);
+ if (!actual) {
+ if (index != WHOLE_COLLECTION) {
+ buffer.append('[').append(index + 1).append(']');
+ }
+ }
+ else if (
+ index != WHOLE_COLLECTION
+ && (getNode() == null || isCollection())) {
+ buffer.append('[').append(index + 1).append(']');
+ }
+ return buffer.toString();
+ }
+
+ @Override
+ public NodeIterator childIterator(
+ final NodeTest test,
+ final boolean reverse,
+ final NodePointer startWith) {
+ return getValuePointer().childIterator(test, reverse, startWith);
+ }
+
+ @Override
+ public NodeIterator attributeIterator(final QName name) {
+ return getValuePointer().attributeIterator(name);
+ }
+
+ @Override
+ public NodeIterator namespaceIterator() {
+ return getValuePointer().namespaceIterator();
+ }
+
+ @Override
+ public NodePointer namespacePointer(final String name) {
+ return getValuePointer().namespacePointer(name);
+ }
+
+ @Override
+ public boolean testNode(final NodeTest nodeTest) {
+ return getValuePointer().testNode(nodeTest);
+ }
+
+ @Override
+ public int compareChildNodePointers(
+ final NodePointer pointer1,
+ final NodePointer pointer2) {
+ return pointer1.getIndex() - pointer2.getIndex();
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/VariablePointerFactory.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/VariablePointerFactory.java
new file mode 100644
index 00000000000..eb6f5a4e9ca
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/VariablePointerFactory.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model;
+
+import java.util.Locale;
+
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.Variables;
+import org.apache.commons.jxpath.ri.QName;
+
+/**
+ * NodePointerFactory to create {@link VariablePointer VariablePointers}.
+ * @since JXPath 1.3
+ */
+public class VariablePointerFactory implements NodePointerFactory {
+ /** Factory order constant */
+ public static final int VARIABLE_POINTER_FACTORY_ORDER = 890;
+
+ /**
+ * Node value wrapper to trigger a VariablePointerFactory.
+ */
+ public static final class VariableContextWrapper {
+ private final JXPathContext context;
+
+ /**
+ * Create a new VariableContextWrapper.
+ * @param context to wrap
+ */
+ private VariableContextWrapper(final JXPathContext context) {
+ this.context = context;
+ }
+
+ /**
+ * Gets the original (unwrapped) context.
+ *
+ * @return JXPathContext.
+ */
+ public JXPathContext getContext() {
+ return context;
+ }
+ }
+
+ /**
+ * VariableContextWrapper factory method.
+ * @param context the JXPathContext to wrap.
+ * @return VariableContextWrapper.
+ */
+ public static VariableContextWrapper contextWrapper(final JXPathContext context) {
+ return new VariableContextWrapper(context);
+ }
+
+ @Override
+ public NodePointer createNodePointer(final QName name, final Object object,
+ final Locale locale) {
+ if (object instanceof VariableContextWrapper) {
+ JXPathContext varCtx = ((VariableContextWrapper) object).getContext();
+ while (varCtx != null) {
+ final Variables vars = varCtx.getVariables();
+ if (vars.isDeclaredVariable(name.toString())) {
+ return new VariablePointer(vars, name);
+ }
+ varCtx = varCtx.getParentContext();
+ }
+ // The variable is not declared, but we will create
+ // a pointer anyway in case the user wants to set, rather
+ // than get, the value of the variable.
+ return new VariablePointer(name);
+ }
+ return null;
+ }
+
+ @Override
+ public NodePointer createNodePointer(final NodePointer parent, final QName name,
+ final Object object) {
+ return createNodePointer(name, object, null);
+ }
+
+ @Override
+ public int getOrder() {
+ return VARIABLE_POINTER_FACTORY_ORDER;
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/BeanAttributeIterator.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/BeanAttributeIterator.java
new file mode 100644
index 00000000000..a6d961ceffe
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/BeanAttributeIterator.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.beans;
+
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * An iterator of attributes of a JavaBean. Returns bean properties as
+ * well as the "xml:lang" attribute.
+ */
+public class BeanAttributeIterator extends PropertyIterator {
+ private final NodePointer parent;
+ private int position = 0;
+ private final boolean includeXmlLang;
+
+ /**
+ * Create a new BeanAttributeIterator.
+ * @param parent parent pointer
+ * @param name name of this bean
+ */
+ public BeanAttributeIterator(final PropertyOwnerPointer parent, final QName name) {
+ super(
+ parent,
+ name.getPrefix() == null
+ && (name.getName() == null || name.getName().equals("*"))
+ ? null
+ : name.toString(),
+ false,
+ null);
+ this.parent = parent;
+ includeXmlLang =
+ name.getPrefix() != null && name.getPrefix().equals("xml")
+ && (name.getName().equals("lang")
+ || name.getName().equals("*"));
+ }
+
+ @Override
+ public NodePointer getNodePointer() {
+ return includeXmlLang && position == 1 ? new LangAttributePointer(parent) : super.getNodePointer();
+ }
+
+ @Override
+ public int getPosition() {
+ return position;
+ }
+
+ @Override
+ public boolean setPosition(final int position) {
+ this.position = position;
+ if (includeXmlLang) {
+ return position == 1 || super.setPosition(position - 1);
+ }
+ return super.setPosition(position);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/BeanPointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/BeanPointer.java
new file mode 100644
index 00000000000..d4b60a30871
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/BeanPointer.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.beans;
+
+import java.util.Locale;
+
+import org.apache.commons.jxpath.JXPathBeanInfo;
+import org.apache.commons.jxpath.JXPathIntrospector;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * A Pointer that points to a JavaBean or a collection. It is either
+ * the first element of a path or a pointer for a property value.
+ * Typically there is a {@link BeanPropertyPointer} between two BeanPointers
+ * in the chain.
+ */
+public class BeanPointer extends PropertyOwnerPointer {
+ private final QName name;
+ private final Object bean;
+ private final JXPathBeanInfo beanInfo;
+
+ private static final long serialVersionUID = -8227317938284982440L;
+
+ /**
+ * Create a new BeanPointer.
+ * @param name is the name given to the first node
+ * @param bean pointed
+ * @param beanInfo JXPathBeanInfo
+ * @param locale Locale
+ */
+ public BeanPointer(final QName name, final Object bean, final JXPathBeanInfo beanInfo,
+ final Locale locale) {
+ super(null, locale);
+ this.name = name;
+ this.bean = bean;
+ this.beanInfo = beanInfo;
+ }
+
+ /**
+ * Create a new BeanPointer.
+ * @param parent pointer
+ * @param name is the name given to the first node
+ * @param bean pointed
+ * @param beanInfo JXPathBeanInfo
+ */
+ public BeanPointer(final NodePointer parent, final QName name, final Object bean,
+ final JXPathBeanInfo beanInfo) {
+ super(parent);
+ this.name = name;
+ this.bean = bean;
+ this.beanInfo = beanInfo;
+ }
+
+ @Override
+ public PropertyPointer getPropertyPointer() {
+ return new BeanPropertyPointer(this, beanInfo);
+ }
+
+ @Override
+ public QName getName() {
+ return name;
+ }
+
+ @Override
+ public Object getBaseValue() {
+ return bean;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @return false
+ */
+ @Override
+ public boolean isCollection() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @return 1
+ */
+ @Override
+ public int getLength() {
+ return 1;
+ }
+
+ @Override
+ public boolean isLeaf() {
+ final Object value = getNode();
+ return value == null
+ || JXPathIntrospector.getBeanInfo(value.getClass()).isAtomic();
+ }
+
+ @Override
+ public int hashCode() {
+ return name == null ? 0 : name.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ if (object == this) {
+ return true;
+ }
+
+ if (!(object instanceof BeanPointer)) {
+ return false;
+ }
+
+ final BeanPointer other = (BeanPointer) object;
+ if (parent != other.parent && (parent == null || !parent.equals(other.parent))) {
+ return false;
+ }
+
+ if (name == null && other.name != null
+ || name != null && !name.equals(other.name)) {
+ return false;
+ }
+
+ final int iThis = index == WHOLE_COLLECTION ? 0 : index;
+ final int iOther = other.index == WHOLE_COLLECTION ? 0 : other.index;
+ if (iThis != iOther) {
+ return false;
+ }
+
+ if (bean instanceof Number
+ || bean instanceof String
+ || bean instanceof Boolean) {
+ return bean.equals(other.bean);
+ }
+ return bean == other.bean;
+ }
+
+ /**
+ * {@inheritDoc}
+ * If the pointer has a parent, then parent's path.
+ * If the bean is null, "null()".
+ * If the bean is a primitive value, the value itself.
+ * Otherwise - an empty string.
+ */
+ @Override
+ public String asPath() {
+ if (parent != null) {
+ return super.asPath();
+ }
+ if (bean == null) {
+ return "null()";
+ }
+ if (bean instanceof Number) {
+ String string = bean.toString();
+ if (string.endsWith(".0")) {
+ string = string.substring(0, string.length() - 2);
+ }
+ return string;
+ }
+ if (bean instanceof Boolean) {
+ return ((Boolean) bean).booleanValue() ? "true()" : "false()";
+ }
+ if (bean instanceof String) {
+ return "'" + bean + "'";
+ }
+ return "/";
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/BeanPointerFactory.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/BeanPointerFactory.java
new file mode 100644
index 00000000000..1eb62e956f9
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/BeanPointerFactory.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.beans;
+
+import java.util.Locale;
+
+import org.apache.commons.jxpath.JXPathBeanInfo;
+import org.apache.commons.jxpath.JXPathIntrospector;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.ri.model.NodePointerFactory;
+
+/**
+ * Implements NodePointerFactory for JavaBeans.
+ */
+public class BeanPointerFactory implements NodePointerFactory {
+
+ /** Factory order constant */
+ public static final int BEAN_POINTER_FACTORY_ORDER = 900;
+
+ @Override
+ public int getOrder() {
+ return BEAN_POINTER_FACTORY_ORDER;
+ }
+
+ @Override
+ public NodePointer createNodePointer(final QName name, final Object bean, final Locale locale) {
+ final JXPathBeanInfo bi = JXPathIntrospector.getBeanInfo(bean.getClass());
+ return new BeanPointer(name, bean, bi, locale);
+ }
+
+ @Override
+ public NodePointer createNodePointer(final NodePointer parent, final QName name,
+ final Object bean) {
+ if (bean == null) {
+ return new NullPointer(parent, name);
+ }
+
+ final JXPathBeanInfo bi = JXPathIntrospector.getBeanInfo(bean.getClass());
+ return new BeanPointer(parent, name, bean, bi);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/BeanPropertyPointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/BeanPropertyPointer.java
new file mode 100644
index 00000000000..17fb7a32b52
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/BeanPropertyPointer.java
@@ -0,0 +1,324 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.beans;
+
+import java.beans.IndexedPropertyDescriptor;
+import java.beans.PropertyDescriptor;
+
+import org.apache.commons.jxpath.JXPathBeanInfo;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.JXPathInvalidAccessException;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.util.ValueUtils;
+
+/**
+ * Pointer pointing to a property of a JavaBean.
+ */
+public class BeanPropertyPointer extends PropertyPointer {
+ private static final long serialVersionUID = -6008991447676468786L;
+
+ private static final Object UNINITIALIZED = new Object();
+
+ private String propertyName;
+ private final JXPathBeanInfo beanInfo;
+ private Object baseValue = UNINITIALIZED;
+ private Object value = UNINITIALIZED;
+ private transient String[] names;
+ private transient PropertyDescriptor[] propertyDescriptors;
+ private transient PropertyDescriptor propertyDescriptor;
+
+ /**
+ * Create a new BeanPropertyPointer.
+ * @param parent parent pointer
+ * @param beanInfo describes the target property/ies.
+ */
+ public BeanPropertyPointer(final NodePointer parent, final JXPathBeanInfo beanInfo) {
+ super(parent);
+ this.beanInfo = beanInfo;
+ }
+
+ /**
+ * This type of node is auxiliary.
+ * @return true
+ */
+ @Override
+ public boolean isContainer() {
+ return true;
+ }
+
+ @Override
+ public int getPropertyCount() {
+ if (beanInfo.isAtomic()) {
+ return 0;
+ }
+ return getPropertyDescriptors().length;
+ }
+
+ /**
+ * Gets the names of all properties, sorted alphabetically
+ * @return String[]
+ */
+ @Override
+ public String[] getPropertyNames() {
+ if (names == null) {
+ final PropertyDescriptor[] pds = getPropertyDescriptors();
+ names = new String[pds.length];
+ for (int i = 0; i < names.length; i++) {
+ names[i] = pds[i].getName();
+ }
+ }
+ return names;
+ }
+
+ /**
+ * Select a property by name.
+ * @param propertyName String name
+ */
+ @Override
+ public void setPropertyName(final String propertyName) {
+ setPropertyIndex(UNSPECIFIED_PROPERTY);
+ this.propertyName = propertyName;
+ }
+
+ /**
+ * Selects a property by its offset in the alphabetically sorted list.
+ * @param index property index
+ */
+ @Override
+ public void setPropertyIndex(final int index) {
+ if (propertyIndex != index) {
+ super.setPropertyIndex(index);
+ propertyName = null;
+ propertyDescriptor = null;
+ baseValue = UNINITIALIZED;
+ value = UNINITIALIZED;
+ }
+ }
+
+ /**
+ * Gets the value of the currently selected property.
+ * @return Object value
+ */
+ @Override
+ public Object getBaseValue() {
+ if (baseValue == UNINITIALIZED) {
+ final PropertyDescriptor pd = getPropertyDescriptor();
+ if (pd == null) {
+ return null;
+ }
+ baseValue = ValueUtils.getValue(getBean(), pd);
+ }
+ return baseValue;
+ }
+
+ @Override
+ public void setIndex(final int index) {
+ if (this.index == index) {
+ return;
+ }
+ // When dealing with a scalar, index == 0 is equivalent to
+ // WHOLE_COLLECTION, so do not change it.
+ if (this.index != WHOLE_COLLECTION
+ || index != 0
+ || isCollection()) {
+ super.setIndex(index);
+ value = UNINITIALIZED;
+ }
+ }
+
+ /**
+ * If index == WHOLE_COLLECTION, the value of the property, otherwise
+ * the value of the index'th element of the collection represented by the
+ * property. If the property is not a collection, index should be zero
+ * and the value will be the property itself.
+ * @return Object
+ */
+ @Override
+ public Object getImmediateNode() {
+ if (value == UNINITIALIZED) {
+ if (index == WHOLE_COLLECTION) {
+ value = ValueUtils.getValue(getBaseValue());
+ }
+ else {
+ final PropertyDescriptor pd = getPropertyDescriptor();
+ if (pd == null) {
+ value = null;
+ }
+ else {
+ value = ValueUtils.getValue(getBean(), pd, index);
+ }
+ }
+ }
+ return value;
+ }
+
+ @Override
+ protected boolean isActualProperty() {
+ return getPropertyDescriptor() != null;
+ }
+
+ @Override
+ public boolean isCollection() {
+ final PropertyDescriptor pd = getPropertyDescriptor();
+ if (pd == null) {
+ return false;
+ }
+
+ if (pd instanceof IndexedPropertyDescriptor) {
+ return true;
+ }
+
+ final int hint = ValueUtils.getCollectionHint(pd.getPropertyType());
+ if (hint == -1) {
+ return false;
+ }
+ if (hint == 1) {
+ return true;
+ }
+
+ final Object value = getBaseValue();
+ return value != null && ValueUtils.isCollection(value);
+ }
+
+ /**
+ * If the property contains a collection, then the length of that
+ * collection, otherwise - 1.
+ * @return int length
+ */
+ @Override
+ public int getLength() {
+ final PropertyDescriptor pd = getPropertyDescriptor();
+ if (pd == null) {
+ return 1;
+ }
+
+ if (pd instanceof IndexedPropertyDescriptor) {
+ return ValueUtils.getIndexedPropertyLength(
+ getBean(),
+ (IndexedPropertyDescriptor) pd);
+ }
+
+ final int hint = ValueUtils.getCollectionHint(pd.getPropertyType());
+ if (hint == -1) {
+ return 1;
+ }
+ return super.getLength();
+ }
+
+ /**
+ * If index == WHOLE_COLLECTION, change the value of the property, otherwise
+ * change the value of the index'th element of the collection
+ * represented by the property.
+ * @param value value to set
+ */
+ @Override
+ public void setValue(final Object value) {
+ final PropertyDescriptor pd = getPropertyDescriptor();
+ if (pd == null) {
+ throw new JXPathInvalidAccessException(
+ "Cannot set property: " + asPath() + " - no such property");
+ }
+
+ if (index == WHOLE_COLLECTION) {
+ ValueUtils.setValue(getBean(), pd, value);
+ }
+ else {
+ ValueUtils.setValue(getBean(), pd, index, value);
+ }
+ this.value = value;
+ }
+
+ @Override
+ public NodePointer createPath(final JXPathContext context) {
+ if (getImmediateNode() == null) {
+ super.createPath(context);
+ baseValue = UNINITIALIZED;
+ value = UNINITIALIZED;
+ }
+ return this;
+ }
+
+ @Override
+ public void remove() {
+ if (index == WHOLE_COLLECTION) {
+ setValue(null);
+ }
+ else if (isCollection()) {
+ final Object o = getBaseValue();
+ final Object collection = ValueUtils.remove(getBaseValue(), index);
+ if (collection != o) {
+ ValueUtils.setValue(getBean(), getPropertyDescriptor(), collection);
+ }
+ }
+ else if (index == 0) {
+ index = WHOLE_COLLECTION;
+ setValue(null);
+ }
+ }
+
+ /**
+ * Gets the name of the currently selected property.
+ * @return String property name
+ */
+ @Override
+ public String getPropertyName() {
+ if (propertyName == null) {
+ final PropertyDescriptor pd = getPropertyDescriptor();
+ if (pd != null) {
+ propertyName = pd.getName();
+ }
+ }
+ return propertyName != null ? propertyName : "*";
+ }
+
+ /**
+ * Finds the property descriptor corresponding to the current property
+ * index.
+ * @return PropertyDescriptor
+ */
+ private PropertyDescriptor getPropertyDescriptor() {
+ if (propertyDescriptor == null) {
+ final int inx = getPropertyIndex();
+ if (inx == UNSPECIFIED_PROPERTY) {
+ propertyDescriptor =
+ beanInfo.getPropertyDescriptor(propertyName);
+ }
+ else {
+ final PropertyDescriptor[] propertyDescriptors =
+ getPropertyDescriptors();
+ if (inx >= 0 && inx < propertyDescriptors.length) {
+ propertyDescriptor = propertyDescriptors[inx];
+ }
+ else {
+ propertyDescriptor = null;
+ }
+ }
+ }
+ return propertyDescriptor;
+ }
+
+ /**
+ * Gets all PropertyDescriptors.
+ * @return PropertyDescriptor[]
+ */
+ protected synchronized PropertyDescriptor[] getPropertyDescriptors() {
+ if (propertyDescriptors == null) {
+ propertyDescriptors = beanInfo.getPropertyDescriptors();
+ }
+ return propertyDescriptors;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/CollectionAttributeNodeIterator.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/CollectionAttributeNodeIterator.java
new file mode 100644
index 00000000000..ab3eaa9067b
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/CollectionAttributeNodeIterator.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.beans;
+
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.model.NodeIterator;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * Combines attribute node iterators of all elements of a collection into one
+ * aggregate attribute node iterator.
+ */
+public class CollectionAttributeNodeIterator extends CollectionNodeIterator {
+
+ private final QName name;
+
+ /**
+ * Create a new CollectionAttributeNodeIterator.
+ * @param pointer collection pointer
+ * @param name attribute name
+ */
+ public CollectionAttributeNodeIterator(
+ final CollectionPointer pointer,
+ final QName name) {
+ super(pointer, false, null);
+ this.name = name;
+ }
+
+ @Override
+ protected NodeIterator getElementNodeIterator(final NodePointer elementPointer) {
+ return elementPointer.attributeIterator(name);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/CollectionChildNodeIterator.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/CollectionChildNodeIterator.java
new file mode 100644
index 00000000000..efb7dc6196c
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/CollectionChildNodeIterator.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.beans;
+
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.model.NodeIterator;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * Combines child node iterators of all elements of a collection into one
+ * aggregate child node iterator.
+ */
+public class CollectionChildNodeIterator extends CollectionNodeIterator {
+
+ private final NodeTest test;
+
+ /**
+ * Create a new CollectionChildNodeIterator.
+ * @param pointer CollectionPointer
+ * @param test child test
+ * @param reverse iteration order
+ * @param startWith starting pointer
+ */
+ public CollectionChildNodeIterator(
+ final CollectionPointer pointer,
+ final NodeTest test,
+ final boolean reverse,
+ final NodePointer startWith) {
+ super(pointer, reverse, startWith);
+ this.test = test;
+ }
+
+ @Override
+ protected NodeIterator getElementNodeIterator(final NodePointer elementPointer) {
+ return elementPointer.childIterator(test, false, null);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/CollectionNodeIterator.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/CollectionNodeIterator.java
new file mode 100644
index 00000000000..a48b600de6c
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/CollectionNodeIterator.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.beans;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.jxpath.JXPathException;
+import org.apache.commons.jxpath.ri.model.NodeIterator;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * Combines node iterators of all elements of a collection into one
+ * aggregate node iterator.
+ */
+public abstract class CollectionNodeIterator implements NodeIterator {
+ private final CollectionPointer pointer;
+ private final boolean reverse;
+ private final NodePointer startWith;
+ private int position;
+ private List collection;
+
+ /**
+ * Create a new CollectionNodeIterator.
+ * @param pointer collection pointer
+ * @param reverse iteration order
+ * @param startWith starting pointer
+ */
+ protected CollectionNodeIterator(
+ final CollectionPointer pointer,
+ final boolean reverse,
+ final NodePointer startWith) {
+ this.pointer = pointer;
+ this.reverse = reverse;
+ this.startWith = startWith;
+ }
+
+ /**
+ * Implemented by subclasses to produce child/attribute node iterators.
+ * @param elementPointer owning pointer
+ * @return NodeIterator
+ */
+ protected abstract NodeIterator
+ getElementNodeIterator(NodePointer elementPointer);
+
+ @Override
+ public int getPosition() {
+ return position;
+ }
+
+ @Override
+ public boolean setPosition(final int position) {
+ if (collection == null) {
+ prepare();
+ }
+
+ if (position < 1 || position > collection.size()) {
+ return false;
+ }
+ this.position = position;
+ return true;
+ }
+
+ @Override
+ public NodePointer getNodePointer() {
+ if (position == 0) {
+ return null;
+ }
+ return (NodePointer) collection.get(position - 1);
+ }
+
+ /**
+ * Prepare...
+ */
+ private void prepare() {
+ collection = new ArrayList();
+ final NodePointer ptr = (NodePointer) pointer.clone();
+ final int length = ptr.getLength();
+ for (int i = 0; i < length; i++) {
+ ptr.setIndex(i);
+ final NodePointer elementPointer = ptr.getValuePointer();
+ final NodeIterator iter = getElementNodeIterator(elementPointer);
+
+ for (int j = 1; iter.setPosition(j); j++) {
+ final NodePointer childPointer = iter.getNodePointer();
+ if (reverse) {
+ collection.add(0, childPointer);
+ }
+ else {
+ collection.add(childPointer);
+ }
+ }
+ }
+ if (startWith != null) {
+ final int index = collection.indexOf(startWith);
+ if (index == -1) {
+ throw new JXPathException(
+ "Invalid starting pointer for iterator: " + startWith);
+ }
+ while (collection.size() > index) {
+ if (!reverse) {
+ collection.remove(collection.size() - 1);
+ }
+ else {
+ collection.remove(0);
+ }
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/CollectionPointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/CollectionPointer.java
new file mode 100644
index 00000000000..c3466578df9
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/CollectionPointer.java
@@ -0,0 +1,254 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.beans;
+
+import java.util.Locale;
+
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.JXPathIntrospector;
+import org.apache.commons.jxpath.ri.Compiler;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
+import org.apache.commons.jxpath.ri.model.NodeIterator;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.util.ValueUtils;
+
+/**
+ * Transparent pointer to a collection (array or Collection).
+ */
+public class CollectionPointer extends NodePointer {
+ private Object collection;
+ private NodePointer valuePointer;
+
+ private static final long serialVersionUID = 8620254915563256588L;
+
+ /**
+ * Create a new CollectionPointer.
+ * @param collection value
+ * @param locale Locale
+ */
+ public CollectionPointer(final Object collection, final Locale locale) {
+ super(null, locale);
+ this.collection = collection;
+ }
+
+ /**
+ * Create a new CollectionPointer.
+ * @param parent parent NodePointer
+ * @param collection value
+ */
+ public CollectionPointer(final NodePointer parent, final Object collection) {
+ super(parent);
+ this.collection = collection;
+ }
+
+ @Override
+ public QName getName() {
+ return null;
+ }
+
+ @Override
+ public Object getBaseValue() {
+ return collection;
+ }
+
+ @Override
+ public boolean isCollection() {
+ return true;
+ }
+
+ @Override
+ public int getLength() {
+ return ValueUtils.getLength(getBaseValue());
+ }
+
+ @Override
+ public boolean isLeaf() {
+ final Object value = getNode();
+ return value == null || JXPathIntrospector.getBeanInfo(value.getClass()).isAtomic();
+ }
+
+ @Override
+ public boolean isContainer() {
+ return index != WHOLE_COLLECTION;
+ }
+
+ @Override
+ public Object getImmediateNode() {
+ return index == WHOLE_COLLECTION ? ValueUtils.getValue(collection)
+ : ValueUtils.getValue(collection, index);
+ }
+
+ @Override
+ public void setValue(final Object value) {
+ if (index == WHOLE_COLLECTION) {
+ parent.setValue(value);
+ }
+ else {
+ ValueUtils.setValue(collection, index, value);
+ }
+ }
+
+ @Override
+ public void setIndex(final int index) {
+ super.setIndex(index);
+ valuePointer = null;
+ }
+
+ @Override
+ public NodePointer getValuePointer() {
+ if (valuePointer == null) {
+ if (index == WHOLE_COLLECTION) {
+ valuePointer = this;
+ }
+ else {
+ final Object value = getImmediateNode();
+ valuePointer = newChildNodePointer(this, getName(), value);
+ }
+ }
+ return valuePointer;
+ }
+
+ @Override
+ public NodePointer createPath(final JXPathContext context) {
+ if (ValueUtils.getLength(getBaseValue()) <= index) {
+ collection = ValueUtils.expandCollection(getNode(), index + 1);
+ }
+ return this;
+ }
+
+ @Override
+ public NodePointer createPath(final JXPathContext context, final Object value) {
+ final NodePointer ptr = createPath(context);
+ ptr.setValue(value);
+ return ptr;
+ }
+
+ @Override
+ public NodePointer createChild(
+ final JXPathContext context,
+ final QName name,
+ final int index,
+ final Object value) {
+ final NodePointer ptr = (NodePointer) clone();
+ ptr.setIndex(index);
+ return ptr.createPath(context, value);
+ }
+
+ @Override
+ public NodePointer createChild(
+ final JXPathContext context,
+ final QName name,
+ final int index) {
+ final NodePointer ptr = (NodePointer) clone();
+ ptr.setIndex(index);
+ return ptr.createPath(context);
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(collection) + index;
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ if (object == this) {
+ return true;
+ }
+
+ if (!(object instanceof CollectionPointer)) {
+ return false;
+ }
+
+ final CollectionPointer other = (CollectionPointer) object;
+ return collection == other.collection && index == other.index;
+ }
+
+ @Override
+ public NodeIterator childIterator(final NodeTest test,
+ final boolean reverse, final NodePointer startWith) {
+ if (index == WHOLE_COLLECTION) {
+ return new CollectionChildNodeIterator(
+ this,
+ test,
+ reverse,
+ startWith);
+ }
+ return getValuePointer().childIterator(test, reverse, startWith);
+ }
+
+ @Override
+ public NodeIterator attributeIterator(final QName name) {
+ return index == WHOLE_COLLECTION ? new CollectionAttributeNodeIterator(this, name)
+ : getValuePointer().attributeIterator(name);
+ }
+
+ @Override
+ public NodeIterator namespaceIterator() {
+ return index == WHOLE_COLLECTION ? null : getValuePointer().namespaceIterator();
+ }
+
+ @Override
+ public NodePointer namespacePointer(final String namespace) {
+ return index == WHOLE_COLLECTION ? null : getValuePointer().namespacePointer(namespace);
+ }
+
+ @Override
+ public boolean testNode(final NodeTest test) {
+ if (index == WHOLE_COLLECTION) {
+ if (test == null) {
+ return true;
+ }
+ if (test instanceof NodeNameTest) {
+ return false;
+ }
+ return test instanceof NodeTypeTest && ((NodeTypeTest) test).getNodeType() == Compiler.NODE_TYPE_NODE;
+ }
+ return getValuePointer().testNode(test);
+ }
+
+ @Override
+ public int compareChildNodePointers(
+ final NodePointer pointer1, final NodePointer pointer2) {
+ return pointer1.getIndex() - pointer2.getIndex();
+ }
+
+ @Override
+ public String asPath() {
+ final StringBuilder buffer = new StringBuilder();
+ final NodePointer parent = getImmediateParentPointer();
+ if (parent != null) {
+ buffer.append(parent.asPath());
+ if (index != WHOLE_COLLECTION) {
+ // Address the list[1][2] case
+ if (parent.getIndex() != WHOLE_COLLECTION) {
+ buffer.append("/.");
+ }
+ buffer.append("[").append(index + 1).append(']');
+ }
+ }
+ else if (index != WHOLE_COLLECTION) {
+ buffer.append("/.[").append(index + 1).append(']');
+ }
+ else {
+ buffer.append("/");
+ }
+ return buffer.toString();
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/CollectionPointerFactory.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/CollectionPointerFactory.java
new file mode 100644
index 00000000000..82ebf16aaf1
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/CollectionPointerFactory.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.beans;
+
+import java.util.Locale;
+
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.ri.model.NodePointerFactory;
+import org.apache.commons.jxpath.util.ValueUtils;
+
+/**
+ * Implements NodePointerFactory for stand-alone collections.
+ */
+public class CollectionPointerFactory implements NodePointerFactory {
+
+ /** Factory order constant */
+ public static final int COLLECTION_POINTER_FACTORY_ORDER = 10;
+
+ @Override
+ public int getOrder() {
+ return COLLECTION_POINTER_FACTORY_ORDER;
+ }
+
+ @Override
+ public NodePointer createNodePointer(final QName name, final Object bean, final Locale locale) {
+ return ValueUtils.isCollection(bean) ? new CollectionPointer(bean, locale) : null;
+ }
+
+ @Override
+ public NodePointer createNodePointer(final NodePointer parent, final QName name,
+ final Object bean) {
+ return ValueUtils.isCollection(bean) ? new CollectionPointer(parent, bean) : null;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/LangAttributePointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/LangAttributePointer.java
new file mode 100644
index 00000000000..0a0a2fc1c0e
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/LangAttributePointer.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.beans;
+
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * A Pointer that points to the "lang" attribute of a JavaBean. The value
+ * of the attribute is based on the locale supplied to it in the constructor.
+ */
+public class LangAttributePointer extends NodePointer {
+
+ private static final long serialVersionUID = -8665319197100034134L;
+
+ /**
+ * Create a new LangAttributePointer.
+ * @param parent parent pointer.
+ */
+ public LangAttributePointer(final NodePointer parent) {
+ super(parent);
+ }
+
+ @Override
+ public QName getName() {
+ return new QName("xml", "lang");
+ }
+
+ @Override
+ public String getNamespaceURI() {
+ return null;
+ }
+
+ @Override
+ public boolean isCollection() {
+ return false;
+ }
+
+ @Override
+ public int getLength() {
+ return 1;
+ }
+
+ @Override
+ public Object getBaseValue() {
+ return parent.getLocale().toString().replace('_', '-');
+ }
+
+ @Override
+ public Object getImmediateNode() {
+ return getBaseValue();
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Throws UnsupportedOperationException.
+ * @param value Object
+ */
+ @Override
+ public void setValue(final Object value) {
+ throw new UnsupportedOperationException(
+ "Cannot change locale using the 'lang' attribute");
+ }
+
+ @Override
+ public String asPath() {
+ final StringBuilder buffer = new StringBuilder();
+ if (parent != null) {
+ buffer.append(parent.asPath());
+ if (buffer.length() == 0
+ || buffer.charAt(buffer.length() - 1) != '/') {
+ buffer.append('/');
+ }
+ }
+ buffer.append("@xml:lang");
+ return buffer.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ return object instanceof LangAttributePointer;
+ }
+
+ @Override
+ public boolean testNode(final NodeTest test) {
+ return false;
+ }
+
+ @Override
+ public int compareChildNodePointers(
+ final NodePointer pointer1,
+ final NodePointer pointer2) {
+ // Won't happen - lang attributes don't have children
+ return 0;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/NullElementPointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/NullElementPointer.java
new file mode 100644
index 00000000000..c5e2cdfa98e
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/NullElementPointer.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.beans;
+
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * Used when there is a need to construct a Pointer for a collection element
+ * that does not exist. For example, if the path is "foo[3]", but the
+ * collection "foo" only has one element or is empty or is null, the
+ * NullElementPointer can be used to capture this situation without putting a
+ * regular NodePointer into an invalid state. Just create a NullElementPointer
+ * with index 2 (= 3 - 1) and a "foo" pointer as the parent.
+ */
+public class NullElementPointer extends CollectionPointer {
+
+ private static final long serialVersionUID = 8714236818791036721L;
+
+ /**
+ * Create a new NullElementPointer.
+ * @param parent parent pointer
+ * @param index int
+ */
+ public NullElementPointer(final NodePointer parent, final int index) {
+ super(parent, (Object) null);
+ this.index = index;
+ }
+
+ @Override
+ public QName getName() {
+ return null;
+ }
+
+ @Override
+ public Object getBaseValue() {
+ return null;
+ }
+
+ @Override
+ public Object getImmediateNode() {
+ return null;
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return true;
+ }
+
+ @Override
+ public boolean isCollection() {
+ return false;
+ }
+
+ /**
+ * Gets the property pointer for this.
+ * @return PropertyPointer
+ */
+ public PropertyPointer getPropertyPointer() {
+ return new NullPropertyPointer(this);
+ }
+
+ @Override
+ public NodePointer getValuePointer() {
+ return new NullPointer(this, getName());
+ }
+
+ @Override
+ public void setValue(final Object value) {
+ throw new UnsupportedOperationException(
+ "Collection element does not exist: " + this);
+ }
+
+ @Override
+ public boolean isActual() {
+ return false;
+ }
+
+ @Override
+ public boolean isContainer() {
+ return true;
+ }
+
+ @Override
+ public NodePointer createPath(final JXPathContext context) {
+ return parent.createChild(context, null, index);
+ }
+
+ @Override
+ public NodePointer createPath(final JXPathContext context, final Object value) {
+ return parent.createChild(context, null, index, value);
+ }
+
+ @Override
+ public int hashCode() {
+ return getImmediateParentPointer().hashCode() + index;
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ if (object == this) {
+ return true;
+ }
+
+ if (!(object instanceof NullElementPointer)) {
+ return false;
+ }
+
+ final NullElementPointer other = (NullElementPointer) object;
+ return getImmediateParentPointer() == other.getImmediateParentPointer()
+ && index == other.index;
+ }
+
+ @Override
+ public int getLength() {
+ return 0;
+ }
+
+ @Override
+ public String asPath() {
+ final StringBuilder buffer = new StringBuilder();
+ final NodePointer parent = getImmediateParentPointer();
+ if (parent != null) {
+ buffer.append(parent.asPath());
+ }
+ if (index != WHOLE_COLLECTION) {
+ // Address the list[1][2] case
+ if (parent != null && parent.getIndex() != WHOLE_COLLECTION) {
+ buffer.append("/.");
+ }
+ else if (parent != null
+ && parent.getImmediateParentPointer() != null
+ && parent.getImmediateParentPointer().getIndex() != WHOLE_COLLECTION) {
+ buffer.append("/.");
+ }
+ buffer.append("[").append(index + 1).append(']');
+ }
+
+ return buffer.toString();
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/NullPointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/NullPointer.java
new file mode 100644
index 00000000000..3f52cd9eed6
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/NullPointer.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.beans;
+
+import java.util.Locale;
+
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * Pointer whose value is {@code null}.
+ */
+public class NullPointer extends PropertyOwnerPointer {
+ private QName name;
+ private String id;
+
+ private static final long serialVersionUID = 2193425983220679887L;
+
+ /**
+ * Create a new NullPointer.
+ * @param name node name
+ * @param locale Locale
+ */
+ public NullPointer(final QName name, final Locale locale) {
+ super(null, locale);
+ this.name = name;
+ }
+
+ /**
+ * Used for the root node.
+ * @param parent parent pointer
+ * @param name node name
+ */
+ public NullPointer(final NodePointer parent, final QName name) {
+ super(parent);
+ this.name = name;
+ }
+
+ /**
+ * Create a new NullPointer.
+ * @param locale Locale
+ * @param id String
+ */
+ public NullPointer(final Locale locale, final String id) {
+ super(null, locale);
+ this.id = id;
+ }
+
+ @Override
+ public QName getName() {
+ return name;
+ }
+
+ @Override
+ public Object getBaseValue() {
+ return null;
+ }
+
+ @Override
+ public boolean isCollection() {
+ return false;
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return true;
+ }
+
+ @Override
+ public boolean isActual() {
+ return false;
+ }
+
+ @Override
+ public PropertyPointer getPropertyPointer() {
+ return new NullPropertyPointer(this);
+ }
+
+ @Override
+ public NodePointer createPath(final JXPathContext context, final Object value) {
+ if (parent != null) {
+ return parent.createPath(context, value).getValuePointer();
+ }
+ throw new UnsupportedOperationException(
+ "Cannot create the root object: " + asPath());
+ }
+
+ @Override
+ public NodePointer createPath(final JXPathContext context) {
+ if (parent != null) {
+ return parent.createPath(context).getValuePointer();
+ }
+ throw new UnsupportedOperationException(
+ "Cannot create the root object: " + asPath());
+ }
+
+ @Override
+ public NodePointer createChild(
+ final JXPathContext context,
+ final QName name,
+ final int index) {
+ return createPath(context).createChild(context, name, index);
+ }
+
+ @Override
+ public NodePointer createChild(
+ final JXPathContext context,
+ final QName name,
+ final int index,
+ final Object value) {
+ return createPath(context).createChild(context, name, index, value);
+ }
+
+ @Override
+ public int hashCode() {
+ return name == null ? 0 : name.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ if (object == this) {
+ return true;
+ }
+
+ if (!(object instanceof NullPointer)) {
+ return false;
+ }
+
+ final NullPointer other = (NullPointer) object;
+ return name == other.name || name != null && name.equals(other.name);
+ }
+
+ @Override
+ public String asPath() {
+ if (id != null) {
+ return "id(" + id + ")";
+ }
+ return parent == null ? "null()" : super.asPath();
+ }
+
+ @Override
+ public int getLength() {
+ return 0;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/NullPropertyPointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/NullPropertyPointer.java
new file mode 100644
index 00000000000..4f7d49dbcf4
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/NullPropertyPointer.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.beans;
+
+import org.apache.commons.jxpath.AbstractFactory;
+import org.apache.commons.jxpath.JXPathAbstractFactoryException;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.JXPathInvalidAccessException;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ */
+public class NullPropertyPointer extends PropertyPointer {
+
+ private String propertyName = "*";
+ private boolean byNameAttribute = false;
+
+ private static final long serialVersionUID = 5296593071854982754L;
+
+ /**
+ * Create a new NullPropertyPointer.
+ * @param parent pointer
+ */
+ public NullPropertyPointer(final NodePointer parent) {
+ super(parent);
+ }
+
+ @Override
+ public QName getName() {
+ return new QName(propertyName);
+ }
+
+ @Override
+ public void setPropertyIndex(final int index) {
+ }
+
+ @Override
+ public int getLength() {
+ return 0;
+ }
+
+ @Override
+ public Object getBaseValue() {
+ return null;
+ }
+
+ @Override
+ public Object getImmediateNode() {
+ return null;
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return true;
+ }
+
+ @Override
+ public NodePointer getValuePointer() {
+ return new NullPointer(this, new QName(getPropertyName()));
+ }
+
+ @Override
+ protected boolean isActualProperty() {
+ return false;
+ }
+
+ @Override
+ public boolean isActual() {
+ return false;
+ }
+
+ @Override
+ public boolean isContainer() {
+ return true;
+ }
+
+ @Override
+ public void setValue(final Object value) {
+ if (parent == null || parent.isContainer()) {
+ throw new JXPathInvalidAccessException(
+ "Cannot set property "
+ + asPath()
+ + ", the target object is null");
+ }
+ if (parent instanceof PropertyOwnerPointer
+ && ((PropertyOwnerPointer) parent)
+ .isDynamicPropertyDeclarationSupported()) {
+ // If the parent property owner can create
+ // a property automatically - let it do so
+ final PropertyPointer propertyPointer =
+ ((PropertyOwnerPointer) parent).getPropertyPointer();
+ propertyPointer.setPropertyName(propertyName);
+ propertyPointer.setValue(value);
+ }
+ else {
+ throw new JXPathInvalidAccessException(
+ "Cannot set property "
+ + asPath()
+ + ", path does not match a changeable location");
+ }
+ }
+
+ @Override
+ public NodePointer createPath(final JXPathContext context) {
+ NodePointer newParent = parent.createPath(context);
+ if (isAttribute()) {
+ return newParent.createAttribute(context, getName());
+ }
+ if (parent instanceof NullPointer && parent.equals(newParent)) {
+ throw createBadFactoryException(context.getFactory());
+ }
+ // Consider these two use cases:
+ // 1. The parent pointer of NullPropertyPointer is
+ // a PropertyOwnerPointer other than NullPointer. When we call
+ // createPath on it, it most likely returns itself. We then
+ // take a PropertyPointer from it and get the PropertyPointer
+ // to expand the collection for the corresponding property.
+ //
+ // 2. The parent pointer of NullPropertyPointer is a NullPointer.
+ // When we call createPath, it may return a PropertyOwnerPointer
+ // or it may return anything else, like a DOMNodePointer.
+ // In the former case we need to do exactly what we did in use
+ // case 1. In the latter case, we simply request that the
+ // non-property pointer expand the collection by itself.
+ if (newParent instanceof PropertyOwnerPointer) {
+ final PropertyOwnerPointer pop = (PropertyOwnerPointer) newParent;
+ newParent = pop.getPropertyPointer();
+ }
+ return newParent.createChild(context, getName(), getIndex());
+ }
+
+ @Override
+ public NodePointer createPath(final JXPathContext context, final Object value) {
+ NodePointer newParent = parent.createPath(context);
+ if (isAttribute()) {
+ final NodePointer pointer = newParent.createAttribute(context, getName());
+ pointer.setValue(value);
+ return pointer;
+ }
+ if (parent instanceof NullPointer && parent.equals(newParent)) {
+ throw createBadFactoryException(context.getFactory());
+ }
+ if (newParent instanceof PropertyOwnerPointer) {
+ final PropertyOwnerPointer pop = (PropertyOwnerPointer) newParent;
+ newParent = pop.getPropertyPointer();
+ }
+ return newParent.createChild(context, getName(), index, value);
+ }
+
+ @Override
+ public NodePointer createChild(final JXPathContext context, final QName name, final int index) {
+ return createPath(context).createChild(context, name, index);
+ }
+
+ @Override
+ public NodePointer createChild(final JXPathContext context, final QName name,
+ final int index, final Object value) {
+ return createPath(context).createChild(context, name, index, value);
+ }
+
+ @Override
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ @Override
+ public void setPropertyName(final String propertyName) {
+ this.propertyName = propertyName;
+ }
+
+ /**
+ * Sets the name attribute.
+ * @param attributeValue value to set
+ */
+ public void setNameAttributeValue(final String attributeValue) {
+ this.propertyName = attributeValue;
+ byNameAttribute = true;
+ }
+
+ @Override
+ public boolean isCollection() {
+ return getIndex() != WHOLE_COLLECTION;
+ }
+
+ @Override
+ public int getPropertyCount() {
+ return 0;
+ }
+
+ @Override
+ public String[] getPropertyNames() {
+ return new String[0];
+ }
+
+ @Override
+ public String asPath() {
+ if (!byNameAttribute) {
+ return super.asPath();
+ }
+ final StringBuilder buffer = new StringBuilder();
+ buffer.append(getImmediateParentPointer().asPath());
+ buffer.append("[@name='");
+ buffer.append(escape(getPropertyName()));
+ buffer.append("']");
+ if (index != WHOLE_COLLECTION) {
+ buffer.append('[').append(index + 1).append(']');
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Create a "bad factory" JXPathAbstractFactoryException for the specified AbstractFactory.
+ * @param factory AbstractFactory
+ * @return JXPathAbstractFactoryException
+ */
+ private JXPathAbstractFactoryException createBadFactoryException(final AbstractFactory factory) {
+ return new JXPathAbstractFactoryException("Factory " + factory
+ + " reported success creating object for path: " + asPath()
+ + " but object was null. Terminating to avoid stack recursion.");
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/PropertyIterator.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/PropertyIterator.java
new file mode 100644
index 00000000000..f0f4a580801
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/PropertyIterator.java
@@ -0,0 +1,326 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.beans;
+
+import org.apache.commons.jxpath.JXPathException;
+import org.apache.commons.jxpath.ri.model.NodeIterator;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * Iterates property values of an object pointed at with a {@link PropertyOwnerPointer}.
+ * Examples of such objects are JavaBeans and objects with Dynamic Properties.
+ */
+public class PropertyIterator implements NodeIterator {
+ private boolean empty = false;
+ private final boolean reverse;
+ private final String name;
+ private int startIndex = 0;
+ private boolean targetReady = false;
+ private int position = 0;
+ private final PropertyPointer propertyNodePointer;
+ private int startPropertyIndex;
+
+ private boolean includeStart = false;
+
+ /**
+ * Create a new PropertyIterator.
+ * @param pointer owning pointer
+ * @param name property name
+ * @param reverse iteration order
+ * @param startWith beginning pointer
+ */
+ public PropertyIterator(
+ final PropertyOwnerPointer pointer,
+ final String name,
+ final boolean reverse,
+ NodePointer startWith) {
+ propertyNodePointer =
+ (PropertyPointer) pointer.getPropertyPointer().clone();
+ this.name = name;
+ this.reverse = reverse;
+ this.includeStart = true;
+ if (reverse) {
+ this.startPropertyIndex = PropertyPointer.UNSPECIFIED_PROPERTY;
+ this.startIndex = -1;
+ }
+ if (startWith != null) {
+ while (startWith != null
+ && startWith.getImmediateParentPointer() != pointer) {
+ startWith = startWith.getImmediateParentPointer();
+ }
+ if (startWith == null) {
+ throw new JXPathException(
+ "PropertyIerator startWith parameter is "
+ + "not a child of the supplied parent");
+ }
+ this.startPropertyIndex =
+ ((PropertyPointer) startWith).getPropertyIndex();
+ this.startIndex = startWith.getIndex();
+ if (this.startIndex == NodePointer.WHOLE_COLLECTION) {
+ this.startIndex = 0;
+ }
+ this.includeStart = false;
+ if (reverse && startIndex == -1) {
+ this.includeStart = true;
+ }
+ }
+ }
+
+ /**
+ * Gets the property pointer.
+ * @return NodePointer
+ */
+ protected NodePointer getPropertyPointer() {
+ return propertyNodePointer;
+ }
+
+ /**
+ * Reset property iteration.
+ */
+ public void reset() {
+ position = 0;
+ targetReady = false;
+ }
+
+ @Override
+ public NodePointer getNodePointer() {
+ if (position == 0) {
+ if (name != null) {
+ if (!targetReady) {
+ prepareForIndividualProperty(name);
+ }
+ // If there is no such property - return null
+ if (empty) {
+ return null;
+ }
+ }
+ else {
+ if (!setPosition(1)) {
+ return null;
+ }
+ reset();
+ }
+ }
+ try {
+ return propertyNodePointer.getValuePointer();
+ }
+ catch (final Throwable t) {
+ propertyNodePointer.handle(t);
+ final NullPropertyPointer npp =
+ new NullPropertyPointer(
+ propertyNodePointer.getImmediateParentPointer());
+ npp.setPropertyName(propertyNodePointer.getPropertyName());
+ npp.setIndex(propertyNodePointer.getIndex());
+ return npp.getValuePointer();
+ }
+ }
+
+ @Override
+ public int getPosition() {
+ return position;
+ }
+
+ @Override
+ public boolean setPosition(final int position) {
+ return name == null ? setPositionAllProperties(position) : setPositionIndividualProperty(position);
+ }
+
+ /**
+ * Sets position for an individual property.
+ * @param position int position
+ * @return whether this was a valid position
+ */
+ private boolean setPositionIndividualProperty(final int position) {
+ this.position = position;
+ if (position < 1) {
+ return false;
+ }
+
+ if (!targetReady) {
+ prepareForIndividualProperty(name);
+ }
+
+ if (empty) {
+ return false;
+ }
+
+ final int length = getLength();
+ int index;
+ if (!reverse) {
+ index = position + startIndex;
+ if (!includeStart) {
+ index++;
+ }
+ if (index > length) {
+ return false;
+ }
+ }
+ else {
+ int end = startIndex;
+ if (end == -1) {
+ end = length - 1;
+ }
+ index = end - position + 2;
+ if (!includeStart) {
+ index--;
+ }
+ if (index < 1) {
+ return false;
+ }
+ }
+ propertyNodePointer.setIndex(index - 1);
+ return true;
+ }
+
+ /**
+ * Sets position for all properties
+ * @param position int position
+ * @return whether this was a valid position
+ */
+ private boolean setPositionAllProperties(final int position) {
+ this.position = position;
+ if (position < 1) {
+ return false;
+ }
+
+ int offset;
+ final int count = propertyNodePointer.getPropertyCount();
+ if (!reverse) {
+ int index = 1;
+ for (int i = startPropertyIndex; i < count; i++) {
+ propertyNodePointer.setPropertyIndex(i);
+ int length = getLength();
+ if (i == startPropertyIndex) {
+ length -= startIndex;
+ if (!includeStart) {
+ length--;
+ }
+ offset = startIndex + position - index;
+ if (!includeStart) {
+ offset++;
+ }
+ }
+ else {
+ offset = position - index;
+ }
+ if (index <= position && position < index + length) {
+ propertyNodePointer.setIndex(offset);
+ return true;
+ }
+ index += length;
+ }
+ }
+ else {
+ int index = 1;
+ int start = startPropertyIndex;
+ if (start == PropertyPointer.UNSPECIFIED_PROPERTY) {
+ start = count - 1;
+ }
+ for (int i = start; i >= 0; i--) {
+ propertyNodePointer.setPropertyIndex(i);
+ int length = getLength();
+ if (i == startPropertyIndex) {
+ int end = startIndex;
+ if (end == -1) {
+ end = length - 1;
+ }
+ length = end + 1;
+ offset = end - position + 1;
+ if (!includeStart) {
+ offset--;
+ length--;
+ }
+ }
+ else {
+ offset = length - (position - index) - 1;
+ }
+
+ if (index <= position && position < index + length) {
+ propertyNodePointer.setIndex(offset);
+ return true;
+ }
+ index += length;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Prepare for an individual property.
+ * @param name property name
+ */
+ protected void prepareForIndividualProperty(final String name) {
+ targetReady = true;
+ empty = true;
+
+ final String[] names = propertyNodePointer.getPropertyNames();
+ if (!reverse) {
+ if (startPropertyIndex == PropertyPointer.UNSPECIFIED_PROPERTY) {
+ startPropertyIndex = 0;
+ }
+ if (startIndex == NodePointer.WHOLE_COLLECTION) {
+ startIndex = 0;
+ }
+ for (int i = startPropertyIndex; i < names.length; i++) {
+ if (names[i].equals(name)) {
+ propertyNodePointer.setPropertyIndex(i);
+ if (i != startPropertyIndex) {
+ startIndex = 0;
+ includeStart = true;
+ }
+ empty = false;
+ break;
+ }
+ }
+ }
+ else {
+ if (startPropertyIndex == PropertyPointer.UNSPECIFIED_PROPERTY) {
+ startPropertyIndex = names.length - 1;
+ }
+ if (startIndex == NodePointer.WHOLE_COLLECTION) {
+ startIndex = -1;
+ }
+ for (int i = startPropertyIndex; i >= 0; i--) {
+ if (names[i].equals(name)) {
+ propertyNodePointer.setPropertyIndex(i);
+ if (i != startPropertyIndex) {
+ startIndex = -1;
+ includeStart = true;
+ }
+ empty = false;
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Computes length for the current pointer - ignores any exceptions.
+ * @return length
+ */
+ private int getLength() {
+ int length;
+ try {
+ length = propertyNodePointer.getLength(); // TBD: cache length
+ }
+ catch (final Throwable t) {
+ propertyNodePointer.handle(t);
+ length = 0;
+ }
+ return length;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/PropertyOwnerPointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/PropertyOwnerPointer.java
new file mode 100644
index 00000000000..c109ea931e4
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/PropertyOwnerPointer.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.beans;
+
+import java.util.Locale;
+
+import org.apache.commons.jxpath.JXPathInvalidAccessException;
+import org.apache.commons.jxpath.ri.Compiler;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
+import org.apache.commons.jxpath.ri.model.NodeIterator;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.util.ValueUtils;
+
+/**
+ * A pointer describing a node that has properties, each of which could be
+ * a collection.
+ */
+public abstract class PropertyOwnerPointer extends NodePointer {
+ private static final long serialVersionUID = 1L;
+
+ private static final Object UNINITIALIZED = new Object();
+
+ private Object value = UNINITIALIZED;
+
+ @Override
+ public NodeIterator childIterator(final NodeTest test, final boolean reverse,
+ final NodePointer startWith) {
+ if (test == null) {
+ return createNodeIterator(null, reverse, startWith);
+ }
+ if (test instanceof NodeNameTest) {
+ final NodeNameTest nodeNameTest = (NodeNameTest) test;
+ final QName testName = nodeNameTest.getNodeName();
+ if (isValidProperty(testName)) {
+ return createNodeIterator(nodeNameTest.isWildcard() ? null
+ : testName.toString(), reverse, startWith);
+ }
+ return null;
+ }
+ return test instanceof NodeTypeTest && ((NodeTypeTest) test).getNodeType() == Compiler.NODE_TYPE_NODE
+ ? createNodeIterator(null, reverse, startWith) : null;
+ }
+
+ /**
+ * Create a NodeIterator.
+ * @param property property name
+ * @param reverse whether to iterate in reverse
+ * @param startWith first pointer to return
+ * @return NodeIterator
+ */
+ public NodeIterator createNodeIterator(final String property, final boolean reverse,
+ final NodePointer startWith) {
+ return new PropertyIterator(this, property, reverse, startWith);
+ }
+
+ @Override
+ public NodeIterator attributeIterator(final QName name) {
+ return new BeanAttributeIterator(this, name);
+ }
+
+ /**
+ * Create a new PropertyOwnerPointer.
+ * @param parent parent pointer
+ * @param locale Locale
+ */
+ protected PropertyOwnerPointer(final NodePointer parent, final Locale locale) {
+ super(parent, locale);
+ }
+
+ /**
+ * Create a new PropertyOwnerPointer.
+ * @param parent pointer
+ */
+ protected PropertyOwnerPointer(final NodePointer parent) {
+ super(parent);
+ }
+
+ @Override
+ public void setIndex(final int index) {
+ if (this.index != index) {
+ super.setIndex(index);
+ value = UNINITIALIZED;
+ }
+ }
+
+ @Override
+ public Object getImmediateNode() {
+ if (value == UNINITIALIZED) {
+ value = index == WHOLE_COLLECTION ? ValueUtils.getValue(getBaseValue())
+ : ValueUtils.getValue(getBaseValue(), index);
+ }
+ return value;
+ }
+
+ @Override
+ public abstract QName getName();
+
+ /**
+ * Learn whether {@code name} is a valid child name for this PropertyOwnerPointer.
+ * @param name the QName to test
+ * @return {@code true} if {@code QName} is a valid property name.
+ * @since JXPath 1.3
+ */
+ public boolean isValidProperty(final QName name) {
+ return isDefaultNamespace(name.getPrefix());
+ }
+
+ /**
+ * Throws an exception if you try to change the root element, otherwise
+ * forwards the call to the parent pointer.
+ * @param value to set
+ */
+ @Override
+ public void setValue(final Object value) {
+ this.value = value;
+ if (parent != null) {
+ if (parent.isContainer()) {
+ parent.setValue(value);
+ }
+ else {
+ if (index == WHOLE_COLLECTION) {
+ throw new UnsupportedOperationException(
+ "Cannot setValue of an object that is not "
+ + "some other object's property");
+ }
+ throw new JXPathInvalidAccessException(
+ "The specified collection element does not exist: " + this);
+ }
+ }
+ else {
+ throw new UnsupportedOperationException(
+ "Cannot replace the root object");
+ }
+ }
+
+ /**
+ * If this is a root node pointer, throws an exception; otherwise
+ * forwards the call to the parent node.
+ */
+ @Override
+ public void remove() {
+ this.value = null;
+ if (parent != null) {
+ parent.remove();
+ }
+ else {
+ throw new UnsupportedOperationException(
+ "Cannot remove an object that is not "
+ + "some other object's property or a collection element");
+ }
+ }
+
+ /**
+ * Gets a PropertyPointer for this PropertyOwnerPointer.
+ * @return PropertyPointer
+ */
+ public abstract PropertyPointer getPropertyPointer();
+
+ /**
+ * Learn whether dynamic property declaration is supported.
+ * @return true if the property owner can set a property "does not exist".
+ * A good example is a Map. You can always assign a value to any
+ * key even if it has never been "declared".
+ */
+ public boolean isDynamicPropertyDeclarationSupported() {
+ return false;
+ }
+
+ @Override
+ public int compareChildNodePointers(final NodePointer pointer1,
+ final NodePointer pointer2) {
+ final int r = pointer1.getName().toString().compareTo(pointer2.getName().toString());
+ return r == 0 ? pointer1.getIndex() - pointer2.getIndex() : r;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/PropertyPointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/PropertyPointer.java
new file mode 100644
index 00000000000..4c5f0f718f1
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/beans/PropertyPointer.java
@@ -0,0 +1,268 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.beans;
+
+import org.apache.commons.jxpath.AbstractFactory;
+import org.apache.commons.jxpath.JXPathAbstractFactoryException;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.JXPathIntrospector;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.util.ValueUtils;
+
+/**
+ * A pointer allocated by a PropertyOwnerPointer to represent the value of
+ * a property of the parent object.
+ */
+public abstract class PropertyPointer extends NodePointer {
+ private static final long serialVersionUID = 1L;
+
+ public static final int UNSPECIFIED_PROPERTY = Integer.MIN_VALUE;
+
+ /** Property index */
+ protected int propertyIndex = UNSPECIFIED_PROPERTY;
+
+ /** Owning object */
+ protected Object bean;
+
+ /**
+ * Takes a javabean, a descriptor of a property of that object and
+ * an offset within that property (starting with 0).
+ * @param parent parent pointer
+ */
+ public PropertyPointer(final NodePointer parent) {
+ super(parent);
+ }
+
+ /**
+ * Gets the property index.
+ * @return int index
+ */
+ public int getPropertyIndex() {
+ return propertyIndex;
+ }
+
+ /**
+ * Sets the property index.
+ * @param index property index
+ */
+ public void setPropertyIndex(final int index) {
+ if (propertyIndex != index) {
+ propertyIndex = index;
+ setIndex(WHOLE_COLLECTION);
+ }
+ }
+
+ /**
+ * Gets the parent bean.
+ * @return Object
+ */
+ public Object getBean() {
+ if (bean == null) {
+ bean = getImmediateParentPointer().getNode();
+ }
+ return bean;
+ }
+
+ @Override
+ public QName getName() {
+ return new QName(null, getPropertyName());
+ }
+
+ /**
+ * Gets the property name.
+ * @return String property name.
+ */
+ public abstract String getPropertyName();
+
+ /**
+ * Sets the property name.
+ * @param propertyName property name to set.
+ */
+ public abstract void setPropertyName(String propertyName);
+
+ /**
+ * Count the number of properties represented.
+ * @return int
+ */
+ public abstract int getPropertyCount();
+
+ /**
+ * Gets the names of the included properties.
+ * @return String[]
+ */
+ public abstract String[] getPropertyNames();
+
+ /**
+ * Learn whether this pointer references an actual property.
+ * @return true if actual
+ */
+ protected abstract boolean isActualProperty();
+
+ @Override
+ public boolean isActual() {
+ if (!isActualProperty()) {
+ return false;
+ }
+
+ return super.isActual();
+ }
+
+ private static final Object UNINITIALIZED = new Object();
+
+ private Object value = UNINITIALIZED;
+
+ @Override
+ public Object getImmediateNode() {
+ if (value == UNINITIALIZED) {
+ value = index == WHOLE_COLLECTION ? ValueUtils.getValue(getBaseValue())
+ : ValueUtils.getValue(getBaseValue(), index);
+ }
+ return value;
+ }
+
+ @Override
+ public boolean isCollection() {
+ final Object value = getBaseValue();
+ return value != null && ValueUtils.isCollection(value);
+ }
+
+ @Override
+ public boolean isLeaf() {
+ final Object value = getNode();
+ return value == null || JXPathIntrospector.getBeanInfo(value.getClass()).isAtomic();
+ }
+
+ /**
+ * If the property contains a collection, then the length of that
+ * collection, otherwise - 1.
+ * @return int length
+ */
+ @Override
+ public int getLength() {
+ final Object baseValue = getBaseValue();
+ return baseValue == null ? 1 : ValueUtils.getLength(baseValue);
+ }
+
+ /**
+ * Returns a NodePointer that can be used to access the currently
+ * selected property value.
+ * @return NodePointer
+ */
+ @Override
+ public NodePointer getImmediateValuePointer() {
+ return newChildNodePointer(
+ (NodePointer) clone(),
+ getName(),
+ getImmediateNode());
+ }
+
+ @Override
+ public NodePointer createPath(final JXPathContext context) {
+ if (getImmediateNode() == null) {
+ final AbstractFactory factory = getAbstractFactory(context);
+ final int inx = index == WHOLE_COLLECTION ? 0 : index;
+ final boolean success =
+ factory.createObject(
+ context,
+ this,
+ getBean(),
+ getPropertyName(),
+ inx);
+ if (!success) {
+ throw new JXPathAbstractFactoryException("Factory " + factory
+ + " could not create an object for path: " + asPath());
+ }
+ }
+ return this;
+ }
+
+ @Override
+ public NodePointer createPath(final JXPathContext context, final Object value) {
+ // If neccessary, expand collection
+ if (index != WHOLE_COLLECTION && index >= getLength()) {
+ createPath(context);
+ }
+ setValue(value);
+ return this;
+ }
+
+ @Override
+ public NodePointer createChild(
+ final JXPathContext context,
+ final QName name,
+ final int index,
+ final Object value) {
+ final PropertyPointer prop = (PropertyPointer) clone();
+ if (name != null) {
+ prop.setPropertyName(name.toString());
+ }
+ prop.setIndex(index);
+ return prop.createPath(context, value);
+ }
+
+ @Override
+ public NodePointer createChild(
+ final JXPathContext context,
+ final QName name,
+ final int index) {
+ final PropertyPointer prop = (PropertyPointer) clone();
+ if (name != null) {
+ prop.setPropertyName(name.toString());
+ }
+ prop.setIndex(index);
+ return prop.createPath(context);
+ }
+
+ @Override
+ public int hashCode() {
+ return getImmediateParentPointer().hashCode() + propertyIndex + index;
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ if (object == this) {
+ return true;
+ }
+
+ if (!(object instanceof PropertyPointer)) {
+ return false;
+ }
+
+ final PropertyPointer other = (PropertyPointer) object;
+ if (parent != other.parent && (parent == null || !parent.equals(other.parent))) {
+ return false;
+ }
+
+ if (getPropertyIndex() != other.getPropertyIndex()
+ || !getPropertyName().equals(other.getPropertyName())) {
+ return false;
+ }
+
+ final int iThis = index == WHOLE_COLLECTION ? 0 : index;
+ final int iOther = other.index == WHOLE_COLLECTION ? 0 : other.index;
+ return iThis == iOther;
+ }
+
+ @Override
+ public int compareChildNodePointers(
+ final NodePointer pointer1,
+ final NodePointer pointer2) {
+ return getValuePointer().compareChildNodePointers(pointer1, pointer2);
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/container/ContainerPointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/container/ContainerPointer.java
new file mode 100644
index 00000000000..5795cbb6642
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/container/ContainerPointer.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.container;
+
+import java.util.Locale;
+
+import org.apache.commons.jxpath.Container;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.model.NodeIterator;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.util.ValueUtils;
+
+/**
+ * Transparent pointer to a Container. The {@link #getValue()} method
+ * returns the contents of the container, rather than the container
+ * itself.
+ */
+public class ContainerPointer extends NodePointer {
+ private final Container container;
+ private NodePointer valuePointer;
+
+ private static final long serialVersionUID = 6140752946621686118L;
+
+ /**
+ * Create a new ContainerPointer.
+ * @param container Container object
+ * @param locale Locale
+ */
+ public ContainerPointer(final Container container, final Locale locale) {
+ super(null, locale);
+ this.container = container;
+ }
+
+ /**
+ * Create a new ContainerPointer.
+ * @param parent parent pointer
+ * @param container Container object
+ */
+ public ContainerPointer(final NodePointer parent, final Container container) {
+ super(parent);
+ this.container = container;
+ }
+
+ /**
+ * This type of node is auxiliary.
+ * @return {@code true}.
+ */
+ @Override
+ public boolean isContainer() {
+ return true;
+ }
+
+ @Override
+ public QName getName() {
+ return null;
+ }
+
+ @Override
+ public Object getBaseValue() {
+ return container;
+ }
+
+ @Override
+ public boolean isCollection() {
+ final Object value = getBaseValue();
+ return value != null && ValueUtils.isCollection(value);
+ }
+
+ @Override
+ public int getLength() {
+ final Object value = getBaseValue();
+ return value == null ? 1 : ValueUtils.getLength(value);
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return getValuePointer().isLeaf();
+ }
+
+ @Override
+ public Object getImmediateNode() {
+ final Object value = getBaseValue();
+ if (index != WHOLE_COLLECTION) {
+ return index >= 0 && index < getLength() ? ValueUtils.getValue(value, index) : null;
+ }
+ return ValueUtils.getValue(value);
+ }
+
+ @Override
+ public void setValue(final Object value) {
+ // TODO: what if this is a collection?
+ container.setValue(value);
+ }
+
+ @Override
+ public NodePointer getImmediateValuePointer() {
+ if (valuePointer == null) {
+ final Object value = getImmediateNode();
+ valuePointer = newChildNodePointer(this, getName(), value);
+ }
+ return valuePointer;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(container) + index;
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ if (object == this) {
+ return true;
+ }
+
+ if (!(object instanceof ContainerPointer)) {
+ return false;
+ }
+
+ final ContainerPointer other = (ContainerPointer) object;
+ return container == other.container && index == other.index;
+ }
+
+ @Override
+ public NodeIterator childIterator(
+ final NodeTest test,
+ final boolean reverse,
+ final NodePointer startWith) {
+ return getValuePointer().childIterator(test, reverse, startWith);
+ }
+
+ @Override
+ public NodeIterator attributeIterator(final QName name) {
+ return getValuePointer().attributeIterator(name);
+ }
+
+ @Override
+ public NodeIterator namespaceIterator() {
+ return getValuePointer().namespaceIterator();
+ }
+
+ @Override
+ public NodePointer namespacePointer(final String namespace) {
+ return getValuePointer().namespacePointer(namespace);
+ }
+
+ @Override
+ public boolean testNode(final NodeTest nodeTest) {
+ return getValuePointer().testNode(nodeTest);
+ }
+
+ @Override
+ public int compareChildNodePointers(
+ final NodePointer pointer1,
+ final NodePointer pointer2) {
+ return pointer1.getIndex() - pointer2.getIndex();
+ }
+
+ @Override
+ public String getNamespaceURI(final String prefix) {
+ return getValuePointer().getNamespaceURI(prefix);
+ }
+
+ @Override
+ public String asPath() {
+ return parent == null ? "/" : parent.asPath();
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/container/ContainerPointerFactory.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/container/ContainerPointerFactory.java
new file mode 100644
index 00000000000..ff72ed48102
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/container/ContainerPointerFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.container;
+
+import java.util.Locale;
+
+import org.apache.commons.jxpath.Container;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.ri.model.NodePointerFactory;
+
+/**
+ * Implements NodePointerFactory for {@link Container} objects.
+ */
+public class ContainerPointerFactory implements NodePointerFactory {
+ /** Factory order for this factory */
+ public static final int CONTAINER_POINTER_FACTORY_ORDER = 200;
+
+ @Override
+ public int getOrder() {
+ return CONTAINER_POINTER_FACTORY_ORDER;
+ }
+
+ @Override
+ public NodePointer createNodePointer(final QName name, final Object bean, final Locale locale) {
+ return bean instanceof Container ? new ContainerPointer(
+ (Container) bean, locale) : null;
+ }
+
+ @Override
+ public NodePointer createNodePointer(final NodePointer parent, final QName name,
+ final Object bean) {
+ return bean instanceof Container ? new ContainerPointer(parent,
+ (Container) bean) : null;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dom/DOMAttributeIterator.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dom/DOMAttributeIterator.java
new file mode 100644
index 00000000000..ae3ad3a0434
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dom/DOMAttributeIterator.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.dom;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.model.NodeIterator;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/**
+ * An iterator of attributes of a DOM Node.
+ */
+public class DOMAttributeIterator implements NodeIterator {
+ private final NodePointer parent;
+ private final QName name;
+ private final List attributes;
+ private int position = 0;
+
+ /**
+ * Create a new DOMAttributeIterator.
+ * @param parent pointer
+ * @param name to test
+ */
+ public DOMAttributeIterator(final NodePointer parent, final QName name) {
+ this.parent = parent;
+ this.name = name;
+ attributes = new ArrayList();
+ final Node node = (Node) parent.getNode();
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ final String lname = name.getName();
+ if (!lname.equals("*")) {
+ final Attr attr = getAttribute((Element) node, name);
+ if (attr != null) {
+ attributes.add(attr);
+ }
+ }
+ else {
+ final NamedNodeMap map = node.getAttributes();
+ final int count = map.getLength();
+ for (int i = 0; i < count; i++) {
+ final Attr attr = (Attr) map.item(i);
+ if (testAttr(attr)) {
+ attributes.add(attr);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Test an attribute.
+ * @param attr to test
+ * @return whether test succeeded
+ */
+ private boolean testAttr(final Attr attr) {
+ final String nodePrefix = DOMNodePointer.getPrefix(attr);
+ final String nodeLocalName = DOMNodePointer.getLocalName(attr);
+
+ if (nodePrefix != null && nodePrefix.equals("xmlns")) {
+ return false;
+ }
+
+ if (nodePrefix == null && nodeLocalName.equals("xmlns")) {
+ return false;
+ }
+
+ final String testLocalName = name.getName();
+ if (testLocalName.equals("*") || testLocalName.equals(nodeLocalName)) {
+ final String testPrefix = name.getPrefix();
+
+ if (testPrefix == null || Objects.equals(testPrefix, nodePrefix)) {
+ return true;
+ }
+ if (nodePrefix == null) {
+ return false;
+ }
+ return Objects.equals(parent.getNamespaceURI(testPrefix), parent
+ .getNamespaceURI(nodePrefix));
+ }
+ return false;
+ }
+
+ /**
+ * Gets the named attribute.
+ * @param element to search
+ * @param name to match
+ * @return Attr found
+ */
+ private Attr getAttribute(final Element element, final QName name) {
+ final String testPrefix = name.getPrefix();
+ String testNS = null;
+
+ if (testPrefix != null) {
+ testNS = parent.getNamespaceResolver().getNamespaceURI(testPrefix);
+ }
+
+ if (testNS != null) {
+ Attr attr = element.getAttributeNodeNS(testNS, name.getName());
+ if (attr != null) {
+ return attr;
+ }
+
+ // This may mean that the parser does not support NS for
+ // attributes, example - the version of Crimson bundled
+ // with JDK 1.4.0
+ final NamedNodeMap nnm = element.getAttributes();
+ for (int i = 0; i < nnm.getLength(); i++) {
+ attr = (Attr) nnm.item(i);
+ if (testAttr(attr)) {
+ return attr;
+ }
+ }
+ return null;
+ }
+ return element.getAttributeNode(name.getName());
+ }
+
+ @Override
+ public NodePointer getNodePointer() {
+ if (position == 0) {
+ if (!setPosition(1)) {
+ return null;
+ }
+ position = 0;
+ }
+ int index = position - 1;
+ if (index < 0) {
+ index = 0;
+ }
+ return new DOMAttributePointer(parent, (Attr) attributes.get(index));
+ }
+
+ @Override
+ public int getPosition() {
+ return position;
+ }
+
+ @Override
+ public boolean setPosition(final int position) {
+ this.position = position;
+ return position >= 1 && position <= attributes.size();
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dom/DOMAttributePointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dom/DOMAttributePointer.java
new file mode 100644
index 00000000000..90ac734a3ab
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dom/DOMAttributePointer.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.dom;
+
+import org.apache.commons.jxpath.ri.Compiler;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.util.TypeUtils;
+import org.w3c.dom.Attr;
+
+/**
+ * A Pointer that points to a DOM node. Because the underlying DOM Attr is not Serializable,
+ * neither is this pointer class truly so.
+ */
+public class DOMAttributePointer extends NodePointer {
+ private static final long serialVersionUID = 1115085175427555951L;
+
+ private final Attr attr;
+
+ /**
+ * Create a new DOMAttributePointer.
+ * @param parent pointer
+ * @param attr pointed
+ */
+ public DOMAttributePointer(final NodePointer parent, final Attr attr) {
+ super(parent);
+ this.attr = attr;
+ }
+
+ @Override
+ public QName getName() {
+ return new QName(
+ DOMNodePointer.getPrefix(attr),
+ DOMNodePointer.getLocalName(attr));
+ }
+
+ @Override
+ public String getNamespaceURI() {
+ final String prefix = DOMNodePointer.getPrefix(attr);
+ return prefix == null ? null : parent.getNamespaceURI(prefix);
+ }
+
+ @Override
+ public Object getValue() {
+ final String value = attr.getValue();
+ if (value == null || value.isEmpty() && !attr.getSpecified()) {
+ return null;
+ }
+ return value;
+ }
+
+ @Override
+ public Object getBaseValue() {
+ return attr;
+ }
+
+ @Override
+ public boolean isCollection() {
+ return false;
+ }
+
+ @Override
+ public int getLength() {
+ return 1;
+ }
+
+ @Override
+ public Object getImmediateNode() {
+ return attr;
+ }
+
+ @Override
+ public boolean isActual() {
+ return true;
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return true;
+ }
+
+ @Override
+ public boolean testNode(final NodeTest nodeTest) {
+ return nodeTest == null
+ || nodeTest instanceof NodeTypeTest
+ && ((NodeTypeTest) nodeTest).getNodeType() == Compiler.NODE_TYPE_NODE;
+ }
+
+ /**
+ * Sets the value of this attribute.
+ * @param value to set
+ */
+ @Override
+ public void setValue(final Object value) {
+ attr.setValue((String) TypeUtils.convert(value, String.class));
+ }
+
+ @Override
+ public void remove() {
+ attr.getOwnerElement().removeAttributeNode(attr);
+ }
+
+ @Override
+ public String asPath() {
+ final StringBuilder buffer = new StringBuilder();
+ if (parent != null) {
+ buffer.append(parent.asPath());
+ if (buffer.length() == 0
+ || buffer.charAt(buffer.length() - 1) != '/') {
+ buffer.append('/');
+ }
+ }
+ buffer.append('@');
+ buffer.append(getName());
+ return buffer.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(attr);
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ return object == this || object instanceof DOMAttributePointer
+ && attr == ((DOMAttributePointer) object).attr;
+ }
+
+ @Override
+ public int compareChildNodePointers(final NodePointer pointer1,
+ final NodePointer pointer2) {
+ // Won't happen - attributes don't have children
+ return 0;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dom/DOMNamespaceIterator.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dom/DOMNamespaceIterator.java
new file mode 100644
index 00000000000..96febca3804
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dom/DOMNamespaceIterator.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.dom;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.jxpath.ri.model.NodeIterator;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/**
+ * An iterator of namespaces of a DOM Node.
+ */
+public class DOMNamespaceIterator implements NodeIterator {
+ private final NodePointer parent;
+ private final List attributes;
+ private int position = 0;
+
+ /**
+ * Create a new DOMNamespaceIterator.
+ * @param parent parent pointer
+ */
+ public DOMNamespaceIterator(final NodePointer parent) {
+ this.parent = parent;
+ attributes = new ArrayList();
+ collectNamespaces(attributes, (Node) parent.getNode());
+ }
+
+ /**
+ * Collect namespaces from attribute nodes.
+ * @param attributes attribute list
+ * @param node target node
+ */
+ private void collectNamespaces(final List attributes, Node node) {
+ final Node parent = node.getParentNode();
+ if (parent != null) {
+ collectNamespaces(attributes, parent);
+ }
+ if (node.getNodeType() == Node.DOCUMENT_NODE) {
+ node = ((Document) node).getDocumentElement();
+ }
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ final NamedNodeMap map = node.getAttributes();
+ final int count = map.getLength();
+ for (int i = 0; i < count; i++) {
+ final Attr attr = (Attr) map.item(i);
+ final String prefix = DOMNodePointer.getPrefix(attr);
+ final String name = DOMNodePointer.getLocalName(attr);
+ if (prefix != null && prefix.equals("xmlns")
+ || prefix == null && name.equals("xmlns")) {
+ attributes.add(attr);
+ }
+ }
+ }
+ }
+
+ @Override
+ public NodePointer getNodePointer() {
+ if (position == 0) {
+ if (!setPosition(1)) {
+ return null;
+ }
+ position = 0;
+ }
+ int index = position - 1;
+ if (index < 0) {
+ index = 0;
+ }
+ String prefix = "";
+ final Attr attr = (Attr) attributes.get(index);
+ final String name = attr.getPrefix();
+ if (name != null && name.equals("xmlns")) {
+ prefix = DOMNodePointer.getLocalName(attr);
+ }
+ return new NamespacePointer(parent, prefix, attr.getValue());
+ }
+
+ @Override
+ public int getPosition() {
+ return position;
+ }
+
+ @Override
+ public boolean setPosition(final int position) {
+ this.position = position;
+ return position >= 1 && position <= attributes.size();
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dom/DOMNodeIterator.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dom/DOMNodeIterator.java
new file mode 100644
index 00000000000..6c527119b3e
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dom/DOMNodeIterator.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.dom;
+
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.model.NodeIterator;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.w3c.dom.Node;
+
+/**
+ * An iterator of children of a DOM Node.
+ */
+public class DOMNodeIterator implements NodeIterator {
+ private final NodePointer parent;
+ private final NodeTest nodeTest;
+ private final Node node;
+ private Node child = null;
+ private final boolean reverse;
+ private int position = 0;
+
+ /**
+ * Create a new DOMNodeIterator.
+ * @param parent parent pointer
+ * @param nodeTest test
+ * @param reverse whether to iterate in reverse
+ * @param startWith starting pointer
+ */
+ public DOMNodeIterator(
+ final NodePointer parent,
+ final NodeTest nodeTest,
+ final boolean reverse,
+ final NodePointer startWith) {
+ this.parent = parent;
+ this.node = (Node) parent.getNode();
+ if (startWith != null) {
+ this.child = (Node) startWith.getNode();
+ }
+ this.nodeTest = nodeTest;
+ this.reverse = reverse;
+ }
+
+ @Override
+ public NodePointer getNodePointer() {
+ if (position == 0) {
+ setPosition(1);
+ }
+ return child == null ? null : new DOMNodePointer(parent, child);
+ }
+
+ @Override
+ public int getPosition() {
+ return position;
+ }
+
+ @Override
+ public boolean setPosition(final int position) {
+ while (this.position < position) {
+ if (!next()) {
+ return false;
+ }
+ }
+ while (this.position > position) {
+ if (!previous()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Sets the previous position.
+ * @return whether valid
+ */
+ private boolean previous() {
+ position--;
+ if (!reverse) {
+ if (position == 0) {
+ child = null;
+ }
+ else if (child == null) {
+ child = node.getLastChild();
+ }
+ else {
+ child = child.getPreviousSibling();
+ }
+ while (child != null && !testChild()) {
+ child = child.getPreviousSibling();
+ }
+ }
+ else {
+ child = child.getNextSibling();
+ while (child != null && !testChild()) {
+ child = child.getNextSibling();
+ }
+ }
+ return child != null;
+ }
+
+ /**
+ * Sets the next position.
+ * @return whether valid
+ */
+ private boolean next() {
+ position++;
+ if (!reverse) {
+ if (position == 1 && child == null) {
+ child = node.getFirstChild();
+ }
+ else {
+ child = child.getNextSibling();
+ }
+ while (child != null && !testChild()) {
+ child = child.getNextSibling();
+ }
+ }
+ else {
+ if (position == 1 && child == null) {
+ child = node.getLastChild();
+ }
+ else {
+ child = child.getPreviousSibling();
+ }
+ while (child != null && !testChild()) {
+ child = child.getPreviousSibling();
+ }
+ }
+ return child != null;
+ }
+
+ /**
+ * Test child.
+ * @return result of the test
+ */
+ private boolean testChild() {
+ return DOMNodePointer.testNode(child, nodeTest);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dom/DOMNodePointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dom/DOMNodePointer.java
new file mode 100644
index 00000000000..098d2da818a
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dom/DOMNodePointer.java
@@ -0,0 +1,804 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.dom;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.commons.jxpath.JXPathAbstractFactoryException;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.JXPathException;
+import org.apache.commons.jxpath.Pointer;
+import org.apache.commons.jxpath.ri.Compiler;
+import org.apache.commons.jxpath.ri.NamespaceResolver;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
+import org.apache.commons.jxpath.ri.compiler.ProcessingInstructionTest;
+import org.apache.commons.jxpath.ri.model.NodeIterator;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.ri.model.beans.NullPointer;
+import org.apache.commons.jxpath.util.TypeUtils;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.ProcessingInstruction;
+
+/**
+ * A Pointer that points to a DOM node. Because a DOM Node is not guaranteed Serializable,
+ * a DOMNodePointer instance may likewise not be properly Serializable.
+ */
+public class DOMNodePointer extends NodePointer {
+
+ private static final long serialVersionUID = -8751046933894857319L;
+
+ private final Node node;
+ private Map namespaces;
+ private String defaultNamespace;
+ private String id;
+ private NamespaceResolver localNamespaceResolver;
+
+ /** XML namespace URI */
+ public static final String XML_NAMESPACE_URI =
+ "http://www.w3.org/XML/1998/namespace";
+
+ /** XMLNS namespace URI */
+ public static final String XMLNS_NAMESPACE_URI =
+ "http://www.w3.org/2000/xmlns/";
+
+ /**
+ * Create a new DOMNodePointer.
+ * @param node pointed at
+ * @param locale Locale
+ * @param id string id
+ */
+ public DOMNodePointer(final Node node, final Locale locale, final String id) {
+ super(null, locale);
+ this.node = node;
+ this.id = id;
+ }
+
+ /**
+ * Create a new DOMNodePointer.
+ * @param parent pointer
+ * @param node pointed
+ */
+ public DOMNodePointer(final NodePointer parent, final Node node) {
+ super(parent);
+ this.node = node;
+ }
+
+ @Override
+ public boolean testNode(final NodeTest test) {
+ return testNode(node, test);
+ }
+
+ /**
+ * Test a Node.
+ * @param node to test
+ * @param test to execute
+ * @return true if node passes test
+ */
+ public static boolean testNode(final Node node, final NodeTest test) {
+ if (test == null) {
+ return true;
+ }
+ if (test instanceof NodeNameTest) {
+ if (node.getNodeType() != Node.ELEMENT_NODE) {
+ return false;
+ }
+
+ final NodeNameTest nodeNameTest = (NodeNameTest) test;
+ final QName testName = nodeNameTest.getNodeName();
+ final String namespaceURI = nodeNameTest.getNamespaceURI();
+ final boolean wildcard = nodeNameTest.isWildcard();
+ final String testPrefix = testName.getPrefix();
+ if (wildcard && testPrefix == null) {
+ return true;
+ }
+ if (wildcard
+ || testName.getName()
+ .equals(getLocalName(node))) {
+ final String nodeNS = getNamespaceURI(node);
+ return equalStrings(namespaceURI, nodeNS) || nodeNS == null
+ && equalStrings(testPrefix, getPrefix(node));
+ }
+ return false;
+ }
+ if (test instanceof NodeTypeTest) {
+ final int nodeType = node.getNodeType();
+ switch (((NodeTypeTest) test).getNodeType()) {
+ case Compiler.NODE_TYPE_NODE :
+ return true;
+ case Compiler.NODE_TYPE_TEXT :
+ return nodeType == Node.CDATA_SECTION_NODE
+ || nodeType == Node.TEXT_NODE;
+ case Compiler.NODE_TYPE_COMMENT :
+ return nodeType == Node.COMMENT_NODE;
+ case Compiler.NODE_TYPE_PI :
+ return nodeType == Node.PROCESSING_INSTRUCTION_NODE;
+ default:
+ return false;
+ }
+ }
+ if (test instanceof ProcessingInstructionTest
+ && node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
+ final String testPI = ((ProcessingInstructionTest) test).getTarget();
+ final String nodePI = ((ProcessingInstruction) node).getTarget();
+ return testPI.equals(nodePI);
+ }
+ return false;
+ }
+
+ /**
+ * Test string equality.
+ * @param s1 String 1
+ * @param s2 String 2
+ * @return true if == or .equals()
+ */
+ private static boolean equalStrings(String s1, String s2) {
+ if (s1 == s2) {
+ return true;
+ }
+ s1 = s1 == null ? "" : s1.trim();
+ s2 = s2 == null ? "" : s2.trim();
+ return s1.equals(s2);
+ }
+
+ @Override
+ public QName getName() {
+ String ln = null;
+ String ns = null;
+ final int type = node.getNodeType();
+ if (type == Node.ELEMENT_NODE) {
+ ns = getPrefix(node);
+ ln = getLocalName(node);
+ }
+ else if (type == Node.PROCESSING_INSTRUCTION_NODE) {
+ ln = ((ProcessingInstruction) node).getTarget();
+ }
+ return new QName(ns, ln);
+ }
+
+ @Override
+ public String getNamespaceURI() {
+ return getNamespaceURI(node);
+ }
+
+ @Override
+ public NodeIterator childIterator(final NodeTest test, final boolean reverse,
+ final NodePointer startWith) {
+ return new DOMNodeIterator(this, test, reverse, startWith);
+ }
+
+ @Override
+ public NodeIterator attributeIterator(final QName name) {
+ return new DOMAttributeIterator(this, name);
+ }
+
+ @Override
+ public NodePointer namespacePointer(final String prefix) {
+ return new NamespacePointer(this, prefix);
+ }
+
+ @Override
+ public NodeIterator namespaceIterator() {
+ return new DOMNamespaceIterator(this);
+ }
+
+ @Override
+ public synchronized NamespaceResolver getNamespaceResolver() {
+ if (localNamespaceResolver == null) {
+ localNamespaceResolver = new NamespaceResolver(super.getNamespaceResolver());
+ localNamespaceResolver.setNamespaceContextPointer(this);
+ }
+ return localNamespaceResolver;
+ }
+
+ @Override
+ public String getNamespaceURI(final String prefix) {
+ if (prefix == null || prefix.isEmpty()) {
+ return getDefaultNamespaceURI();
+ }
+
+ if (prefix.equals("xml")) {
+ return XML_NAMESPACE_URI;
+ }
+
+ if (prefix.equals("xmlns")) {
+ return XMLNS_NAMESPACE_URI;
+ }
+
+ String namespace = null;
+ if (namespaces == null) {
+ namespaces = new HashMap();
+ }
+ else {
+ namespace = (String) namespaces.get(prefix);
+ }
+
+ if (namespace == null) {
+ final String qname = "xmlns:" + prefix;
+ Node aNode = node;
+ if (aNode instanceof Document) {
+ aNode = ((Document) aNode).getDocumentElement();
+ }
+ while (aNode != null) {
+ if (aNode.getNodeType() == Node.ELEMENT_NODE) {
+ final Attr attr = ((Element) aNode).getAttributeNode(qname);
+ if (attr != null) {
+ namespace = attr.getValue();
+ break;
+ }
+ }
+ aNode = aNode.getParentNode();
+ }
+ if (namespace == null || namespace.isEmpty()) {
+ namespace = UNKNOWN_NAMESPACE;
+ }
+ }
+
+ namespaces.put(prefix, namespace);
+ if (namespace == UNKNOWN_NAMESPACE) {
+ return null;
+ }
+
+ // TBD: We are supposed to resolve relative URIs to absolute ones.
+ return namespace;
+ }
+
+ @Override
+ public String getDefaultNamespaceURI() {
+ if (defaultNamespace == null) {
+ Node aNode = node;
+ if (aNode instanceof Document) {
+ aNode = ((Document) aNode).getDocumentElement();
+ }
+ while (aNode != null) {
+ if (aNode.getNodeType() == Node.ELEMENT_NODE) {
+ final Attr attr = ((Element) aNode).getAttributeNode("xmlns");
+ if (attr != null) {
+ defaultNamespace = attr.getValue();
+ break;
+ }
+ }
+ aNode = aNode.getParentNode();
+ }
+ }
+ if (defaultNamespace == null) {
+ defaultNamespace = "";
+ }
+ // TBD: We are supposed to resolve relative URIs to absolute ones.
+ return defaultNamespace.isEmpty() ? null : defaultNamespace;
+ }
+
+ @Override
+ public Object getBaseValue() {
+ return node;
+ }
+
+ @Override
+ public Object getImmediateNode() {
+ return node;
+ }
+
+ @Override
+ public boolean isActual() {
+ return true;
+ }
+
+ @Override
+ public boolean isCollection() {
+ return false;
+ }
+
+ @Override
+ public int getLength() {
+ return 1;
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return !node.hasChildNodes();
+ }
+
+ /**
+ * Returns true if the xml:lang attribute for the current node
+ * or its parent has the specified prefix lang.
+ * If no node has this prefix, calls {@code super.isLanguage(lang)}.
+ * @param lang ns to test
+ * @return boolean
+ */
+ @Override
+ public boolean isLanguage(final String lang) {
+ final String current = getLanguage();
+ return current == null ? super.isLanguage(lang)
+ : current.toUpperCase(Locale.ENGLISH).startsWith(lang.toUpperCase(Locale.ENGLISH));
+ }
+
+ /**
+ * Find the nearest occurrence of the specified attribute
+ * on the specified and enclosing elements.
+ * @param n current node
+ * @param attrName attribute name
+ * @return attribute value
+ */
+ protected static String findEnclosingAttribute(Node n, final String attrName) {
+ while (n != null) {
+ if (n.getNodeType() == Node.ELEMENT_NODE) {
+ final Element e = (Element) n;
+ final String attr = e.getAttribute(attrName);
+ if (attr != null && !attr.isEmpty()) {
+ return attr;
+ }
+ }
+ n = n.getParentNode();
+ }
+ return null;
+ }
+
+ /**
+ * Gets the language attribute for this node.
+ * @return String language name
+ */
+ protected String getLanguage() {
+ return findEnclosingAttribute(node, "xml:lang");
+ }
+
+ /**
+ * Sets contents of the node to the specified value. If the value is
+ * a String, the contents of the node are replaced with this text.
+ * If the value is an Element or Document, the children of the
+ * node are replaced with the children of the passed node.
+ * @param value to set
+ */
+ @Override
+ public void setValue(final Object value) {
+ if (node.getNodeType() == Node.TEXT_NODE
+ || node.getNodeType() == Node.CDATA_SECTION_NODE) {
+ final String string = (String) TypeUtils.convert(value, String.class);
+ if (string != null && !string.isEmpty()) {
+ node.setNodeValue(string);
+ }
+ else {
+ node.getParentNode().removeChild(node);
+ }
+ }
+ else {
+ NodeList children = node.getChildNodes();
+ final int count = children.getLength();
+ for (int i = count; --i >= 0;) {
+ final Node child = children.item(i);
+ node.removeChild(child);
+ }
+
+ if (value instanceof Node) {
+ final Node valueNode = (Node) value;
+ if (valueNode instanceof Element
+ || valueNode instanceof Document) {
+ children = valueNode.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ final Node child = children.item(i);
+ node.appendChild(child.cloneNode(true));
+ }
+ }
+ else {
+ node.appendChild(valueNode.cloneNode(true));
+ }
+ }
+ else {
+ final String string = (String) TypeUtils.convert(value, String.class);
+ if (string != null && !string.isEmpty()) {
+ final Node textNode =
+ node.getOwnerDocument().createTextNode(string);
+ node.appendChild(textNode);
+ }
+ }
+ }
+ }
+
+ @Override
+ public NodePointer createChild(final JXPathContext context, final QName name, int index) {
+ if (index == WHOLE_COLLECTION) {
+ index = 0;
+ }
+ final boolean success =
+ getAbstractFactory(context).createObject(
+ context,
+ this,
+ node,
+ name.toString(),
+ index);
+ if (success) {
+ NodeTest nodeTest;
+ final String prefix = name.getPrefix();
+ final String namespaceURI = prefix == null ? null : context
+ .getNamespaceURI(prefix);
+ nodeTest = new NodeNameTest(name, namespaceURI);
+
+ final NodeIterator it = childIterator(nodeTest, false, null);
+ if (it != null && it.setPosition(index + 1)) {
+ return it.getNodePointer();
+ }
+ }
+ throw new JXPathAbstractFactoryException(
+ "Factory could not create a child node for path: " + asPath()
+ + "/" + name + "[" + (index + 1) + "]");
+ }
+
+ @Override
+ public NodePointer createChild(final JXPathContext context, final QName name,
+ final int index, final Object value) {
+ final NodePointer ptr = createChild(context, name, index);
+ ptr.setValue(value);
+ return ptr;
+ }
+
+ @Override
+ public NodePointer createAttribute(final JXPathContext context, final QName name) {
+ if (!(node instanceof Element)) {
+ return super.createAttribute(context, name);
+ }
+ final Element element = (Element) node;
+ final String prefix = name.getPrefix();
+ if (prefix != null) {
+ String ns = null;
+ final NamespaceResolver nsr = getNamespaceResolver();
+ if (nsr != null) {
+ ns = nsr.getNamespaceURI(prefix);
+ }
+ if (ns == null) {
+ throw new JXPathException(
+ "Unknown namespace prefix: " + prefix);
+ }
+ element.setAttributeNS(ns, name.toString(), "");
+ }
+ else if (!element.hasAttribute(name.getName())) {
+ element.setAttribute(name.getName(), "");
+ }
+ final NodeIterator it = attributeIterator(name);
+ it.setPosition(1);
+ return it.getNodePointer();
+ }
+
+ @Override
+ public void remove() {
+ final Node parent = node.getParentNode();
+ if (parent == null) {
+ throw new JXPathException("Cannot remove root DOM node");
+ }
+ parent.removeChild(node);
+ }
+
+ @Override
+ public String asPath() {
+ if (id != null) {
+ return "id('" + escape(id) + "')";
+ }
+
+ final StringBuilder buffer = new StringBuilder();
+ if (parent != null) {
+ buffer.append(parent.asPath());
+ }
+ switch (node.getNodeType()) {
+ case Node.ELEMENT_NODE :
+ // If the parent pointer is not a DOMNodePointer, it is
+ // the parent's responsibility to produce the node test part
+ // of the path
+ if (parent instanceof DOMNodePointer) {
+ if (buffer.length() == 0
+ || buffer.charAt(buffer.length() - 1) != '/') {
+ buffer.append('/');
+ }
+ final String ln = getLocalName(node);
+ final String nsURI = getNamespaceURI();
+ if (nsURI == null) {
+ buffer.append(ln);
+ buffer.append('[');
+ buffer.append(getRelativePositionByQName()).append(']');
+ }
+ else {
+ final String prefix = getNamespaceResolver().getPrefix(nsURI);
+ if (prefix != null) {
+ buffer.append(prefix);
+ buffer.append(':');
+ buffer.append(ln);
+ buffer.append('[');
+ buffer.append(getRelativePositionByQName());
+ }
+ else {
+ buffer.append("node()");
+ buffer.append('[');
+ buffer.append(getRelativePositionOfElement());
+ }
+ buffer.append(']');
+ }
+ }
+ break;
+ case Node.TEXT_NODE :
+ case Node.CDATA_SECTION_NODE :
+ buffer.append("/text()");
+ buffer.append('[');
+ buffer.append(getRelativePositionOfTextNode()).append(']');
+ break;
+ case Node.PROCESSING_INSTRUCTION_NODE :
+ buffer.append("/processing-instruction(\'");
+ buffer.append(((ProcessingInstruction) node).getTarget()).append("')");
+ buffer.append('[');
+ buffer.append(getRelativePositionOfPI()).append(']');
+ break;
+ case Node.DOCUMENT_NODE :
+ // That'll be empty
+ break;
+ default:
+ break;
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Gets relative position of this among like-named siblings.
+ * @return 1..n
+ */
+ private int getRelativePositionByQName() {
+ int count = 1;
+ Node n = node.getPreviousSibling();
+ while (n != null) {
+ if (n.getNodeType() == Node.ELEMENT_NODE && matchesQName(n)) {
+ count++;
+ }
+ n = n.getPreviousSibling();
+ }
+ return count;
+ }
+
+ private boolean matchesQName(final Node n) {
+ if (getNamespaceURI() != null) {
+ return equalStrings(getNamespaceURI(n), getNamespaceURI())
+ && equalStrings(node.getLocalName(), n.getLocalName());
+ }
+ return equalStrings(node.getNodeName(), n.getNodeName());
+ }
+
+ /**
+ * Gets relative position of this among all siblings.
+ * @return 1..n
+ */
+ private int getRelativePositionOfElement() {
+ int count = 1;
+ Node n = node.getPreviousSibling();
+ while (n != null) {
+ if (n.getNodeType() == Node.ELEMENT_NODE) {
+ count++;
+ }
+ n = n.getPreviousSibling();
+ }
+ return count;
+ }
+
+ /**
+ * Gets the relative position of this among sibling text nodes.
+ * @return 1..n
+ */
+ private int getRelativePositionOfTextNode() {
+ int count = 1;
+ Node n = node.getPreviousSibling();
+ while (n != null) {
+ if (n.getNodeType() == Node.TEXT_NODE
+ || n.getNodeType() == Node.CDATA_SECTION_NODE) {
+ count++;
+ }
+ n = n.getPreviousSibling();
+ }
+ return count;
+ }
+
+ /**
+ * Gets the relative position of this among same-target processing instruction siblings.
+ * @return 1..n
+ */
+ private int getRelativePositionOfPI() {
+ int count = 1;
+ final String target = ((ProcessingInstruction) node).getTarget();
+ Node n = node.getPreviousSibling();
+ while (n != null) {
+ if (n.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE
+ && ((ProcessingInstruction) n).getTarget().equals(target)) {
+ count++;
+ }
+ n = n.getPreviousSibling();
+ }
+ return count;
+ }
+
+ @Override
+ public int hashCode() {
+ return node.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ return object == this || object instanceof DOMNodePointer && node == ((DOMNodePointer) object).node;
+ }
+
+ /**
+ * Gets any prefix from the specified node.
+ * @param node the node to check
+ * @return String xml prefix
+ */
+ public static String getPrefix(final Node node) {
+ final String prefix = node.getPrefix();
+ if (prefix != null) {
+ return prefix;
+ }
+
+ final String name = node.getNodeName();
+ final int index = name.lastIndexOf(':');
+ return index < 0 ? null : name.substring(0, index);
+ }
+
+ /**
+ * Gets the local name of the specified node.
+ * @param node node to check
+ * @return String local name
+ */
+ public static String getLocalName(final Node node) {
+ final String localName = node.getLocalName();
+ if (localName != null) {
+ return localName;
+ }
+
+ final String name = node.getNodeName();
+ final int index = name.lastIndexOf(':');
+ return index < 0 ? name : name.substring(index + 1);
+ }
+
+ /**
+ * Gets the ns uri of the specified node.
+ * @param node Node to check
+ * @return String ns uri
+ */
+ public static String getNamespaceURI(Node node) {
+ if (node instanceof Document) {
+ node = ((Document) node).getDocumentElement();
+ }
+
+ final Element element = (Element) node;
+
+ String uri = element.getNamespaceURI();
+ if (uri == null) {
+ final String prefix = getPrefix(node);
+ final String qname = prefix == null ? "xmlns" : "xmlns:" + prefix;
+
+ Node aNode = node;
+ while (aNode != null) {
+ if (aNode.getNodeType() == Node.ELEMENT_NODE) {
+ final Attr attr = ((Element) aNode).getAttributeNode(qname);
+ if (attr != null) {
+ uri = attr.getValue();
+ break;
+ }
+ }
+ aNode = aNode.getParentNode();
+ }
+ }
+ return "".equals(uri) ? null : uri;
+ }
+
+ @Override
+ public Object getValue() {
+ if (node.getNodeType() == Node.COMMENT_NODE) {
+ final String text = ((Comment) node).getData();
+ return text == null ? "" : text.trim();
+ }
+ return stringValue(node);
+ }
+
+ /**
+ * Gets the string value of the specified node.
+ * @param node Node to check
+ * @return String
+ */
+ private String stringValue(final Node node) {
+ final int nodeType = node.getNodeType();
+ if (nodeType == Node.COMMENT_NODE) {
+ return "";
+ }
+ final boolean trim = !"preserve".equals(findEnclosingAttribute(node, "xml:space"));
+ if (nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE) {
+ final String text = node.getNodeValue();
+ return text == null ? "" : trim ? text.trim() : text;
+ }
+ if (nodeType == Node.PROCESSING_INSTRUCTION_NODE) {
+ final String text = ((ProcessingInstruction) node).getData();
+ return text == null ? "" : trim ? text.trim() : text;
+ }
+ final NodeList list = node.getChildNodes();
+ final StringBuilder buf = new StringBuilder();
+ for (int i = 0; i < list.getLength(); i++) {
+ final Node child = list.item(i);
+ buf.append(stringValue(child));
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Locates a node by ID.
+ * @param context starting context
+ * @param id to find
+ * @return Pointer
+ */
+ @Override
+ public Pointer getPointerByID(final JXPathContext context, final String id) {
+ final Document document = node.getNodeType() == Node.DOCUMENT_NODE ? (Document) node
+ : node.getOwnerDocument();
+ final Element element = document.getElementById(id);
+ return element == null ? (Pointer) new NullPointer(getLocale(), id)
+ : new DOMNodePointer(element, getLocale(), id);
+ }
+
+ @Override
+ public int compareChildNodePointers(final NodePointer pointer1,
+ final NodePointer pointer2) {
+ final Node node1 = (Node) pointer1.getBaseValue();
+ final Node node2 = (Node) pointer2.getBaseValue();
+ if (node1 == node2) {
+ return 0;
+ }
+
+ final int t1 = node1.getNodeType();
+ final int t2 = node2.getNodeType();
+ if (t1 == Node.ATTRIBUTE_NODE && t2 != Node.ATTRIBUTE_NODE) {
+ return -1;
+ }
+ if (t1 != Node.ATTRIBUTE_NODE && t2 == Node.ATTRIBUTE_NODE) {
+ return 1;
+ }
+ if (t1 == Node.ATTRIBUTE_NODE && t2 == Node.ATTRIBUTE_NODE) {
+ final NamedNodeMap map = ((Node) getNode()).getAttributes();
+ final int length = map.getLength();
+ for (int i = 0; i < length; i++) {
+ final Node n = map.item(i);
+ if (n == node1) {
+ return -1;
+ }
+ if (n == node2) {
+ return 1;
+ }
+ }
+ return 0; // Should not happen
+ }
+
+ Node current = node.getFirstChild();
+ while (current != null) {
+ if (current == node1) {
+ return -1;
+ }
+ if (current == node2) {
+ return 1;
+ }
+ current = current.getNextSibling();
+ }
+ return 0;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dom/NamespacePointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dom/NamespacePointer.java
new file mode 100644
index 00000000000..5e4c6c2728b
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dom/NamespacePointer.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.dom;
+
+import org.apache.commons.jxpath.ri.Compiler;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.compiler.NodeTest;
+import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+
+/**
+ * Represents a namespace node.
+ */
+public class NamespacePointer extends NodePointer {
+ private final String prefix;
+ private String namespaceURI;
+
+ private static final long serialVersionUID = -7622456151550131709L;
+
+ /**
+ * Create a new NamespacePointer.
+ * @param parent parent pointer
+ * @param prefix associated ns prefix.
+ */
+ public NamespacePointer(final NodePointer parent, final String prefix) {
+ super(parent);
+ this.prefix = prefix;
+ }
+
+ /**
+ * Create a new NamespacePointer.
+ * @param parent parent pointer
+ * @param prefix associated ns prefix.
+ * @param namespaceURI associated ns URI.
+ */
+ public NamespacePointer(
+ final NodePointer parent,
+ final String prefix,
+ final String namespaceURI) {
+ super(parent);
+ this.prefix = prefix;
+ this.namespaceURI = namespaceURI;
+ }
+
+ @Override
+ public QName getName() {
+ return new QName(prefix);
+ }
+
+ @Override
+ public Object getBaseValue() {
+ return null;
+ }
+
+ @Override
+ public boolean isCollection() {
+ return false;
+ }
+
+ @Override
+ public int getLength() {
+ return 1;
+ }
+
+ @Override
+ public Object getImmediateNode() {
+ return getNamespaceURI();
+ }
+
+ @Override
+ public String getNamespaceURI() {
+ if (namespaceURI == null) {
+ namespaceURI = parent.getNamespaceURI(prefix);
+ }
+ return namespaceURI;
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return true;
+ }
+
+ /**
+ * Throws UnsupportedOperationException.
+ * @param value Object
+ */
+ @Override
+ public void setValue(final Object value) {
+ throw new UnsupportedOperationException("Cannot modify DOM trees");
+ }
+
+ @Override
+ public boolean testNode(final NodeTest nodeTest) {
+ return nodeTest == null
+ || nodeTest instanceof NodeTypeTest
+ && ((NodeTypeTest) nodeTest).getNodeType()
+ == Compiler.NODE_TYPE_NODE;
+ }
+
+ @Override
+ public String asPath() {
+ final StringBuilder buffer = new StringBuilder();
+ if (parent != null) {
+ buffer.append(parent.asPath());
+ if (buffer.length() == 0
+ || buffer.charAt(buffer.length() - 1) != '/') {
+ buffer.append('/');
+ }
+ }
+ buffer.append("namespace::");
+ buffer.append(prefix);
+ return buffer.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return prefix.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ if (object == this) {
+ return true;
+ }
+
+ if (!(object instanceof NamespacePointer)) {
+ return false;
+ }
+
+ final NamespacePointer other = (NamespacePointer) object;
+ return prefix.equals(other.prefix);
+ }
+
+ @Override
+ public int compareChildNodePointers(
+ final NodePointer pointer1,
+ final NodePointer pointer2) {
+ // Won't happen - namespaces don't have children
+ return 0;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dynamic/DynamicAttributeIterator.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dynamic/DynamicAttributeIterator.java
new file mode 100644
index 00000000000..c33a2d7feae
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dynamic/DynamicAttributeIterator.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.dynamic;
+
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.model.beans.BeanAttributeIterator;
+import org.apache.commons.jxpath.ri.model.beans.PropertyOwnerPointer;
+
+/**
+ * {@code DynamicAttributeIterator} is different from a regular
+ * {@code BeanAttributeIterator} in that given a property name it
+ * will always find that property (albeit with a null value).
+ */
+public class DynamicAttributeIterator extends BeanAttributeIterator {
+
+ /**
+ * Create a new DynamicAttributeIterator.
+ * @param parent pointer
+ * @param name to match properties
+ */
+ public DynamicAttributeIterator(final PropertyOwnerPointer parent, final QName name) {
+ super(parent, name);
+ }
+
+ @Override
+ protected void prepareForIndividualProperty(final String name) {
+ ((DynamicPropertyPointer) getPropertyPointer()).setPropertyName(name);
+ super.prepareForIndividualProperty(name);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dynamic/DynamicPointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dynamic/DynamicPointer.java
new file mode 100644
index 00000000000..ff692ec4107
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dynamic/DynamicPointer.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.dynamic;
+
+import java.util.Locale;
+
+import org.apache.commons.jxpath.DynamicPropertyHandler;
+import org.apache.commons.jxpath.JXPathIntrospector;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.model.NodeIterator;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.ri.model.beans.PropertyIterator;
+import org.apache.commons.jxpath.ri.model.beans.PropertyOwnerPointer;
+import org.apache.commons.jxpath.ri.model.beans.PropertyPointer;
+
+/**
+ * A Pointer that points to an object with Dynamic Properties. It is used for
+ * the first element of a path; following elements will by of type
+ * {@link PropertyPointer}.
+ */
+public class DynamicPointer extends PropertyOwnerPointer {
+ private final QName name;
+ private final Object bean;
+ private final DynamicPropertyHandler handler;
+
+ private static final long serialVersionUID = -1842347025295904256L;
+
+ /**
+ * Create a new DynamicPointer.
+ * @param name property name
+ * @param bean owning bean
+ * @param handler DynamicPropertyHandler
+ * @param locale Locale
+ */
+ public DynamicPointer(final QName name, final Object bean,
+ final DynamicPropertyHandler handler, final Locale locale) {
+ super(null, locale);
+ this.name = name;
+ this.bean = bean;
+ this.handler = handler;
+ }
+
+ /**
+ * Create a new DynamicPointer.
+ * @param parent parent pointer
+ * @param name property name
+ * @param bean owning bean
+ * @param handler DynamicPropertyHandler
+ */
+ public DynamicPointer(final NodePointer parent, final QName name,
+ final Object bean, final DynamicPropertyHandler handler) {
+ super(parent);
+ this.name = name;
+ this.bean = bean;
+ this.handler = handler;
+ }
+
+ @Override
+ public PropertyPointer getPropertyPointer() {
+ return new DynamicPropertyPointer(this, handler);
+ }
+
+ @Override
+ public NodeIterator createNodeIterator(
+ final String property, final boolean reverse, final NodePointer startWith) {
+ return new PropertyIterator(this, property, reverse, startWith);
+ }
+
+ @Override
+ public NodeIterator attributeIterator(final QName name) {
+ return new DynamicAttributeIterator(this, name);
+ }
+
+ @Override
+ public QName getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isDynamicPropertyDeclarationSupported() {
+ return true;
+ }
+
+ /**
+ * Returns the DP object iself.
+ * @return Object
+ */
+ @Override
+ public Object getBaseValue() {
+ return bean;
+ }
+
+ @Override
+ public boolean isLeaf() {
+ final Object value = getNode();
+ return value == null || JXPathIntrospector.getBeanInfo(value.getClass()).isAtomic();
+ }
+
+ @Override
+ public boolean isCollection() {
+ return false;
+ }
+
+ /**
+ * Returns 1.
+ * @return int
+ */
+ @Override
+ public int getLength() {
+ return 1;
+ }
+
+ @Override
+ public String asPath() {
+ return parent == null ? "/" : super.asPath();
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(bean) + (name == null ? 0 : name.hashCode());
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ if (object == this) {
+ return true;
+ }
+
+ if (!(object instanceof DynamicPointer)) {
+ return false;
+ }
+
+ final DynamicPointer other = (DynamicPointer) object;
+ if (bean != other.bean) {
+ return false;
+ }
+ return name == other.name || name != null && name.equals(other.name);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dynamic/DynamicPointerFactory.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dynamic/DynamicPointerFactory.java
new file mode 100644
index 00000000000..c206f6153a0
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dynamic/DynamicPointerFactory.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.dynamic;
+
+import java.util.Locale;
+
+import org.apache.commons.jxpath.DynamicPropertyHandler;
+import org.apache.commons.jxpath.JXPathBeanInfo;
+import org.apache.commons.jxpath.JXPathIntrospector;
+import org.apache.commons.jxpath.ri.QName;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.ri.model.NodePointerFactory;
+import org.apache.commons.jxpath.ri.model.beans.NullPointer;
+import org.apache.commons.jxpath.util.ValueUtils;
+
+/**
+ * Implements NodePointerFactory for Dynamic classes like Map.
+ */
+public class DynamicPointerFactory implements NodePointerFactory {
+
+ /**
+ * Factory order constant.
+ */
+ public static final int DYNAMIC_POINTER_FACTORY_ORDER = 800;
+
+ @Override
+ public int getOrder() {
+ return DYNAMIC_POINTER_FACTORY_ORDER;
+ }
+
+ @Override
+ public NodePointer createNodePointer(
+ final QName name,
+ final Object bean,
+ final Locale locale) {
+ final JXPathBeanInfo bi = JXPathIntrospector.getBeanInfo(bean.getClass());
+ if (bi.isDynamic()) {
+ final DynamicPropertyHandler handler =
+ ValueUtils.getDynamicPropertyHandler(
+ bi.getDynamicPropertyHandlerClass());
+ return new DynamicPointer(name, bean, handler, locale);
+ }
+ return null;
+ }
+
+ @Override
+ public NodePointer createNodePointer(
+ final NodePointer parent,
+ final QName name,
+ final Object bean) {
+ if (bean == null) {
+ return new NullPointer(parent, name);
+ }
+
+ final JXPathBeanInfo bi = JXPathIntrospector.getBeanInfo(bean.getClass());
+ if (bi.isDynamic()) {
+ final DynamicPropertyHandler handler =
+ ValueUtils.getDynamicPropertyHandler(
+ bi.getDynamicPropertyHandlerClass());
+ return new DynamicPointer(parent, name, bean, handler);
+ }
+ return null;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dynamic/DynamicPropertyPointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dynamic/DynamicPropertyPointer.java
new file mode 100644
index 00000000000..a54c259b433
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/model/dynamic/DynamicPropertyPointer.java
@@ -0,0 +1,316 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.model.dynamic;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import org.apache.commons.jxpath.AbstractFactory;
+import org.apache.commons.jxpath.DynamicPropertyHandler;
+import org.apache.commons.jxpath.JXPathAbstractFactoryException;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.JXPathInvalidAccessException;
+import org.apache.commons.jxpath.ri.model.NodePointer;
+import org.apache.commons.jxpath.ri.model.beans.PropertyPointer;
+import org.apache.commons.jxpath.util.ValueUtils;
+
+/**
+ * Pointer pointing to a property of an object with dynamic properties.
+ */
+public class DynamicPropertyPointer extends PropertyPointer {
+
+ private static final long serialVersionUID = -5720585681149150822L;
+
+ private final DynamicPropertyHandler handler;
+ private String name;
+ private String[] names;
+ private String requiredPropertyName;
+
+ /**
+ * Create a new DynamicPropertyPointer.
+ * @param parent pointer
+ * @param handler DynamicPropertyHandler
+ */
+ public DynamicPropertyPointer(final NodePointer parent,
+ final DynamicPropertyHandler handler) {
+ super(parent);
+ this.handler = handler;
+ }
+
+ /**
+ * This type of node is auxiliary.
+ * @return true
+ */
+ @Override
+ public boolean isContainer() {
+ return true;
+ }
+
+ /**
+ * Number of the DP object's properties.
+ * @return int
+ */
+ @Override
+ public int getPropertyCount() {
+ return getPropertyNames().length;
+ }
+
+ /**
+ * Names of all properties, sorted alphabetically.
+ * @return String[]
+ */
+ @Override
+ public String[] getPropertyNames() {
+ if (names == null) {
+ String[] allNames = handler.getPropertyNames(getBean());
+ names = new String[allNames.length];
+ System.arraycopy(allNames, 0, names, 0, names.length);
+ Arrays.sort(names);
+ if (requiredPropertyName != null) {
+ final int inx = Arrays.binarySearch(names, requiredPropertyName);
+ if (inx < 0) {
+ allNames = names;
+ names = new String[allNames.length + 1];
+ names[0] = requiredPropertyName;
+ System.arraycopy(allNames, 0, names, 1, allNames.length);
+ Arrays.sort(names);
+ }
+ }
+ }
+ return names;
+ }
+
+ /**
+ * Returns the name of the currently selected property or "*"
+ * if none has been selected.
+ * @return String
+ */
+ @Override
+ public String getPropertyName() {
+ if (name == null) {
+ final String[] names = getPropertyNames();
+ name = propertyIndex >= 0 && propertyIndex < names.length ? names[propertyIndex] : "*";
+ }
+ return name;
+ }
+
+ /**
+ * Select a property by name. If the supplied name is
+ * not one of the object's existing properties, it implicitly
+ * adds this name to the object's property name list. It does not
+ * set the property value though. In order to set the property
+ * value, call setValue().
+ * @param propertyName to set
+ */
+ @Override
+ public void setPropertyName(final String propertyName) {
+ setPropertyIndex(UNSPECIFIED_PROPERTY);
+ this.name = propertyName;
+ requiredPropertyName = propertyName;
+ if (names != null && Arrays.binarySearch(names, propertyName) < 0) {
+ names = null;
+ }
+ }
+
+ /**
+ * Index of the currently selected property in the list of all
+ * properties sorted alphabetically.
+ * @return int
+ */
+ @Override
+ public int getPropertyIndex() {
+ if (propertyIndex == UNSPECIFIED_PROPERTY) {
+ final String[] names = getPropertyNames();
+ for (int i = 0; i < names.length; i++) {
+ if (names[i].equals(name)) {
+ setPropertyIndex(i);
+ break;
+ }
+ }
+ }
+ return super.getPropertyIndex();
+ }
+
+ /**
+ * Index a property by its index in the list of all
+ * properties sorted alphabetically.
+ * @param index to set
+ */
+ @Override
+ public void setPropertyIndex(final int index) {
+ if (propertyIndex != index) {
+ super.setPropertyIndex(index);
+ name = null;
+ }
+ }
+
+ /**
+ * Returns the value of the property, not an element of the collection
+ * represented by the property, if any.
+ * @return Object
+ */
+ @Override
+ public Object getBaseValue() {
+ return handler.getProperty(getBean(), getPropertyName());
+ }
+
+ /**
+ * If index == WHOLE_COLLECTION, the value of the property, otherwise
+ * the value of the index'th element of the collection represented by the
+ * property. If the property is not a collection, index should be zero
+ * and the value will be the property itself.
+ * @return Object
+ */
+ @Override
+ public Object getImmediateNode() {
+ Object value;
+ if (index == WHOLE_COLLECTION) {
+ value = ValueUtils.getValue(handler.getProperty(
+ getBean(),
+ getPropertyName()));
+ }
+ else {
+ value = ValueUtils.getValue(handler.getProperty(
+ getBean(),
+ getPropertyName()), index);
+ }
+ return value;
+ }
+
+ /**
+ * A dynamic property is always considered actual - all keys are apparently
+ * existing with possibly the value of null.
+ * @return boolean
+ */
+ @Override
+ protected boolean isActualProperty() {
+ return true;
+ }
+
+ /**
+ * If index == WHOLE_COLLECTION, change the value of the property, otherwise
+ * change the value of the index'th element of the collection
+ * represented by the property.
+ * @param value to set
+ */
+ @Override
+ public void setValue(final Object value) {
+ if (index == WHOLE_COLLECTION) {
+ handler.setProperty(getBean(), getPropertyName(), value);
+ }
+ else {
+ ValueUtils.setValue(
+ handler.getProperty(getBean(), getPropertyName()),
+ index,
+ value);
+ }
+ }
+
+ @Override
+ public NodePointer createPath(final JXPathContext context) {
+ // Ignore the name passed to us, use our own data
+ Object collection = getBaseValue();
+ if (collection == null) {
+ final AbstractFactory factory = getAbstractFactory(context);
+ final boolean success =
+ factory.createObject(
+ context,
+ this,
+ getBean(),
+ getPropertyName(),
+ 0);
+ if (!success) {
+ throw new JXPathAbstractFactoryException(
+ "Factory could not create an object for path: " + asPath());
+ }
+ collection = getBaseValue();
+ }
+
+ if (index != WHOLE_COLLECTION) {
+ if (index < 0) {
+ throw new JXPathInvalidAccessException("Index is less than 1: "
+ + asPath());
+ }
+
+ if (index >= getLength()) {
+ collection = ValueUtils.expandCollection(collection, index + 1);
+ handler.setProperty(getBean(), getPropertyName(), collection);
+ }
+ }
+
+ return this;
+ }
+
+ @Override
+ public NodePointer createPath(final JXPathContext context, final Object value) {
+ if (index == WHOLE_COLLECTION) {
+ handler.setProperty(getBean(), getPropertyName(), value);
+ }
+ else {
+ createPath(context);
+ ValueUtils.setValue(getBaseValue(), index, value);
+ }
+ return this;
+ }
+
+ @Override
+ public void remove() {
+ if (index == WHOLE_COLLECTION) {
+ removeKey();
+ }
+ else if (isCollection()) {
+ final Object collection = ValueUtils.remove(getBaseValue(), index);
+ handler.setProperty(getBean(), getPropertyName(), collection);
+ }
+ else if (index == 0) {
+ removeKey();
+ }
+ }
+
+ /**
+ * Remove the current property.
+ */
+ private void removeKey() {
+ final Object bean = getBean();
+ if (bean instanceof Map) {
+ ((Map) bean).remove(getPropertyName());
+ }
+ else {
+ handler.setProperty(bean, getPropertyName(), null);
+ }
+ }
+
+ @Override
+ public String asPath() {
+ final StringBuilder buffer = new StringBuilder();
+ buffer.append(getImmediateParentPointer().asPath());
+ if (buffer.length() == 0) {
+ buffer.append("/.");
+ }
+ else if (buffer.charAt(buffer.length() - 1) == '/') {
+ buffer.append('.');
+ }
+ buffer.append("[@name='");
+ buffer.append(escape(getPropertyName()));
+ buffer.append("']");
+ if (index != WHOLE_COLLECTION && isCollection()) {
+ buffer.append('[').append(index + 1).append(']');
+ }
+ return buffer.toString();
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/ParseException.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/ParseException.java
new file mode 100644
index 00000000000..7ee1adc44ad
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/ParseException.java
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 3.0 */
+package org.apache.commons.jxpath.ri.parser;
+
+/**
+ * This exception is thrown when parse errors are encountered.
+ * You can explicitly create objects of this exception type by
+ * calling the method generateParseException in the generated
+ * parser.
+ *
+ * You can modify this class to customize your error reporting
+ * mechanisms so long as you retain the public fields.
+ */
+public class ParseException extends Exception {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * This constructor is used by the method "generateParseException" in the generated parser. Calling this constructor generates a new object of this type
+ * with the fields "currentToken", "expectedTokenSequences", and "tokenImage" set. The boolean flag "specialConstructor" is also set to true to indicate
+ * that this constructor was used to create this object. This constructor calls its super class with the empty string to force the "toString" method of
+ * parent class "Throwable" to print the error message in the form: ParseException: "result of getMessage"
+ *
+ * @param currentTokenVal TODO
+ * @param expectedTokenSequencesVal TODO
+ * @param tokenImageVal TODO
+ */
+ public ParseException(final Token currentTokenVal,
+ final int[][] expectedTokenSequencesVal,
+ final String[] tokenImageVal
+ )
+ {
+ super("");
+ specialConstructor = true;
+ currentToken = currentTokenVal;
+ expectedTokenSequences = expectedTokenSequencesVal;
+ tokenImage = tokenImageVal;
+ }
+
+ /**
+ * The following constructors are for use by you for whatever
+ * purpose you can think of. Constructing the exception in this
+ * manner makes the exception behave in the normal way - i.e., as
+ * documented in the class "Throwable". The fields "errorToken",
+ * "expectedTokenSequences", and "tokenImage" do not contain
+ * relevant information. The JavaCC generated code does not use
+ * these constructors.
+ */
+
+ public ParseException() {
+ specialConstructor = false;
+ }
+
+ /**
+ * This variable determines which constructor was used to create
+ * this object and thereby affects the semantics of the
+ * "getMessage" method (see below).
+ */
+ protected boolean specialConstructor;
+
+ /**
+ * This is the last token that has been consumed successfully. If
+ * this object has been created due to a parse error, the token
+ * followng this token will (therefore) be the first error token.
+ */
+ public Token currentToken;
+
+ /**
+ * Each entry in this array is an array of integers. Each array
+ * of integers represents a sequence of tokens (by their ordinal
+ * values) that is expected at this point of the parse.
+ */
+ public int[][] expectedTokenSequences;
+
+ /**
+ * This is a reference to the "tokenImage" array of the generated
+ * parser within which the parse error occurred. This array is
+ * defined in the generated ...Constants interface.
+ */
+ public String[] tokenImage;
+
+ /**
+ * This method has the standard behavior when this object has been
+ * created using the standard constructors. Otherwise, it uses
+ * "currentToken" and "expectedTokenSequences" to generate a parse
+ * error message and returns it. If this object has been created
+ * due to a parse error, and you do not catch it (it gets thrown
+ * from the parser), then this method is called during the printing
+ * of the final stack trace, and hence the correct error message
+ * gets displayed.
+ */
+ @Override
+public String getMessage() {
+ if (!specialConstructor) {
+ return super.getMessage();
+ }
+ String expected = "";
+ int maxSize = 0;
+ for (final int[] element : expectedTokenSequences) {
+ if (maxSize < element.length) {
+ maxSize = element.length;
+ }
+ for (final int element2 : element) {
+ expected += tokenImage[element2] + " ";
+ }
+ if (element[element.length - 1] != 0) {
+ expected += "...";
+ }
+ expected += eol + " ";
+ }
+ String retval = "Encountered \"";
+ Token tok = currentToken.next;
+ for (int i = 0; i < maxSize; i++) {
+ if (i != 0) {
+ retval += " ";
+ }
+ if (tok.kind == 0) {
+ retval += tokenImage[0];
+ break;
+ }
+ retval += add_escapes(tok.image);
+ tok = tok.next;
+ }
+ retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
+ retval += "." + eol;
+ if (expectedTokenSequences.length == 1) {
+ retval += "Was expecting:" + eol + " ";
+ } else {
+ retval += "Was expecting one of:" + eol + " ";
+ }
+ retval += expected;
+ return retval;
+ }
+
+ /**
+ * The end of line string for this machine.
+ */
+ protected String eol = System.getProperty("line.separator", "\n");
+
+ /**
+ * Used to convert raw characters to their escaped version
+ * when these raw version cannot be used as part of an ASCII
+ * string literal.
+ *
+ * @param str raw characters.
+ * @return escaped version of the input.
+ */
+ protected String add_escapes(final String str) {
+ final StringBuilder retval = new StringBuilder();
+ char ch;
+ for (int i = 0; i < str.length(); i++) {
+ switch (str.charAt(i))
+ {
+ case 0 :
+ continue;
+ case '\b':
+ retval.append("\\b");
+ continue;
+ case '\t':
+ retval.append("\\t");
+ continue;
+ case '\n':
+ retval.append("\\n");
+ continue;
+ case '\f':
+ retval.append("\\f");
+ continue;
+ case '\r':
+ retval.append("\\r");
+ continue;
+ case '\"':
+ retval.append("\\\"");
+ continue;
+ case '\'':
+ retval.append("\\\'");
+ continue;
+ case '\\':
+ retval.append("\\\\");
+ continue;
+ default:
+ if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+ final String s = "0000" + Integer.toString(ch, 16);
+ retval.append("\\u" + s.substring(s.length() - 4));
+ } else {
+ retval.append(ch);
+ }
+ continue;
+ }
+ }
+ return retval.toString();
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/SimpleCharStream.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/SimpleCharStream.java
new file mode 100644
index 00000000000..18141c611f5
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/SimpleCharStream.java
@@ -0,0 +1,318 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 3.0 */
+package org.apache.commons.jxpath.ri.parser;
+
+/**
+ * An implementation of interface CharStream, where the stream is assumed to
+ * contain only ASCII characters (without unicode processing).
+ */
+
+public class SimpleCharStream
+{
+ public static final boolean staticFlag = false;
+ int bufsize;
+ int available;
+ int tokenBegin;
+ public int bufpos = -1;
+ protected int bufline[];
+ protected int bufcolumn[];
+
+ protected int column = 0;
+ protected int line = 1;
+
+ protected boolean prevCharIsCR = false;
+ protected boolean prevCharIsLF = false;
+
+ protected java.io.Reader inputStream;
+
+ protected char[] buffer;
+ protected int maxNextCharInd = 0;
+ protected int inBuf = 0;
+
+ protected void ExpandBuff(final boolean wrapAround)
+ {
+ final char[] newbuffer = new char[bufsize + 2048];
+ final int newbufline[] = new int[bufsize + 2048];
+ final int newbufcolumn[] = new int[bufsize + 2048];
+
+ try
+ {
+ if (wrapAround)
+ {
+ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+ System.arraycopy(buffer, 0, newbuffer,
+ bufsize - tokenBegin, bufpos);
+ buffer = newbuffer;
+
+ System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+ System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos);
+ bufline = newbufline;
+
+ System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+ System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos);
+ bufcolumn = newbufcolumn;
+
+ maxNextCharInd = bufpos += bufsize - tokenBegin;
+ }
+ else
+ {
+ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+ buffer = newbuffer;
+
+ System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+ bufline = newbufline;
+
+ System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+ bufcolumn = newbufcolumn;
+
+ maxNextCharInd = bufpos -= tokenBegin;
+ }
+ }
+ catch (final Throwable t)
+ {
+ throw new Error(t.getMessage());
+ }
+
+ bufsize += 2048;
+ available = bufsize;
+ tokenBegin = 0;
+ }
+
+ protected void FillBuff() throws java.io.IOException
+ {
+ if (maxNextCharInd == available)
+ {
+ if (available == bufsize)
+ {
+ if (tokenBegin > 2048)
+ {
+ bufpos = maxNextCharInd = 0;
+ available = tokenBegin;
+ }
+ else if (tokenBegin < 0) {
+ bufpos = maxNextCharInd = 0;
+ } else {
+ ExpandBuff(false);
+ }
+ }
+ else if (available > tokenBegin) {
+ available = bufsize;
+ } else if (tokenBegin - available < 2048) {
+ ExpandBuff(true);
+ } else {
+ available = tokenBegin;
+ }
+ }
+
+ int i;
+ try {
+ if ((i = inputStream.read(buffer, maxNextCharInd,
+ available - maxNextCharInd)) == -1)
+ {
+ inputStream.close();
+ throw new java.io.IOException();
+ }
+ maxNextCharInd += i;
+ }
+ catch (final java.io.IOException e) {
+ --bufpos;
+ backup(0);
+ if (tokenBegin == -1) {
+ tokenBegin = bufpos;
+ }
+ throw e;
+ }
+ }
+
+ public char BeginToken() throws java.io.IOException
+ {
+ tokenBegin = -1;
+ final char c = readChar();
+ tokenBegin = bufpos;
+
+ return c;
+ }
+
+ protected void UpdateLineColumn(final char c)
+ {
+ column++;
+
+ if (prevCharIsLF)
+ {
+ prevCharIsLF = false;
+ line += column = 1;
+ }
+ else if (prevCharIsCR)
+ {
+ prevCharIsCR = false;
+ if (c == '\n')
+ {
+ prevCharIsLF = true;
+ } else {
+ line += column = 1;
+ }
+ }
+
+ switch (c)
+ {
+ case '\r' :
+ prevCharIsCR = true;
+ break;
+ case '\n' :
+ prevCharIsLF = true;
+ break;
+ case '\t' :
+ column--;
+ column += 8 - (column & 07);
+ break;
+ default :
+ break;
+ }
+
+ bufline[bufpos] = line;
+ bufcolumn[bufpos] = column;
+ }
+
+ public char readChar() throws java.io.IOException
+ {
+ if (inBuf > 0)
+ {
+ --inBuf;
+
+ if (++bufpos == bufsize) {
+ bufpos = 0;
+ }
+
+ return buffer[bufpos];
+ }
+
+ if (++bufpos >= maxNextCharInd) {
+ FillBuff();
+ }
+
+ final char c = buffer[bufpos];
+
+ UpdateLineColumn(c);
+ return c;
+ }
+
+ /**
+ * @deprecated
+ * @return the end column.
+ * @see #getEndColumn
+ */
+ @Deprecated
+public int getColumn() {
+ return bufcolumn[bufpos];
+ }
+
+ /**
+ * @deprecated
+ * @return the line number.
+ * @see #getEndLine
+ */
+ @Deprecated
+public int getLine() {
+ return bufline[bufpos];
+ }
+
+ public int getEndColumn() {
+ return bufcolumn[bufpos];
+ }
+
+ public int getEndLine() {
+ return bufline[bufpos];
+ }
+
+ public int getBeginColumn() {
+ return bufcolumn[tokenBegin];
+ }
+
+ public int getBeginLine() {
+ return bufline[tokenBegin];
+ }
+
+ public void backup(final int amount) {
+
+ inBuf += amount;
+ if ((bufpos -= amount) < 0) {
+ bufpos += bufsize;
+ }
+ }
+
+ public SimpleCharStream(final java.io.Reader dstream, final int startLine,
+ final int startColumn, final int bufferSize)
+ {
+ inputStream = dstream;
+ line = startLine;
+ column = startColumn - 1;
+
+ available = bufsize = bufferSize;
+ buffer = new char[bufferSize];
+ bufline = new int[bufferSize];
+ bufcolumn = new int[bufferSize];
+ }
+
+ public SimpleCharStream(final java.io.Reader dstream, final int startLine,
+ final int startColumn)
+ {
+ this(dstream, startLine, startColumn, 4096);
+ }
+
+ public void ReInit(final java.io.Reader dstream, final int startLine,
+ final int startColumn, final int bufferSize)
+ {
+ inputStream = dstream;
+ line = startLine;
+ column = startColumn - 1;
+
+ if (buffer == null || bufferSize != buffer.length)
+ {
+ available = bufsize = bufferSize;
+ buffer = new char[bufferSize];
+ bufline = new int[bufferSize];
+ bufcolumn = new int[bufferSize];
+ }
+ prevCharIsLF = prevCharIsCR = false;
+ tokenBegin = inBuf = maxNextCharInd = 0;
+ bufpos = -1;
+ }
+
+ public void ReInit(final java.io.Reader dstream, final int startLine,
+ final int startColumn)
+ {
+ ReInit(dstream, startLine, startColumn, 4096);
+ }
+
+ public void ReInit(final java.io.InputStream dstream, final int startLine,
+ final int startColumn, final int buffersize)
+ {
+ ReInit(new java.io.InputStreamReader(dstream), startLine, startColumn, 4096);
+ }
+
+ public String GetImage()
+ {
+ if (bufpos >= tokenBegin) {
+ return new String(buffer, tokenBegin, bufpos - tokenBegin + 1);
+ }
+ return new String(buffer, tokenBegin, bufsize - tokenBegin) +
+ new String(buffer, 0, bufpos + 1);
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/Token.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/Token.java
new file mode 100644
index 00000000000..e8ff17c8866
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/Token.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Generated By:JavaCC: Do not edit this line. Token.java Version 3.0 */
+package org.apache.commons.jxpath.ri.parser;
+
+/**
+ * Describes the input token stream.
+ */
+public class Token {
+
+ /**
+ * An integer that describes the kind of this token. This numbering
+ * system is determined by JavaCCParser, and a table of these numbers is
+ * stored in the file ...Constants.java.
+ */
+ public int kind;
+
+ /**
+ * beginLine and beginColumn describe the position of the first character
+ * of this token; endLine and endColumn describe the position of the
+ * last character of this token.
+ */
+ public int beginLine;
+
+ public int beginColumn;
+
+ public int endLine;
+
+ public int endColumn;
+
+ /**
+ * The string image of the token.
+ */
+ public String image;
+
+ /**
+ * A reference to the next regular (non-special) token from the input
+ * stream. If this is the last token from the input stream, or if the
+ * token manager has not read tokens beyond this one, this field is
+ * set to null. This is true only if this token is also a regular
+ * token. Otherwise, see below for a description of the contents of
+ * this field.
+ */
+ public Token next;
+
+ /**
+ * Returns the image.
+ */
+ @Override
+public String toString()
+ {
+ return image;
+ }
+
+ /**
+ * Returns a new Token object, by default. However, if you want, you can create and return subclass objects based on the value of ofKind. Simply add the cases
+ * to the switch for all those special cases. For example, if you have a subclass of Token called IDToken that you want to create if ofKind is ID, simlpy add
+ * something like :
+ *
+ *
+ * case MyParserConstants.ID : return new IDToken();
+ *
+ *
+ * to the following switch statement. Then you can cast matchedToken variable to the appropriate type and use it in your lexical actions.
+ *
+ * @param ofKind TODO
+ * @return TODO
+ */
+ public static final Token newToken(final int ofKind)
+ {
+ switch(ofKind)
+ {
+ default : return new Token();
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/TokenMgrError.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/TokenMgrError.java
new file mode 100644
index 00000000000..2dd3f96f617
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/TokenMgrError.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 3.0 *
+ *
+ * !!!MODIFIED BY DMITRI PLOTNIKOV - DO NOT REGENERATE!!!
+ */
+package org.apache.commons.jxpath.ri.parser;
+
+public class TokenMgrError extends Error
+{
+ /*
+ * Ordinals for various reasons why an Error of this type can be thrown.
+ */
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+/**
+ * Lexical error occurred.
+ */
+ static final int LEXICAL_ERROR = 0;
+
+ /**
+ * Tried to change to an invalid lexical state.
+ */
+ static final int INVALID_LEXICAL_STATE = 2;
+
+ /**
+ * Replaces unprintable characters by their escaped (or Unicode escaped) equivalents in the given string
+ *
+ * @param str TODO
+ * @return TODO
+ */
+ public static final String addEscapes(final String str) {
+ final StringBuilder retval = new StringBuilder();
+ char ch;
+ for (int i = 0; i < str.length(); i++) {
+ switch (str.charAt(i))
+ {
+ case 0 :
+ continue;
+ case '\b':
+ retval.append("\\b");
+ continue;
+ case '\t':
+ retval.append("\\t");
+ continue;
+ case '\n':
+ retval.append("\\n");
+ continue;
+ case '\f':
+ retval.append("\\f");
+ continue;
+ case '\r':
+ retval.append("\\r");
+ continue;
+ case '\"':
+ retval.append("\\\"");
+ continue;
+ case '\'':
+ retval.append("\\\'");
+ continue;
+ case '\\':
+ retval.append("\\\\");
+ continue;
+ default:
+ if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+ final String s = "0000" + Integer.toString(ch, 16);
+ retval.append("\\u" + s.substring(s.length() - 4));
+ } else {
+ retval.append(ch);
+ }
+ continue;
+ }
+ }
+ return retval.toString();
+ }
+
+ /**
+ * Returns a detailed message for the Error when it is thrown by the token manager to indicate a lexical error.
+ *
+ * Note: You can customize the lexical error message by modifying this method.
+ *
+ * @param EOFSeen indicates if EOF caused the lexicl error
+ * @param lexState lexical state in which this error occurred
+ * @param errorLine line number when the error occurred
+ * @param errorColumn column number when the error occurred
+ * @param errorAfter prefix that was seen before this error occurred
+ * @param curChar the offending character
+ * @return TODO
+ */
+ protected static String LexicalError(final boolean EOFSeen, final int lexState, final int errorLine, final int errorColumn, final String errorAfter, final char curChar) {
+ return "Lexical error at line " +
+ errorLine + ", column " +
+ errorColumn + ". Encountered: " +
+ (EOFSeen ? " " : "\"" + addEscapes(String.valueOf(curChar)) + "\"" + " (" + (int)curChar + "), ") +
+ "after : \"" + addEscapes(errorAfter) + "\"";
+ }
+
+ /**
+ * You can also modify the body of this method to customize your error messages.
+ * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not
+ * of end-users concern, so you can return something like :
+ *
+ * "Internal Error : Please file a bug report .... "
+ *
+ * from this method for such cases in the release version of your parser.
+ */
+ @Override
+public String getMessage() {
+ return super.getMessage();
+ }
+
+ /*
+ * Constructors of various flavors follow.
+ */
+
+ public TokenMgrError() {
+ }
+
+ public TokenMgrError(final String message, final int reason) {
+ super(message);
+ }
+
+ public TokenMgrError(final boolean EOFSeen, final int lexState, final int errorLine, final int errorColumn, final String errorAfter, final char curChar, final int reason) {
+ this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason);
+
+ // ADDED BY ME FROM THIS POINT TO THE EOF - DMITRI PLOTNIKOV
+ position = errorColumn - 1;
+ character = curChar;
+ }
+
+ private int position;
+ private char character;
+
+ public int getPosition(){
+ return position;
+ }
+
+ public char getCharacter(){
+ return character;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/XPathParser.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/XPathParser.java
new file mode 100644
index 00000000000..e4065ff1e0a
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/XPathParser.java
@@ -0,0 +1,3793 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Generated By:JavaCC: Do not edit this line. XPathParser.java */
+ package org.apache.commons.jxpath.ri.parser;
+
+ import java.util.ArrayList;
+
+import org.apache.commons.jxpath.ri.Compiler;
+
+ public class XPathParser implements XPathParserConstants {
+ private Compiler compiler;
+
+ public void setCompiler(final Compiler compiler){
+ this.compiler = compiler;
+ }
+
+ private String unescape(String string){
+ int index = string.indexOf("'");
+ while (index != -1){
+ string = string.substring(0, index) + "\'" + string.substring(index + 6);
+ index = string.indexOf("'");
+ }
+ index = string.indexOf(""");
+ while (index != -1){
+ string = string.substring(0, index) + "\"" + string.substring(index + 6);
+ index = string.indexOf(""");
+ }
+ return string;
+ }
+
+// Note: XPath does not have reserved words, so we have to include all these terminals
+ final public String NCName() throws ParseException {
+ switch (jj_nt.kind) {
+ case OR:
+ case AND:
+ case MOD:
+ case DIV:
+ case NCName:
+ NCName_Without_CoreFunctions();
+ break;
+ case NODE:
+ jj_consume_token(NODE);
+ break;
+ case TEXT:
+ jj_consume_token(TEXT);
+ break;
+ case COMMENT:
+ jj_consume_token(COMMENT);
+ break;
+ case PI:
+ jj_consume_token(PI);
+ break;
+ case FUNCTION_LAST:
+ jj_consume_token(FUNCTION_LAST);
+ break;
+ case FUNCTION_POSITION:
+ jj_consume_token(FUNCTION_POSITION);
+ break;
+ case FUNCTION_COUNT:
+ jj_consume_token(FUNCTION_COUNT);
+ break;
+ case FUNCTION_ID:
+ jj_consume_token(FUNCTION_ID);
+ break;
+ case FUNCTION_LOCAL_NAME:
+ jj_consume_token(FUNCTION_LOCAL_NAME);
+ break;
+ case FUNCTION_NAMESPACE_URI:
+ jj_consume_token(FUNCTION_NAMESPACE_URI);
+ break;
+ case FUNCTION_NAME:
+ jj_consume_token(FUNCTION_NAME);
+ break;
+ case FUNCTION_STRING:
+ jj_consume_token(FUNCTION_STRING);
+ break;
+ case FUNCTION_CONCAT:
+ jj_consume_token(FUNCTION_CONCAT);
+ break;
+ case FUNCTION_STARTS_WITH:
+ jj_consume_token(FUNCTION_STARTS_WITH);
+ break;
+ case FUNCTION_ENDS_WITH:
+ jj_consume_token(FUNCTION_ENDS_WITH);
+ break;
+ case FUNCTION_CONTAINS:
+ jj_consume_token(FUNCTION_CONTAINS);
+ break;
+ case FUNCTION_SUBSTRING_BEFORE:
+ jj_consume_token(FUNCTION_SUBSTRING_BEFORE);
+ break;
+ case FUNCTION_SUBSTRING_AFTER:
+ jj_consume_token(FUNCTION_SUBSTRING_AFTER);
+ break;
+ case FUNCTION_SUBSTRING:
+ jj_consume_token(FUNCTION_SUBSTRING);
+ break;
+ case FUNCTION_STRING_LENGTH:
+ jj_consume_token(FUNCTION_STRING_LENGTH);
+ break;
+ case FUNCTION_NORMALIZE_SPACE:
+ jj_consume_token(FUNCTION_NORMALIZE_SPACE);
+ break;
+ case FUNCTION_TRANSLATE:
+ jj_consume_token(FUNCTION_TRANSLATE);
+ break;
+ case FUNCTION_BOOLEAN:
+ jj_consume_token(FUNCTION_BOOLEAN);
+ break;
+ case FUNCTION_NOT:
+ jj_consume_token(FUNCTION_NOT);
+ break;
+ case FUNCTION_TRUE:
+ jj_consume_token(FUNCTION_TRUE);
+ break;
+ case FUNCTION_FALSE:
+ jj_consume_token(FUNCTION_FALSE);
+ break;
+ case FUNCTION_NULL:
+ jj_consume_token(FUNCTION_NULL);
+ break;
+ case FUNCTION_LANG:
+ jj_consume_token(FUNCTION_LANG);
+ break;
+ case FUNCTION_NUMBER:
+ jj_consume_token(FUNCTION_NUMBER);
+ break;
+ case FUNCTION_SUM:
+ jj_consume_token(FUNCTION_SUM);
+ break;
+ case FUNCTION_FLOOR:
+ jj_consume_token(FUNCTION_FLOOR);
+ break;
+ case FUNCTION_CEILING:
+ jj_consume_token(FUNCTION_CEILING);
+ break;
+ case FUNCTION_ROUND:
+ jj_consume_token(FUNCTION_ROUND);
+ break;
+ case FUNCTION_KEY:
+ jj_consume_token(FUNCTION_KEY);
+ break;
+ case FUNCTION_FORMAT_NUMBER:
+ jj_consume_token(FUNCTION_FORMAT_NUMBER);
+ break;
+ default:
+ jj_la1[0] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ {if (true) {
+ return token.image;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+ final public String NCName_Without_CoreFunctions() throws ParseException {
+ switch (jj_nt.kind) {
+ case NCName:
+ jj_consume_token(NCName);
+ break;
+ case OR:
+ jj_consume_token(OR);
+ break;
+ case AND:
+ jj_consume_token(AND);
+ break;
+ case MOD:
+ jj_consume_token(MOD);
+ break;
+ case DIV:
+ jj_consume_token(DIV);
+ break;
+ default:
+ jj_la1[1] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ {if (true) {
+ return token.image;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+ final public int CoreFunctionName() throws ParseException {
+ int code;
+ switch (jj_nt.kind) {
+ case FUNCTION_LAST:
+ jj_consume_token(FUNCTION_LAST);
+ code = Compiler.FUNCTION_LAST;
+ break;
+ case FUNCTION_POSITION:
+ jj_consume_token(FUNCTION_POSITION);
+ code = Compiler.FUNCTION_POSITION;
+ break;
+ case FUNCTION_COUNT:
+ jj_consume_token(FUNCTION_COUNT);
+ code = Compiler.FUNCTION_COUNT;
+ break;
+ case FUNCTION_ID:
+ jj_consume_token(FUNCTION_ID);
+ code = Compiler.FUNCTION_ID;
+ break;
+ case FUNCTION_LOCAL_NAME:
+ jj_consume_token(FUNCTION_LOCAL_NAME);
+ code = Compiler.FUNCTION_LOCAL_NAME;
+ break;
+ case FUNCTION_NAMESPACE_URI:
+ jj_consume_token(FUNCTION_NAMESPACE_URI);
+ code = Compiler.FUNCTION_NAMESPACE_URI;
+ break;
+ case FUNCTION_NAME:
+ jj_consume_token(FUNCTION_NAME);
+ code = Compiler.FUNCTION_NAME;
+ break;
+ case FUNCTION_STRING:
+ jj_consume_token(FUNCTION_STRING);
+ code = Compiler.FUNCTION_STRING;
+ break;
+ case FUNCTION_CONCAT:
+ jj_consume_token(FUNCTION_CONCAT);
+ code = Compiler.FUNCTION_CONCAT;
+ break;
+ case FUNCTION_STARTS_WITH:
+ jj_consume_token(FUNCTION_STARTS_WITH);
+ code = Compiler.FUNCTION_STARTS_WITH;
+ break;
+ case FUNCTION_ENDS_WITH:
+ jj_consume_token(FUNCTION_ENDS_WITH);
+ code = Compiler.FUNCTION_ENDS_WITH;
+ break;
+ case FUNCTION_CONTAINS:
+ jj_consume_token(FUNCTION_CONTAINS);
+ code = Compiler.FUNCTION_CONTAINS;
+ break;
+ case FUNCTION_SUBSTRING_BEFORE:
+ jj_consume_token(FUNCTION_SUBSTRING_BEFORE);
+ code = Compiler.FUNCTION_SUBSTRING_BEFORE;
+ break;
+ case FUNCTION_SUBSTRING_AFTER:
+ jj_consume_token(FUNCTION_SUBSTRING_AFTER);
+ code = Compiler.FUNCTION_SUBSTRING_AFTER;
+ break;
+ case FUNCTION_SUBSTRING:
+ jj_consume_token(FUNCTION_SUBSTRING);
+ code = Compiler.FUNCTION_SUBSTRING;
+ break;
+ case FUNCTION_STRING_LENGTH:
+ jj_consume_token(FUNCTION_STRING_LENGTH);
+ code = Compiler.FUNCTION_STRING_LENGTH;
+ break;
+ case FUNCTION_NORMALIZE_SPACE:
+ jj_consume_token(FUNCTION_NORMALIZE_SPACE);
+ code = Compiler.FUNCTION_NORMALIZE_SPACE;
+ break;
+ case FUNCTION_TRANSLATE:
+ jj_consume_token(FUNCTION_TRANSLATE);
+ code = Compiler.FUNCTION_TRANSLATE;
+ break;
+ case FUNCTION_BOOLEAN:
+ jj_consume_token(FUNCTION_BOOLEAN);
+ code = Compiler.FUNCTION_BOOLEAN;
+ break;
+ case FUNCTION_NOT:
+ jj_consume_token(FUNCTION_NOT);
+ code = Compiler.FUNCTION_NOT;
+ break;
+ case FUNCTION_TRUE:
+ jj_consume_token(FUNCTION_TRUE);
+ code = Compiler.FUNCTION_TRUE;
+ break;
+ case FUNCTION_FALSE:
+ jj_consume_token(FUNCTION_FALSE);
+ code = Compiler.FUNCTION_FALSE;
+ break;
+ case FUNCTION_NULL:
+ jj_consume_token(FUNCTION_NULL);
+ code = Compiler.FUNCTION_NULL;
+ break;
+ case FUNCTION_LANG:
+ jj_consume_token(FUNCTION_LANG);
+ code = Compiler.FUNCTION_LANG;
+ break;
+ case FUNCTION_NUMBER:
+ jj_consume_token(FUNCTION_NUMBER);
+ code = Compiler.FUNCTION_NUMBER;
+ break;
+ case FUNCTION_SUM:
+ jj_consume_token(FUNCTION_SUM);
+ code = Compiler.FUNCTION_SUM;
+ break;
+ case FUNCTION_FLOOR:
+ jj_consume_token(FUNCTION_FLOOR);
+ code = Compiler.FUNCTION_FLOOR;
+ break;
+ case FUNCTION_CEILING:
+ jj_consume_token(FUNCTION_CEILING);
+ code = Compiler.FUNCTION_CEILING;
+ break;
+ case FUNCTION_ROUND:
+ jj_consume_token(FUNCTION_ROUND);
+ code = Compiler.FUNCTION_ROUND;
+ break;
+ case FUNCTION_KEY:
+ jj_consume_token(FUNCTION_KEY);
+ code = Compiler.FUNCTION_KEY;
+ break;
+ case FUNCTION_FORMAT_NUMBER:
+ jj_consume_token(FUNCTION_FORMAT_NUMBER);
+ code = Compiler.FUNCTION_FORMAT_NUMBER;
+ break;
+ default:
+ jj_la1[2] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ {if (true) {
+ return code;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+ final public Object QName() throws ParseException {
+ String nc1, nc2 = null;
+ nc1 = NCName();
+ switch (jj_nt.kind) {
+ case 80:
+ jj_consume_token(80);
+ nc2 = NCName();
+ break;
+ default:
+ jj_la1[3] = jj_gen;
+ ;
+ }
+ if (nc2 == null){
+ {if (true) {
+ return compiler.qname(null, nc1);
+ }}
+ }
+ else {
+ {if (true) {
+ return compiler.qname(nc1, nc2);
+ }}
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ final public Object QName_Without_CoreFunctions() throws ParseException {
+ String nc1, nc2 = null;
+ if (jj_2_1(2147483647)) {
+ nc1 = NCName();
+ jj_consume_token(80);
+ nc2 = NCName();
+ } else {
+ switch (jj_nt.kind) {
+ case OR:
+ case AND:
+ case MOD:
+ case DIV:
+ case NCName:
+ nc1 = NCName_Without_CoreFunctions();
+ break;
+ default:
+ jj_la1[4] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ if (nc2 == null){
+ {if (true) {
+ return compiler.qname(null, nc1);
+ }}
+ }
+ else {
+ {if (true) {
+ return compiler.qname(nc1, nc2);
+ }}
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ final public Object parseExpression() throws ParseException {
+ Object ex;
+ ex = Expression();
+ jj_consume_token(0);
+ {if (true) {
+ return ex;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/* ################################################################################### */
+/* XSLT Patterns (http://www.w3.org/1999/08/WD-xslt-19990813) */
+/* ################################################################################### */
+
+/* [XSLT1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern */
+
+//void Pattern() :
+//{}
+//{
+// LocationPathPattern() ( LocationPathPattern() )*
+//}
+//
+//
+// /* [XSLT2] LocationPathPattern ::=
+// '/' RelativePathPattern? | IdKeyPattern (('/' | '//' RelativePathPattern)? | '//'? RelativePathPattern
+//*/
+//
+//void LocationPathPattern() :
+//{}
+//{
+// ( RelativePathPattern() )?
+// | (
+// LOOKAHEAD(IdKeyPattern())
+// IdKeyPattern() ( ( | ) RelativePathPattern() )?
+// | ( )? RelativePathPattern()
+// )
+//}
+//
+//
+//
+// /* [XSLT3] IdKeyPattern ::= 'id' '(' Literal ')' | 'key' '(' Literal ',' Literal ')' */
+//
+//void IdKeyPattern() :
+//{}
+//{
+// "(" ")"
+// | "(" "," ")"
+//}
+//
+//
+// /* [XSLT4] RelativePathPattern ::= StepPattern | RelativePathPattern '/' StepPattern
+// | RelativePathPattern '//' StepPattern
+//*/
+//void RelativePathPattern() :
+//{}
+//{
+// StepPattern() ( ( | ) StepPattern() )*
+//}
+//
+//
+// /* [XSLT5] StepPattern ::= AbbreviatedAxisSpecifier NodeTest Predicate* */
+//void StepPattern() :
+//{}
+//{
+// AbbreviatedAxisSpecifier() NodeTest() (Predicate())*
+//}
+
+
+// See XPath Syntax (http://www.w3.org/TR/xpath )
+
+//void XPath() :
+//{}
+//{
+// LocationPath()
+//
+//}
+
+/* [1] LocationPath ::= RelativeLocationPath | AbsoluteLocationPath */
+ final public Object LocationPath() throws ParseException {
+ Object ex = null;
+ switch (jj_nt.kind) {
+ case OR:
+ case AND:
+ case MOD:
+ case DIV:
+ case NODE:
+ case TEXT:
+ case COMMENT:
+ case PI:
+ case AXIS_SELF:
+ case AXIS_CHILD:
+ case AXIS_PARENT:
+ case AXIS_ANCESTOR:
+ case AXIS_ATTRIBUTE:
+ case AXIS_NAMESPACE:
+ case AXIS_PRECEDING:
+ case AXIS_FOLLOWING:
+ case AXIS_DESCENDANT:
+ case AXIS_ANCESTOR_OR_SELF:
+ case AXIS_FOLLOWING_SIBLING:
+ case AXIS_PRECEDING_SIBLING:
+ case AXIS_DESCENDANT_OR_SELF:
+ case FUNCTION_LAST:
+ case FUNCTION_POSITION:
+ case FUNCTION_COUNT:
+ case FUNCTION_ID:
+ case FUNCTION_KEY:
+ case FUNCTION_LOCAL_NAME:
+ case FUNCTION_NAMESPACE_URI:
+ case FUNCTION_NAME:
+ case FUNCTION_STRING:
+ case FUNCTION_CONCAT:
+ case FUNCTION_STARTS_WITH:
+ case FUNCTION_ENDS_WITH:
+ case FUNCTION_CONTAINS:
+ case FUNCTION_SUBSTRING_BEFORE:
+ case FUNCTION_SUBSTRING_AFTER:
+ case FUNCTION_SUBSTRING:
+ case FUNCTION_STRING_LENGTH:
+ case FUNCTION_NORMALIZE_SPACE:
+ case FUNCTION_TRANSLATE:
+ case FUNCTION_BOOLEAN:
+ case FUNCTION_NOT:
+ case FUNCTION_TRUE:
+ case FUNCTION_FALSE:
+ case FUNCTION_NULL:
+ case FUNCTION_LANG:
+ case FUNCTION_NUMBER:
+ case FUNCTION_SUM:
+ case FUNCTION_FLOOR:
+ case FUNCTION_CEILING:
+ case FUNCTION_ROUND:
+ case FUNCTION_FORMAT_NUMBER:
+ case NCName:
+ case 83:
+ case 84:
+ case 87:
+ case 89:
+ ex = RelativeLocationPath();
+ break;
+ case SLASH:
+ case SLASHSLASH:
+ ex = AbsoluteLocationPath();
+ break;
+ default:
+ jj_la1[5] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ {if (true) {
+ return ex;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/* [2] AbsoluteLocationPath ::= '/' RelativeLocationPath? | AbbreviatedAbsoluteLocationPath */
+/* [10] AbbreviatedAbsoluteLocationPath ::= '//' RelativeLocationPath */
+ final public Object AbsoluteLocationPath() throws ParseException {
+ final ArrayList steps = new ArrayList();
+ if (jj_2_2(2147483647)) {
+ LocationStep(steps);
+ label_1:
+ while (true) {
+ switch (jj_nt.kind) {
+ case SLASH:
+ case SLASHSLASH:
+ ;
+ break;
+ default:
+ jj_la1[6] = jj_gen;
+ break label_1;
+ }
+ LocationStep(steps);
+ }
+ } else {
+ switch (jj_nt.kind) {
+ case SLASH:
+ jj_consume_token(SLASH);
+ break;
+ default:
+ jj_la1[7] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ {if (true) {
+ return compiler.locationPath(true, steps.toArray());
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/* [3] RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | AbbreviatedRelativeLocationPath */
+ final public Object RelativeLocationPath() throws ParseException {
+ final ArrayList steps = new ArrayList();
+ NodeTest(steps);
+ label_2:
+ while (true) {
+ switch (jj_nt.kind) {
+ case SLASH:
+ case SLASHSLASH:
+ ;
+ break;
+ default:
+ jj_la1[8] = jj_gen;
+ break label_2;
+ }
+ LocationStep(steps);
+ }
+ {if (true) {
+ return compiler.locationPath(false, steps.toArray());
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/* [3] RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | AbbreviatedRelativeLocationPath */
+/* [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step */
+
+/*--------------------*/
+/* 2.1 Location Steps */
+/*--------------------*/
+
+/* [4] Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep */
+ final public void LocationStep(final ArrayList steps) throws ParseException {
+ Object t;
+ final Object s;
+ switch (jj_nt.kind) {
+ case SLASH:
+ jj_consume_token(SLASH);
+ break;
+ case SLASHSLASH:
+ jj_consume_token(SLASHSLASH);
+ // Abbreviated step: descendant-or-self::node()
+ t = compiler.nodeTypeTest(Compiler.NODE_TYPE_NODE);
+ steps.add(compiler.step(Compiler.AXIS_DESCENDANT_OR_SELF, t, null));
+ break;
+ default:
+ jj_la1[9] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ NodeTest(steps);
+ }
+
+/* [7] NodeTest ::= WildcardName | NodeType '(' ')' | 'processing-instruction' '(' Literal ')' */
+ final public void NodeTest(final ArrayList steps) throws ParseException {
+ int axis;
+ int type = -1;
+ String instruction = null;
+ Object name = null;
+ Object s;
+ Object p;
+ final ArrayList ps = new ArrayList();
+ switch (jj_nt.kind) {
+ case OR:
+ case AND:
+ case MOD:
+ case DIV:
+ case NODE:
+ case TEXT:
+ case COMMENT:
+ case PI:
+ case AXIS_SELF:
+ case AXIS_CHILD:
+ case AXIS_PARENT:
+ case AXIS_ANCESTOR:
+ case AXIS_ATTRIBUTE:
+ case AXIS_NAMESPACE:
+ case AXIS_PRECEDING:
+ case AXIS_FOLLOWING:
+ case AXIS_DESCENDANT:
+ case AXIS_ANCESTOR_OR_SELF:
+ case AXIS_FOLLOWING_SIBLING:
+ case AXIS_PRECEDING_SIBLING:
+ case AXIS_DESCENDANT_OR_SELF:
+ case FUNCTION_LAST:
+ case FUNCTION_POSITION:
+ case FUNCTION_COUNT:
+ case FUNCTION_ID:
+ case FUNCTION_KEY:
+ case FUNCTION_LOCAL_NAME:
+ case FUNCTION_NAMESPACE_URI:
+ case FUNCTION_NAME:
+ case FUNCTION_STRING:
+ case FUNCTION_CONCAT:
+ case FUNCTION_STARTS_WITH:
+ case FUNCTION_ENDS_WITH:
+ case FUNCTION_CONTAINS:
+ case FUNCTION_SUBSTRING_BEFORE:
+ case FUNCTION_SUBSTRING_AFTER:
+ case FUNCTION_SUBSTRING:
+ case FUNCTION_STRING_LENGTH:
+ case FUNCTION_NORMALIZE_SPACE:
+ case FUNCTION_TRANSLATE:
+ case FUNCTION_BOOLEAN:
+ case FUNCTION_NOT:
+ case FUNCTION_TRUE:
+ case FUNCTION_FALSE:
+ case FUNCTION_NULL:
+ case FUNCTION_LANG:
+ case FUNCTION_NUMBER:
+ case FUNCTION_SUM:
+ case FUNCTION_FLOOR:
+ case FUNCTION_CEILING:
+ case FUNCTION_ROUND:
+ case FUNCTION_FORMAT_NUMBER:
+ case NCName:
+ case 87:
+ case 89:
+ axis = AxisSpecifier();
+ if (jj_2_3(2147483647)) {
+ type = NodeType();
+ jj_consume_token(81);
+ jj_consume_token(82);
+ } else if (jj_2_4(2147483647)) {
+ jj_consume_token(PI);
+ jj_consume_token(81);
+ jj_consume_token(Literal);
+ instruction = unescape(token.image.substring(1, token.image.length() - 1));
+ jj_consume_token(82);
+ } else {
+ switch (jj_nt.kind) {
+ case OR:
+ case AND:
+ case MOD:
+ case DIV:
+ case NODE:
+ case TEXT:
+ case COMMENT:
+ case PI:
+ case FUNCTION_LAST:
+ case FUNCTION_POSITION:
+ case FUNCTION_COUNT:
+ case FUNCTION_ID:
+ case FUNCTION_KEY:
+ case FUNCTION_LOCAL_NAME:
+ case FUNCTION_NAMESPACE_URI:
+ case FUNCTION_NAME:
+ case FUNCTION_STRING:
+ case FUNCTION_CONCAT:
+ case FUNCTION_STARTS_WITH:
+ case FUNCTION_ENDS_WITH:
+ case FUNCTION_CONTAINS:
+ case FUNCTION_SUBSTRING_BEFORE:
+ case FUNCTION_SUBSTRING_AFTER:
+ case FUNCTION_SUBSTRING:
+ case FUNCTION_STRING_LENGTH:
+ case FUNCTION_NORMALIZE_SPACE:
+ case FUNCTION_TRANSLATE:
+ case FUNCTION_BOOLEAN:
+ case FUNCTION_NOT:
+ case FUNCTION_TRUE:
+ case FUNCTION_FALSE:
+ case FUNCTION_NULL:
+ case FUNCTION_LANG:
+ case FUNCTION_NUMBER:
+ case FUNCTION_SUM:
+ case FUNCTION_FLOOR:
+ case FUNCTION_CEILING:
+ case FUNCTION_ROUND:
+ case FUNCTION_FORMAT_NUMBER:
+ case NCName:
+ case 89:
+ name = WildcardName();
+ break;
+ default:
+ jj_la1[10] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ break;
+ case 83:
+ jj_consume_token(83);
+ axis = Compiler.AXIS_SELF;
+ type = Compiler.NODE_TYPE_NODE;
+ break;
+ case 84:
+ jj_consume_token(84);
+ axis = Compiler.AXIS_PARENT;
+ type = Compiler.NODE_TYPE_NODE;
+ break;
+ default:
+ jj_la1[11] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ label_3:
+ while (true) {
+ switch (jj_nt.kind) {
+ case 85:
+ ;
+ break;
+ default:
+ jj_la1[12] = jj_gen;
+ break label_3;
+ }
+ p = Predicate();
+ ps.add(p);
+ }
+ if (name != null){
+ s = compiler.nodeNameTest(name);
+ }
+ else if (instruction != null){
+ s = compiler.processingInstructionTest(instruction);
+ }
+ else {
+ s = compiler.nodeTypeTest(type);
+ }
+ steps.add(compiler.step(axis, s, ps.toArray()));
+ }
+
+/* [5] AxisSpecifier ::= AxisName '::' | AbbreviatedAxisSpecifier */
+ final public int AxisSpecifier() throws ParseException {
+ int axis;
+ switch (jj_nt.kind) {
+ case AXIS_SELF:
+ case AXIS_CHILD:
+ case AXIS_PARENT:
+ case AXIS_ANCESTOR:
+ case AXIS_ATTRIBUTE:
+ case AXIS_NAMESPACE:
+ case AXIS_PRECEDING:
+ case AXIS_FOLLOWING:
+ case AXIS_DESCENDANT:
+ case AXIS_ANCESTOR_OR_SELF:
+ case AXIS_FOLLOWING_SIBLING:
+ case AXIS_PRECEDING_SIBLING:
+ case AXIS_DESCENDANT_OR_SELF:
+ axis = AxisName();
+ break;
+ default:
+ jj_la1[13] = jj_gen;
+ axis = AbbreviatedAxisSpecifier();
+ }
+ {if (true) {
+ return axis;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/*----------*/
+/* 2.2 Axes */
+/*----------*/
+
+/* [6] AxisName ::= 'ancestor' | 'ancestor-or-self' | 'attribute' | 'child' | 'descendant'
+ | 'descendant-or-self' | 'following' | 'following-sibling' | 'namespace'
+ | 'parent' | 'preceding' | 'preceding-sibling' | 'self'
+*/
+ final public int AxisName() throws ParseException {
+ int axis = 0;
+ switch (jj_nt.kind) {
+ case AXIS_SELF:
+ jj_consume_token(AXIS_SELF);
+ axis = Compiler.AXIS_SELF;
+ break;
+ case AXIS_CHILD:
+ jj_consume_token(AXIS_CHILD);
+ axis = Compiler.AXIS_CHILD;
+ break;
+ case AXIS_PARENT:
+ jj_consume_token(AXIS_PARENT);
+ axis = Compiler.AXIS_PARENT;
+ break;
+ case AXIS_ANCESTOR:
+ jj_consume_token(AXIS_ANCESTOR);
+ axis = Compiler.AXIS_ANCESTOR;
+ break;
+ case AXIS_ATTRIBUTE:
+ jj_consume_token(AXIS_ATTRIBUTE);
+ axis = Compiler.AXIS_ATTRIBUTE;
+ break;
+ case AXIS_NAMESPACE:
+ jj_consume_token(AXIS_NAMESPACE);
+ axis = Compiler.AXIS_NAMESPACE;
+ break;
+ case AXIS_PRECEDING:
+ jj_consume_token(AXIS_PRECEDING);
+ axis = Compiler.AXIS_PRECEDING;
+ break;
+ case AXIS_FOLLOWING:
+ jj_consume_token(AXIS_FOLLOWING);
+ axis = Compiler.AXIS_FOLLOWING;
+ break;
+ case AXIS_DESCENDANT:
+ jj_consume_token(AXIS_DESCENDANT);
+ axis = Compiler.AXIS_DESCENDANT;
+ break;
+ case AXIS_ANCESTOR_OR_SELF:
+ jj_consume_token(AXIS_ANCESTOR_OR_SELF);
+ axis = Compiler.AXIS_ANCESTOR_OR_SELF;
+ break;
+ case AXIS_FOLLOWING_SIBLING:
+ jj_consume_token(AXIS_FOLLOWING_SIBLING);
+ axis = Compiler.AXIS_FOLLOWING_SIBLING;
+ break;
+ case AXIS_PRECEDING_SIBLING:
+ jj_consume_token(AXIS_PRECEDING_SIBLING);
+ axis = Compiler.AXIS_PRECEDING_SIBLING;
+ break;
+ case AXIS_DESCENDANT_OR_SELF:
+ jj_consume_token(AXIS_DESCENDANT_OR_SELF);
+ axis = Compiler.AXIS_DESCENDANT_OR_SELF;
+ break;
+ default:
+ jj_la1[14] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ {if (true) {
+ return axis;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/*----------------*/
+/* 2.3 Node Tests */
+/*----------------*/
+
+/*----------------*/
+/* 2.4 Predicates */
+/*----------------*/
+
+/* [8] Predicate ::= '[' PredicateExpr ']' */
+/* [9] PredicateExpr ::= Expr */
+ final public Object Predicate() throws ParseException {
+ Object ex;
+ jj_consume_token(85);
+ ex = Expression();
+ jj_consume_token(86);
+ {if (true) {
+ return ex;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/* [12] AbbreviatedStep ::= '.' | '..' */
+
+/* [13] AbbreviatedAxisSpecifier ::= '@'? */
+ final public int AbbreviatedAxisSpecifier() throws ParseException {
+ int axis = Compiler.AXIS_CHILD;
+ switch (jj_nt.kind) {
+ case 87:
+ jj_consume_token(87);
+ axis = Compiler.AXIS_ATTRIBUTE;
+ break;
+ default:
+ jj_la1[15] = jj_gen;
+ ;
+ }
+ {if (true) {
+ return axis;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/*---------------*/
+/* 3 Expressions */
+/*---------------*/
+
+/*------------*/
+/* 3.1 Basics */
+/*------------*/
+
+/*
+The effect of the grammar is that the order of precedence is (lowest precedence first):
+ or
+ and
+ =, !=
+ <=, <, >=, >
+and all operators are left associative.
+For example, 3 > 2 > 1 is equivalent to (3 > 2) > 1, which evaluates to false.
+*/
+
+/* [14] Expr ::= OrExpr */
+ final public Object Expression() throws ParseException {
+ Object ex;
+ ex = OrExpr();
+ {if (true) {
+ return ex;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/* [15] PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall */
+ final public Object PrimaryExpr() throws ParseException {
+ Object ex = null;
+ switch (jj_nt.kind) {
+ case VARIABLE:
+ ex = VariableReference();
+ break;
+ case 81:
+ jj_consume_token(81);
+ ex = Expression();
+ jj_consume_token(82);
+ break;
+ case Literal:
+ jj_consume_token(Literal);
+ ex = compiler.literal(unescape(token.image.substring(1, token.image.length() - 1)));
+ break;
+ case Number:
+ jj_consume_token(Number);
+ ex = compiler.number(token.image);
+ break;
+ default:
+ jj_la1[16] = jj_gen;
+ if (jj_2_5(2147483647)) {
+ ex = CoreFunctionCall();
+ } else {
+ switch (jj_nt.kind) {
+ case OR:
+ case AND:
+ case MOD:
+ case DIV:
+ case NODE:
+ case TEXT:
+ case COMMENT:
+ case PI:
+ case FUNCTION_LAST:
+ case FUNCTION_POSITION:
+ case FUNCTION_COUNT:
+ case FUNCTION_ID:
+ case FUNCTION_KEY:
+ case FUNCTION_LOCAL_NAME:
+ case FUNCTION_NAMESPACE_URI:
+ case FUNCTION_NAME:
+ case FUNCTION_STRING:
+ case FUNCTION_CONCAT:
+ case FUNCTION_STARTS_WITH:
+ case FUNCTION_ENDS_WITH:
+ case FUNCTION_CONTAINS:
+ case FUNCTION_SUBSTRING_BEFORE:
+ case FUNCTION_SUBSTRING_AFTER:
+ case FUNCTION_SUBSTRING:
+ case FUNCTION_STRING_LENGTH:
+ case FUNCTION_NORMALIZE_SPACE:
+ case FUNCTION_TRANSLATE:
+ case FUNCTION_BOOLEAN:
+ case FUNCTION_NOT:
+ case FUNCTION_TRUE:
+ case FUNCTION_FALSE:
+ case FUNCTION_NULL:
+ case FUNCTION_LANG:
+ case FUNCTION_NUMBER:
+ case FUNCTION_SUM:
+ case FUNCTION_FLOOR:
+ case FUNCTION_CEILING:
+ case FUNCTION_ROUND:
+ case FUNCTION_FORMAT_NUMBER:
+ case NCName:
+ ex = FunctionCall();
+ break;
+ default:
+ jj_la1[17] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ }
+ {if (true) {
+ return ex;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/*--------------------*/
+/* 3.2 Function Calls */
+/*--------------------*/
+
+/* [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')' */
+ final public Object FunctionCall() throws ParseException {
+ Object name;
+ ArrayList args;
+ name = FunctionName();
+ args = ArgumentList();
+ if (args == null){
+ {if (true) {
+ return compiler.function(name, null);
+ }}
+ }
+ else {
+ {if (true) {
+ return compiler.function(name, args.toArray());
+ }}
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ final public Object CoreFunctionCall() throws ParseException {
+ int code = 0;
+ ArrayList args;
+ code = CoreFunctionName();
+ args = ArgumentList();
+ if (args == null){
+ {if (true) {
+ return compiler.function(code, null);
+ }}
+ }
+ else {
+ {if (true) {
+ return compiler.function(code, args.toArray());
+ }}
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ final public ArrayList ArgumentList() throws ParseException {
+ ArrayList args = null;
+ Object arg;
+ jj_consume_token(81);
+ switch (jj_nt.kind) {
+ case SLASH:
+ case SLASHSLASH:
+ case MINUS:
+ case VARIABLE:
+ case Literal:
+ case Number:
+ case OR:
+ case AND:
+ case MOD:
+ case DIV:
+ case NODE:
+ case TEXT:
+ case COMMENT:
+ case PI:
+ case AXIS_SELF:
+ case AXIS_CHILD:
+ case AXIS_PARENT:
+ case AXIS_ANCESTOR:
+ case AXIS_ATTRIBUTE:
+ case AXIS_NAMESPACE:
+ case AXIS_PRECEDING:
+ case AXIS_FOLLOWING:
+ case AXIS_DESCENDANT:
+ case AXIS_ANCESTOR_OR_SELF:
+ case AXIS_FOLLOWING_SIBLING:
+ case AXIS_PRECEDING_SIBLING:
+ case AXIS_DESCENDANT_OR_SELF:
+ case FUNCTION_LAST:
+ case FUNCTION_POSITION:
+ case FUNCTION_COUNT:
+ case FUNCTION_ID:
+ case FUNCTION_KEY:
+ case FUNCTION_LOCAL_NAME:
+ case FUNCTION_NAMESPACE_URI:
+ case FUNCTION_NAME:
+ case FUNCTION_STRING:
+ case FUNCTION_CONCAT:
+ case FUNCTION_STARTS_WITH:
+ case FUNCTION_ENDS_WITH:
+ case FUNCTION_CONTAINS:
+ case FUNCTION_SUBSTRING_BEFORE:
+ case FUNCTION_SUBSTRING_AFTER:
+ case FUNCTION_SUBSTRING:
+ case FUNCTION_STRING_LENGTH:
+ case FUNCTION_NORMALIZE_SPACE:
+ case FUNCTION_TRANSLATE:
+ case FUNCTION_BOOLEAN:
+ case FUNCTION_NOT:
+ case FUNCTION_TRUE:
+ case FUNCTION_FALSE:
+ case FUNCTION_NULL:
+ case FUNCTION_LANG:
+ case FUNCTION_NUMBER:
+ case FUNCTION_SUM:
+ case FUNCTION_FLOOR:
+ case FUNCTION_CEILING:
+ case FUNCTION_ROUND:
+ case FUNCTION_FORMAT_NUMBER:
+ case NCName:
+ case 81:
+ case 83:
+ case 84:
+ case 87:
+ case 89:
+ arg = Argument();
+ args = new ArrayList(); args.add(arg);
+ label_4:
+ while (true) {
+ switch (jj_nt.kind) {
+ case 88:
+ ;
+ break;
+ default:
+ jj_la1[18] = jj_gen;
+ break label_4;
+ }
+ jj_consume_token(88);
+ arg = Argument();
+ args.add(arg);
+ }
+ break;
+ default:
+ jj_la1[19] = jj_gen;
+ ;
+ }
+ jj_consume_token(82);
+ {if (true) {
+ return args;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/* [17] Argument ::= Expr */
+ final public Object Argument() throws ParseException {
+ Object ex;
+ ex = Expression();
+ {if (true) {
+ return ex;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/*---------------*/
+/* 3.3 Node-sets */
+/*---------------*/
+
+/* [18] UnionExpr ::= PathExpr | UnionExpr '|' PathExpr */
+ final public Object UnionExpr() throws ParseException {
+ Object ex, r;
+ ArrayList list = null;
+ ex = PathExpr();
+ label_5:
+ while (true) {
+ switch (jj_nt.kind) {
+ case UNION:
+ ;
+ break;
+ default:
+ jj_la1[20] = jj_gen;
+ break label_5;
+ }
+ jj_consume_token(UNION);
+ r = PathExpr();
+ if (list == null){
+ list = new ArrayList();
+ list.add(ex);
+ }
+ list.add(r);
+ }
+ if (list != null){
+ ex = compiler.union(list.toArray());
+ }
+ {if (true) {
+ return ex;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/* [19] PathExpr ::= LocationPath | FilterExpr | FilterExpr '/' RelativeLocationPath | FilterExpr '//' RelativeLocationPath */
+ final public Object PathExpr() throws ParseException {
+ Object ex = null;
+ final Object[] steps;
+ if (jj_2_6(2147483647)) {
+ ex = FilterExpr();
+ } else {
+ switch (jj_nt.kind) {
+ case SLASH:
+ case SLASHSLASH:
+ case OR:
+ case AND:
+ case MOD:
+ case DIV:
+ case NODE:
+ case TEXT:
+ case COMMENT:
+ case PI:
+ case AXIS_SELF:
+ case AXIS_CHILD:
+ case AXIS_PARENT:
+ case AXIS_ANCESTOR:
+ case AXIS_ATTRIBUTE:
+ case AXIS_NAMESPACE:
+ case AXIS_PRECEDING:
+ case AXIS_FOLLOWING:
+ case AXIS_DESCENDANT:
+ case AXIS_ANCESTOR_OR_SELF:
+ case AXIS_FOLLOWING_SIBLING:
+ case AXIS_PRECEDING_SIBLING:
+ case AXIS_DESCENDANT_OR_SELF:
+ case FUNCTION_LAST:
+ case FUNCTION_POSITION:
+ case FUNCTION_COUNT:
+ case FUNCTION_ID:
+ case FUNCTION_KEY:
+ case FUNCTION_LOCAL_NAME:
+ case FUNCTION_NAMESPACE_URI:
+ case FUNCTION_NAME:
+ case FUNCTION_STRING:
+ case FUNCTION_CONCAT:
+ case FUNCTION_STARTS_WITH:
+ case FUNCTION_ENDS_WITH:
+ case FUNCTION_CONTAINS:
+ case FUNCTION_SUBSTRING_BEFORE:
+ case FUNCTION_SUBSTRING_AFTER:
+ case FUNCTION_SUBSTRING:
+ case FUNCTION_STRING_LENGTH:
+ case FUNCTION_NORMALIZE_SPACE:
+ case FUNCTION_TRANSLATE:
+ case FUNCTION_BOOLEAN:
+ case FUNCTION_NOT:
+ case FUNCTION_TRUE:
+ case FUNCTION_FALSE:
+ case FUNCTION_NULL:
+ case FUNCTION_LANG:
+ case FUNCTION_NUMBER:
+ case FUNCTION_SUM:
+ case FUNCTION_FLOOR:
+ case FUNCTION_CEILING:
+ case FUNCTION_ROUND:
+ case FUNCTION_FORMAT_NUMBER:
+ case NCName:
+ case 83:
+ case 84:
+ case 87:
+ case 89:
+ ex = LocationPath();
+ break;
+ default:
+ jj_la1[21] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ {if (true) {
+ return ex;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/* [20] FilterExpr ::= PrimaryExpr | FilterExpr Predicate */
+ final public Object FilterExpr() throws ParseException {
+ Object ex, p;
+ final ArrayList ps = new ArrayList();
+ boolean path = false;
+ final ArrayList steps = new ArrayList();
+ ex = PrimaryExpr();
+ label_6:
+ while (true) {
+ switch (jj_nt.kind) {
+ case 85:
+ ;
+ break;
+ default:
+ jj_la1[22] = jj_gen;
+ break label_6;
+ }
+ p = Predicate();
+ path = true;
+ ps.add(p);
+ }
+ label_7:
+ while (true) {
+ switch (jj_nt.kind) {
+ case SLASH:
+ case SLASHSLASH:
+ ;
+ break;
+ default:
+ jj_la1[23] = jj_gen;
+ break label_7;
+ }
+ LocationStep(steps);
+ path = true;
+ }
+ if (path){
+ {if (true) {
+ return compiler.expressionPath(ex, ps.toArray(), steps.toArray());
+ }}
+ }
+ else {
+ {if (true) {
+ return ex;
+ }}
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+/*--------------*/
+/* 3.4 Booleans */
+/*--------------*/
+
+/* [21] OrExpr ::= AndExpr | OrExpr 'or' AndExpr */
+ final public Object OrExpr() throws ParseException {
+ Object ex, r;
+ ArrayList list = null;
+ ex = AndExpr();
+ label_8:
+ while (true) {
+ switch (jj_nt.kind) {
+ case OR:
+ ;
+ break;
+ default:
+ jj_la1[24] = jj_gen;
+ break label_8;
+ }
+ jj_consume_token(OR);
+ r = AndExpr();
+ if (list == null){
+ list = new ArrayList();
+ list.add(ex);
+ }
+ list.add(r);
+ }
+ if (list != null){
+ ex = compiler.or(list.toArray());
+ }
+ {if (true) {
+ return ex;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/* [22] AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr */
+ final public Object AndExpr() throws ParseException {
+ Object ex, r;
+ ArrayList list = null;
+ ex = EqualityExpr();
+ label_9:
+ while (true) {
+ switch (jj_nt.kind) {
+ case AND:
+ ;
+ break;
+ default:
+ jj_la1[25] = jj_gen;
+ break label_9;
+ }
+ jj_consume_token(AND);
+ r = EqualityExpr();
+ if (list == null){
+ list = new ArrayList();
+ list.add(ex);
+ }
+ list.add(r);
+ }
+ if (list != null){
+ ex = compiler.and(list.toArray());
+ }
+ {if (true) {
+ return ex;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/* [23] EqualityExpr ::= RelationalExpr | EqualityExpr '=' RelationalExpr | EqualityExpr '!=' RelationalExpr */
+ final public Object EqualityExpr() throws ParseException {
+ Object ex, r;
+ ex = RelationalExpr();
+ label_10:
+ while (true) {
+ switch (jj_nt.kind) {
+ case EQ:
+ case NEQ:
+ ;
+ break;
+ default:
+ jj_la1[26] = jj_gen;
+ break label_10;
+ }
+ switch (jj_nt.kind) {
+ case EQ:
+ jj_consume_token(EQ);
+ r = RelationalExpr();
+ ex = compiler.equal(ex, r);
+ break;
+ case NEQ:
+ jj_consume_token(NEQ);
+ r = RelationalExpr();
+ ex = compiler.notEqual(ex, r);
+ break;
+ default:
+ jj_la1[27] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ {if (true) {
+ return ex;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/* [24] RelationalExpr ::= AdditiveExpr | RelationalExpr '<' AdditiveExpr | RelationalExpr '>' AdditiveExpr
+ | RelationalExpr '<=' AdditiveExpr | RelationalExpr '>=' AdditiveExpr */
+ final public Object RelationalExpr() throws ParseException {
+ Object ex, r;
+ ex = AdditiveExpr();
+ label_11:
+ while (true) {
+ switch (jj_nt.kind) {
+ case LT:
+ case LTE:
+ case GT:
+ case GTE:
+ ;
+ break;
+ default:
+ jj_la1[28] = jj_gen;
+ break label_11;
+ }
+ switch (jj_nt.kind) {
+ case LT:
+ jj_consume_token(LT);
+ r = AdditiveExpr();
+ ex = compiler.lessThan(ex, r);
+ break;
+ case GT:
+ jj_consume_token(GT);
+ r = AdditiveExpr();
+ ex = compiler.greaterThan(ex, r);
+ break;
+ case LTE:
+ jj_consume_token(LTE);
+ r = AdditiveExpr();
+ ex = compiler.lessThanOrEqual(ex, r);
+ break;
+ case GTE:
+ jj_consume_token(GTE);
+ r = AdditiveExpr();
+ ex = compiler.greaterThanOrEqual(ex, r);
+ break;
+ default:
+ jj_la1[29] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ {if (true) {
+ return ex;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/*-------------*/
+/* 3.5 Numbers */
+/*-------------*/
+
+/* [25] AdditiveExpr ::= MultiplicativeExpr | AdditiveExpr '+' MultiplicativeExpr | AdditiveExpr '-' MultiplicativeExpr */
+ final public Object AdditiveExpr() throws ParseException {
+ Object ex, r;
+ ArrayList list = null;
+ ex = SubtractiveExpr();
+ label_12:
+ while (true) {
+ switch (jj_nt.kind) {
+ case PLUS:
+ ;
+ break;
+ default:
+ jj_la1[30] = jj_gen;
+ break label_12;
+ }
+ jj_consume_token(PLUS);
+ r = SubtractiveExpr();
+ if (list == null){
+ list = new ArrayList();
+ list.add(ex);
+ }
+ list.add(r);
+ }
+ if (list != null){
+ ex = compiler.sum(list.toArray());
+ }
+ {if (true) {
+ return ex;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+ final public Object SubtractiveExpr() throws ParseException {
+ Object ex, r = null;
+ ex = MultiplicativeExpr();
+ label_13:
+ while (true) {
+ switch (jj_nt.kind) {
+ case MINUS:
+ ;
+ break;
+ default:
+ jj_la1[31] = jj_gen;
+ break label_13;
+ }
+ jj_consume_token(MINUS);
+ r = MultiplicativeExpr();
+ ex = compiler.minus(ex, r);
+ }
+ {if (true) {
+ return ex;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/* [26] MultiplicativeExpr ::= UnaryExpr | MultiplicativeExpr MultiplyOperator UnaryExpr
+ | MultiplicativeExpr 'div' UnaryExpr | MultiplicativeExpr 'mod' UnaryExpr */
+ final public Object MultiplicativeExpr() throws ParseException {
+ Object ex, r;
+ ex = UnaryExpr();
+ label_14:
+ while (true) {
+ switch (jj_nt.kind) {
+ case MOD:
+ case DIV:
+ case 89:
+ ;
+ break;
+ default:
+ jj_la1[32] = jj_gen;
+ break label_14;
+ }
+ switch (jj_nt.kind) {
+ case 89:
+ jj_consume_token(89);
+ r = UnaryExpr();
+ ex = compiler.multiply(ex, r);
+ break;
+ case DIV:
+ jj_consume_token(DIV);
+ r = UnaryExpr();
+ ex = compiler.divide(ex, r);
+ break;
+ case MOD:
+ jj_consume_token(MOD);
+ r = UnaryExpr();
+ ex = compiler.mod(ex, r);
+ break;
+ default:
+ jj_la1[33] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ {if (true) {
+ return ex;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/* [27] UnaryExpr ::= UnionExpr | '-' UnaryExpr */
+ final public Object UnaryExpr() throws ParseException {
+ Object ex;
+ switch (jj_nt.kind) {
+ case SLASH:
+ case SLASHSLASH:
+ case VARIABLE:
+ case Literal:
+ case Number:
+ case OR:
+ case AND:
+ case MOD:
+ case DIV:
+ case NODE:
+ case TEXT:
+ case COMMENT:
+ case PI:
+ case AXIS_SELF:
+ case AXIS_CHILD:
+ case AXIS_PARENT:
+ case AXIS_ANCESTOR:
+ case AXIS_ATTRIBUTE:
+ case AXIS_NAMESPACE:
+ case AXIS_PRECEDING:
+ case AXIS_FOLLOWING:
+ case AXIS_DESCENDANT:
+ case AXIS_ANCESTOR_OR_SELF:
+ case AXIS_FOLLOWING_SIBLING:
+ case AXIS_PRECEDING_SIBLING:
+ case AXIS_DESCENDANT_OR_SELF:
+ case FUNCTION_LAST:
+ case FUNCTION_POSITION:
+ case FUNCTION_COUNT:
+ case FUNCTION_ID:
+ case FUNCTION_KEY:
+ case FUNCTION_LOCAL_NAME:
+ case FUNCTION_NAMESPACE_URI:
+ case FUNCTION_NAME:
+ case FUNCTION_STRING:
+ case FUNCTION_CONCAT:
+ case FUNCTION_STARTS_WITH:
+ case FUNCTION_ENDS_WITH:
+ case FUNCTION_CONTAINS:
+ case FUNCTION_SUBSTRING_BEFORE:
+ case FUNCTION_SUBSTRING_AFTER:
+ case FUNCTION_SUBSTRING:
+ case FUNCTION_STRING_LENGTH:
+ case FUNCTION_NORMALIZE_SPACE:
+ case FUNCTION_TRANSLATE:
+ case FUNCTION_BOOLEAN:
+ case FUNCTION_NOT:
+ case FUNCTION_TRUE:
+ case FUNCTION_FALSE:
+ case FUNCTION_NULL:
+ case FUNCTION_LANG:
+ case FUNCTION_NUMBER:
+ case FUNCTION_SUM:
+ case FUNCTION_FLOOR:
+ case FUNCTION_CEILING:
+ case FUNCTION_ROUND:
+ case FUNCTION_FORMAT_NUMBER:
+ case NCName:
+ case 81:
+ case 83:
+ case 84:
+ case 87:
+ case 89:
+ ex = UnionExpr();
+ break;
+ case MINUS:
+ jj_consume_token(MINUS);
+ ex = UnaryExpr();
+ ex = compiler.minus(ex);
+ break;
+ default:
+ jj_la1[34] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ {if (true) {
+ return ex;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/*-------------*/
+/* 3.6 Strings */
+/*-------------*/
+
+/*----------------------------------*/
+/* 3.7 Expression Lexical Structure */
+/*----------------------------------*/
+/*
+The following special tokenization rules must be applied in the order
+specified to disambiguate the grammar:
+
+1. If there is a preceding token and the preceding token is not one of
+ @, ::, (, [, , or an Operator,
+ then a * must be recognized as a MultiplyOperator and an NCName must
+ be recognized as an OperatorName.
+
+2. If the character following an NCName (possibly after intervening ExprWhitespace)
+ is (, then the token must be recognized as a NodeType or a FunctionName.
+
+3. If the two characters following an NCName (possibly after intervening ExprWhitespace)
+ are ::, then the token must be recognized as an AxisName.
+
+4. Otherwise, the token must not be recognized as a MultiplyOperator, an OperatorName,
+ a NodeType, a FunctionName, or an AxisName.
+*/
+
+/*
+[28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'
+ | WildcardName | NodeType | Operator | FunctionName | AxisName | Literal
+ | Number | VariableReference
+*/
+/* [34] MultiplyOperator ::= '*' */
+
+/* [35] FunctionName ::= QName - NodeType */
+ final public Object FunctionName() throws ParseException {
+ Object qname;
+ qname = QName_Without_CoreFunctions();
+ {if (true) {
+ return qname;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/* [36] VariableReference ::= '$' QName */
+ final public Object VariableReference() throws ParseException {
+ Object ex;
+ jj_consume_token(VARIABLE);
+ ex = QName();
+ {if (true) {
+ return compiler.variableReference(ex);
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/* [37] WildcardName ::= '*' | NCName ':' '*' | QName */
+ final public Object WildcardName() throws ParseException {
+ Object qn;
+ String nc1, nc2 = null;
+ switch (jj_nt.kind) {
+ case 89:
+ jj_consume_token(89);
+ break;
+ case OR:
+ case AND:
+ case MOD:
+ case DIV:
+ case NODE:
+ case TEXT:
+ case COMMENT:
+ case PI:
+ case FUNCTION_LAST:
+ case FUNCTION_POSITION:
+ case FUNCTION_COUNT:
+ case FUNCTION_ID:
+ case FUNCTION_KEY:
+ case FUNCTION_LOCAL_NAME:
+ case FUNCTION_NAMESPACE_URI:
+ case FUNCTION_NAME:
+ case FUNCTION_STRING:
+ case FUNCTION_CONCAT:
+ case FUNCTION_STARTS_WITH:
+ case FUNCTION_ENDS_WITH:
+ case FUNCTION_CONTAINS:
+ case FUNCTION_SUBSTRING_BEFORE:
+ case FUNCTION_SUBSTRING_AFTER:
+ case FUNCTION_SUBSTRING:
+ case FUNCTION_STRING_LENGTH:
+ case FUNCTION_NORMALIZE_SPACE:
+ case FUNCTION_TRANSLATE:
+ case FUNCTION_BOOLEAN:
+ case FUNCTION_NOT:
+ case FUNCTION_TRUE:
+ case FUNCTION_FALSE:
+ case FUNCTION_NULL:
+ case FUNCTION_LANG:
+ case FUNCTION_NUMBER:
+ case FUNCTION_SUM:
+ case FUNCTION_FLOOR:
+ case FUNCTION_CEILING:
+ case FUNCTION_ROUND:
+ case FUNCTION_FORMAT_NUMBER:
+ case NCName:
+ NCName();
+ break;
+ default:
+ jj_la1[35] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ nc1 = token.image;
+ switch (jj_nt.kind) {
+ case 80:
+ jj_consume_token(80);
+ switch (jj_nt.kind) {
+ case 89:
+ jj_consume_token(89);
+ break;
+ case OR:
+ case AND:
+ case MOD:
+ case DIV:
+ case NODE:
+ case TEXT:
+ case COMMENT:
+ case PI:
+ case FUNCTION_LAST:
+ case FUNCTION_POSITION:
+ case FUNCTION_COUNT:
+ case FUNCTION_ID:
+ case FUNCTION_KEY:
+ case FUNCTION_LOCAL_NAME:
+ case FUNCTION_NAMESPACE_URI:
+ case FUNCTION_NAME:
+ case FUNCTION_STRING:
+ case FUNCTION_CONCAT:
+ case FUNCTION_STARTS_WITH:
+ case FUNCTION_ENDS_WITH:
+ case FUNCTION_CONTAINS:
+ case FUNCTION_SUBSTRING_BEFORE:
+ case FUNCTION_SUBSTRING_AFTER:
+ case FUNCTION_SUBSTRING:
+ case FUNCTION_STRING_LENGTH:
+ case FUNCTION_NORMALIZE_SPACE:
+ case FUNCTION_TRANSLATE:
+ case FUNCTION_BOOLEAN:
+ case FUNCTION_NOT:
+ case FUNCTION_TRUE:
+ case FUNCTION_FALSE:
+ case FUNCTION_NULL:
+ case FUNCTION_LANG:
+ case FUNCTION_NUMBER:
+ case FUNCTION_SUM:
+ case FUNCTION_FLOOR:
+ case FUNCTION_CEILING:
+ case FUNCTION_ROUND:
+ case FUNCTION_FORMAT_NUMBER:
+ case NCName:
+ NCName();
+ break;
+ default:
+ jj_la1[36] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ nc2 = token.image;
+ break;
+ default:
+ jj_la1[37] = jj_gen;
+ ;
+ }
+ if (nc2 != null){
+ qn = compiler.qname(nc1, nc2);
+ }
+ else {
+ qn = compiler.qname(null, nc1);
+ }
+ {if (true) {
+ return qn;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+/* [38] NodeType ::= 'comment' | 'text' | 'processing-instruction' | 'node' */
+ final public int NodeType() throws ParseException {
+ int type;
+ switch (jj_nt.kind) {
+ case TEXT:
+ jj_consume_token(TEXT);
+ type = Compiler.NODE_TYPE_TEXT;
+ break;
+ case NODE:
+ jj_consume_token(NODE);
+ type = Compiler.NODE_TYPE_NODE;
+ break;
+ case COMMENT:
+ jj_consume_token(COMMENT);
+ type = Compiler.NODE_TYPE_COMMENT;
+ break;
+ case PI:
+ jj_consume_token(PI);
+ type = Compiler.NODE_TYPE_PI;
+ break;
+ default:
+ jj_la1[38] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ {if (true) {
+ return type;
+ }}
+ throw new Error("Missing return statement in function");
+ }
+
+ private boolean jj_2_1(final int xla) {
+ jj_la = xla; jj_lastpos = jj_scanpos = token;
+ try { return !jj_3_1(); }
+ catch (final LookaheadSuccess ls) { return true; }
+ finally { jj_save(0, xla); }
+ }
+
+ private boolean jj_2_2(final int xla) {
+ jj_la = xla; jj_lastpos = jj_scanpos = token;
+ try { return !jj_3_2(); }
+ catch (final LookaheadSuccess ls) { return true; }
+ finally { jj_save(1, xla); }
+ }
+
+ private boolean jj_2_3(final int xla) {
+ jj_la = xla; jj_lastpos = jj_scanpos = token;
+ try { return !jj_3_3(); }
+ catch (final LookaheadSuccess ls) { return true; }
+ finally { jj_save(2, xla); }
+ }
+
+ private boolean jj_2_4(final int xla) {
+ jj_la = xla; jj_lastpos = jj_scanpos = token;
+ try { return !jj_3_4(); }
+ catch (final LookaheadSuccess ls) { return true; }
+ finally { jj_save(3, xla); }
+ }
+
+ private boolean jj_2_5(final int xla) {
+ jj_la = xla; jj_lastpos = jj_scanpos = token;
+ try { return !jj_3_5(); }
+ catch (final LookaheadSuccess ls) { return true; }
+ finally { jj_save(4, xla); }
+ }
+
+ private boolean jj_2_6(final int xla) {
+ jj_la = xla; jj_lastpos = jj_scanpos = token;
+ try { return !jj_3_6(); }
+ catch (final LookaheadSuccess ls) { return true; }
+ finally { jj_save(5, xla); }
+ }
+
+ private boolean jj_3_2() {
+ if (jj_3R_16()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_144() {
+ if (jj_3R_16()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_143() {
+ if (jj_3R_77()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_150() {
+ if (jj_3R_16()) {
+ return true;
+ }
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_151()) { jj_scanpos = xsp; break; }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_132() {
+ if (jj_scan_token(MINUS)) {
+ return true;
+ }
+ if (jj_3R_129()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_130() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_133()) {
+ jj_scanpos = xsp;
+ if (jj_3R_134()) {
+ jj_scanpos = xsp;
+ if (jj_3R_135()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_148() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_150()) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(6)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_131() {
+ if (jj_3R_136()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_64() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_scan_token(79)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(27)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(28)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(29)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(30)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_113() {
+ if (jj_scan_token(87)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_91() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_113()) {
+ jj_scanpos = xsp;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_141() {
+ if (jj_3R_19()) {
+ return true;
+ }
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_143()) { jj_scanpos = xsp; break; }
+ }
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_144()) { jj_scanpos = xsp; break; }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_129() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_131()) {
+ jj_scanpos = xsp;
+ if (jj_3R_132()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3_6() {
+ if (jj_3R_19()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_146() {
+ if (jj_3R_148()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_135() {
+ if (jj_scan_token(MOD)) {
+ return true;
+ }
+ if (jj_3R_129()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_145() {
+ if (jj_3R_147()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_134() {
+ if (jj_scan_token(DIV)) {
+ return true;
+ }
+ if (jj_3R_129()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_133() {
+ if (jj_scan_token(89)) {
+ return true;
+ }
+ if (jj_3R_129()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_142() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_145()) {
+ jj_scanpos = xsp;
+ if (jj_3R_146()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_140() {
+ if (jj_3R_142()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_139() {
+ if (jj_3R_141()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_77() {
+ if (jj_scan_token(85)) {
+ return true;
+ }
+ if (jj_3R_70()) {
+ return true;
+ }
+ if (jj_scan_token(86)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_127() {
+ if (jj_3R_129()) {
+ return true;
+ }
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_130()) { jj_scanpos = xsp; break; }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_137() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_139()) {
+ jj_scanpos = xsp;
+ if (jj_3R_140()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_128() {
+ if (jj_scan_token(MINUS)) {
+ return true;
+ }
+ if (jj_3R_127()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_112() {
+ if (jj_scan_token(AXIS_DESCENDANT_OR_SELF)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_111() {
+ if (jj_scan_token(AXIS_PRECEDING_SIBLING)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_110() {
+ if (jj_scan_token(AXIS_FOLLOWING_SIBLING)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_122() {
+ if (jj_scan_token(PLUS)) {
+ return true;
+ }
+ if (jj_3R_121()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_109() {
+ if (jj_scan_token(AXIS_ANCESTOR_OR_SELF)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_108() {
+ if (jj_scan_token(AXIS_DESCENDANT)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_107() {
+ if (jj_scan_token(AXIS_FOLLOWING)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_106() {
+ if (jj_scan_token(AXIS_PRECEDING)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_121() {
+ if (jj_3R_127()) {
+ return true;
+ }
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_128()) { jj_scanpos = xsp; break; }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_105() {
+ if (jj_scan_token(AXIS_NAMESPACE)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_104() {
+ if (jj_scan_token(AXIS_ATTRIBUTE)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_103() {
+ if (jj_scan_token(AXIS_ANCESTOR)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_20() {
+ if (jj_3R_64()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_102() {
+ if (jj_scan_token(AXIS_PARENT)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_101() {
+ if (jj_scan_token(AXIS_CHILD)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_100() {
+ if (jj_scan_token(AXIS_SELF)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_138() {
+ if (jj_scan_token(UNION)) {
+ return true;
+ }
+ if (jj_3R_137()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_15() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_20()) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(31)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(32)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(33)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(34)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(48)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(49)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(50)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(51)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(53)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(54)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(55)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(56)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(57)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(58)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(59)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(60)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(61)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(62)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(63)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(64)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(65)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(66)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(67)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(68)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(69)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(70)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(71)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(72)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(73)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(74)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(75)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(76)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(77)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(52)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(78)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_90() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_100()) {
+ jj_scanpos = xsp;
+ if (jj_3R_101()) {
+ jj_scanpos = xsp;
+ if (jj_3R_102()) {
+ jj_scanpos = xsp;
+ if (jj_3R_103()) {
+ jj_scanpos = xsp;
+ if (jj_3R_104()) {
+ jj_scanpos = xsp;
+ if (jj_3R_105()) {
+ jj_scanpos = xsp;
+ if (jj_3R_106()) {
+ jj_scanpos = xsp;
+ if (jj_3R_107()) {
+ jj_scanpos = xsp;
+ if (jj_3R_108()) {
+ jj_scanpos = xsp;
+ if (jj_3R_109()) {
+ jj_scanpos = xsp;
+ if (jj_3R_110()) {
+ jj_scanpos = xsp;
+ if (jj_3R_111()) {
+ jj_scanpos = xsp;
+ if (jj_3R_112()) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_136() {
+ if (jj_3R_137()) {
+ return true;
+ }
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_138()) { jj_scanpos = xsp; break; }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_118() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_123()) {
+ jj_scanpos = xsp;
+ if (jj_3R_124()) {
+ jj_scanpos = xsp;
+ if (jj_3R_125()) {
+ jj_scanpos = xsp;
+ if (jj_3R_126()) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_117() {
+ if (jj_3R_121()) {
+ return true;
+ }
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_122()) { jj_scanpos = xsp; break; }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_83() {
+ if (jj_3R_91()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_82() {
+ if (jj_3R_90()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_96() {
+ if (jj_3R_70()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_126() {
+ if (jj_scan_token(GTE)) {
+ return true;
+ }
+ if (jj_3R_117()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_97() {
+ if (jj_scan_token(88)) {
+ return true;
+ }
+ if (jj_3R_96()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_125() {
+ if (jj_scan_token(LTE)) {
+ return true;
+ }
+ if (jj_3R_117()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_73() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_82()) {
+ jj_scanpos = xsp;
+ if (jj_3R_83()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_124() {
+ if (jj_scan_token(GT)) {
+ return true;
+ }
+ if (jj_3R_117()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_116() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_119()) {
+ jj_scanpos = xsp;
+ if (jj_3R_120()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_123() {
+ if (jj_scan_token(LT)) {
+ return true;
+ }
+ if (jj_3R_117()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_88() {
+ if (jj_3R_96()) {
+ return true;
+ }
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_97()) { jj_scanpos = xsp; break; }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_115() {
+ if (jj_3R_117()) {
+ return true;
+ }
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_118()) { jj_scanpos = xsp; break; }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_26() {
+ if (jj_scan_token(PI)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_80() {
+ if (jj_scan_token(81)) {
+ return true;
+ }
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_88()) {
+ jj_scanpos = xsp;
+ }
+ if (jj_scan_token(82)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_25() {
+ if (jj_scan_token(COMMENT)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_24() {
+ if (jj_scan_token(NODE)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_120() {
+ if (jj_scan_token(NEQ)) {
+ return true;
+ }
+ if (jj_3R_115()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_23() {
+ if (jj_scan_token(TEXT)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_119() {
+ if (jj_scan_token(EQ)) {
+ return true;
+ }
+ if (jj_3R_115()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_68() {
+ if (jj_3R_77()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3_1() {
+ if (jj_3R_15()) {
+ return true;
+ }
+ if (jj_scan_token(80)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3_4() {
+ if (jj_scan_token(PI)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3_3() {
+ if (jj_3R_17()) {
+ return true;
+ }
+ if (jj_scan_token(81)) {
+ return true;
+ }
+ if (jj_scan_token(82)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_17() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_23()) {
+ jj_scanpos = xsp;
+ if (jj_3R_24()) {
+ jj_scanpos = xsp;
+ if (jj_3R_25()) {
+ jj_scanpos = xsp;
+ if (jj_3R_26()) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_67() {
+ if (jj_scan_token(84)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_114() {
+ if (jj_3R_15()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_66() {
+ if (jj_scan_token(83)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_94() {
+ if (jj_3R_115()) {
+ return true;
+ }
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_116()) { jj_scanpos = xsp; break; }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_76() {
+ if (jj_3R_84()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_99() {
+ if (jj_3R_64()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_98() {
+ if (jj_3R_15()) {
+ return true;
+ }
+ if (jj_scan_token(80)) {
+ return true;
+ }
+ if (jj_3R_15()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_75() {
+ if (jj_scan_token(PI)) {
+ return true;
+ }
+ if (jj_scan_token(81)) {
+ return true;
+ }
+ if (jj_scan_token(Literal)) {
+ return true;
+ }
+ if (jj_scan_token(82)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_92() {
+ if (jj_3R_15()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_74() {
+ if (jj_3R_17()) {
+ return true;
+ }
+ if (jj_scan_token(81)) {
+ return true;
+ }
+ if (jj_scan_token(82)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_71() {
+ if (jj_3R_18()) {
+ return true;
+ }
+ if (jj_3R_80()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_93() {
+ if (jj_scan_token(80)) {
+ return true;
+ }
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_scan_token(89)) {
+ jj_scanpos = xsp;
+ if (jj_3R_114()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_65() {
+ if (jj_3R_73()) {
+ return true;
+ }
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_74()) {
+ jj_scanpos = xsp;
+ if (jj_3R_75()) {
+ jj_scanpos = xsp;
+ if (jj_3R_76()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_85() {
+ if (jj_scan_token(80)) {
+ return true;
+ }
+ if (jj_3R_15()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_89() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_98()) {
+ jj_scanpos = xsp;
+ if (jj_3R_99()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_95() {
+ if (jj_scan_token(AND)) {
+ return true;
+ }
+ if (jj_3R_94()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_84() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_scan_token(89)) {
+ jj_scanpos = xsp;
+ if (jj_3R_92()) {
+ return true;
+ }
+ }
+ xsp = jj_scanpos;
+ if (jj_3R_93()) {
+ jj_scanpos = xsp;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_22() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_65()) {
+ jj_scanpos = xsp;
+ if (jj_3R_66()) {
+ jj_scanpos = xsp;
+ if (jj_3R_67()) {
+ return true;
+ }
+ }
+ }
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_68()) { jj_scanpos = xsp; break; }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_72() {
+ if (jj_3R_81()) {
+ return true;
+ }
+ if (jj_3R_80()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_86() {
+ if (jj_3R_94()) {
+ return true;
+ }
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_95()) { jj_scanpos = xsp; break; }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_151() {
+ if (jj_3R_16()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3_5() {
+ if (jj_3R_18()) {
+ return true;
+ }
+ if (jj_scan_token(81)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_78() {
+ if (jj_3R_15()) {
+ return true;
+ }
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_85()) {
+ jj_scanpos = xsp;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_69() {
+ if (jj_scan_token(VARIABLE)) {
+ return true;
+ }
+ if (jj_3R_78()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_57() {
+ if (jj_scan_token(FUNCTION_FORMAT_NUMBER)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_56() {
+ if (jj_scan_token(FUNCTION_KEY)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_55() {
+ if (jj_scan_token(FUNCTION_ROUND)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_63() {
+ if (jj_3R_72()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_54() {
+ if (jj_scan_token(FUNCTION_CEILING)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_62() {
+ if (jj_3R_71()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_53() {
+ if (jj_scan_token(FUNCTION_FLOOR)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_61() {
+ if (jj_scan_token(Number)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_52() {
+ if (jj_scan_token(FUNCTION_SUM)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_21() {
+ if (jj_scan_token(SLASHSLASH)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_60() {
+ if (jj_scan_token(Literal)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_51() {
+ if (jj_scan_token(FUNCTION_NUMBER)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_59() {
+ if (jj_scan_token(81)) {
+ return true;
+ }
+ if (jj_3R_70()) {
+ return true;
+ }
+ if (jj_scan_token(82)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_87() {
+ if (jj_scan_token(OR)) {
+ return true;
+ }
+ if (jj_3R_86()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_50() {
+ if (jj_scan_token(FUNCTION_LANG)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_58() {
+ if (jj_3R_69()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_81() {
+ if (jj_3R_89()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_49() {
+ if (jj_scan_token(FUNCTION_NULL)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_48() {
+ if (jj_scan_token(FUNCTION_FALSE)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_47() {
+ if (jj_scan_token(FUNCTION_TRUE)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_46() {
+ if (jj_scan_token(FUNCTION_NOT)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_16() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_scan_token(6)) {
+ jj_scanpos = xsp;
+ if (jj_3R_21()) {
+ return true;
+ }
+ }
+ if (jj_3R_22()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_149() {
+ if (jj_3R_16()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_45() {
+ if (jj_scan_token(FUNCTION_BOOLEAN)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_44() {
+ if (jj_scan_token(FUNCTION_TRANSLATE)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_19() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_58()) {
+ jj_scanpos = xsp;
+ if (jj_3R_59()) {
+ jj_scanpos = xsp;
+ if (jj_3R_60()) {
+ jj_scanpos = xsp;
+ if (jj_3R_61()) {
+ jj_scanpos = xsp;
+ if (jj_3R_62()) {
+ jj_scanpos = xsp;
+ if (jj_3R_63()) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_43() {
+ if (jj_scan_token(FUNCTION_NORMALIZE_SPACE)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_42() {
+ if (jj_scan_token(FUNCTION_STRING_LENGTH)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_41() {
+ if (jj_scan_token(FUNCTION_SUBSTRING)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_40() {
+ if (jj_scan_token(FUNCTION_SUBSTRING_AFTER)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_79() {
+ if (jj_3R_86()) {
+ return true;
+ }
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_87()) { jj_scanpos = xsp; break; }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_39() {
+ if (jj_scan_token(FUNCTION_SUBSTRING_BEFORE)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_38() {
+ if (jj_scan_token(FUNCTION_CONTAINS)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_37() {
+ if (jj_scan_token(FUNCTION_ENDS_WITH)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_36() {
+ if (jj_scan_token(FUNCTION_STARTS_WITH)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_35() {
+ if (jj_scan_token(FUNCTION_CONCAT)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_34() {
+ if (jj_scan_token(FUNCTION_STRING)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_33() {
+ if (jj_scan_token(FUNCTION_NAME)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_32() {
+ if (jj_scan_token(FUNCTION_NAMESPACE_URI)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_31() {
+ if (jj_scan_token(FUNCTION_LOCAL_NAME)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_70() {
+ if (jj_3R_79()) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_30() {
+ if (jj_scan_token(FUNCTION_ID)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_29() {
+ if (jj_scan_token(FUNCTION_COUNT)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_28() {
+ if (jj_scan_token(FUNCTION_POSITION)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_27() {
+ if (jj_scan_token(FUNCTION_LAST)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_147() {
+ if (jj_3R_22()) {
+ return true;
+ }
+ Token xsp;
+ while (true) {
+ xsp = jj_scanpos;
+ if (jj_3R_149()) { jj_scanpos = xsp; break; }
+ }
+ return false;
+ }
+
+ private boolean jj_3R_18() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_27()) {
+ jj_scanpos = xsp;
+ if (jj_3R_28()) {
+ jj_scanpos = xsp;
+ if (jj_3R_29()) {
+ jj_scanpos = xsp;
+ if (jj_3R_30()) {
+ jj_scanpos = xsp;
+ if (jj_3R_31()) {
+ jj_scanpos = xsp;
+ if (jj_3R_32()) {
+ jj_scanpos = xsp;
+ if (jj_3R_33()) {
+ jj_scanpos = xsp;
+ if (jj_3R_34()) {
+ jj_scanpos = xsp;
+ if (jj_3R_35()) {
+ jj_scanpos = xsp;
+ if (jj_3R_36()) {
+ jj_scanpos = xsp;
+ if (jj_3R_37()) {
+ jj_scanpos = xsp;
+ if (jj_3R_38()) {
+ jj_scanpos = xsp;
+ if (jj_3R_39()) {
+ jj_scanpos = xsp;
+ if (jj_3R_40()) {
+ jj_scanpos = xsp;
+ if (jj_3R_41()) {
+ jj_scanpos = xsp;
+ if (jj_3R_42()) {
+ jj_scanpos = xsp;
+ if (jj_3R_43()) {
+ jj_scanpos = xsp;
+ if (jj_3R_44()) {
+ jj_scanpos = xsp;
+ if (jj_3R_45()) {
+ jj_scanpos = xsp;
+ if (jj_3R_46()) {
+ jj_scanpos = xsp;
+ if (jj_3R_47()) {
+ jj_scanpos = xsp;
+ if (jj_3R_48()) {
+ jj_scanpos = xsp;
+ if (jj_3R_49()) {
+ jj_scanpos = xsp;
+ if (jj_3R_50()) {
+ jj_scanpos = xsp;
+ if (jj_3R_51()) {
+ jj_scanpos = xsp;
+ if (jj_3R_52()) {
+ jj_scanpos = xsp;
+ if (jj_3R_53()) {
+ jj_scanpos = xsp;
+ if (jj_3R_54()) {
+ jj_scanpos = xsp;
+ if (jj_3R_55()) {
+ jj_scanpos = xsp;
+ if (jj_3R_56()) {
+ jj_scanpos = xsp;
+ if (jj_3R_57()) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public XPathParserTokenManager token_source;
+ SimpleCharStream jj_input_stream;
+ public Token token, jj_nt;
+ private Token jj_scanpos, jj_lastpos;
+ private int jj_la;
+ private boolean jj_semLA;
+ private int jj_gen;
+ final private int[] jj_la1 = new int[39];
+ static private int[] jj_la1_0;
+ static private int[] jj_la1_1;
+ static private int[] jj_la1_2;
+ static {
+ jj_la1_0();
+ jj_la1_1();
+ jj_la1_2();
+ }
+ private static void jj_la1_0() {
+ jj_la1_0 = new int[] {0xf8000000,0x78000000,0x0,0x0,0x78000000,0xf80000c0,0xc0,0x40,0xc0,0xc0,0xf8000000,0xf8000000,0x0,0x0,0x0,0x0,0x160000,0xf8000000,0x0,0xf81604c0,0x100,0xf80000c0,0x0,0xc0,0x8000000,0x10000000,0x1800,0x1800,0x1e000,0x1e000,0x200,0x400,0x60000000,0x60000000,0xf81604c0,0xf8000000,0xf8000000,0x0,0x80000000,};
+ }
+ private static void jj_la1_1() {
+ jj_la1_1 = new int[] {0xffff0007,0x0,0xffff0000,0x0,0x0,0xffffffff,0x0,0x0,0x0,0x0,0xffff0007,0xffffffff,0x0,0xfff8,0xfff8,0x0,0x0,0xffff0007,0x0,0xffffffff,0x0,0xffffffff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffffffff,0xffff0007,0xffff0007,0x0,0x7,};
+ }
+ private static void jj_la1_2() {
+ jj_la1_2 = new int[] {0xffff,0x8000,0x7fff,0x10000,0x8000,0x298ffff,0x0,0x0,0x0,0x0,0x200ffff,0x298ffff,0x200000,0x0,0x0,0x800000,0x20000,0xffff,0x1000000,0x29affff,0x0,0x298ffff,0x200000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2000000,0x2000000,0x29affff,0x200ffff,0x200ffff,0x10000,0x0,};
+ }
+ final private JJCalls[] jj_2_rtns = new JJCalls[6];
+ private boolean jj_rescan = false;
+ private int jj_gc = 0;
+
+ public XPathParser(final java.io.Reader stream) {
+ jj_input_stream = new SimpleCharStream(stream, 1, 1);
+ token_source = new XPathParserTokenManager(jj_input_stream);
+ token = new Token();
+ token.next = jj_nt = token_source.getNextToken();
+ jj_gen = 0;
+ for (int i = 0; i < 39; i++) {
+ jj_la1[i] = -1;
+ }
+ for (int i = 0; i < jj_2_rtns.length; i++) {
+ jj_2_rtns[i] = new JJCalls();
+ }
+ }
+
+ public void ReInit(final java.io.Reader stream) {
+ jj_input_stream.ReInit(stream, 1, 1);
+ token_source.ReInit(jj_input_stream);
+ token = new Token();
+ token.next = jj_nt = token_source.getNextToken();
+ jj_gen = 0;
+ for (int i = 0; i < 39; i++) {
+ jj_la1[i] = -1;
+ }
+ for (int i = 0; i < jj_2_rtns.length; i++) {
+ jj_2_rtns[i] = new JJCalls();
+ }
+ }
+
+ private Token jj_consume_token(final int kind) throws ParseException {
+ final Token oldToken = token;
+ if ((token = jj_nt).next != null) {
+ jj_nt = jj_nt.next;
+ } else {
+ jj_nt = jj_nt.next = token_source.getNextToken();
+ }
+ if (token.kind == kind) {
+ jj_gen++;
+ if (++jj_gc > 100) {
+ jj_gc = 0;
+ for (final JJCalls jj_2_rtn : jj_2_rtns) {
+ JJCalls c = jj_2_rtn;
+ while (c != null) {
+ if (c.gen < jj_gen) {
+ c.first = null;
+ }
+ c = c.next;
+ }
+ }
+ }
+ return token;
+ }
+ jj_nt = token;
+ token = oldToken;
+ jj_kind = kind;
+ throw generateParseException();
+ }
+
+ static private final class LookaheadSuccess extends Error {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L; }
+ final private LookaheadSuccess jj_ls = new LookaheadSuccess();
+ private boolean jj_scan_token(final int kind) {
+ if (jj_scanpos == jj_lastpos) {
+ jj_la--;
+ if (jj_scanpos.next == null) {
+ jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken();
+ } else {
+ jj_lastpos = jj_scanpos = jj_scanpos.next;
+ }
+ } else {
+ jj_scanpos = jj_scanpos.next;
+ }
+ if (jj_rescan) {
+ int i = 0; Token tok = token;
+ while (tok != null && tok != jj_scanpos) { i++; tok = tok.next; }
+ if (tok != null) {
+ jj_add_error_token(kind, i);
+ }
+ }
+ if (jj_scanpos.kind != kind) {
+ return true;
+ }
+ if (jj_la == 0 && jj_scanpos == jj_lastpos) {
+ throw jj_ls;
+ }
+ return false;
+ }
+
+ final public Token getNextToken() {
+ if ((token = jj_nt).next != null) {
+ jj_nt = jj_nt.next;
+ } else {
+ jj_nt = jj_nt.next = token_source.getNextToken();
+ }
+ jj_gen++;
+ return token;
+ }
+
+ private final java.util.Vector jj_expentries = new java.util.Vector();
+ private int[] jj_expentry;
+ private int jj_kind = -1;
+ private final int[] jj_lasttokens = new int[100];
+ private int jj_endpos;
+
+ private void jj_add_error_token(final int kind, final int pos) {
+ if (pos >= 100) {
+ return;
+ }
+ if (pos == jj_endpos + 1) {
+ jj_lasttokens[jj_endpos++] = kind;
+ } else if (jj_endpos != 0) {
+ jj_expentry = new int[jj_endpos];
+ for (int i = 0; i < jj_endpos; i++) {
+ jj_expentry[i] = jj_lasttokens[i];
+ }
+ boolean exists = false;
+ for (final java.util.Enumeration e = jj_expentries.elements(); e.hasMoreElements();) {
+ final int[] oldentry = (int[])e.nextElement();
+ if (oldentry.length == jj_expentry.length) {
+ exists = true;
+ for (int i = 0; i < jj_expentry.length; i++) {
+ if (oldentry[i] != jj_expentry[i]) {
+ exists = false;
+ break;
+ }
+ }
+ if (exists) {
+ break;
+ }
+ }
+ }
+ if (!exists) {
+ jj_expentries.addElement(jj_expentry);
+ }
+ if (pos != 0) {
+ jj_lasttokens[(jj_endpos = pos) - 1] = kind;
+ }
+ }
+ }
+
+ public ParseException generateParseException() {
+ jj_expentries.removeAllElements();
+ final boolean[] la1tokens = new boolean[90];
+ for (int i = 0; i < 90; i++) {
+ la1tokens[i] = false;
+ }
+ if (jj_kind >= 0) {
+ la1tokens[jj_kind] = true;
+ jj_kind = -1;
+ }
+ for (int i = 0; i < 39; i++) {
+ if (jj_la1[i] == jj_gen) {
+ for (int j = 0; j < 32; j++) {
+ if ((jj_la1_0[i] & 1< jj_gen) {
+ jj_la = p.arg; jj_lastpos = jj_scanpos = p.first;
+ switch (i) {
+ case 0: jj_3_1(); break;
+ case 1: jj_3_2(); break;
+ case 2: jj_3_3(); break;
+ case 3: jj_3_4(); break;
+ case 4: jj_3_5(); break;
+ case 5: jj_3_6(); break;
+ }
+ }
+ p = p.next;
+ } while (p != null);
+ }
+ jj_rescan = false;
+ }
+
+ private void jj_save(final int index, final int xla) {
+ JJCalls p = jj_2_rtns[index];
+ while (p.gen > jj_gen) {
+ if (p.next == null) { p = p.next = new JJCalls(); break; }
+ p = p.next;
+ }
+ p.gen = jj_gen + xla - jj_la; p.first = token; p.arg = xla;
+ }
+
+ static final class JJCalls {
+ int gen;
+ Token first;
+ int arg;
+ JJCalls next;
+ }
+
+ }
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/XPathParserConstants.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/XPathParserConstants.java
new file mode 100644
index 00000000000..4342b5c3e50
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/XPathParserConstants.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Generated By:JavaCC: Do not edit this line. XPathParserConstants.java */
+package org.apache.commons.jxpath.ri.parser;
+
+public interface XPathParserConstants {
+
+ int SLASH = 6;
+ int SLASHSLASH = 7;
+ int UNION = 8;
+ int PLUS = 9;
+ int MINUS = 10;
+ int EQ = 11;
+ int NEQ = 12;
+ int LT = 13;
+ int LTE = 14;
+ int GT = 15;
+ int GTE = 16;
+ int VARIABLE = 17;
+ int Literal = 18;
+ int Number = 20;
+ int OR = 27;
+ int AND = 28;
+ int MOD = 29;
+ int DIV = 30;
+ int NODE = 31;
+ int TEXT = 32;
+ int COMMENT = 33;
+ int PI = 34;
+ int AXIS_SELF = 35;
+ int AXIS_CHILD = 36;
+ int AXIS_PARENT = 37;
+ int AXIS_ANCESTOR = 38;
+ int AXIS_ATTRIBUTE = 39;
+ int AXIS_NAMESPACE = 40;
+ int AXIS_PRECEDING = 41;
+ int AXIS_FOLLOWING = 42;
+ int AXIS_DESCENDANT = 43;
+ int AXIS_ANCESTOR_OR_SELF = 44;
+ int AXIS_FOLLOWING_SIBLING = 45;
+ int AXIS_PRECEDING_SIBLING = 46;
+ int AXIS_DESCENDANT_OR_SELF = 47;
+ int FUNCTION_LAST = 48;
+ int FUNCTION_POSITION = 49;
+ int FUNCTION_COUNT = 50;
+ int FUNCTION_ID = 51;
+ int FUNCTION_KEY = 52;
+ int FUNCTION_LOCAL_NAME = 53;
+ int FUNCTION_NAMESPACE_URI = 54;
+ int FUNCTION_NAME = 55;
+ int FUNCTION_STRING = 56;
+ int FUNCTION_CONCAT = 57;
+ int FUNCTION_STARTS_WITH = 58;
+ int FUNCTION_ENDS_WITH = 59;
+ int FUNCTION_CONTAINS = 60;
+ int FUNCTION_SUBSTRING_BEFORE = 61;
+ int FUNCTION_SUBSTRING_AFTER = 62;
+ int FUNCTION_SUBSTRING = 63;
+ int FUNCTION_STRING_LENGTH = 64;
+ int FUNCTION_NORMALIZE_SPACE = 65;
+ int FUNCTION_TRANSLATE = 66;
+ int FUNCTION_BOOLEAN = 67;
+ int FUNCTION_NOT = 68;
+ int FUNCTION_TRUE = 69;
+ int FUNCTION_FALSE = 70;
+ int FUNCTION_NULL = 71;
+ int FUNCTION_LANG = 72;
+ int FUNCTION_NUMBER = 73;
+ int FUNCTION_SUM = 74;
+ int FUNCTION_FLOOR = 75;
+ int FUNCTION_CEILING = 76;
+ int FUNCTION_ROUND = 77;
+ int FUNCTION_FORMAT_NUMBER = 78;
+ int NCName = 79;
+
+ String[] tokenImage = {
+ "",
+ "\" \"",
+ "\"\\t\"",
+ "\"\\n\"",
+ "\"\\r\"",
+ "\"\\f\"",
+ "\"/\"",
+ "\"//\"",
+ "\"|\"",
+ "\"+\"",
+ "\"-\"",
+ "\"=\"",
+ "\"!=\"",
+ "\"<\"",
+ "\"<=\"",
+ "\">\"",
+ "\">=\"",
+ "\"$\"",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "\"or\"",
+ "\"and\"",
+ "\"mod\"",
+ "\"div\"",
+ "\"node\"",
+ "\"text\"",
+ "\"comment\"",
+ "\"processing-instruction\"",
+ "\"self::\"",
+ "\"child::\"",
+ "\"parent::\"",
+ "\"ancestor::\"",
+ "\"attribute::\"",
+ "\"namespace::\"",
+ "\"preceding::\"",
+ "\"following::\"",
+ "\"descendant::\"",
+ "\"ancestor-or-self::\"",
+ "\"following-sibling::\"",
+ "\"preceding-sibling::\"",
+ "\"descendant-or-self::\"",
+ "\"last\"",
+ "\"position\"",
+ "\"count\"",
+ "\"id\"",
+ "\"key\"",
+ "\"local-name\"",
+ "\"namespace-uri\"",
+ "\"name\"",
+ "\"string\"",
+ "\"concat\"",
+ "\"starts-with\"",
+ "\"ends-with\"",
+ "\"contains\"",
+ "\"substring-before\"",
+ "\"substring-after\"",
+ "\"substring\"",
+ "\"string-length\"",
+ "\"normalize-space\"",
+ "\"translate\"",
+ "\"boolean\"",
+ "\"not\"",
+ "\"true\"",
+ "\"false\"",
+ "\"null\"",
+ "\"lang\"",
+ "\"number\"",
+ "\"sum\"",
+ "\"floor\"",
+ "\"ceiling\"",
+ "\"round\"",
+ "\"format-number\"",
+ "",
+ "\":\"",
+ "\"(\"",
+ "\")\"",
+ "\".\"",
+ "\"..\"",
+ "\"[\"",
+ "\"]\"",
+ "\"@\"",
+ "\",\"",
+ "\"*\"",
+ };
+
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/XPathParserTokenManager.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/XPathParserTokenManager.java
new file mode 100644
index 00000000000..2c3c6f316c4
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/ri/parser/XPathParserTokenManager.java
@@ -0,0 +1,1977 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Generated By:JavaCC: Do not edit this line. XPathParserTokenManager.java */
+package org.apache.commons.jxpath.ri.parser;
+
+public class XPathParserTokenManager implements XPathParserConstants
+{
+private int jjStopStringLiteralDfa_0(final int pos, final long active0, final long active1)
+{
+ switch (pos)
+ {
+ case 0:
+ if ((active1 & 0x180000L) != 0L) {
+ return 10;
+ }
+ if ((active0 & 0xfffffffff8000000L) != 0L || (active1 & 0x7fffL) != 0L)
+ {
+ jjmatchedKind = 79;
+ return 12;
+ }
+ return -1;
+ case 1:
+ if ((active0 & 0xfff7fffff0000000L) != 0L || (active1 & 0x7fffL) != 0L)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 1;
+ return 12;
+ }
+ if ((active0 & 0x8000008000000L) != 0L) {
+ return 12;
+ }
+ return -1;
+ case 2:
+ if ((active0 & 0x10000070000000L) != 0L || (active1 & 0x410L) != 0L) {
+ return 12;
+ }
+ if ((active0 & 0xffe7ffff80000000L) != 0L || (active1 & 0x7befL) != 0L)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 2;
+ return 12;
+ }
+ return -1;
+ case 3:
+ if ((active0 & 0xc1010180000000L) != 0L || (active1 & 0x1a0L) != 0L) {
+ return 12;
+ }
+ if ((active0 & 0xff26fefe00000000L) != 0L || (active1 & 0x7a4fL) != 0L)
+ {
+ if (jjmatchedPos != 3)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 3;
+ }
+ return 12;
+ }
+ return -1;
+ case 4:
+ if ((active0 & 0xff62fff600000000L) != 0L || (active1 & 0x520fL) != 0L)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 4;
+ return 12;
+ }
+ if ((active0 & 0x4000000000000L) != 0L || (active1 & 0x2840L) != 0L) {
+ return 12;
+ }
+ if ((active0 & 0x800000000L) != 0L)
+ {
+ if (jjmatchedPos < 3)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 3;
+ }
+ return -1;
+ }
+ return -1;
+ case 5:
+ if ((active0 & 0x300000000000000L) != 0L || (active1 & 0x201L) != 0L) {
+ return 12;
+ }
+ if ((active0 & 0xfc62ffe600000000L) != 0L || (active1 & 0x500eL) != 0L)
+ {
+ if (jjmatchedPos != 5)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 5;
+ }
+ return 12;
+ }
+ if ((active0 & 0x1000000000L) != 0L)
+ {
+ if (jjmatchedPos < 4)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 4;
+ }
+ return -1;
+ }
+ if ((active0 & 0x800000000L) != 0L)
+ {
+ if (jjmatchedPos < 3)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 3;
+ }
+ return -1;
+ }
+ return -1;
+ case 6:
+ if ((active0 & 0x200000000L) != 0L || (active1 & 0x1008L) != 0L) {
+ return 12;
+ }
+ if ((active0 & 0xfc62ffc400000000L) != 0L || (active1 & 0x4007L) != 0L)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 6;
+ return 12;
+ }
+ if ((active0 & 0x2000000000L) != 0L)
+ {
+ if (jjmatchedPos < 5)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 5;
+ }
+ return -1;
+ }
+ if ((active0 & 0x1000000000L) != 0L)
+ {
+ if (jjmatchedPos < 4)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 4;
+ }
+ return -1;
+ }
+ return -1;
+ case 7:
+ if ((active0 & 0x1002000000000000L) != 0L) {
+ return 12;
+ }
+ if ((active0 & 0x2000000000L) != 0L)
+ {
+ if (jjmatchedPos < 5)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 5;
+ }
+ return -1;
+ }
+ if ((active0 & 0xec60ffc400000000L) != 0L || (active1 & 0x4007L) != 0L)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 7;
+ return 12;
+ }
+ return -1;
+ case 8:
+ if ((active0 & 0xe800000000000000L) != 0L || (active1 & 0x4L) != 0L) {
+ return 12;
+ }
+ if ((active0 & 0x460ff8400000000L) != 0L || (active1 & 0x4003L) != 0L)
+ {
+ if (jjmatchedPos != 8)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 8;
+ }
+ return 12;
+ }
+ if ((active0 & 0x4000000000L) != 0L)
+ {
+ if (jjmatchedPos < 7)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 7;
+ }
+ return -1;
+ }
+ return -1;
+ case 9:
+ if ((active0 & 0x20000000000000L) != 0L) {
+ return 12;
+ }
+ if ((active0 & 0x78000000000L) != 0L)
+ {
+ if (jjmatchedPos < 8)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 8;
+ }
+ return -1;
+ }
+ if ((active0 & 0x4000000000L) != 0L)
+ {
+ if (jjmatchedPos < 7)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 7;
+ }
+ return -1;
+ }
+ if ((active0 & 0x6440f80400000000L) != 0L || (active1 & 0x4003L) != 0L)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 9;
+ return 12;
+ }
+ return -1;
+ case 10:
+ if ((active0 & 0x400000000000000L) != 0L) {
+ return 12;
+ }
+ if ((active0 & 0x6040f00400000000L) != 0L || (active1 & 0x4003L) != 0L)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 10;
+ return 12;
+ }
+ if ((active0 & 0x80000000000L) != 0L)
+ {
+ if (jjmatchedPos < 9)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 9;
+ }
+ return -1;
+ }
+ if ((active0 & 0x78000000000L) != 0L)
+ {
+ if (jjmatchedPos < 8)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 8;
+ }
+ return -1;
+ }
+ return -1;
+ case 11:
+ if ((active0 & 0x80000000000L) != 0L)
+ {
+ if (jjmatchedPos < 9)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 9;
+ }
+ return -1;
+ }
+ if ((active0 & 0x6040f00400000000L) != 0L || (active1 & 0x4003L) != 0L)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 11;
+ return 12;
+ }
+ return -1;
+ case 12:
+ if ((active0 & 0x6000f00400000000L) != 0L || (active1 & 0x2L) != 0L)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 12;
+ return 12;
+ }
+ if ((active0 & 0x40000000000000L) != 0L || (active1 & 0x4001L) != 0L) {
+ return 12;
+ }
+ return -1;
+ case 13:
+ if ((active0 & 0x6000f00400000000L) != 0L || (active1 & 0x2L) != 0L)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 13;
+ return 12;
+ }
+ return -1;
+ case 14:
+ if ((active0 & 0x4000000000000000L) != 0L || (active1 & 0x2L) != 0L) {
+ return 12;
+ }
+ if ((active0 & 0x2000f00400000000L) != 0L)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 14;
+ return 12;
+ }
+ return -1;
+ case 15:
+ if ((active0 & 0x2000000000000000L) != 0L) {
+ return 12;
+ }
+ if ((active0 & 0xf00400000000L) != 0L)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 15;
+ return 12;
+ }
+ return -1;
+ case 16:
+ if ((active0 & 0xe00400000000L) != 0L)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 16;
+ return 12;
+ }
+ if ((active0 & 0x100000000000L) != 0L)
+ {
+ if (jjmatchedPos < 15)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 15;
+ }
+ return -1;
+ }
+ return -1;
+ case 17:
+ if ((active0 & 0x600000000000L) != 0L)
+ {
+ if (jjmatchedPos < 16)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 16;
+ }
+ return -1;
+ }
+ if ((active0 & 0x100000000000L) != 0L)
+ {
+ if (jjmatchedPos < 15)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 15;
+ }
+ return -1;
+ }
+ if ((active0 & 0x800400000000L) != 0L)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 17;
+ return 12;
+ }
+ return -1;
+ case 18:
+ if ((active0 & 0x800000000000L) != 0L)
+ {
+ if (jjmatchedPos < 17)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 17;
+ }
+ return -1;
+ }
+ if ((active0 & 0x600000000000L) != 0L)
+ {
+ if (jjmatchedPos < 16)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 16;
+ }
+ return -1;
+ }
+ if ((active0 & 0x400000000L) != 0L)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 18;
+ return 12;
+ }
+ return -1;
+ case 19:
+ if ((active0 & 0x400000000L) != 0L)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 19;
+ return 12;
+ }
+ if ((active0 & 0x800000000000L) != 0L)
+ {
+ if (jjmatchedPos < 17)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 17;
+ }
+ return -1;
+ }
+ return -1;
+ case 20:
+ if ((active0 & 0x400000000L) != 0L)
+ {
+ jjmatchedKind = 79;
+ jjmatchedPos = 20;
+ return 12;
+ }
+ return -1;
+ default :
+ return -1;
+ }
+}
+private int jjStartNfa_0(final int pos, final long active0, final long active1)
+{
+ return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0, active1), pos + 1);
+}
+private int jjStopAtPos(final int pos, final int kind)
+{
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ return pos + 1;
+}
+private int jjStartNfaWithStates_0(final int pos, final int kind, final int state)
+{
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) { return pos + 1; }
+ return jjMoveNfa_0(state, pos + 1);
+}
+private int jjMoveStringLiteralDfa0_0()
+{
+ switch(curChar)
+ {
+ case 33:
+ return jjMoveStringLiteralDfa1_0(0x1000L, 0x0L);
+ case 36:
+ return jjStopAtPos(0, 17);
+ case 40:
+ return jjStopAtPos(0, 81);
+ case 41:
+ return jjStopAtPos(0, 82);
+ case 42:
+ return jjStopAtPos(0, 89);
+ case 43:
+ return jjStopAtPos(0, 9);
+ case 44:
+ return jjStopAtPos(0, 88);
+ case 45:
+ return jjStopAtPos(0, 10);
+ case 46:
+ jjmatchedKind = 83;
+ return jjMoveStringLiteralDfa1_0(0x0L, 0x100000L);
+ case 47:
+ jjmatchedKind = 6;
+ return jjMoveStringLiteralDfa1_0(0x80L, 0x0L);
+ case 58:
+ return jjStopAtPos(0, 80);
+ case 60:
+ jjmatchedKind = 13;
+ return jjMoveStringLiteralDfa1_0(0x4000L, 0x0L);
+ case 61:
+ return jjStopAtPos(0, 11);
+ case 62:
+ jjmatchedKind = 15;
+ return jjMoveStringLiteralDfa1_0(0x10000L, 0x0L);
+ case 64:
+ return jjStopAtPos(0, 87);
+ case 91:
+ return jjStopAtPos(0, 85);
+ case 93:
+ return jjStopAtPos(0, 86);
+ case 97:
+ return jjMoveStringLiteralDfa1_0(0x10c010000000L, 0x0L);
+ case 98:
+ return jjMoveStringLiteralDfa1_0(0x0L, 0x8L);
+ case 99:
+ return jjMoveStringLiteralDfa1_0(0x1204001200000000L, 0x1000L);
+ case 100:
+ return jjMoveStringLiteralDfa1_0(0x880040000000L, 0x0L);
+ case 101:
+ return jjMoveStringLiteralDfa1_0(0x800000000000000L, 0x0L);
+ case 102:
+ return jjMoveStringLiteralDfa1_0(0x240000000000L, 0x4840L);
+ case 105:
+ return jjMoveStringLiteralDfa1_0(0x8000000000000L, 0x0L);
+ case 107:
+ return jjMoveStringLiteralDfa1_0(0x10000000000000L, 0x0L);
+ case 108:
+ return jjMoveStringLiteralDfa1_0(0x21000000000000L, 0x100L);
+ case 109:
+ return jjMoveStringLiteralDfa1_0(0x20000000L, 0x0L);
+ case 110:
+ return jjMoveStringLiteralDfa1_0(0xc0010080000000L, 0x292L);
+ case 111:
+ return jjMoveStringLiteralDfa1_0(0x8000000L, 0x0L);
+ case 112:
+ return jjMoveStringLiteralDfa1_0(0x2422400000000L, 0x0L);
+ case 114:
+ return jjMoveStringLiteralDfa1_0(0x0L, 0x2000L);
+ case 115:
+ return jjMoveStringLiteralDfa1_0(0xe500000800000000L, 0x401L);
+ case 116:
+ return jjMoveStringLiteralDfa1_0(0x100000000L, 0x24L);
+ case 124:
+ return jjStopAtPos(0, 8);
+ default :
+ return jjMoveNfa_0(0, 0);
+ }
+}
+private int jjMoveStringLiteralDfa1_0(final long active0, final long active1)
+{
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(0, active0, active1);
+ return 1;
+ }
+ switch(curChar)
+ {
+ case 46:
+ if ((active1 & 0x100000L) != 0L) {
+ return jjStopAtPos(1, 84);
+ }
+ break;
+ case 47:
+ if ((active0 & 0x80L) != 0L) {
+ return jjStopAtPos(1, 7);
+ }
+ break;
+ case 61:
+ if ((active0 & 0x1000L) != 0L) {
+ return jjStopAtPos(1, 12);
+ } else if ((active0 & 0x4000L) != 0L) {
+ return jjStopAtPos(1, 14);
+ } else if ((active0 & 0x10000L) != 0L) {
+ return jjStopAtPos(1, 16);
+ }
+ break;
+ case 97:
+ return jjMoveStringLiteralDfa2_0(active0, 0xc1012000000000L, active1, 0x140L);
+ case 100:
+ if ((active0 & 0x8000000000000L) != 0L) {
+ return jjStartNfaWithStates_0(1, 51, 12);
+ }
+ break;
+ case 101:
+ return jjMoveStringLiteralDfa2_0(active0, 0x10880900000000L, active1, 0x1000L);
+ case 104:
+ return jjMoveStringLiteralDfa2_0(active0, 0x1000000000L, active1, 0L);
+ case 105:
+ return jjMoveStringLiteralDfa2_0(active0, 0x40000000L, active1, 0L);
+ case 108:
+ return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x800L);
+ case 110:
+ return jjMoveStringLiteralDfa2_0(active0, 0x800104010000000L, active1, 0L);
+ case 111:
+ return jjMoveStringLiteralDfa2_0(active0, 0x12262402a0000000L, active1, 0x601aL);
+ case 114:
+ if ((active0 & 0x8000000L) != 0L) {
+ return jjStartNfaWithStates_0(1, 27, 12);
+ }
+ return jjMoveStringLiteralDfa2_0(active0, 0x420400000000L, active1, 0x24L);
+ case 116:
+ return jjMoveStringLiteralDfa2_0(active0, 0x500008000000000L, active1, 0x1L);
+ case 117:
+ return jjMoveStringLiteralDfa2_0(active0, 0xe000000000000000L, active1, 0x680L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(0, active0, active1);
+}
+private int jjMoveStringLiteralDfa2_0(final long old0, long active0, final long old1, long active1)
+{
+ if (((active0 &= old0) | (active1 &= old1)) == 0L) {
+ return jjStartNfa_0(0, old0, old1);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(1, active0, active1);
+ return 2;
+ }
+ switch(curChar)
+ {
+ case 97:
+ return jjMoveStringLiteralDfa3_0(active0, 0x400000000000000L, active1, 0x4L);
+ case 98:
+ return jjMoveStringLiteralDfa3_0(active0, 0xe000000000000000L, active1, 0L);
+ case 99:
+ return jjMoveStringLiteralDfa3_0(active0, 0x20104000000000L, active1, 0L);
+ case 100:
+ if ((active0 & 0x10000000L) != 0L) {
+ return jjStartNfaWithStates_0(2, 28, 12);
+ } else if ((active0 & 0x20000000L) != 0L) {
+ return jjStartNfaWithStates_0(2, 29, 12);
+ }
+ return jjMoveStringLiteralDfa3_0(active0, 0x800000080000000L, active1, 0L);
+ case 101:
+ return jjMoveStringLiteralDfa3_0(active0, 0x420000000000L, active1, 0L);
+ case 105:
+ return jjMoveStringLiteralDfa3_0(active0, 0x1000000000L, active1, 0x1000L);
+ case 108:
+ return jjMoveStringLiteralDfa3_0(active0, 0x240800000000L, active1, 0xc0L);
+ case 109:
+ if ((active1 & 0x400L) != 0L) {
+ return jjStartNfaWithStates_0(2, 74, 12);
+ }
+ return jjMoveStringLiteralDfa3_0(active0, 0xc0010200000000L, active1, 0x200L);
+ case 110:
+ return jjMoveStringLiteralDfa3_0(active0, 0x1200000000000000L, active1, 0x100L);
+ case 111:
+ return jjMoveStringLiteralDfa3_0(active0, 0x400000000L, active1, 0x808L);
+ case 114:
+ return jjMoveStringLiteralDfa3_0(active0, 0x100002000000000L, active1, 0x4003L);
+ case 115:
+ return jjMoveStringLiteralDfa3_0(active0, 0x3880000000000L, active1, 0L);
+ case 116:
+ if ((active1 & 0x10L) != 0L) {
+ return jjStartNfaWithStates_0(2, 68, 12);
+ }
+ return jjMoveStringLiteralDfa3_0(active0, 0x8000000000L, active1, 0L);
+ case 117:
+ return jjMoveStringLiteralDfa3_0(active0, 0x4000000000000L, active1, 0x2020L);
+ case 118:
+ if ((active0 & 0x40000000L) != 0L) {
+ return jjStartNfaWithStates_0(2, 30, 12);
+ }
+ break;
+ case 120:
+ return jjMoveStringLiteralDfa3_0(active0, 0x100000000L, active1, 0L);
+ case 121:
+ if ((active0 & 0x10000000000000L) != 0L) {
+ return jjStartNfaWithStates_0(2, 52, 12);
+ }
+ break;
+ default :
+ break;
+ }
+ return jjStartNfa_0(1, active0, active1);
+}
+private int jjMoveStringLiteralDfa3_0(final long old0, long active0, final long old1, long active1)
+{
+ if (((active0 &= old0) | (active1 &= old1)) == 0L) {
+ return jjStartNfa_0(1, old0, old1);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(2, active0, active1);
+ return 3;
+ }
+ switch(curChar)
+ {
+ case 97:
+ return jjMoveStringLiteralDfa4_0(active0, 0x20000000000000L, active1, 0L);
+ case 98:
+ return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x200L);
+ case 99:
+ return jjMoveStringLiteralDfa4_0(active0, 0x200ca0400000000L, active1, 0L);
+ case 101:
+ if ((active0 & 0x80000000L) != 0L) {
+ return jjStartNfaWithStates_0(3, 31, 12);
+ } else if ((active0 & 0x80000000000000L) != 0L)
+ {
+ jjmatchedKind = 55;
+ jjmatchedPos = 3;
+ }
+ else if ((active1 & 0x20L) != 0L) {
+ return jjStartNfaWithStates_0(3, 69, 12);
+ }
+ return jjMoveStringLiteralDfa4_0(active0, 0x40116000000000L, active1, 0L);
+ case 102:
+ return jjMoveStringLiteralDfa4_0(active0, 0x800000000L, active1, 0L);
+ case 103:
+ if ((active1 & 0x100L) != 0L) {
+ return jjStartNfaWithStates_0(3, 72, 12);
+ }
+ break;
+ case 105:
+ return jjMoveStringLiteralDfa4_0(active0, 0x102000000000000L, active1, 0x1L);
+ case 108:
+ if ((active1 & 0x80L) != 0L) {
+ return jjStartNfaWithStates_0(3, 71, 12);
+ }
+ return jjMoveStringLiteralDfa4_0(active0, 0x241000000000L, active1, 0x1008L);
+ case 109:
+ return jjMoveStringLiteralDfa4_0(active0, 0x200000000L, active1, 0x4002L);
+ case 110:
+ return jjMoveStringLiteralDfa4_0(active0, 0x4000000000000L, active1, 0x2004L);
+ case 111:
+ return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x800L);
+ case 114:
+ return jjMoveStringLiteralDfa4_0(active0, 0x400008000000000L, active1, 0L);
+ case 115:
+ return jjMoveStringLiteralDfa4_0(active0, 0xe800000000000000L, active1, 0x40L);
+ case 116:
+ if ((active0 & 0x100000000L) != 0L) {
+ return jjStartNfaWithStates_0(3, 32, 12);
+ } else if ((active0 & 0x1000000000000L) != 0L) {
+ return jjStartNfaWithStates_0(3, 48, 12);
+ }
+ return jjMoveStringLiteralDfa4_0(active0, 0x1000000000000000L, active1, 0L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(2, active0, active1);
+}
+private int jjMoveStringLiteralDfa4_0(final long old0, long active0, final long old1, long active1)
+{
+ if (((active0 &= old0) | (active1 &= old1)) == 0L) {
+ return jjStartNfa_0(2, old0, old1);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(3, active0, active1);
+ return 4;
+ }
+ switch(curChar)
+ {
+ case 45:
+ return jjMoveStringLiteralDfa5_0(active0, 0x800000000000000L, active1, 0L);
+ case 58:
+ return jjMoveStringLiteralDfa5_0(active0, 0x800000000L, active1, 0L);
+ case 97:
+ return jjMoveStringLiteralDfa5_0(active0, 0x1200000000000000L, active1, 0x4002L);
+ case 100:
+ if ((active1 & 0x2000L) != 0L) {
+ return jjStartNfaWithStates_0(4, 77, 12);
+ }
+ return jjMoveStringLiteralDfa5_0(active0, 0x1000000000L, active1, 0L);
+ case 101:
+ if ((active1 & 0x40L) != 0L) {
+ return jjStartNfaWithStates_0(4, 70, 12);
+ }
+ return jjMoveStringLiteralDfa5_0(active0, 0xca0600000000L, active1, 0x208L);
+ case 105:
+ return jjMoveStringLiteralDfa5_0(active0, 0x8000000000L, active1, 0x1000L);
+ case 108:
+ return jjMoveStringLiteralDfa5_0(active0, 0x20000000000000L, active1, 0L);
+ case 110:
+ return jjMoveStringLiteralDfa5_0(active0, 0x100002000000000L, active1, 0x1L);
+ case 111:
+ return jjMoveStringLiteralDfa5_0(active0, 0x240000000000L, active1, 0L);
+ case 114:
+ if ((active1 & 0x800L) != 0L) {
+ return jjStartNfaWithStates_0(4, 75, 12);
+ }
+ break;
+ case 115:
+ return jjMoveStringLiteralDfa5_0(active0, 0x40114000000000L, active1, 0x4L);
+ case 116:
+ if ((active0 & 0x4000000000000L) != 0L) {
+ return jjStartNfaWithStates_0(4, 50, 12);
+ }
+ return jjMoveStringLiteralDfa5_0(active0, 0xe402000000000000L, active1, 0L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(3, active0, active1);
+}
+private int jjMoveStringLiteralDfa5_0(final long old0, long active0, final long old1, long active1)
+{
+ if (((active0 &= old0) | (active1 &= old1)) == 0L) {
+ return jjStartNfa_0(3, old0, old1);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(4, active0, active1);
+ return 5;
+ }
+ switch(curChar)
+ {
+ case 45:
+ return jjMoveStringLiteralDfa6_0(active0, 0x20000000000000L, active1, 0L);
+ case 58:
+ if ((active0 & 0x800000000L) != 0L) {
+ return jjStopAtPos(5, 35);
+ }
+ return jjMoveStringLiteralDfa6_0(active0, 0x1000000000L, active1, 0L);
+ case 97:
+ return jjMoveStringLiteralDfa6_0(active0, 0L, active1, 0x8L);
+ case 98:
+ return jjMoveStringLiteralDfa6_0(active0, 0x8000000000L, active1, 0L);
+ case 100:
+ return jjMoveStringLiteralDfa6_0(active0, 0x420000000000L, active1, 0L);
+ case 103:
+ if ((active0 & 0x100000000000000L) != 0L)
+ {
+ jjmatchedKind = 56;
+ jjmatchedPos = 5;
+ }
+ return jjMoveStringLiteralDfa6_0(active0, 0L, active1, 0x1L);
+ case 105:
+ return jjMoveStringLiteralDfa6_0(active0, 0x1002000000000000L, active1, 0L);
+ case 108:
+ return jjMoveStringLiteralDfa6_0(active0, 0L, active1, 0x6L);
+ case 110:
+ return jjMoveStringLiteralDfa6_0(active0, 0x880200000000L, active1, 0x1000L);
+ case 112:
+ return jjMoveStringLiteralDfa6_0(active0, 0x40010000000000L, active1, 0L);
+ case 114:
+ if ((active1 & 0x200L) != 0L) {
+ return jjStartNfaWithStates_0(5, 73, 12);
+ }
+ return jjMoveStringLiteralDfa6_0(active0, 0xe000000000000000L, active1, 0L);
+ case 115:
+ return jjMoveStringLiteralDfa6_0(active0, 0x400000400000000L, active1, 0L);
+ case 116:
+ if ((active0 & 0x200000000000000L) != 0L) {
+ return jjStartNfaWithStates_0(5, 57, 12);
+ }
+ return jjMoveStringLiteralDfa6_0(active0, 0x106000000000L, active1, 0x4000L);
+ case 119:
+ return jjMoveStringLiteralDfa6_0(active0, 0x800240000000000L, active1, 0L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(4, active0, active1);
+}
+private int jjMoveStringLiteralDfa6_0(final long old0, long active0, final long old1, long active1)
+{
+ if (((active0 &= old0) | (active1 &= old1)) == 0L) {
+ return jjStartNfa_0(4, old0, old1);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(5, active0, active1);
+ return 6;
+ }
+ switch(curChar)
+ {
+ case 45:
+ return jjMoveStringLiteralDfa7_0(active0, 0x400000000000000L, active1, 0x4001L);
+ case 58:
+ if ((active0 & 0x1000000000L) != 0L) {
+ return jjStopAtPos(6, 36);
+ }
+ return jjMoveStringLiteralDfa7_0(active0, 0x2000000000L, active1, 0L);
+ case 97:
+ return jjMoveStringLiteralDfa7_0(active0, 0x40010000000000L, active1, 0x4L);
+ case 100:
+ return jjMoveStringLiteralDfa7_0(active0, 0x880000000000L, active1, 0L);
+ case 103:
+ if ((active1 & 0x1000L) != 0L) {
+ return jjStartNfaWithStates_0(6, 76, 12);
+ }
+ break;
+ case 105:
+ return jjMoveStringLiteralDfa7_0(active0, 0xe800660000000000L, active1, 0x2L);
+ case 110:
+ if ((active1 & 0x8L) != 0L) {
+ return jjStartNfaWithStates_0(6, 67, 12);
+ }
+ return jjMoveStringLiteralDfa7_0(active0, 0x1020000000000000L, active1, 0L);
+ case 111:
+ return jjMoveStringLiteralDfa7_0(active0, 0x2104000000000L, active1, 0L);
+ case 115:
+ return jjMoveStringLiteralDfa7_0(active0, 0x400000000L, active1, 0L);
+ case 116:
+ if ((active0 & 0x200000000L) != 0L) {
+ return jjStartNfaWithStates_0(6, 33, 12);
+ }
+ break;
+ case 117:
+ return jjMoveStringLiteralDfa7_0(active0, 0x8000000000L, active1, 0L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(5, active0, active1);
+}
+private int jjMoveStringLiteralDfa7_0(final long old0, long active0, final long old1, long active1)
+{
+ if (((active0 &= old0) | (active1 &= old1)) == 0L) {
+ return jjStartNfa_0(5, old0, old1);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(6, active0, active1);
+ return 7;
+ }
+ switch(curChar)
+ {
+ case 58:
+ if ((active0 & 0x2000000000L) != 0L) {
+ return jjStopAtPos(7, 37);
+ }
+ break;
+ case 97:
+ return jjMoveStringLiteralDfa8_0(active0, 0x20880000000000L, active1, 0L);
+ case 99:
+ return jjMoveStringLiteralDfa8_0(active0, 0x40010000000000L, active1, 0L);
+ case 105:
+ return jjMoveStringLiteralDfa8_0(active0, 0x400000000L, active1, 0L);
+ case 108:
+ return jjMoveStringLiteralDfa8_0(active0, 0L, active1, 0x1L);
+ case 110:
+ if ((active0 & 0x2000000000000L) != 0L) {
+ return jjStartNfaWithStates_0(7, 49, 12);
+ }
+ return jjMoveStringLiteralDfa8_0(active0, 0xe000660000000000L, active1, 0x4000L);
+ case 114:
+ return jjMoveStringLiteralDfa8_0(active0, 0x104000000000L, active1, 0L);
+ case 115:
+ if ((active0 & 0x1000000000000000L) != 0L) {
+ return jjStartNfaWithStates_0(7, 60, 12);
+ }
+ break;
+ case 116:
+ return jjMoveStringLiteralDfa8_0(active0, 0x800008000000000L, active1, 0x4L);
+ case 119:
+ return jjMoveStringLiteralDfa8_0(active0, 0x400000000000000L, active1, 0L);
+ case 122:
+ return jjMoveStringLiteralDfa8_0(active0, 0L, active1, 0x2L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(6, active0, active1);
+}
+private int jjMoveStringLiteralDfa8_0(final long old0, long active0, final long old1, long active1)
+{
+ if (((active0 &= old0) | (active1 &= old1)) == 0L) {
+ return jjStartNfa_0(6, old0, old1);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(7, active0, active1);
+ return 8;
+ }
+ switch(curChar)
+ {
+ case 45:
+ return jjMoveStringLiteralDfa9_0(active0, 0x100000000000L, active1, 0L);
+ case 58:
+ return jjMoveStringLiteralDfa9_0(active0, 0x4000000000L, active1, 0L);
+ case 101:
+ if ((active1 & 0x4L) != 0L) {
+ return jjStartNfaWithStates_0(8, 66, 12);
+ }
+ return jjMoveStringLiteralDfa9_0(active0, 0x40018000000000L, active1, 0x3L);
+ case 103:
+ if ((active0 & 0x8000000000000000L) != 0L)
+ {
+ jjmatchedKind = 63;
+ jjmatchedPos = 8;
+ }
+ return jjMoveStringLiteralDfa9_0(active0, 0x6000660000000000L, active1, 0L);
+ case 104:
+ if ((active0 & 0x800000000000000L) != 0L) {
+ return jjStartNfaWithStates_0(8, 59, 12);
+ }
+ break;
+ case 105:
+ return jjMoveStringLiteralDfa9_0(active0, 0x400000000000000L, active1, 0L);
+ case 109:
+ return jjMoveStringLiteralDfa9_0(active0, 0x20000000000000L, active1, 0L);
+ case 110:
+ return jjMoveStringLiteralDfa9_0(active0, 0x880400000000L, active1, 0L);
+ case 117:
+ return jjMoveStringLiteralDfa9_0(active0, 0L, active1, 0x4000L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(7, active0, active1);
+}
+private int jjMoveStringLiteralDfa9_0(final long old0, long active0, final long old1, long active1)
+{
+ if (((active0 &= old0) | (active1 &= old1)) == 0L) {
+ return jjStartNfa_0(7, old0, old1);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(8, active0, active1);
+ return 9;
+ }
+ switch(curChar)
+ {
+ case 45:
+ return jjMoveStringLiteralDfa10_0(active0, 0x6040600000000000L, active1, 0x2L);
+ case 58:
+ if ((active0 & 0x4000000000L) != 0L) {
+ return jjStopAtPos(9, 38);
+ }
+ return jjMoveStringLiteralDfa10_0(active0, 0x78000000000L, active1, 0L);
+ case 101:
+ if ((active0 & 0x20000000000000L) != 0L) {
+ return jjStartNfaWithStates_0(9, 53, 12);
+ }
+ break;
+ case 103:
+ return jjMoveStringLiteralDfa10_0(active0, 0x400000000L, active1, 0L);
+ case 109:
+ return jjMoveStringLiteralDfa10_0(active0, 0L, active1, 0x4000L);
+ case 110:
+ return jjMoveStringLiteralDfa10_0(active0, 0L, active1, 0x1L);
+ case 111:
+ return jjMoveStringLiteralDfa10_0(active0, 0x100000000000L, active1, 0L);
+ case 116:
+ return jjMoveStringLiteralDfa10_0(active0, 0x400880000000000L, active1, 0L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(8, active0, active1);
+}
+private int jjMoveStringLiteralDfa10_0(final long old0, long active0, final long old1, long active1)
+{
+ if (((active0 &= old0) | (active1 &= old1)) == 0L) {
+ return jjStartNfa_0(8, old0, old1);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(9, active0, active1);
+ return 10;
+ }
+ switch(curChar)
+ {
+ case 45:
+ return jjMoveStringLiteralDfa11_0(active0, 0x800400000000L, active1, 0L);
+ case 58:
+ if ((active0 & 0x8000000000L) != 0L) {
+ return jjStopAtPos(10, 39);
+ } else if ((active0 & 0x10000000000L) != 0L) {
+ return jjStopAtPos(10, 40);
+ } else if ((active0 & 0x20000000000L) != 0L) {
+ return jjStopAtPos(10, 41);
+ } else if ((active0 & 0x40000000000L) != 0L) {
+ return jjStopAtPos(10, 42);
+ }
+ return jjMoveStringLiteralDfa11_0(active0, 0x80000000000L, active1, 0L);
+ case 97:
+ return jjMoveStringLiteralDfa11_0(active0, 0x4000000000000000L, active1, 0L);
+ case 98:
+ return jjMoveStringLiteralDfa11_0(active0, 0x2000000000000000L, active1, 0x4000L);
+ case 103:
+ return jjMoveStringLiteralDfa11_0(active0, 0L, active1, 0x1L);
+ case 104:
+ if ((active0 & 0x400000000000000L) != 0L) {
+ return jjStartNfaWithStates_0(10, 58, 12);
+ }
+ break;
+ case 114:
+ return jjMoveStringLiteralDfa11_0(active0, 0x100000000000L, active1, 0L);
+ case 115:
+ return jjMoveStringLiteralDfa11_0(active0, 0x600000000000L, active1, 0x2L);
+ case 117:
+ return jjMoveStringLiteralDfa11_0(active0, 0x40000000000000L, active1, 0L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(9, active0, active1);
+}
+private int jjMoveStringLiteralDfa11_0(final long old0, long active0, final long old1, long active1)
+{
+ if (((active0 &= old0) | (active1 &= old1)) == 0L) {
+ return jjStartNfa_0(9, old0, old1);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(10, active0, active1);
+ return 11;
+ }
+ switch(curChar)
+ {
+ case 45:
+ return jjMoveStringLiteralDfa12_0(active0, 0x100000000000L, active1, 0L);
+ case 58:
+ if ((active0 & 0x80000000000L) != 0L) {
+ return jjStopAtPos(11, 43);
+ }
+ break;
+ case 101:
+ return jjMoveStringLiteralDfa12_0(active0, 0x2000000000000000L, active1, 0x4000L);
+ case 102:
+ return jjMoveStringLiteralDfa12_0(active0, 0x4000000000000000L, active1, 0L);
+ case 105:
+ return jjMoveStringLiteralDfa12_0(active0, 0x600400000000L, active1, 0L);
+ case 111:
+ return jjMoveStringLiteralDfa12_0(active0, 0x800000000000L, active1, 0L);
+ case 112:
+ return jjMoveStringLiteralDfa12_0(active0, 0L, active1, 0x2L);
+ case 114:
+ return jjMoveStringLiteralDfa12_0(active0, 0x40000000000000L, active1, 0L);
+ case 116:
+ return jjMoveStringLiteralDfa12_0(active0, 0L, active1, 0x1L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(10, active0, active1);
+}
+private int jjMoveStringLiteralDfa12_0(final long old0, long active0, final long old1, long active1)
+{
+ if (((active0 &= old0) | (active1 &= old1)) == 0L) {
+ return jjStartNfa_0(10, old0, old1);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(11, active0, active1);
+ return 12;
+ }
+ switch(curChar)
+ {
+ case 97:
+ return jjMoveStringLiteralDfa13_0(active0, 0L, active1, 0x2L);
+ case 98:
+ return jjMoveStringLiteralDfa13_0(active0, 0x600000000000L, active1, 0L);
+ case 102:
+ return jjMoveStringLiteralDfa13_0(active0, 0x2000000000000000L, active1, 0L);
+ case 104:
+ if ((active1 & 0x1L) != 0L) {
+ return jjStartNfaWithStates_0(12, 64, 12);
+ }
+ break;
+ case 105:
+ if ((active0 & 0x40000000000000L) != 0L) {
+ return jjStartNfaWithStates_0(12, 54, 12);
+ }
+ break;
+ case 110:
+ return jjMoveStringLiteralDfa13_0(active0, 0x400000000L, active1, 0L);
+ case 114:
+ if ((active1 & 0x4000L) != 0L) {
+ return jjStartNfaWithStates_0(12, 78, 12);
+ }
+ return jjMoveStringLiteralDfa13_0(active0, 0x800000000000L, active1, 0L);
+ case 115:
+ return jjMoveStringLiteralDfa13_0(active0, 0x100000000000L, active1, 0L);
+ case 116:
+ return jjMoveStringLiteralDfa13_0(active0, 0x4000000000000000L, active1, 0L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(11, active0, active1);
+}
+private int jjMoveStringLiteralDfa13_0(final long old0, long active0, final long old1, long active1)
+{
+ if (((active0 &= old0) | (active1 &= old1)) == 0L) {
+ return jjStartNfa_0(11, old0, old1);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(12, active0, active1);
+ return 13;
+ }
+ switch(curChar)
+ {
+ case 45:
+ return jjMoveStringLiteralDfa14_0(active0, 0x800000000000L, active1, 0L);
+ case 99:
+ return jjMoveStringLiteralDfa14_0(active0, 0L, active1, 0x2L);
+ case 101:
+ return jjMoveStringLiteralDfa14_0(active0, 0x4000100000000000L, active1, 0L);
+ case 108:
+ return jjMoveStringLiteralDfa14_0(active0, 0x600000000000L, active1, 0L);
+ case 111:
+ return jjMoveStringLiteralDfa14_0(active0, 0x2000000000000000L, active1, 0L);
+ case 115:
+ return jjMoveStringLiteralDfa14_0(active0, 0x400000000L, active1, 0L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(12, active0, active1);
+}
+private int jjMoveStringLiteralDfa14_0(final long old0, long active0, final long old1, long active1)
+{
+ if (((active0 &= old0) | (active1 &= old1)) == 0L) {
+ return jjStartNfa_0(12, old0, old1);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(13, active0, active1);
+ return 14;
+ }
+ switch(curChar)
+ {
+ case 101:
+ if ((active1 & 0x2L) != 0L) {
+ return jjStartNfaWithStates_0(14, 65, 12);
+ }
+ break;
+ case 105:
+ return jjMoveStringLiteralDfa15_0(active0, 0x600000000000L, active1, 0L);
+ case 108:
+ return jjMoveStringLiteralDfa15_0(active0, 0x100000000000L, active1, 0L);
+ case 114:
+ if ((active0 & 0x4000000000000000L) != 0L) {
+ return jjStartNfaWithStates_0(14, 62, 12);
+ }
+ return jjMoveStringLiteralDfa15_0(active0, 0x2000000000000000L, active1, 0L);
+ case 115:
+ return jjMoveStringLiteralDfa15_0(active0, 0x800000000000L, active1, 0L);
+ case 116:
+ return jjMoveStringLiteralDfa15_0(active0, 0x400000000L, active1, 0L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(13, active0, active1);
+}
+private int jjMoveStringLiteralDfa15_0(final long old0, long active0, final long old1, long active1)
+{
+ if (((active0 &= old0) | (active1 &= old1)) == 0L) {
+ return jjStartNfa_0(13, old0, old1);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(14, active0, 0L);
+ return 15;
+ }
+ switch(curChar)
+ {
+ case 101:
+ if ((active0 & 0x2000000000000000L) != 0L) {
+ return jjStartNfaWithStates_0(15, 61, 12);
+ }
+ return jjMoveStringLiteralDfa16_0(active0, 0x800000000000L);
+ case 102:
+ return jjMoveStringLiteralDfa16_0(active0, 0x100000000000L);
+ case 110:
+ return jjMoveStringLiteralDfa16_0(active0, 0x600000000000L);
+ case 114:
+ return jjMoveStringLiteralDfa16_0(active0, 0x400000000L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(14, active0, 0L);
+}
+private int jjMoveStringLiteralDfa16_0(final long old0, long active0)
+{
+ if ((active0 &= old0) == 0L) {
+ return jjStartNfa_0(14, old0, 0L);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(15, active0, 0L);
+ return 16;
+ }
+ switch(curChar)
+ {
+ case 58:
+ return jjMoveStringLiteralDfa17_0(active0, 0x100000000000L);
+ case 103:
+ return jjMoveStringLiteralDfa17_0(active0, 0x600000000000L);
+ case 108:
+ return jjMoveStringLiteralDfa17_0(active0, 0x800000000000L);
+ case 117:
+ return jjMoveStringLiteralDfa17_0(active0, 0x400000000L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(15, active0, 0L);
+}
+private int jjMoveStringLiteralDfa17_0(final long old0, long active0)
+{
+ if ((active0 &= old0) == 0L) {
+ return jjStartNfa_0(15, old0, 0L);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(16, active0, 0L);
+ return 17;
+ }
+ switch(curChar)
+ {
+ case 58:
+ if ((active0 & 0x100000000000L) != 0L) {
+ return jjStopAtPos(17, 44);
+ }
+ return jjMoveStringLiteralDfa18_0(active0, 0x600000000000L);
+ case 99:
+ return jjMoveStringLiteralDfa18_0(active0, 0x400000000L);
+ case 102:
+ return jjMoveStringLiteralDfa18_0(active0, 0x800000000000L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(16, active0, 0L);
+}
+private int jjMoveStringLiteralDfa18_0(final long old0, long active0)
+{
+ if ((active0 &= old0) == 0L) {
+ return jjStartNfa_0(16, old0, 0L);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(17, active0, 0L);
+ return 18;
+ }
+ switch(curChar)
+ {
+ case 58:
+ if ((active0 & 0x200000000000L) != 0L) {
+ return jjStopAtPos(18, 45);
+ } else if ((active0 & 0x400000000000L) != 0L) {
+ return jjStopAtPos(18, 46);
+ }
+ return jjMoveStringLiteralDfa19_0(active0, 0x800000000000L);
+ case 116:
+ return jjMoveStringLiteralDfa19_0(active0, 0x400000000L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(17, active0, 0L);
+}
+private int jjMoveStringLiteralDfa19_0(final long old0, long active0)
+{
+ if ((active0 &= old0) == 0L) {
+ return jjStartNfa_0(17, old0, 0L);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(18, active0, 0L);
+ return 19;
+ }
+ switch(curChar)
+ {
+ case 58:
+ if ((active0 & 0x800000000000L) != 0L) {
+ return jjStopAtPos(19, 47);
+ }
+ break;
+ case 105:
+ return jjMoveStringLiteralDfa20_0(active0, 0x400000000L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(18, active0, 0L);
+}
+private int jjMoveStringLiteralDfa20_0(final long old0, long active0)
+{
+ if ((active0 &= old0) == 0L) {
+ return jjStartNfa_0(18, old0, 0L);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(19, active0, 0L);
+ return 20;
+ }
+ switch(curChar)
+ {
+ case 111:
+ return jjMoveStringLiteralDfa21_0(active0, 0x400000000L);
+ default :
+ break;
+ }
+ return jjStartNfa_0(19, active0, 0L);
+}
+private int jjMoveStringLiteralDfa21_0(final long old0, long active0)
+{
+ if ((active0 &= old0) == 0L) {
+ return jjStartNfa_0(19, old0, 0L);
+}
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) {
+ jjStopStringLiteralDfa_0(20, active0, 0L);
+ return 21;
+ }
+ switch(curChar)
+ {
+ case 110:
+ if ((active0 & 0x400000000L) != 0L) {
+ return jjStartNfaWithStates_0(21, 34, 12);
+ }
+ break;
+ default :
+ break;
+ }
+ return jjStartNfa_0(20, active0, 0L);
+}
+private void jjCheckNAdd(final int state)
+{
+ if (jjrounds[state] != jjround)
+ {
+ jjstateSet[jjnewStateCnt++] = state;
+ jjrounds[state] = jjround;
+ }
+}
+private void jjAddStates(int start, final int end)
+{
+ do {
+ jjstateSet[jjnewStateCnt++] = jjnextStates[start];
+ } while (start++ != end);
+}
+private void jjCheckNAddTwoStates(final int state1, final int state2)
+{
+ jjCheckNAdd(state1);
+ jjCheckNAdd(state2);
+}
+static final long[] jjbitVec0 = {
+ 0xfffffffffffffffeL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL
+};
+static final long[] jjbitVec2 = {
+ 0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL
+};
+static final long[] jjbitVec3 = {
+ 0x0L, 0xffffffffffffc000L, 0xfffff0007fffffffL, 0x7fffffL
+};
+static final long[] jjbitVec4 = {
+ 0x0L, 0x0L, 0x0L, 0xff7fffffff7fffffL
+};
+static final long[] jjbitVec5 = {
+ 0x7ff3ffffffffffffL, 0x7ffffffffffffdfeL, 0xffffffffffffffffL, 0xfc31ffffffffe00fL
+};
+static final long[] jjbitVec6 = {
+ 0xffffffL, 0xffffffffffff0000L, 0xf80001ffffffffffL, 0x3L
+};
+static final long[] jjbitVec7 = {
+ 0x0L, 0x0L, 0xfffffffbffffd740L, 0xffffd547f7fffL
+};
+static final long[] jjbitVec8 = {
+ 0xffffffffffffdffeL, 0xffffffffdffeffffL, 0xffffffffffff0003L, 0x33fcfffffff199fL
+};
+static final long[] jjbitVec9 = {
+ 0xfffe000000000000L, 0xfffffffe027fffffL, 0x7fL, 0x707ffffff0000L
+};
+static final long[] jjbitVec10 = {
+ 0x7fffffe00000000L, 0xfffe0000000007feL, 0x7cffffffffffffffL, 0x60002f7fffL
+};
+static final long[] jjbitVec11 = {
+ 0x23ffffffffffffe0L, 0x3ff000000L, 0x3c5fdfffff99fe0L, 0x30003b0000000L
+};
+static final long[] jjbitVec12 = {
+ 0x36dfdfffff987e0L, 0x1c00005e000000L, 0x23edfdfffffbafe0L, 0x100000000L
+};
+static final long[] jjbitVec13 = {
+ 0x23cdfdfffff99fe0L, 0x3b0000000L, 0x3bfc718d63dc7e0L, 0x0L
+};
+static final long[] jjbitVec14 = {
+ 0x3effdfffffddfe0L, 0x300000000L, 0x3effdfffffddfe0L, 0x340000000L
+};
+static final long[] jjbitVec15 = {
+ 0x3fffdfffffddfe0L, 0x300000000L, 0x0L, 0x0L
+};
+static final long[] jjbitVec16 = {
+ 0xd7ffffffffffeL, 0x3fL, 0x200d6caefef02596L, 0x1fL
+};
+static final long[] jjbitVec17 = {
+ 0x0L, 0x3fffffffeffL, 0x0L, 0x0L
+};
+static final long[] jjbitVec18 = {
+ 0x0L, 0x0L, 0xffffffff00000000L, 0x7fffffffff003fL
+};
+static final long[] jjbitVec19 = {
+ 0x500000000007daedL, 0x2c62ab82315001L, 0xf580c90040000000L, 0x201080000000007L
+};
+static final long[] jjbitVec20 = {
+ 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffff0fffffffL, 0x3ffffffffffffffL
+};
+static final long[] jjbitVec21 = {
+ 0xffffffff3f3fffffL, 0x3fffffffaaff3f3fL, 0x5fdfffffffffffffL, 0x1fdc1fff0fcf1fdcL
+};
+static final long[] jjbitVec22 = {
+ 0x4c4000000000L, 0x0L, 0x7L, 0x0L
+};
+static final long[] jjbitVec23 = {
+ 0x3fe00000080L, 0xfffffffffffffffeL, 0xfffffffe001fffffL, 0x7ffffffffffffffL
+};
+static final long[] jjbitVec24 = {
+ 0x1fffffffffe0L, 0x0L, 0x0L, 0x0L
+};
+static final long[] jjbitVec25 = {
+ 0xffffffffffffffffL, 0xffffffffffffffffL, 0x3fffffffffL, 0x0L
+};
+static final long[] jjbitVec26 = {
+ 0xffffffffffffffffL, 0xffffffffffffffffL, 0xfffffffffL, 0x0L
+};
+static final long[] jjbitVec27 = {
+ 0x0L, 0x0L, 0x80000000000000L, 0xff7fffffff7fffffL
+};
+static final long[] jjbitVec28 = {
+ 0xffffffL, 0xffffffffffff0000L, 0xf80001ffffffffffL, 0x30003L
+};
+static final long[] jjbitVec29 = {
+ 0xffffffffffffffffL, 0x30000003fL, 0xfffffffbffffd7c0L, 0xffffd547f7fffL
+};
+static final long[] jjbitVec30 = {
+ 0xffffffffffffdffeL, 0xffffffffdffeffffL, 0xffffffffffff007bL, 0x33fcfffffff199fL
+};
+static final long[] jjbitVec31 = {
+ 0xfffe000000000000L, 0xfffffffe027fffffL, 0xbbfffffbfffe007fL, 0x707ffffff0016L
+};
+static final long[] jjbitVec32 = {
+ 0x7fffffe00000000L, 0xffff03ff0007ffffL, 0x7cffffffffffffffL, 0x3ff3dffffef7fffL
+};
+static final long[] jjbitVec33 = {
+ 0xf3ffffffffffffeeL, 0xffcfff1e3fffL, 0xd3c5fdfffff99feeL, 0x3ffcfb080399fL
+};
+static final long[] jjbitVec34 = {
+ 0xd36dfdfffff987e4L, 0x1fffc05e003987L, 0xf3edfdfffffbafeeL, 0xffc100003bbfL
+};
+static final long[] jjbitVec35 = {
+ 0xf3cdfdfffff99feeL, 0xffc3b0c0398fL, 0xc3bfc718d63dc7ecL, 0xff8000803dc7L
+};
+static final long[] jjbitVec36 = {
+ 0xc3effdfffffddfeeL, 0xffc300603ddfL, 0xc3effdfffffddfecL, 0xffc340603ddfL
+};
+static final long[] jjbitVec37 = {
+ 0xc3fffdfffffddfecL, 0xffc300803dcfL, 0x0L, 0x0L
+};
+static final long[] jjbitVec38 = {
+ 0x7ff7ffffffffffeL, 0x3ff7fffL, 0x3bff6caefef02596L, 0x3ff3f5fL
+};
+static final long[] jjbitVec39 = {
+ 0xc2a003ff03000000L, 0xfffe03fffffffeffL, 0x2fe3ffffebf0fdfL, 0x0L
+};
+static final long[] jjbitVec40 = {
+ 0x0L, 0x0L, 0x0L, 0x21fff0000L
+};
+static final long[] jjbitVec41 = {
+ 0x3efffe000000a0L, 0xfffffffffffffffeL, 0xfffffffe661fffffL, 0x77ffffffffffffffL
+};
+private int jjMoveNfa_0(final int startState, int curPos)
+{
+ int startsAt = 0;
+ jjnewStateCnt = 13;
+ int i = 1;
+ jjstateSet[0] = startState;
+int kind = 0x7fffffff;
+ for (;;)
+ {
+ if (++jjround == 0x7fffffff) {
+ ReInitRounds();
+ }
+ if (curChar < 64)
+ {
+ final long l = 1L << curChar;
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if ((0x3ff000000000000L & l) != 0L)
+ {
+ if (kind > 20) {
+ kind = 20;
+ }
+ jjCheckNAddTwoStates(6, 7);
+ } else {
+ switch (curChar) {
+ case 46:
+ jjCheckNAdd(10);
+ break;
+ case 39:
+ jjCheckNAddTwoStates(4, 5);
+ break;
+ case 34:
+ jjCheckNAddTwoStates(1, 2);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case 1:
+ if ((0xfffffffbffffffffL & l) != 0L) {
+ jjCheckNAddTwoStates(1, 2);
+ }
+ break;
+ case 2:
+ if (curChar == 34 && kind > 18) {
+ kind = 18;
+ }
+ break;
+ case 3:
+ if (curChar == 39) {
+ jjCheckNAddTwoStates(4, 5);
+ }
+ break;
+ case 4:
+ if ((0xffffff7fffffffffL & l) != 0L) {
+ jjCheckNAddTwoStates(4, 5);
+ }
+ break;
+ case 5:
+ if (curChar == 39 && kind > 18) {
+ kind = 18;
+ }
+ break;
+ case 6:
+ if ((0x3ff000000000000L & l) == 0L) {
+ break;
+ }
+ if (kind > 20) {
+ kind = 20;
+ }
+ jjCheckNAddTwoStates(6, 7);
+ break;
+ case 7:
+ if (curChar != 46) {
+ break;
+ }
+ if (kind > 20) {
+ kind = 20;
+ }
+ jjCheckNAdd(8);
+ break;
+ case 8:
+ if ((0x3ff000000000000L & l) == 0L) {
+ break;
+ }
+ if (kind > 20) {
+ kind = 20;
+ }
+ jjCheckNAdd(8);
+ break;
+ case 9:
+ if (curChar == 46) {
+ jjCheckNAdd(10);
+ }
+ break;
+ case 10:
+ if ((0x3ff000000000000L & l) == 0L) {
+ break;
+ }
+ if (kind > 20) {
+ kind = 20;
+ }
+ jjCheckNAdd(10);
+ break;
+ case 12:
+ if ((0x3ff600000000000L & l) == 0L) {
+ break;
+ }
+ if (kind > 79) {
+ kind = 79;
+ }
+ jjstateSet[jjnewStateCnt++] = 12;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else if (curChar < 128)
+ {
+ final long l = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ case 12:
+ if ((0x7fffffe87fffffeL & l) == 0L) {
+ break;
+ }
+ if (kind > 79) {
+ kind = 79;
+ }
+ jjCheckNAdd(12);
+ break;
+ case 1:
+ jjAddStates(0, 1);
+ break;
+ case 4:
+ jjAddStates(2, 3);
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else
+ {
+ final int hiByte = curChar >> 8;
+ final int i1 = hiByte >> 6;
+ final long l1 = 1L << (hiByte & 077);
+ final int i2 = (curChar & 0xff) >> 6;
+ final long l2 = 1L << (curChar & 077);
+ MatchLoop: do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 0:
+ if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) {
+ break;
+ }
+ if (kind > 79) {
+ kind = 79;
+ }
+ jjCheckNAdd(12);
+ break;
+ case 1:
+ if (jjCanMove_0(hiByte, i1, i2, l1, l2)) {
+ jjAddStates(0, 1);
+ }
+ break;
+ case 4:
+ if (jjCanMove_0(hiByte, i1, i2, l1, l2)) {
+ jjAddStates(2, 3);
+ }
+ break;
+ case 12:
+ if (!jjCanMove_2(hiByte, i1, i2, l1, l2)) {
+ break;
+ }
+ if (kind > 79) {
+ kind = 79;
+ }
+ jjCheckNAdd(12);
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ if (kind != 0x7fffffff)
+ {
+ jjmatchedKind = kind;
+ jjmatchedPos = curPos;
+ kind = 0x7fffffff;
+ }
+ ++curPos;
+ if ((i = jjnewStateCnt) == (startsAt = 13 - (jjnewStateCnt = startsAt))) {
+ return curPos;
+ }
+ try { curChar = input_stream.readChar(); }
+ catch (final java.io.IOException e) { return curPos; }
+ }
+}
+static final int[] jjnextStates = {
+ 1, 2, 4, 5,
+};
+private static boolean jjCanMove_0(final int hiByte, final int i1, final int i2, final long l1, final long l2)
+{
+ switch(hiByte)
+ {
+ case 0:
+ return (jjbitVec2[i2] & l2) != 0L;
+ default :
+ if ((jjbitVec0[i1] & l1) != 0L) {
+ return true;
+ }
+ return false;
+ }
+}
+private static boolean jjCanMove_1(final int hiByte, final int i1, final int i2, final long l1, final long l2)
+{
+ switch(hiByte)
+ {
+ case 0:
+ return (jjbitVec4[i2] & l2) != 0L;
+ case 1:
+ return (jjbitVec5[i2] & l2) != 0L;
+ case 2:
+ return (jjbitVec6[i2] & l2) != 0L;
+ case 3:
+ return (jjbitVec7[i2] & l2) != 0L;
+ case 4:
+ return (jjbitVec8[i2] & l2) != 0L;
+ case 5:
+ return (jjbitVec9[i2] & l2) != 0L;
+ case 6:
+ return (jjbitVec10[i2] & l2) != 0L;
+ case 9:
+ return (jjbitVec11[i2] & l2) != 0L;
+ case 10:
+ return (jjbitVec12[i2] & l2) != 0L;
+ case 11:
+ return (jjbitVec13[i2] & l2) != 0L;
+ case 12:
+ return (jjbitVec14[i2] & l2) != 0L;
+ case 13:
+ return (jjbitVec15[i2] & l2) != 0L;
+ case 14:
+ return (jjbitVec16[i2] & l2) != 0L;
+ case 15:
+ return (jjbitVec17[i2] & l2) != 0L;
+ case 16:
+ return (jjbitVec18[i2] & l2) != 0L;
+ case 17:
+ return (jjbitVec19[i2] & l2) != 0L;
+ case 30:
+ return (jjbitVec20[i2] & l2) != 0L;
+ case 31:
+ return (jjbitVec21[i2] & l2) != 0L;
+ case 33:
+ return (jjbitVec22[i2] & l2) != 0L;
+ case 48:
+ return (jjbitVec23[i2] & l2) != 0L;
+ case 49:
+ return (jjbitVec24[i2] & l2) != 0L;
+ case 159:
+ return (jjbitVec25[i2] & l2) != 0L;
+ case 215:
+ return (jjbitVec26[i2] & l2) != 0L;
+ default :
+ if ((jjbitVec3[i1] & l1) != 0L) {
+ return true;
+ }
+ return false;
+ }
+}
+private static boolean jjCanMove_2(final int hiByte, final int i1, final int i2, final long l1, final long l2)
+{
+ switch(hiByte)
+ {
+ case 0:
+ return (jjbitVec27[i2] & l2) != 0L;
+ case 1:
+ return (jjbitVec5[i2] & l2) != 0L;
+ case 2:
+ return (jjbitVec28[i2] & l2) != 0L;
+ case 3:
+ return (jjbitVec29[i2] & l2) != 0L;
+ case 4:
+ return (jjbitVec30[i2] & l2) != 0L;
+ case 5:
+ return (jjbitVec31[i2] & l2) != 0L;
+ case 6:
+ return (jjbitVec32[i2] & l2) != 0L;
+ case 9:
+ return (jjbitVec33[i2] & l2) != 0L;
+ case 10:
+ return (jjbitVec34[i2] & l2) != 0L;
+ case 11:
+ return (jjbitVec35[i2] & l2) != 0L;
+ case 12:
+ return (jjbitVec36[i2] & l2) != 0L;
+ case 13:
+ return (jjbitVec37[i2] & l2) != 0L;
+ case 14:
+ return (jjbitVec38[i2] & l2) != 0L;
+ case 15:
+ return (jjbitVec39[i2] & l2) != 0L;
+ case 16:
+ return (jjbitVec18[i2] & l2) != 0L;
+ case 17:
+ return (jjbitVec19[i2] & l2) != 0L;
+ case 30:
+ return (jjbitVec20[i2] & l2) != 0L;
+ case 31:
+ return (jjbitVec21[i2] & l2) != 0L;
+ case 32:
+ return (jjbitVec40[i2] & l2) != 0L;
+ case 33:
+ return (jjbitVec22[i2] & l2) != 0L;
+ case 48:
+ return (jjbitVec41[i2] & l2) != 0L;
+ case 49:
+ return (jjbitVec24[i2] & l2) != 0L;
+ case 159:
+ return (jjbitVec25[i2] & l2) != 0L;
+ case 215:
+ return (jjbitVec26[i2] & l2) != 0L;
+ default :
+ if ((jjbitVec3[i1] & l1) != 0L) {
+ return true;
+ }
+ return false;
+ }
+}
+public static final String[] jjstrLiteralImages = {
+"", null, null, null, null, null, "\57", "\57\57", "\174", "\53", "\55",
+"\75", "\41\75", "\74", "\74\75", "\76", "\76\75", "\44", null, null, null, null,
+null, null, null, null, null, "\157\162", "\141\156\144", "\155\157\144",
+"\144\151\166", "\156\157\144\145", "\164\145\170\164", "\143\157\155\155\145\156\164",
+"\160\162\157\143\145\163\163\151\156\147\55\151\156\163\164\162\165\143\164\151\157\156", "\163\145\154\146\72\72", "\143\150\151\154\144\72\72",
+"\160\141\162\145\156\164\72\72", "\141\156\143\145\163\164\157\162\72\72",
+"\141\164\164\162\151\142\165\164\145\72\72", "\156\141\155\145\163\160\141\143\145\72\72",
+"\160\162\145\143\145\144\151\156\147\72\72", "\146\157\154\154\157\167\151\156\147\72\72",
+"\144\145\163\143\145\156\144\141\156\164\72\72", "\141\156\143\145\163\164\157\162\55\157\162\55\163\145\154\146\72\72",
+"\146\157\154\154\157\167\151\156\147\55\163\151\142\154\151\156\147\72\72", "\160\162\145\143\145\144\151\156\147\55\163\151\142\154\151\156\147\72\72",
+"\144\145\163\143\145\156\144\141\156\164\55\157\162\55\163\145\154\146\72\72", "\154\141\163\164", "\160\157\163\151\164\151\157\156",
+"\143\157\165\156\164", "\151\144", "\153\145\171", "\154\157\143\141\154\55\156\141\155\145",
+"\156\141\155\145\163\160\141\143\145\55\165\162\151", "\156\141\155\145", "\163\164\162\151\156\147", "\143\157\156\143\141\164",
+"\163\164\141\162\164\163\55\167\151\164\150", "\145\156\144\163\55\167\151\164\150", "\143\157\156\164\141\151\156\163",
+"\163\165\142\163\164\162\151\156\147\55\142\145\146\157\162\145", "\163\165\142\163\164\162\151\156\147\55\141\146\164\145\162",
+"\163\165\142\163\164\162\151\156\147", "\163\164\162\151\156\147\55\154\145\156\147\164\150",
+"\156\157\162\155\141\154\151\172\145\55\163\160\141\143\145", "\164\162\141\156\163\154\141\164\145", "\142\157\157\154\145\141\156",
+"\156\157\164", "\164\162\165\145", "\146\141\154\163\145", "\156\165\154\154",
+"\154\141\156\147", "\156\165\155\142\145\162", "\163\165\155", "\146\154\157\157\162",
+"\143\145\151\154\151\156\147", "\162\157\165\156\144", "\146\157\162\155\141\164\55\156\165\155\142\145\162",
+null, "\72", "\50", "\51", "\56", "\56\56", "\133", "\135", "\100", "\54", "\52", };
+static final long[] jjtoToken = {
+ 0xfffffffff817ffc1L, 0x3ffffffL,
+};
+protected SimpleCharStream input_stream;
+private final int[] jjrounds = new int[13];
+private final int[] jjstateSet = new int[26];
+protected char curChar;
+public XPathParserTokenManager(final SimpleCharStream stream)
+{
+ if (SimpleCharStream.staticFlag) {
+ throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer.");
+}
+ input_stream = stream;
+}
+public void ReInit(final SimpleCharStream stream)
+{
+ jjmatchedPos = jjnewStateCnt = 0;
+ curLexState = defaultLexState;
+ input_stream = stream;
+ ReInitRounds();
+}
+private void ReInitRounds()
+{
+ int i;
+ jjround = 0x80000001;
+ for (i = 13; i-- > 0;) {
+ jjrounds[i] = 0x80000000;
+}
+}
+
+protected Token jjFillToken()
+{
+ final Token t = Token.newToken(jjmatchedKind);
+ t.kind = jjmatchedKind;
+ final String im = jjstrLiteralImages[jjmatchedKind];
+ t.image = im == null ? input_stream.GetImage() : im;
+ t.beginLine = input_stream.getBeginLine();
+ t.beginColumn = input_stream.getBeginColumn();
+ t.endLine = input_stream.getEndLine();
+ t.endColumn = input_stream.getEndColumn();
+ return t;
+}
+
+int curLexState = 0;
+int defaultLexState = 0;
+int jjnewStateCnt;
+int jjround;
+int jjmatchedPos;
+int jjmatchedKind;
+
+public Token getNextToken()
+{
+ Token matchedToken;
+ int curPos = 0;
+
+ EOFLoop :
+ for (;;)
+ {
+ try
+ {
+ curChar = input_stream.BeginToken();
+ }
+ catch (final java.io.IOException e)
+ {
+ jjmatchedKind = 0;
+ matchedToken = jjFillToken();
+ return matchedToken;
+ }
+
+ try { input_stream.backup(0);
+ while (curChar <= 32 && (0x100003600L & 1L << curChar) != 0L) {
+ curChar = input_stream.BeginToken();
+ }
+ }
+ catch (final java.io.IOException e1) { continue EOFLoop; }
+ jjmatchedKind = 0x7fffffff;
+ jjmatchedPos = 0;
+ curPos = jjMoveStringLiteralDfa0_0();
+ if (jjmatchedKind != 0x7fffffff)
+ {
+ if (jjmatchedPos + 1 < curPos) {
+ input_stream.backup(curPos - jjmatchedPos - 1);
+ }
+ if ((jjtoToken[jjmatchedKind >> 6] & 1L << (jjmatchedKind & 077)) != 0L)
+ {
+ matchedToken = jjFillToken();
+ return matchedToken;
+ }
+ continue EOFLoop;
+ }
+ int error_line = input_stream.getEndLine();
+ int error_column = input_stream.getEndColumn();
+ String error_after = null;
+ boolean EOFSeen = false;
+ try { input_stream.readChar(); input_stream.backup(1); }
+ catch (final java.io.IOException e1) {
+ EOFSeen = true;
+ error_after = curPos <= 1 ? "" : input_stream.GetImage();
+ if (curChar == '\n' || curChar == '\r') {
+ error_line++;
+ error_column = 0;
+ } else {
+ error_column++;
+ }
+ }
+ if (!EOFSeen) {
+ input_stream.backup(1);
+ error_after = curPos <= 1 ? "" : input_stream.GetImage();
+ }
+ throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR);
+ }
+}
+
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/BasicTypeConverter.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/BasicTypeConverter.java
new file mode 100644
index 00000000000..8f6ce79e193
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/BasicTypeConverter.java
@@ -0,0 +1,461 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.util;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Modifier;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+
+import org.apache.commons.jxpath.JXPathInvalidAccessException;
+import org.apache.commons.jxpath.JXPathTypeConversionException;
+import org.apache.commons.jxpath.NodeSet;
+import org.apache.commons.jxpath.Pointer;
+
+/**
+ * The default implementation of TypeConverter.
+ */
+public class BasicTypeConverter implements TypeConverter {
+
+ /**
+ * Returns true if it can convert the supplied
+ * object to the specified class.
+ * @param object to check
+ * @param toType prospective destination class
+ * @return boolean
+ */
+ @Override
+ public boolean canConvert(final Object object, final Class toType) {
+ if (object == null) {
+ return true;
+ }
+ final Class useType = TypeUtils.wrapPrimitive(toType);
+ final Class fromType = object.getClass();
+
+ if (useType.isAssignableFrom(fromType)) {
+ return true;
+ }
+
+ if (useType == String.class) {
+ return true;
+ }
+
+ if (object instanceof Boolean && (Number.class.isAssignableFrom(useType)
+ || "java.util.concurrent.atomic.AtomicBoolean"
+ .equals(useType.getName()))) {
+ return true;
+ }
+ if (object instanceof Number
+ && (Number.class.isAssignableFrom(useType) || useType == Boolean.class)) {
+ return true;
+ }
+ if (object instanceof String
+ && (useType == Boolean.class
+ || useType == Character.class
+ || useType == Byte.class
+ || useType == Short.class
+ || useType == Integer.class
+ || useType == Long.class
+ || useType == Float.class
+ || useType == Double.class)) {
+ return true;
+ }
+ if (fromType.isArray()) {
+ // Collection -> array
+ if (useType.isArray()) {
+ final Class cType = useType.getComponentType();
+ final int length = Array.getLength(object);
+ for (int i = 0; i < length; i++) {
+ final Object value = Array.get(object, i);
+ if (!canConvert(value, cType)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ if (Collection.class.isAssignableFrom(useType)) {
+ return canCreateCollection(useType);
+ }
+ if (Array.getLength(object) > 0) {
+ final Object value = Array.get(object, 0);
+ return canConvert(value, useType);
+ }
+ return canConvert("", useType);
+ }
+ if (object instanceof Collection) {
+ // Collection -> array
+ if (useType.isArray()) {
+ final Class cType = useType.getComponentType();
+ final Iterator it = ((Collection) object).iterator();
+ while (it.hasNext()) {
+ final Object value = it.next();
+ if (!canConvert(value, cType)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ if (Collection.class.isAssignableFrom(useType)) {
+ return canCreateCollection(useType);
+ }
+ if (((Collection) object).size() > 0) {
+ Object value;
+ if (object instanceof List) {
+ value = ((List) object).get(0);
+ }
+ else {
+ final Iterator it = ((Collection) object).iterator();
+ value = it.next();
+ }
+ return canConvert(value, useType);
+ }
+ return canConvert("", useType);
+ }
+ if (object instanceof NodeSet) {
+ return canConvert(((NodeSet) object).getValues(), useType);
+ }
+ if (object instanceof Pointer) {
+ return canConvert(((Pointer) object).getValue(), useType);
+ }
+ return false;
+ }
+
+ /**
+ * Converts the supplied object to the specified
+ * type. Throws a runtime exception if the conversion is
+ * not possible.
+ * @param object to convert
+ * @param toType destination class
+ * @return converted object
+ */
+ @Override
+ public Object convert(final Object object, final Class toType) {
+ if (object == null) {
+ return toType.isPrimitive() ? convertNullToPrimitive(toType) : null;
+ }
+
+ if (toType == Object.class) {
+ if (object instanceof NodeSet) {
+ return convert(((NodeSet) object).getValues(), toType);
+ }
+ if (object instanceof Pointer) {
+ return convert(((Pointer) object).getValue(), toType);
+ }
+ return object;
+ }
+ final Class useType = TypeUtils.wrapPrimitive(toType);
+ final Class fromType = object.getClass();
+
+ if (useType.isAssignableFrom(fromType)) {
+ return object;
+ }
+
+ if (fromType.isArray()) {
+ final int length = Array.getLength(object);
+ if (useType.isArray()) {
+ final Class cType = useType.getComponentType();
+
+ final Object array = Array.newInstance(cType, length);
+ for (int i = 0; i < length; i++) {
+ final Object value = Array.get(object, i);
+ Array.set(array, i, convert(value, cType));
+ }
+ return array;
+ }
+ if (Collection.class.isAssignableFrom(useType)) {
+ final Collection collection = allocateCollection(useType);
+ for (int i = 0; i < length; i++) {
+ collection.add(Array.get(object, i));
+ }
+ return unmodifiableCollection(collection);
+ }
+ if (length > 0) {
+ final Object value = Array.get(object, 0);
+ return convert(value, useType);
+ }
+ return convert("", useType);
+ }
+ if (object instanceof Collection) {
+ final int length = ((Collection) object).size();
+ if (useType.isArray()) {
+ final Class cType = useType.getComponentType();
+ final Object array = Array.newInstance(cType, length);
+ final Iterator it = ((Collection) object).iterator();
+ for (int i = 0; i < length; i++) {
+ final Object value = it.next();
+ Array.set(array, i, convert(value, cType));
+ }
+ return array;
+ }
+ if (Collection.class.isAssignableFrom(useType)) {
+ final Collection collection = allocateCollection(useType);
+ collection.addAll((Collection) object);
+ return unmodifiableCollection(collection);
+ }
+ if (length > 0) {
+ Object value;
+ if (object instanceof List) {
+ value = ((List) object).get(0);
+ }
+ else {
+ final Iterator it = ((Collection) object).iterator();
+ value = it.next();
+ }
+ return convert(value, useType);
+ }
+ return convert("", useType);
+ }
+ if (object instanceof NodeSet) {
+ return convert(((NodeSet) object).getValues(), useType);
+ }
+ if (object instanceof Pointer) {
+ return convert(((Pointer) object).getValue(), useType);
+ }
+ if (useType == String.class) {
+ return object.toString();
+ }
+ if (object instanceof Boolean) {
+ if (Number.class.isAssignableFrom(useType)) {
+ return allocateNumber(useType, ((Boolean) object).booleanValue() ? 1 : 0);
+ }
+ if ("java.util.concurrent.atomic.AtomicBoolean".equals(useType.getName())) {
+ try {
+ return useType.getConstructor(new Class[] { boolean.class })
+ .newInstance(object);
+ }
+ catch (final Exception e) {
+ throw new JXPathTypeConversionException(useType.getName(), e);
+ }
+ }
+ }
+ if (object instanceof Number) {
+ final double value = ((Number) object).doubleValue();
+ if (useType == Boolean.class) {
+ return value == 0.0 ? Boolean.FALSE : Boolean.TRUE;
+ }
+ if (Number.class.isAssignableFrom(useType)) {
+ return allocateNumber(useType, value);
+ }
+ }
+ if (object instanceof String) {
+ final Object value = convertStringToPrimitive(object, useType);
+ if (value != null) {
+ return value;
+ }
+ }
+
+ throw new JXPathTypeConversionException("Cannot convert "
+ + object.getClass() + " to " + useType);
+ }
+
+ /**
+ * Convert null to a primitive type.
+ * @param toType destination class
+ * @return a wrapper
+ */
+ protected Object convertNullToPrimitive(final Class toType) {
+ if (toType == boolean.class) {
+ return Boolean.FALSE;
+ }
+ if (toType == char.class) {
+ return Character.valueOf('\0');
+ }
+ if (toType == byte.class) {
+ return Byte.valueOf((byte) 0);
+ }
+ if (toType == short.class) {
+ return Short.valueOf((short) 0);
+ }
+ if (toType == int.class) {
+ return Integer.valueOf(0);
+ }
+ if (toType == long.class) {
+ return Long.valueOf(0L);
+ }
+ if (toType == float.class) {
+ return Float.valueOf(0.0f);
+ }
+ if (toType == double.class) {
+ return Double.valueOf(0.0);
+ }
+ return null;
+ }
+
+ /**
+ * Convert a string to a primitive type.
+ * @param object String
+ * @param toType destination class
+ * @return wrapper
+ */
+ protected Object convertStringToPrimitive(final Object object, Class toType) {
+ toType = TypeUtils.wrapPrimitive(toType);
+ if (toType == Boolean.class) {
+ return Boolean.valueOf((String) object);
+ }
+ if (toType == Character.class) {
+ return Character.valueOf(((String) object).charAt(0));
+ }
+ if (toType == Byte.class) {
+ return Byte.valueOf((String) object);
+ }
+ if (toType == Short.class) {
+ return Short.valueOf((String) object);
+ }
+ if (toType == Integer.class) {
+ return Integer.valueOf((String) object);
+ }
+ if (toType == Long.class) {
+ return Long.valueOf((String) object);
+ }
+ if (toType == Float.class) {
+ return Float.valueOf((String) object);
+ }
+ if (toType == Double.class) {
+ return Double.valueOf((String) object);
+ }
+ return null;
+ }
+
+ /**
+ * Allocate a number of a given type and value.
+ * @param type destination class
+ * @param value double
+ * @return Number
+ */
+ protected Number allocateNumber(Class type, final double value) {
+ type = TypeUtils.wrapPrimitive(type);
+ if (type == Byte.class) {
+ return Byte.valueOf((byte) value);
+ }
+ if (type == Short.class) {
+ return Short.valueOf((short) value);
+ }
+ if (type == Integer.class) {
+ return Integer.valueOf((int) value);
+ }
+ if (type == Long.class) {
+ return Long.valueOf((long) value);
+ }
+ if (type == Float.class) {
+ return Float.valueOf((float) value);
+ }
+ if (type == Double.class) {
+ return Double.valueOf(value);
+ }
+ if (type == BigInteger.class) {
+ return BigInteger.valueOf((long) value);
+ }
+ if (type == BigDecimal.class) {
+ // TODO ? https://pmd.sourceforge.io/pmd-6.50.0/pmd_rules_java_errorprone.html#avoiddecimalliteralsinbigdecimalconstructor
+ return new BigDecimal(value); // NOPMD
+ }
+ final String className = type.getName();
+ Class initialValueType = null;
+ if ("java.util.concurrent.atomic.AtomicInteger".equals(className)) {
+ initialValueType = int.class;
+ }
+ if ("java.util.concurrent.atomic.AtomicLong".equals(className)) {
+ initialValueType = long.class;
+ }
+ if (initialValueType != null) {
+ try {
+ return (Number) type.getConstructor(
+ new Class[] { initialValueType })
+ .newInstance(
+ allocateNumber(initialValueType,
+ value));
+ }
+ catch (final Exception e) {
+ throw new JXPathTypeConversionException(className, e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Learn whether this BasicTypeConverter can create a collection of the specified type.
+ * @param type prospective destination class
+ * @return boolean
+ */
+ protected boolean canCreateCollection(final Class type) {
+ if (!type.isInterface()
+ && (type.getModifiers() & Modifier.ABSTRACT) == 0) {
+ try {
+ type.getConstructor();
+ return true;
+ }
+ catch (final Exception e) {
+ return false;
+ }
+ }
+ return type == List.class || type == Collection.class || type == Set.class;
+ }
+
+ /**
+ * Create a collection of a given type.
+ * @param type destination class
+ * @return Collection
+ */
+ protected Collection allocateCollection(final Class type) {
+ if (!type.isInterface()
+ && (type.getModifiers() & Modifier.ABSTRACT) == 0) {
+ try {
+ return (Collection) type.getConstructor().newInstance();
+ }
+ catch (final Exception ex) {
+ throw new JXPathInvalidAccessException(
+ "Cannot create collection of type: " + type, ex);
+ }
+ }
+
+ if (type == List.class || type == Collection.class) {
+ return new ArrayList();
+ }
+ if (type == Set.class) {
+ return new HashSet();
+ }
+ throw new JXPathInvalidAccessException(
+ "Cannot create collection of type: " + type);
+ }
+
+ /**
+ * Gets an unmodifiable version of a collection.
+ * @param collection to wrap
+ * @return Collection
+ */
+ protected Collection unmodifiableCollection(final Collection collection) {
+ if (collection instanceof List) {
+ return Collections.unmodifiableList((List) collection);
+ }
+ if (collection instanceof SortedSet) {
+ return Collections.unmodifiableSortedSet((SortedSet) collection);
+ }
+ if (collection instanceof Set) {
+ return Collections.unmodifiableSet((Set) collection);
+ }
+ return Collections.unmodifiableCollection(collection);
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/ClassLoaderUtil.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/ClassLoaderUtil.java
new file mode 100644
index 00000000000..41704718b9e
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/ClassLoaderUtil.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.util;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Port of class loading methods from {@code org.apache.commons.lang3.ClassUtils} from
+ * the Apache Commons Lang Component. Some adjustments made to remove dependency on
+ * {@code org.apache.commons.lang3.StringUtils}. Also modified to fall back on the
+ * current class loader when an attempt to load a class with the context class loader
+ * results in a {@code java.lang.ClassNotFoundException}.
+ *
+ * See org.apache.commons.lang3.ClassUtils
+ */
+public class ClassLoaderUtil {
+ /**
+ * Maps a primitive class name to its corresponding abbreviation used in array class names.
+ */
+ private static Map abbreviationMap = new HashMap();
+
+ /**
+ * Add primitive type abbreviation to maps of abbreviations.
+ *
+ * @param primitive Canonical name of primitive type
+ * @param abbreviation Corresponding abbreviation of primitive type
+ */
+ private static void addAbbreviation(final String primitive, final String abbreviation) {
+ abbreviationMap.put(primitive, abbreviation);
+ }
+
+ /**
+ * Feed abbreviation maps
+ */
+ static {
+ addAbbreviation("int", "I");
+ addAbbreviation("boolean", "Z");
+ addAbbreviation("float", "F");
+ addAbbreviation("long", "J");
+ addAbbreviation("short", "S");
+ addAbbreviation("byte", "B");
+ addAbbreviation("double", "D");
+ addAbbreviation("char", "C");
+ }
+
+ // Class loading
+ /**
+ * Returns the class represented by {@code className} using the
+ * {@code classLoader}. This implementation supports names like
+ * "{@code java.lang.String[]}" as well as "{@code [Ljava.lang.String;}".
+ *
+ * @param classLoader the class loader to use to load the class
+ * @param className the class name
+ * @param initialize whether the class must be initialized
+ * @return the class represented by {@code className} using the {@code classLoader}
+ * @throws ClassNotFoundException if the class is not found
+ */
+ public static Class getClass(final ClassLoader classLoader, final String className, final boolean initialize)
+ throws ClassNotFoundException {
+ Class clazz;
+ if (abbreviationMap.containsKey(className)) {
+ final String clsName = "[" + abbreviationMap.get(className);
+ clazz = Class.forName(clsName, initialize, classLoader).getComponentType();
+ }
+ else {
+ clazz = Class.forName(toCanonicalName(className), initialize, classLoader);
+ }
+ return clazz;
+ }
+
+
+ /**
+ * Returns the (initialized) class represented by {@code className}
+ * using the current thread's context class loader. This implementation
+ * supports names like "{@code java.lang.String[]}" as well as
+ * "{@code [Ljava.lang.String;}".
+ *
+ * @param className the class name
+ * @return the class represented by {@code className} using the current thread's context class loader
+ * @throws ClassNotFoundException if the class is not found
+ */
+ public static Class getClass(final String className) throws ClassNotFoundException {
+ return getClass(className, true);
+ }
+
+ /**
+ * Returns the class represented by {@code className} using the
+ * current thread's context class loader. This implementation supports
+ * names like "{@code java.lang.String[]}" as well as
+ * "{@code [Ljava.lang.String;}".
+ *
+ * @param className the class name
+ * @param initialize whether the class must be initialized
+ * @return the class represented by {@code className} using the current thread's context class loader
+ * @throws ClassNotFoundException if the class is not found
+ */
+ public static Class getClass(final String className, final boolean initialize) throws ClassNotFoundException {
+ final ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
+ final ClassLoader currentCL = ClassLoaderUtil.class.getClassLoader();
+ if (contextCL != null) {
+ try {
+ return getClass(contextCL, className, initialize);
+ }
+ catch (final ClassNotFoundException ignore) { // NOPMD
+ // ignore this exception and try the current class loader
+ }
+ }
+ return getClass(currentCL, className, initialize);
+ }
+
+ /**
+ * Converts a class name to a JLS style class name.
+ *
+ * @param className the class name
+ * @return the converted name
+ */
+ private static String toCanonicalName(String className) {
+ Objects.requireNonNull(className, "className");
+ if (className.endsWith("[]")) {
+ final StringBuilder classNameBuffer = new StringBuilder();
+ while (className.endsWith("[]")) {
+ className = className.substring(0, className.length() - 2);
+ classNameBuffer.append("[");
+ }
+ final String abbreviation = (String) abbreviationMap.get(className);
+ if (abbreviation != null) {
+ classNameBuffer.append(abbreviation);
+ }
+ else {
+ classNameBuffer.append("L").append(className).append(";");
+ }
+ className = classNameBuffer.toString();
+ }
+ return className;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/KeyManagerUtils.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/KeyManagerUtils.java
new file mode 100644
index 00000000000..bfab41c9e06
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/KeyManagerUtils.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.util;
+
+import org.apache.commons.jxpath.BasicNodeSet;
+import org.apache.commons.jxpath.ExtendedKeyManager;
+import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.jxpath.KeyManager;
+import org.apache.commons.jxpath.NodeSet;
+import org.apache.commons.jxpath.Pointer;
+import org.apache.commons.jxpath.ri.InfoSetUtil;
+
+/**
+ * Utility class.
+ * @since JXPath 1.3
+ */
+public class KeyManagerUtils {
+ /**
+ * Adapt KeyManager to implement ExtendedKeyManager.
+ */
+ private static final class SingleNodeExtendedKeyManager implements
+ ExtendedKeyManager {
+ private final KeyManager delegate;
+
+ /**
+ * Create a new SingleNodeExtendedKeyManager.
+ * @param delegate KeyManager to wrap
+ */
+ public SingleNodeExtendedKeyManager(final KeyManager delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public NodeSet getNodeSetByKey(final JXPathContext context, final String key,
+ final Object value) {
+ final Pointer pointer = delegate.getPointerByKey(context, key, InfoSetUtil.stringValue(value));
+ final BasicNodeSet result = new BasicNodeSet();
+ result.add(pointer);
+ return result;
+ }
+
+ @Override
+ public Pointer getPointerByKey(final JXPathContext context, final String keyName,
+ final String keyValue) {
+ return delegate.getPointerByKey(context, keyName, keyValue);
+ }
+ }
+
+ /**
+ * Gets an ExtendedKeyManager from the specified KeyManager.
+ * @param keyManager to adapt, if necessary
+ * @return {@code keyManager} if it implements ExtendedKeyManager
+ * or a basic single-result ExtendedKeyManager that delegates to
+ * {@code keyManager}.
+ */
+ public static ExtendedKeyManager getExtendedKeyManager(final KeyManager keyManager) {
+ return keyManager instanceof ExtendedKeyManager ? (ExtendedKeyManager) keyManager
+ : new SingleNodeExtendedKeyManager(keyManager);
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/MethodLookupUtils.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/MethodLookupUtils.java
new file mode 100644
index 00000000000..de0dbbef4cd
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/MethodLookupUtils.java
@@ -0,0 +1,316 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.util;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+
+import org.apache.commons.jxpath.ExpressionContext;
+import org.apache.commons.jxpath.JXPathException;
+
+/**
+ * Method lookup utilities, which find static and non-static methods as well
+ * as constructors based on a name and list of parameters.
+ */
+public class MethodLookupUtils {
+
+ private static final int NO_MATCH = 0;
+ private static final int APPROXIMATE_MATCH = 1;
+ private static final int EXACT_MATCH = 2;
+
+ /**
+ * Look up a constructor.
+ * @param targetClass the class constructed
+ * @param parameters arguments
+ * @return Constructor found if any.
+ */
+ public static Constructor lookupConstructor(
+ final Class targetClass,
+ final Object[] parameters) {
+ boolean tryExact = true;
+ final int count = parameters == null ? 0 : parameters.length;
+ final Class[] types = new Class[count];
+ for (int i = 0; i < count; i++) {
+ final Object param = parameters[i];
+ if (param != null) {
+ types[i] = param.getClass();
+ }
+ else {
+ types[i] = null;
+ tryExact = false;
+ }
+ }
+
+ Constructor constructor = null;
+
+ if (tryExact) {
+ // First - without type conversion
+ try {
+ constructor = targetClass.getConstructor(types);
+ if (constructor != null) {
+ return constructor;
+ }
+ }
+ catch (final NoSuchMethodException ignore) { // NOPMD
+ // Ignore
+ }
+ }
+
+ int currentMatch = 0;
+ boolean ambiguous = false;
+
+ // Then - with type conversion
+ final Constructor[] constructors = targetClass.getConstructors();
+ for (final Constructor constructor2 : constructors) {
+ final int match =
+ matchParameterTypes(
+ constructor2.getParameterTypes(),
+ parameters);
+ if (match != NO_MATCH) {
+ if (match > currentMatch) {
+ constructor = constructor2;
+ currentMatch = match;
+ ambiguous = false;
+ }
+ else if (match == currentMatch) {
+ ambiguous = true;
+ }
+ }
+ }
+ if (ambiguous) {
+ throw new JXPathException(
+ "Ambiguous constructor " + Arrays.asList(parameters));
+ }
+ return constructor;
+ }
+
+ /**
+ * Look up a static method.
+ * @param targetClass the owning class
+ * @param name method name
+ * @param parameters method parameters
+ * @return Method found if any
+ */
+ public static Method lookupStaticMethod(
+ final Class targetClass,
+ final String name,
+ final Object[] parameters) {
+ boolean tryExact = true;
+ final int count = parameters == null ? 0 : parameters.length;
+ final Class[] types = new Class[count];
+ for (int i = 0; i < count; i++) {
+ final Object param = parameters[i];
+ if (param != null) {
+ types[i] = param.getClass();
+ }
+ else {
+ types[i] = null;
+ tryExact = false;
+ }
+ }
+
+ Method method = null;
+
+ if (tryExact) {
+ // First - without type conversion
+ try {
+ method = targetClass.getMethod(name, types);
+ if (method != null
+ && Modifier.isStatic(method.getModifiers())) {
+ return method;
+ }
+ }
+ catch (final NoSuchMethodException ignore) { // NOPMD
+ // Ignore
+ }
+ }
+
+ int currentMatch = 0;
+ boolean ambiguous = false;
+
+ // Then - with type conversion
+ final Method[] methods = targetClass.getMethods();
+ for (final Method method2 : methods) {
+ if (Modifier.isStatic(method2.getModifiers())
+ && method2.getName().equals(name)) {
+ final int match =
+ matchParameterTypes(
+ method2.getParameterTypes(),
+ parameters);
+ if (match != NO_MATCH) {
+ if (match > currentMatch) {
+ method = method2;
+ currentMatch = match;
+ ambiguous = false;
+ }
+ else if (match == currentMatch) {
+ ambiguous = true;
+ }
+ }
+ }
+ }
+ if (ambiguous) {
+ throw new JXPathException("Ambiguous method call: " + name);
+ }
+ return method;
+ }
+
+ /**
+ * Look up a method.
+ * @param targetClass owning class
+ * @param name method name
+ * @param parameters method parameters
+ * @return Method found if any
+ */
+ public static Method lookupMethod(
+ Class targetClass,
+ final String name,
+ final Object[] parameters) {
+ if (parameters == null
+ || parameters.length < 1
+ || parameters[0] == null) {
+ return null;
+ }
+
+ if (matchType(targetClass, parameters[0]) == NO_MATCH) {
+ return null;
+ }
+
+ targetClass = TypeUtils.convert(parameters[0], targetClass).getClass();
+
+ boolean tryExact = true;
+ final int count = parameters.length - 1;
+ final Class[] types = new Class[count];
+ final Object[] arguments = new Object[count];
+ for (int i = 0; i < count; i++) {
+ final Object param = parameters[i + 1];
+ arguments[i] = param;
+ if (param != null) {
+ types[i] = param.getClass();
+ }
+ else {
+ types[i] = null;
+ tryExact = false;
+ }
+ }
+
+ Method method = null;
+
+ if (tryExact) {
+ // First - without type conversion
+ try {
+ method = targetClass.getMethod(name, types);
+ if (method != null
+ && !Modifier.isStatic(method.getModifiers())) {
+ return method;
+ }
+ }
+ catch (final NoSuchMethodException ignore) { // NOPMD
+ // Ignore
+ }
+ }
+
+ int currentMatch = 0;
+ boolean ambiguous = false;
+
+ // Then - with type conversion
+ final Method[] methods = targetClass.getMethods();
+ for (final Method method2 : methods) {
+ if (!Modifier.isStatic(method2.getModifiers())
+ && method2.getName().equals(name)) {
+ final int match =
+ matchParameterTypes(
+ method2.getParameterTypes(),
+ arguments);
+ if (match != NO_MATCH) {
+ if (match > currentMatch) {
+ method = method2;
+ currentMatch = match;
+ ambiguous = false;
+ }
+ else if (match == currentMatch) {
+ ambiguous = true;
+ }
+ }
+ }
+ }
+ if (ambiguous) {
+ throw new JXPathException("Ambiguous method call: " + name);
+ }
+ return method;
+ }
+
+ /**
+ * Return a match code of objects to types.
+ * @param types Class[] of expected types
+ * @param parameters Object[] to attempt to match
+ * @return int code
+ */
+ private static int matchParameterTypes(
+ final Class[] types,
+ final Object[] parameters) {
+ int pi = 0;
+ if (types.length >= 1
+ && ExpressionContext.class.isAssignableFrom(types[0])) {
+ pi++;
+ }
+ final int length = parameters == null ? 0 : parameters.length;
+ if (types.length != length + pi) {
+ return NO_MATCH;
+ }
+ int totalMatch = EXACT_MATCH;
+ for (int i = 0; i < length; i++) {
+ final int match = matchType(types[i + pi], parameters[i]);
+ if (match == NO_MATCH) {
+ return NO_MATCH;
+ }
+ if (match < totalMatch) {
+ totalMatch = match;
+ }
+ }
+ return totalMatch;
+ }
+
+ /**
+ * Return a match code between an object and type.
+ * @param expected class to test
+ * @param object object to test
+ * @return int code
+ */
+ private static int matchType(final Class expected, final Object object) {
+ if (object == null) {
+ return APPROXIMATE_MATCH;
+ }
+
+ final Class actual = object.getClass();
+
+ if (expected.equals(actual)) {
+ return EXACT_MATCH;
+ }
+ if (expected.isAssignableFrom(actual)) {
+ return EXACT_MATCH;
+ }
+
+ if (TypeUtils.canConvert(object, expected)) {
+ return APPROXIMATE_MATCH;
+ }
+
+ return NO_MATCH;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/ReverseComparator.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/ReverseComparator.java
new file mode 100644
index 00000000000..b28880a7052
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/ReverseComparator.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.util;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * Reverse comparator.
+ */
+public final class ReverseComparator implements Comparator, Serializable {
+ private static final long serialVersionUID = -2795475743948616649L;
+
+ /**
+ * Singleton reverse comparator instance.
+ */
+ public static final Comparator INSTANCE = new ReverseComparator();
+
+ /**
+ * Create a new ReverseComparator.
+ */
+ private ReverseComparator() {
+ }
+
+ @Override
+ public int compare(final Object o1, final Object o2) {
+ return ((Comparable) o2).compareTo(o1);
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/TypeConverter.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/TypeConverter.java
new file mode 100644
index 00000000000..4969931b10e
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/TypeConverter.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.util;
+
+/**
+ * A type converter can be installed on {@link TypeUtils} to introduce
+ * additional type conversions for JXPath. Most of
+ * the time {@link BasicTypeConverter} should be used as the superclass.
+ *
+ * @see TypeUtils#setTypeConverter
+ */
+public interface TypeConverter {
+
+ /**
+ * Returns true if it can convert the supplied
+ * object to the specified class.
+ * @param object object to test
+ * @param toType target class
+ * @return boolean
+ */
+ boolean canConvert(Object object, Class toType);
+
+ /**
+ * Converts the supplied object to the specified
+ * type. Throws a runtime exception if the conversion is
+ * not possible.
+ * @param object object to convert
+ * @param toType target class
+ * @return resulting Object
+ */
+ Object convert(Object object, Class toType);
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/TypeUtils.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/TypeUtils.java
new file mode 100644
index 00000000000..715b0bef760
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/TypeUtils.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.util;
+
+import java.util.HashMap;
+
+/**
+ * Global type conversion utilities.
+ */
+public class TypeUtils {
+ private static TypeConverter typeConverter = new BasicTypeConverter();
+ private static final HashMap PRIMITIVE_TYPE_MAP = new HashMap() {
+ private static final long serialVersionUID = 1L;
+
+ {
+ put(int.class, Integer.class);
+ put(byte.class, Byte.class);
+ put(short.class, Short.class);
+ put(char.class, Character.class);
+ put(long.class, Long.class);
+ put(float.class, Float.class);
+ put(double.class, Double.class);
+ put(boolean.class, Boolean.class);
+ }
+ };
+
+ /**
+ * Returns true if the global converter can convert the supplied
+ * object to the specified type.
+ * @param object object to test
+ * @param toType target class
+ * @return boolean
+ */
+ public static boolean canConvert(final Object object, final Class toType) {
+ return typeConverter.canConvert(object, toType);
+ }
+
+ /**
+ * Converts the supplied object to the specified type. May
+ * throw a RuntimeException.
+ * @param object object to convert
+ * @param toType target class
+ * @return resulting Object
+ */
+ public static Object convert(final Object object, final Class toType) {
+ return typeConverter.convert(object, toType);
+ }
+
+ /**
+ * Return the appropriate wrapper type for the specified class.
+ * @param p Class for which to retrieve a wrapper class.
+ * @return the wrapper if {@code p} is primitive, else {@code p}.
+ * @since JXPath 1.3
+ */
+ public static Class wrapPrimitive(final Class p) {
+ return p.isPrimitive() ? (Class) PRIMITIVE_TYPE_MAP.get(p) : p;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/ValueUtils.java b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/ValueUtils.java
new file mode 100644
index 00000000000..7e8a27bb86e
--- /dev/null
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/apache/commons/jxpath/util/ValueUtils.java
@@ -0,0 +1,641 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.util;
+
+import java.beans.IndexedPropertyDescriptor;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.jxpath.Container;
+import org.apache.commons.jxpath.DynamicPropertyHandler;
+import org.apache.commons.jxpath.JXPathException;
+
+/**
+ * Collection and property access utilities.
+ */
+public class ValueUtils {
+ private static Map dynamicPropertyHandlerMap = new HashMap();
+ private static final int UNKNOWN_LENGTH_MAX_COUNT = 16000;
+
+ /**
+ * Returns true if the object is an array or a Collection.
+ * @param value to test
+ * @return boolean
+ */
+ public static boolean isCollection(Object value) {
+ value = getValue(value);
+ if (value == null) {
+ return false;
+ }
+ if (value.getClass().isArray()) {
+ return true;
+ }
+ return value instanceof Collection;
+ }
+
+ /**
+ * Returns 1 if the type is a collection,
+ * -1 if it is definitely not
+ * and 0 if it may be a collection in some cases.
+ * @param clazz to test
+ * @return int
+ */
+ public static int getCollectionHint(final Class clazz) {
+ if (clazz.isArray()) {
+ return 1;
+ }
+
+ if (Collection.class.isAssignableFrom(clazz)) {
+ return 1;
+ }
+
+ if (clazz.isPrimitive()) {
+ return -1;
+ }
+
+ if (clazz.isInterface()) {
+ return 0;
+ }
+
+ if (Modifier.isFinal(clazz.getModifiers())) {
+ return -1;
+ }
+
+ return 0;
+ }
+
+ /**
+ * If there is a regular non-indexed read method for this property,
+ * uses this method to obtain the collection and then returns its
+ * length.
+ * Otherwise, attempts to guess the length of the collection by
+ * calling the indexed get method repeatedly. The method is supposed
+ * to throw an exception if the index is out of bounds.
+ * @param object collection
+ * @param pd IndexedPropertyDescriptor
+ * @return int
+ */
+ public static int getIndexedPropertyLength(final Object object,
+ final IndexedPropertyDescriptor pd) {
+ if (pd.getReadMethod() != null) {
+ return getLength(getValue(object, pd));
+ }
+
+ final Method readMethod = pd.getIndexedReadMethod();
+ if (readMethod == null) {
+ throw new JXPathException(
+ "No indexed read method for property " + pd.getName());
+ }
+
+ for (int i = 0; i < UNKNOWN_LENGTH_MAX_COUNT; i++) {
+ try {
+ readMethod.invoke(object, Integer.valueOf(i));
+ }
+ catch (final Throwable t) {
+ return i;
+ }
+ }
+
+ throw new JXPathException(
+ "Cannot determine the length of the indexed property "
+ + pd.getName());
+ }
+
+ /**
+ * Returns the length of the supplied collection. If the supplied object
+ * is not a collection, returns 1. If collection is null, returns 0.
+ * @param collection to check
+ * @return int
+ */
+ public static int getLength(Object collection) {
+ if (collection == null) {
+ return 0;
+ }
+ collection = getValue(collection);
+ if (collection.getClass().isArray()) {
+ return Array.getLength(collection);
+ }
+ if (collection instanceof Collection) {
+ return ((Collection) collection).size();
+ }
+ return 1;
+ }
+
+ /**
+ * Returns an iterator for the supplied collection. If the argument
+ * is null, returns an empty iterator. If the argument is not
+ * a collection, returns an iterator that produces just that one object.
+ * @param collection to iterate
+ * @return Iterator
+ */
+ public static Iterator iterate(final Object collection) {
+ if (collection == null) {
+ return Collections.EMPTY_LIST.iterator();
+ }
+ if (collection.getClass().isArray()) {
+ final int length = Array.getLength(collection);
+ if (length == 0) {
+ return Collections.EMPTY_LIST.iterator();
+ }
+ final ArrayList list = new ArrayList();
+ for (int i = 0; i < length; i++) {
+ list.add(Array.get(collection, i));
+ }
+ return list.iterator();
+ }
+ if (collection instanceof Collection) {
+ return ((Collection) collection).iterator();
+ }
+ return Collections.singletonList(collection).iterator();
+ }
+
+ /**
+ * Grows the collection if necessary to the specified size. Returns
+ * the new, expanded collection.
+ * @param collection to expand
+ * @param size desired size
+ * @return collection or array
+ */
+ public static Object expandCollection(final Object collection, final int size) {
+ if (collection == null) {
+ return null;
+ }
+ if (size < getLength(collection)) {
+ throw new JXPathException("adjustment of " + collection
+ + " to size " + size + " is not an expansion");
+ }
+ if (collection.getClass().isArray()) {
+ final Object bigger =
+ Array.newInstance(
+ collection.getClass().getComponentType(),
+ size);
+ System.arraycopy(
+ collection,
+ 0,
+ bigger,
+ 0,
+ Array.getLength(collection));
+ return bigger;
+ }
+ if (collection instanceof Collection) {
+ while (((Collection) collection).size() < size) {
+ ((Collection) collection).add(null);
+ }
+ return collection;
+ }
+ throw new JXPathException(
+ "Cannot turn "
+ + collection.getClass().getName()
+ + " into a collection of size "
+ + size);
+ }
+
+ /**
+ * Remove the index'th element from the supplied collection.
+ * @param collection to edit
+ * @param index int
+ * @return the resulting collection
+ */
+ public static Object remove(Object collection, final int index) {
+ collection = getValue(collection);
+ if (collection == null) {
+ return null;
+ }
+ if (index >= getLength(collection)) {
+ throw new JXPathException("No such element at index " + index);
+ }
+ if (collection.getClass().isArray()) {
+ final int length = Array.getLength(collection);
+ final Object smaller =
+ Array.newInstance(
+ collection.getClass().getComponentType(),
+ length - 1);
+ if (index > 0) {
+ System.arraycopy(collection, 0, smaller, 0, index);
+ }
+ if (index < length - 1) {
+ System.arraycopy(
+ collection,
+ index + 1,
+ smaller,
+ index,
+ length - index - 1);
+ }
+ return smaller;
+ }
+ if (collection instanceof List) {
+ final int size = ((List) collection).size();
+ if (index < size) {
+ ((List) collection).remove(index);
+ }
+ return collection;
+ }
+ if (collection instanceof Collection) {
+ final Iterator it = ((Collection) collection).iterator();
+ for (int i = 0; i < index; i++) {
+ if (!it.hasNext()) {
+ break;
+ }
+ it.next();
+ }
+ if (it.hasNext()) {
+ it.next();
+ it.remove();
+ }
+ return collection;
+ }
+ throw new JXPathException(
+ "Cannot remove "
+ + collection.getClass().getName()
+ + "["
+ + index
+ + "]");
+ }
+
+ /**
+ * Returns the index'th element of the supplied collection.
+ * @param collection to read
+ * @param index int
+ * @return collection[index]
+ */
+ public static Object getValue(Object collection, final int index) {
+ collection = getValue(collection);
+ Object value = collection;
+ if (collection != null) {
+ if (collection.getClass().isArray()) {
+ if (index < 0 || index >= Array.getLength(collection)) {
+ return null;
+ }
+ value = Array.get(collection, index);
+ }
+ else if (collection instanceof List) {
+ if (index < 0 || index >= ((List) collection).size()) {
+ return null;
+ }
+ value = ((List) collection).get(index);
+ }
+ else if (collection instanceof Collection) {
+ if (index < 0 || index >= ((Collection) collection).size()) {
+ return null;
+ }
+
+ int i = 0;
+ final Iterator it = ((Collection) collection).iterator();
+ for (; i < index; i++) {
+ it.next();
+ }
+ if (it.hasNext()) {
+ value = it.next();
+ }
+ else {
+ value = null;
+ }
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Modifies the index'th element of the supplied collection.
+ * Converts the value to the required type if necessary.
+ * @param collection to edit
+ * @param index to replace
+ * @param value new value
+ */
+ public static void setValue(Object collection, final int index, final Object value) {
+ collection = getValue(collection);
+ if (collection != null) {
+ if (collection.getClass().isArray()) {
+ Array.set(
+ collection,
+ index,
+ convert(value, collection.getClass().getComponentType()));
+ }
+ else if (collection instanceof List) {
+ ((List) collection).set(index, value);
+ }
+ else if (collection instanceof Collection) {
+ throw new UnsupportedOperationException(
+ "Cannot set value of an element of a "
+ + collection.getClass().getName());
+ }
+ }
+ }
+
+ /**
+ * Returns the value of the bean's property represented by
+ * the supplied property descriptor.
+ * @param bean to read
+ * @param propertyDescriptor indicating what to read
+ * @return Object value
+ */
+ public static Object getValue(final Object bean,
+ final PropertyDescriptor propertyDescriptor) {
+ Object value;
+ try {
+ final Method method =
+ getAccessibleMethod(propertyDescriptor.getReadMethod());
+ if (method == null) {
+ throw new JXPathException("No read method");
+ }
+ value = method.invoke(bean);
+ }
+ catch (final Exception ex) {
+ throw new JXPathException(
+ "Cannot access property: "
+ + (bean == null ? "null" : bean.getClass().getName())
+ + "."
+ + propertyDescriptor.getName(),
+ ex);
+ }
+ return value;
+ }
+
+ /**
+ * Modifies the value of the bean's property represented by
+ * the supplied property descriptor.
+ * @param bean to read
+ * @param propertyDescriptor indicating what to read
+ * @param value to set
+ */
+ public static void setValue(final Object bean,
+ final PropertyDescriptor propertyDescriptor, Object value) {
+ try {
+ final Method method =
+ getAccessibleMethod(propertyDescriptor.getWriteMethod());
+ if (method == null) {
+ throw new JXPathException("No write method");
+ }
+ value = convert(value, propertyDescriptor.getPropertyType());
+ method.invoke(bean, value);
+ }
+ catch (final Exception ex) {
+ throw new JXPathException(
+ "Cannot modify property: "
+ + (bean == null ? "null" : bean.getClass().getName())
+ + "."
+ + propertyDescriptor.getName(),
+ ex);
+ }
+ }
+
+ /**
+ * Convert value to type.
+ * @param value Object
+ * @param type destination
+ * @return conversion result
+ */
+ private static Object convert(final Object value, final Class type) {
+ try {
+ return TypeUtils.convert(value, type);
+ }
+ catch (final Exception ex) {
+ throw new JXPathException(
+ "Cannot convert value of class "
+ + (value == null ? "null" : value.getClass().getName())
+ + " to type "
+ + type,
+ ex);
+ }
+ }
+
+ /**
+ * Returns the index'th element of the bean's property represented by
+ * the supplied property descriptor.
+ * @param bean to read
+ * @param propertyDescriptor indicating what to read
+ * @param index int
+ * @return Object
+ */
+ public static Object getValue(final Object bean,
+ final PropertyDescriptor propertyDescriptor, final int index) {
+ if (propertyDescriptor instanceof IndexedPropertyDescriptor) {
+ try {
+ final IndexedPropertyDescriptor ipd =
+ (IndexedPropertyDescriptor) propertyDescriptor;
+ final Method method = ipd.getIndexedReadMethod();
+ if (method != null) {
+ return method.invoke(
+ bean,
+ Integer.valueOf(index));
+ }
+ }
+ catch (final InvocationTargetException ex) {
+ final Throwable t = ex.getTargetException();
+ if (t instanceof IndexOutOfBoundsException) {
+ return null;
+ }
+ throw new JXPathException(
+ "Cannot access property: " + propertyDescriptor.getName(),
+ t);
+ }
+ catch (final Throwable ex) {
+ throw new JXPathException(
+ "Cannot access property: " + propertyDescriptor.getName(),
+ ex);
+ }
+ }
+
+ // We will fall through if there is no indexed read
+
+ return getValue(getValue(bean, propertyDescriptor), index);
+ }
+
+ /**
+ * Modifies the index'th element of the bean's property represented by
+ * the supplied property descriptor. Converts the value to the required
+ * type if necessary.
+ * @param bean to edit
+ * @param propertyDescriptor indicating what to set
+ * @param index int
+ * @param value to set
+ */
+ public static void setValue(final Object bean,
+ final PropertyDescriptor propertyDescriptor, final int index, final Object value) {
+ if (propertyDescriptor instanceof IndexedPropertyDescriptor) {
+ try {
+ final IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) propertyDescriptor;
+ final Method method = ipd.getIndexedWriteMethod();
+ if (method != null) {
+ method.invoke(bean, Integer.valueOf(index), convert(value, ipd.getIndexedPropertyType()));
+ return;
+ }
+ }
+ catch (final Exception ex) {
+ throw new IllegalArgumentException("Cannot access property: " + propertyDescriptor.getName() + ", " + ex.getMessage());
+ }
+ }
+ // We will fall through if there is no indexed read
+ final Object collection = getValue(bean, propertyDescriptor);
+ if (isCollection(collection)) {
+ setValue(collection, index, value);
+ }
+ else if (index == 0) {
+ setValue(bean, propertyDescriptor, value);
+ }
+ else {
+ throw new IllegalArgumentException("Not a collection: " + propertyDescriptor.getName());
+ }
+ }
+
+ /**
+ * If the parameter is a container, opens the container and
+ * return the contents. The method is recursive.
+ * @param object to read
+ * @return Object
+ */
+ public static Object getValue(Object object) {
+ while (object instanceof Container) {
+ object = ((Container) object).getValue();
+ }
+ return object;
+ }
+
+ /**
+ * Returns a shared instance of the dynamic property handler class
+ * returned by {@code getDynamicPropertyHandlerClass()}.
+ * @param clazz to handle
+ * @return DynamicPropertyHandler
+ */
+ public static DynamicPropertyHandler getDynamicPropertyHandler(final Class clazz) {
+ return (DynamicPropertyHandler) dynamicPropertyHandlerMap.computeIfAbsent(clazz, k -> {
+ try {
+ return (DynamicPropertyHandler) clazz.getConstructor().newInstance();
+ }
+ catch (final Exception ex) {
+ throw new JXPathException("Cannot allocate dynamic property handler of class " + clazz.getName(), ex);
+ }
+ });
+ }
+
+ //
+ // The rest of the code in this file was copied FROM
+ // org.apache.commons.beanutils.PropertyUtil. We don't want to introduce
+ // a dependency on BeanUtils yet - DP.
+ //
+
+ /**
+ * Gets an accessible method (that is, one that can be invoked via
+ * reflection) that implements the specified Method. If no such method
+ * can be found, return {@code null}.
+ *
+ * @param method The method that we wish to call
+ * @return Method
+ */
+ public static Method getAccessibleMethod(final Method method) {
+
+ // Make sure we have a method to check
+ if (method == null) {
+ return null;
+ }
+
+ // If the requested method is not public we cannot call it
+ if (!Modifier.isPublic(method.getModifiers())) {
+ return null;
+ }
+
+ // If the declaring class is public, we are done
+ Class clazz = method.getDeclaringClass();
+ if (Modifier.isPublic(clazz.getModifiers())) {
+ return method;
+ }
+
+ final String name = method.getName();
+ final Class[] parameterTypes = method.getParameterTypes();
+ while (clazz != null) {
+ // Check the implemented interfaces and subinterfaces
+ final Method aMethod = getAccessibleMethodFromInterfaceNest(clazz,
+ name, parameterTypes);
+ if (aMethod != null) {
+ return aMethod;
+ }
+
+ clazz = clazz.getSuperclass();
+ if (clazz != null && Modifier.isPublic(clazz.getModifiers())) {
+ try {
+ return clazz.getDeclaredMethod(name, parameterTypes);
+ }
+ catch (final NoSuchMethodException ignore) { // NOPMD
+ //ignore
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets an accessible method (that is, one that can be invoked via
+ * reflection) that implements the specified method, by scanning through
+ * all implemented interfaces and subinterfaces. If no such Method
+ * can be found, return {@code null}.
+ *
+ * @param clazz Parent class for the interfaces to be checked
+ * @param methodName Method name of the method we wish to call
+ * @param parameterTypes The parameter type signatures
+ * @return Method
+ */
+ private static Method getAccessibleMethodFromInterfaceNest(final Class clazz,
+ final String methodName, final Class[] parameterTypes) {
+
+ Method method = null;
+
+ // Check the implemented interfaces of the parent class
+ final Class[] interfaces = clazz.getInterfaces();
+ for (final Class element : interfaces) {
+
+ // Is this interface public?
+ if (!Modifier.isPublic(element.getModifiers())) {
+ continue;
+ }
+
+ // Does the method exist on this interface?
+ try {
+ method =
+ element.getDeclaredMethod(methodName, parameterTypes);
+ }
+ catch (final NoSuchMethodException ignore) { // NOPMD
+ // ignore
+ }
+ if (method != null) {
+ break;
+ }
+
+ // Recursively check our parent interfaces
+ method =
+ getAccessibleMethodFromInterfaceNest(
+ element,
+ methodName,
+ parameterTypes);
+ if (method != null) {
+ break;
+ }
+ }
+
+ // Return whatever we have found
+ return method;
+ }
+}
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/internal/xpath/CollectionPointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/internal/xpath/CollectionPointer.java
index ed3ee924e85..3eae942ba31 100644
--- a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/internal/xpath/CollectionPointer.java
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/internal/xpath/CollectionPointer.java
@@ -29,8 +29,6 @@
******************************************************************************/
package org.eclipse.e4.emf.internal.xpath;
-import java.util.Locale;
-
import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.jxpath.JXPathIntrospector;
import org.apache.commons.jxpath.ri.Compiler;
@@ -51,16 +49,6 @@ public class CollectionPointer extends NodePointer {
private static final long serialVersionUID = 8620254915563256588L;
- /**
- * Create a new CollectionPointer.
- * @param collection value
- * @param locale Locale
- */
- public CollectionPointer(Object collection, Locale locale) {
- super(null, locale);
- this.collection = collection;
- }
-
/**
* Create a new CollectionPointer.
* @param parent parent NodePointer
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/internal/xpath/EStructuralFeatureIterator.java b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/internal/xpath/EStructuralFeatureIterator.java
index 57b1b84485a..e3df4c7871d 100644
--- a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/internal/xpath/EStructuralFeatureIterator.java
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/internal/xpath/EStructuralFeatureIterator.java
@@ -93,14 +93,6 @@ public EStructuralFeatureIterator(
}
}
- /**
- * Get the property pointer.
- * @return NodePointer
- */
- protected NodePointer getPropertyPointer() {
- return propertyNodePointer;
- }
-
/**
* Reset property iteration.
*/
diff --git a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/internal/xpath/NullPointer.java b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/internal/xpath/NullPointer.java
index c9493e874e5..9c0f478a86c 100644
--- a/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/internal/xpath/NullPointer.java
+++ b/bundles/org.eclipse.e4.emf.xpath/src/org/eclipse/e4/emf/internal/xpath/NullPointer.java
@@ -29,7 +29,6 @@
******************************************************************************/
package org.eclipse.e4.emf.internal.xpath;
-import java.util.Locale;
import java.util.Objects;
import org.apache.commons.jxpath.JXPathContext;
@@ -45,16 +44,6 @@ public class NullPointer extends EStructuralFeatureOwnerPointer {
private static final long serialVersionUID = 2193425983220679887L;
- /**
- * Create a new NullPointer.
- * @param name node name
- * @param locale Locale
- */
- public NullPointer(QName name, Locale locale) {
- super(null, locale);
- this.name = name;
- }
-
/**
* Used for the root node.
* @param parent parent pointer
@@ -65,16 +54,6 @@ public NullPointer(NodePointer parent, QName name) {
this.name = name;
}
- /**
- * Create a new NullPointer.
- * @param locale Locale
- * @param id String
- */
- public NullPointer(Locale locale, String id) {
- super(null, locale);
- this.id = id;
- }
-
@Override
public QName getName() {
return name;
diff --git a/tests/org.eclipse.e4.emf.xpath.test/META-INF/MANIFEST.MF b/tests/org.eclipse.e4.emf.xpath.test/META-INF/MANIFEST.MF
index 6324cff101a..e11a73ff2cd 100644
--- a/tests/org.eclipse.e4.emf.xpath.test/META-INF/MANIFEST.MF
+++ b/tests/org.eclipse.e4.emf.xpath.test/META-INF/MANIFEST.MF
@@ -12,7 +12,6 @@ Export-Package: org.eclipse.e4.emf.xpath.test.model.xpathtest,
Require-Bundle: org.eclipse.e4.ui.model.workbench,
org.eclipse.e4.emf.xpath,
org.junit,
- org.apache.commons.jxpath,
org.eclipse.emf.ecore.xmi,
org.eclipse.core.runtime,
org.eclipse.emf.ecore,
diff --git a/tests/org.eclipse.e4.emf.xpath.test/src/org/eclipse/e4/emf/xpath/test/ExampleQueriesTestCase.java b/tests/org.eclipse.e4.emf.xpath.test/src/org/eclipse/e4/emf/xpath/test/ExampleQueriesTestCase.java
index b6a5d552e2f..7031707a0bf 100644
--- a/tests/org.eclipse.e4.emf.xpath.test/src/org/eclipse/e4/emf/xpath/test/ExampleQueriesTestCase.java
+++ b/tests/org.eclipse.e4.emf.xpath.test/src/org/eclipse/e4/emf/xpath/test/ExampleQueriesTestCase.java
@@ -23,7 +23,6 @@
import java.util.Iterator;
import java.util.List;
-import org.apache.commons.jxpath.JXPathNotFoundException;
import org.eclipse.e4.emf.xpath.EcoreXPathContextFactory;
import org.eclipse.e4.emf.xpath.XPathContext;
import org.eclipse.e4.emf.xpath.XPathContextFactory;
@@ -83,7 +82,7 @@ public void testSimpleQuery() {
assertNotNull(application);
assertSame(RootImpl.class, application.getClass());
- assertThrows(JXPathNotFoundException.class, () -> xpathContext.getValue(".[@id='nixda']"));
+ assertThrows(RuntimeException.class, () -> xpathContext.getValue(".[@id='nixda']"));
application = xpathContext.getValue(".[@id='root']");
assertNotNull(application);