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: + *

+ * @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: + *

+ * @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: + *
    + *
  1. Build an implementation of the DynamicPropertyHandler interface + * for the desired collection type.
  2. + *
  3. Invoke the static method {@link JXPathIntrospector#registerDynamicClass + * JXPathIntrospector.registerDynamicClass(class, handlerClass)}
  4. + *
+ * JXPath allows access to dynamic properties using these three formats: + * + */ +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

+ * + * + * 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: + *

+ * @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: + *

+ * @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);