Do not use jaxen XPath 37/71537/2
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 28 Apr 2018 10:37:52 +0000 (12:37 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Sat, 28 Apr 2018 10:51:41 +0000 (12:51 +0200)
We are really interested only in Expr and its evaluation, hence we
can skip the BaseXPath class, which is hiding things from us.

This also lowers the memory overhead of compiled expression very
slightly and improves performance, as the result is not needlessly
obfuscated via a singleton list.

Furthermore we no longer go through XPathReaderFactory, which incurs
reflection overhead, but rather instantiate
org.jaxen.saxpath.base.XPathReader directly.

Change-Id: I7fd315c17f4013f927e724226408ba12f8445606
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
yang/yang-data-jaxen/src/main/java/org/opendaylight/yangtools/yang/data/jaxen/JaxenXPath.java

index df2c4f49b5ad0056cb915fe6d2194833c3e7166a..0eb03a1e05c7c1360ca50f9bcd5decf5119bdacf 100644 (file)
@@ -18,11 +18,12 @@ import java.util.List;
 import java.util.Optional;
 import javax.annotation.Nonnull;
 import javax.xml.xpath.XPathExpressionException;
-import org.jaxen.BaseXPath;
-import org.jaxen.ContextSupport;
 import org.jaxen.JaxenException;
-import org.jaxen.XPath;
+import org.jaxen.JaxenHandler;
+import org.jaxen.XPathSyntaxException;
 import org.jaxen.expr.Expr;
+import org.jaxen.saxpath.SAXPathException;
+import org.jaxen.saxpath.XPathReader;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.xpath.XPathBooleanResult;
@@ -41,34 +42,38 @@ final class JaxenXPath implements XPathExpression {
 
     private final Converter<String, QNameModule> converter;
     private final SchemaPath schemaPath;
-    private final XPath xpath;
+    private final Expr expr;
 
     private JaxenXPath(final Converter<String, QNameModule> converter, final SchemaPath schemaPath,
-            final XPath xpath) {
+            final Expr expr) {
         this.converter = requireNonNull(converter);
         this.schemaPath = requireNonNull(schemaPath);
-        this.xpath = requireNonNull(xpath);
+        this.expr = requireNonNull(expr);
     }
 
     static JaxenXPath create(final Converter<String, QNameModule> converter, final SchemaPath schemaPath,
             final String xpath) throws JaxenException {
-        final BaseXPath compiled = new BaseXPath(xpath) {
-            private static final long serialVersionUID = 1L;
 
-            @Override
-            protected ContextSupport getContextSupport() {
-                throw new UnsupportedOperationException(xpath);
-            }
-        };
+        final Expr parsed;
+        try {
+            final XPathReader reader = new org.jaxen.saxpath.base.XPathReader();
+            final JaxenHandler handler = new JaxenHandler();
+            reader.setXPathHandler(handler);
+            reader.parse(xpath);
+            parsed = handler.getXPathExpr().getRootExpr();
+        } catch (org.jaxen.saxpath.XPathSyntaxException e) {
+            throw new XPathSyntaxException(e);
+        } catch (SAXPathException e) {
+            throw new JaxenException(e);
+        }
 
-        final Expr expr = compiled.getRootExpr();
-        LOG.debug("Compiled {} to expression {}", xpath, expr);
+        LOG.debug("Compiled {} to expression {}", xpath, parsed);
 
         new ExprWalker(new ExprListener() {
             // FIXME: perform expression introspection to understand things like apex, etc.
-        }).walk(expr);
+        }).walk(parsed);
 
-        return new JaxenXPath(converter, schemaPath, compiled);
+        return new JaxenXPath(converter, schemaPath, parsed);
     }
 
     @Override
@@ -79,13 +84,7 @@ final class JaxenXPath implements XPathExpression {
         final NormalizedNodeContextSupport contextSupport = NormalizedNodeContextSupport.create(
             (JaxenDocument)document, converter);
 
-        final Object result;
-        try {
-            result = xpath.evaluate(contextSupport.createContext(path));
-        } catch (JaxenException e) {
-            throw new XPathExpressionException(e);
-        }
-
+        final Object result = evaluate(contextSupport.createContext(path));
         if (result instanceof String) {
             return Optional.of((XPathStringResult) () -> (String) result);
         } else if (result instanceof Number) {
@@ -106,6 +105,27 @@ final class JaxenXPath implements XPathExpression {
         });
     }
 
+    private Object evaluate(final NormalizedNodeContext context) throws XPathExpressionException {
+        final Object result;
+        try {
+            result = expr.evaluate(context);
+        } catch (JaxenException e) {
+            throw new XPathExpressionException(e);
+        }
+
+        if (result instanceof List) {
+            final List<?> list = (List<?>) result;
+            if (list.size() == 1) {
+                final Object first = list.get(0);
+                if (first instanceof String || first instanceof Number || first instanceof Boolean) {
+                    return first;
+                }
+            }
+        }
+
+        return result;
+    }
+
     @Nonnull
     @Override
     public SchemaPath getEvaluationPath() {