Permalink
Browse files

Fix some API-related issues with the XPath API.

- changed Namespace-related methods to use Namespace instead of a String URI
- added methods to get/set variables based on the 'qname' of the variable (symmetric with the XPathFactory.compile)
- added method to query all the Namespaces on an XPathExpression
  • Loading branch information...
1 parent c1c63c5 commit 5d153c56e08c71f21ba30565a8a2ea19f60e2555 @rolfl rolfl committed Apr 2, 2012
@@ -78,7 +78,7 @@ public JavaXPathExpression(final String query, final Filter<T> filter,
@Override
public String getNamespaceURI(String prefix) {
- return getNamespace(prefix);
+ return getNamespace(prefix).getURI();
}
@Override
@@ -126,8 +126,8 @@ public String getPrefix(String namespaceURI) {
@Override
public Object resolveVariable(QName variableName) {
- return getVariable(
- variableName.getNamespaceURI(), variableName.getLocalPart());
+ return getVariable(variableName.getLocalPart(),
+ Namespace.getNamespace(variableName.getNamespaceURI()));
}
private Object wrapContext(Object context) {
@@ -53,8 +53,8 @@ public XObject getVariableOrParam(XPathContext xctxt, QName qname)
if (qname == null) {
throw new IllegalArgumentException("Null qname");
}
- final Object varValue = getVariable(
- qname.getNamespaceURI(), qname.getLocalName());
+ final Object varValue = getVariable(qname.getLocalName(),
+ Namespace.getNamespace(qname.getNamespaceURI()));
if ( varValue == null ) {
throw new TransformerException(
"No such variable " + qname.toNamespacedString());
@@ -180,13 +180,13 @@ protected Object evaluateRawFirst(Object context) {
@Override
public String getNamespaceForPrefix(String prefix) {
- return getNamespace(prefix);
+ return getNamespace(prefix).getURI();
}
@Override
public String getNamespaceForPrefix(String prefix, Node context) {
if (context == null) {
- return getNamespace(prefix);
+ return getNamespace(prefix).getPrefix();
}
if (prefix == null) {
prefix = "";
@@ -105,7 +105,7 @@ public XPathBuilder(String expression, Filter<T> filter) {
* Variables can have a null value. The {@link XPathExpression} can change
* the variable value before the expression is evaluated, and, some XPath
* libraries support a null variable value. See
- * {@link XPathExpression#setVariable(String, String, Object)}.
+ * {@link XPathExpression#setVariable(String, Namespace, Object)}.
* <p>
* In order to validate that a Variable is unique you have to know the
* namespace associated with the prefix. This class is designed to make it
@@ -241,7 +241,7 @@ public Object getVariable(String qname) {
}
/**
- * Get the variable value associated with the given name.
+ * Get the Namespace associated with the given prefix.
*
* @param prefix
* The Namespace prefix to get the Namespace for.
@@ -56,6 +56,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
import java.util.List;
+import org.jdom2.Namespace;
import org.jdom2.filter.Filter;
/**
@@ -105,15 +106,22 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
public String getExpression();
/**
- * Get the Namespace URI associated with a given prefix.
+ * Get the Namespace associated with a given prefix.
*
* @param prefix
* The prefix to select the Namespace URI for.
* @return the URI of the specified Namespace prefix
* @throws IllegalArgumentException
* if that prefix is not defined.
*/
- public String getNamespace(String prefix);
+ public Namespace getNamespace(String prefix);
+
+ /**
+ * Get the Namespaces that were used to compile this XPathExpression.
+ *
+ * @return a potentially empty array of Namespaces (never null).
+ */
+ public Namespace[] getNamespaces();
/**
* Change the defined value for a variable to some new value. You may not
@@ -125,10 +133,10 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* support a null value it should be translated to something meaningful for
* that library, typically the empty string.
*
+ * @param localname
+ * The variable localname to change.
* @param uri
- * the Namespace URI in which the variable name is declared.
- * @param name
- * The variable to change.
+ * the Namespace in which the variable name is declared.
* @param value
* The new value to set.
* @return The value of the variable prior to this change.
@@ -137,23 +145,70 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* @throws IllegalArgumentException
* if name is not already a variable.
*/
- public Object setVariable(String uri, String name, Object value);
+ public Object setVariable(String localname, Namespace uri, Object value);
+
+ /**
+ * Change the defined value for a variable to some new value. You may not
+ * use this method to add new variables to the compiled XPath, you can only
+ * change existing variable values.
+ * <p>
+ * The value of the variable may be null. Some XPath libraries support a
+ * null value, and if the library that this expression is for does not
+ * support a null value it should be translated to something meaningful for
+ * that library, typically the empty string.
+ * <p>
+ * qname must consist of an optional namespace prefix and colon, followed
+ * by a mandatory variable localname. If the prefix is not specified, then
+ * the Namespace is assumed to be the {@link Namespace#NO_NAMESPACE}. If
+ * the prefix is specified, it must match with one of the declared
+ * Namespaces for this XPathExpression
+ *
+ * @param qname
+ * The variable qname to change.
+ * @param value
+ * The new value to set.
+ * @return The value of the variable prior to this change.
+ * @throws NullPointerException
+ * if qname is null
+ * @throws IllegalArgumentException
+ * if name is not already a variable.
+ */
+ public Object setVariable(String qname, Object value);
/**
- * Get the variable value associated to the given variable name (namespace
- * aware).
+ * Get the variable value associated to the given variable name.
+ *
+ * @param localname
+ * the variable localname to retrieve the value for.
* @param uri
- * the Namespace URI in which the variable name was declared.
- * @param name
- * the variable name to retrieve the value for.
+ * the Namespace in which the variable name was declared.
* @return the value associated to a Variable name.
* @throws NullPointerException
* if name or uri is null
* @throws IllegalArgumentException
* if that variable name is not defined.
*/
- public Object getVariable(String uri, String name);
+ public Object getVariable(String localname, Namespace uri);
+ /**
+ * Get the variable value associated to the given variable qname.
+ * <p>
+ * qname must consist of an optional namespace prefix and colon, followed
+ * by a mandatory variable localname. If the prefix is not specified, then
+ * the Namespace is assumed to be the {@link Namespace#NO_NAMESPACE}. If
+ * the prefix is specified, it must match with one of the declared
+ * Namespaces for this XPathExpression
+ *
+ * @param qname
+ * the variable qname to retrieve the value for.
+ * @return the value associated to a Variable name.
+ * @throws NullPointerException
+ * if qname is null
+ * @throws IllegalArgumentException
+ * if that variable name is not defined.
+ */
+ public Object getVariable(String qname);
+
/**
* Get the {@code Filter<T>} used to coerce the raw XPath results in to
* the correct Generic type.
@@ -158,7 +158,7 @@ public JaxenCompiled(String expression, Filter<T> filter,
@Override
public String translateNamespacePrefixToUri(String prefix) {
- return getNamespace(prefix);
+ return getNamespace(prefix).getURI();
}
@Override
@@ -170,13 +170,15 @@ public Object getVariableValue(String namespaceURI, String prefix,
if (prefix == null) {
prefix = "";
}
- if ("".equals(namespaceURI)) {
- namespaceURI = getNamespace(prefix);
- }
try {
- return getVariable(namespaceURI, localName);
+ if ("".equals(namespaceURI)) {
+ namespaceURI = getNamespace(prefix).getURI();
+ }
+ return getVariable(localName, Namespace.getNamespace(namespaceURI));
} catch (IllegalArgumentException e) {
- throw new UnresolvableException("Unable to resolve variable " + localName + " in namespace '" + namespaceURI + "' to a vaulue.");
+ throw new UnresolvableException("Unable to resolve variable " +
+ localName + " in namespace '" + namespaceURI +
+ "' to a vaulue.");
}
}
@@ -54,7 +54,9 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
package org.jdom2.xpath.util;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -76,6 +78,15 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* @author Rolf Lear
*/
public abstract class AbstractXPathCompiled<T> implements XPathExpression<T> {
+
+ private static final class NamespaceComparator implements Comparator<Namespace> {
+ @Override
+ public int compare(Namespace ns1, Namespace ns2) {
+ return ns1.getPrefix().compareTo(ns2.getPrefix());
+ }
+ }
+
+ private static final NamespaceComparator NSSORT = new NamespaceComparator();
private final Map<String, Namespace> xnamespaces = new HashMap<String, Namespace>();
// Not final to support cloning.
@@ -219,27 +230,36 @@ public final String getExpression() {
}
@Override
- public final String getNamespace(final String prefix) {
+ public final Namespace getNamespace(final String prefix) {
final Namespace ns = xnamespaces.get(prefix);
if (ns == null) {
throw new IllegalArgumentException("Namespace with prefix '"
+ prefix + "' has not been declared.");
}
- return ns.getURI();
+ return ns;
+ }
+
+ @Override
+ public Namespace[] getNamespaces() {
+ final Namespace[] nsa = xnamespaces.values().toArray(
+ new Namespace[xnamespaces.size()]);
+ Arrays.sort(nsa, NSSORT);
+ return nsa;
}
@Override
- public final Object getVariable(final String uri, final String name) {
- final Map<String, Object> vmap = xvariables.get(uri == null ? "" : uri);
+ public final Object getVariable(final String name, Namespace uri) {
+ final Map<String, Object> vmap =
+ xvariables.get(uri == null ? "" : uri.getURI());
if (vmap == null) {
throw new IllegalArgumentException("Variable with name '" + name
- + "' in namespace '" + uri + "' has not been declared.");
+ + "' in namespace '" + uri.getURI() + "' has not been declared.");
}
final Object ret = vmap.get(name);
if (ret == null) {
if (!vmap.containsKey(name)) {
throw new IllegalArgumentException("Variable with name '"
- + name + "' in namespace '" + uri
+ + name + "' in namespace '" + uri.getURI()
+ "' has not been declared.");
}
// leave translating null variable values to the implementation.
@@ -249,12 +269,40 @@ public final Object getVariable(final String uri, final String name) {
}
@Override
- public Object setVariable(String uri, String name, Object value) {
- final Object ret = getVariable(uri, name);
+ public Object getVariable(String qname) {
+ if (qname == null) {
+ throw new NullPointerException(
+ "Cannot get variable value for null qname");
+ }
+ final int pos = qname.indexOf(':');
+ if (pos >= 0) {
+ return getVariable(qname.substring(pos + 1),
+ getNamespace(qname.substring(0, pos)));
+ }
+ return getVariable(qname, Namespace.NO_NAMESPACE);
+ }
+
+ @Override
+ public Object setVariable(String name, Namespace uri, Object value) {
+ final Object ret = getVariable(name, uri);
// if that succeeded then we have it easy....
- xvariables.get(uri).put(name, value);
+ xvariables.get(uri.getURI()).put(name, value);
return ret;
}
+
+ @Override
+ public Object setVariable(String qname, Object value) {
+ if (qname == null) {
+ throw new NullPointerException(
+ "Cannot get variable value for null qname");
+ }
+ final int pos = qname.indexOf(':');
+ if (pos >= 0) {
+ return setVariable(qname.substring(pos + 1),
+ getNamespace(qname.substring(0, pos)), value);
+ }
+ return setVariable(qname, Namespace.NO_NAMESPACE, value);
+ }
@Override
public final Filter<T> getFilter() {
Oops, something went wrong.

0 comments on commit 5d153c5

Please sign in to comment.