Define the concept of unresolved QName 52/81052/8
authorRobert Varga <robert.varga@pantheon.tech>
Wed, 20 Mar 2019 17:37:20 +0000 (18:37 +0100)
committerRobert Varga <nite@hq.sk>
Mon, 25 Mar 2019 20:07:04 +0000 (20:07 +0000)
YANG Xpaths have dynamic namespace resolution for the default
namespace: during parse time we can only bind qualified node
identifiers.

Unqualified node identifiers require an explicit default namespace
binding step, which happens between when the XPath is parsed and
when it is evaluated, when the context node is determined.

This is quite visible in the context of leafref path expressions:
- typedef expressions are bound to the leaf/leafset referencing them
- leaf expressions in groupings are bound only after they have been
  completely instantiated (i.e. 'uses' outside of grouping context)

Change-Id: I65341793dc520bcc36c45dec76a303080be115a1
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
18 files changed:
yang/yang-common/src/main/java/org/opendaylight/yangtools/yang/common/YangNamespaceContext.java
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/QNameReferent.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/QNameReferentBehavior.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/ResolvedQNameReferent.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/UnresolvedQNameReferent.java [new file with mode: 0644]
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangLocationPath.java
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangQNameExpr.java
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathAxis.java
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathExpression.java
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathParser.java
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathParserFactory.java
yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrXPathParser.java
yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrXPathParserFactory.java
yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrYangXPathExpression.java
yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/FunctionSupport.java
yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/InstanceIdentifierParser.java
yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/Utils.java
yang/yang-xpath-impl/src/test/java/org/opendaylight/yangtools/yang/xpath/impl/XPathParserTest.java

index 74815791edc587caf35e4f6ec2629dbe6f9c19ea..584e9ecc70e39ee5f1fec0bb937a8e7f3710087a 100644 (file)
@@ -30,6 +30,9 @@ import org.opendaylight.yangtools.concepts.Immutable;
  */
 @Beta
 public interface YangNamespaceContext extends Immutable, Serializable {
+
+
+
     /**
      * Return the default namespace in this context.
      *
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/QNameReferent.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/QNameReferent.java
new file mode 100644 (file)
index 0000000..d21504f
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.xpath.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * An object referencing a QName, either resolved or unresolved.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface QNameReferent extends Immutable {
+    /**
+     * Return local name part of the referenced QName.
+     *
+     * @return Local name string.
+     */
+    String getLocalName();
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/QNameReferentBehavior.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/QNameReferentBehavior.java
new file mode 100644 (file)
index 0000000..94fed8f
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.xpath.api;
+
+/**
+ * Common interface for {@link ResolvedQNameReferent} and {@link UnresolvedQNameReferent}, ensuring that only one of
+ * them is implemented by any class.
+ *
+ * @param <T> {@link QNameReferent} behavior subclass
+ */
+interface QNameReferentBehavior<T extends QNameReferentBehavior<T>> extends QNameReferent {
+
+}
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/ResolvedQNameReferent.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/ResolvedQNameReferent.java
new file mode 100644 (file)
index 0000000..bb98744
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.xpath.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * A {@link QNameReferent} referencing a resolved QName.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface ResolvedQNameReferent extends QNameReferentBehavior<ResolvedQNameReferent> {
+    /**
+     * Return the referenced QName.
+     *
+     * @return A QName
+     */
+    QName getQName();
+
+    @Override
+    default String getLocalName() {
+        return getQName().getLocalName();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/UnresolvedQNameReferent.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/UnresolvedQNameReferent.java
new file mode 100644 (file)
index 0000000..9566733
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.xpath.api;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.AbstractQName;
+
+/**
+ * A {@link QNameReferent} referencing an unresolved QName.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public interface UnresolvedQNameReferent<T extends ResolvedQNameReferent> extends
+        QNameReferentBehavior<UnresolvedQNameReferent<T>> {
+    /**
+     * Return the referenced {@link AbstractQName}.
+     *
+     * @return An AbstractQName
+     */
+    AbstractQName getQName();
+
+    @Override
+    default String getLocalName() {
+        return getQName().getLocalName();
+    }
+}
index 9563b18d7af9ecaa5de6407aa334915d3c321771..310b3ed376bb8c23c6e59aeee6c1e102dd81f702 100644 (file)
@@ -21,6 +21,7 @@ import java.util.Collection;
 import java.util.Objects;
 import java.util.Set;
 import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.AbstractQName;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 
@@ -146,16 +147,36 @@ public abstract class YangLocationPath implements YangExpr {
         }
     }
 
-    public static class QNameStep extends Step {
+    /**
+     * A step along an axis. This may be either a {@link ResolvedQNameStep} or a {@link UnresolvedQNameStep}.
+     *
+     * @author Robert Varga
+     */
+    public abstract static class QNameStep extends Step implements QNameReferent {
+        private static final long serialVersionUID = 1L;
+
+        QNameStep(final YangXPathAxis axis) {
+            super(axis);
+        }
+    }
+
+    public static class ResolvedQNameStep extends QNameStep implements ResolvedQNameReferent {
         private static final long serialVersionUID = 1L;
 
         private final QName qname;
 
-        QNameStep(final YangXPathAxis axis, final QName qname) {
+        ResolvedQNameStep(final YangXPathAxis axis, final QName qname) {
             super(axis);
             this.qname = requireNonNull(qname);
         }
 
+        static ResolvedQNameStep of(final YangXPathAxis axis, final QName qname,
+                final Collection<YangExpr> predicates) {
+            return predicates.isEmpty() ? new ResolvedQNameStep(axis, qname)
+                    : new ResolvedQNameStepWithPredicates(axis, qname, ImmutableSet.copyOf(predicates));
+        }
+
+        @Override
         public final QName getQName() {
             return qname;
         }
@@ -170,10 +191,10 @@ public abstract class YangLocationPath implements YangExpr {
             if (this == obj) {
                 return true;
             }
-            if (!(obj instanceof QNameStep)) {
+            if (!(obj instanceof ResolvedQNameStep)) {
                 return false;
             }
-            final QNameStep other = (QNameStep) obj;
+            final ResolvedQNameStep other = (ResolvedQNameStep) obj;
             return getAxis().equals(other.getAxis()) && qname.equals(other.qname)
                     && getPredicates().equals(other.getPredicates());
         }
@@ -184,12 +205,71 @@ public abstract class YangLocationPath implements YangExpr {
         }
     }
 
-    static final class QNameStepWithPredicates extends QNameStep {
+    private static final class ResolvedQNameStepWithPredicates extends ResolvedQNameStep {
+        private static final long serialVersionUID = 1L;
+
+        private final ImmutableSet<YangExpr> predicates;
+
+        ResolvedQNameStepWithPredicates(final YangXPathAxis axis, final QName qname,
+                final ImmutableSet<YangExpr> predicates) {
+            super(axis, qname);
+            this.predicates = requireNonNull(predicates);
+        }
+
+        @Override
+        public ImmutableSet<YangExpr> getPredicates() {
+            return predicates;
+        }
+    }
+
+    // FIXME: 4.0.0: integrate this into QName step once QName is a subclass AbstractQName
+    public static class UnresolvedQNameStep extends QNameStep implements UnresolvedQNameReferent<ResolvedQNameStep> {
+        private static final long serialVersionUID = 1L;
+
+        private final AbstractQName qname;
+
+        UnresolvedQNameStep(final YangXPathAxis axis, final AbstractQName qname) {
+            super(axis);
+            this.qname = requireNonNull(qname);
+        }
+
+        static UnresolvedQNameStep of(final YangXPathAxis axis, final AbstractQName qname,
+                final Collection<YangExpr> predicates) {
+            return predicates.isEmpty() ? new UnresolvedQNameStep(axis, qname)
+                    : new UnresolvedQNameStepWithPredicates(axis, qname, ImmutableSet.copyOf(predicates));
+        }
+
+        @Override
+        public final AbstractQName getQName() {
+            return qname;
+        }
+
+        @Override
+        public final int hashCode() {
+            return Objects.hash(getAxis(), qname, getPredicates());
+        }
+
+        @Override
+        public final boolean equals(final @Nullable Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof UnresolvedQNameStep)) {
+                return false;
+            }
+            final UnresolvedQNameStep other = (UnresolvedQNameStep) obj;
+            return getAxis().equals(other.getAxis()) && qname.equals(other.qname)
+                    && getPredicates().equals(other.getPredicates());
+        }
+    }
+
+    private static final class UnresolvedQNameStepWithPredicates extends UnresolvedQNameStep {
         private static final long serialVersionUID = 1L;
 
         private final ImmutableSet<YangExpr> predicates;
 
-        QNameStepWithPredicates(final YangXPathAxis axis, final QName qname, final ImmutableSet<YangExpr> predicates) {
+        UnresolvedQNameStepWithPredicates(final YangXPathAxis axis, final AbstractQName qname,
+                final ImmutableSet<YangExpr> predicates) {
             super(axis, qname);
             this.predicates = requireNonNull(predicates);
         }
index c1306a12999841eda3937b39835cf3ba3afad0ec..1c90f3c58720bc4f63245a1815ff330aad60c630 100644 (file)
@@ -11,6 +11,7 @@ import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.Beta;
 import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.AbstractQName;
 import org.opendaylight.yangtools.yang.common.QName;
 
 /**
@@ -25,35 +26,78 @@ import org.opendaylight.yangtools.yang.common.QName;
  * @author Robert Varga
  */
 @Beta
-public final class YangQNameExpr implements YangExpr {
-    private static final long serialVersionUID = 1L;
+public abstract class YangQNameExpr implements YangExpr, QNameReferent {
+    public static final class Resolved extends YangQNameExpr implements ResolvedQNameReferent {
+        private static final long serialVersionUID = 1L;
 
-    private final QName qname;
+        private final QName qname;
 
-    private YangQNameExpr(final QName qname) {
-        this.qname = requireNonNull(qname);
-    }
+        Resolved(final QName qname) {
+            this.qname = requireNonNull(qname);
+        }
+
+        @Override
+        public QName getQName() {
+            return qname;
+        }
+
+        @Override
+        public int hashCode() {
+            return qname.hashCode();
+        }
 
-    public static YangQNameExpr of(final QName qname) {
-        return new YangQNameExpr(qname);
+        @Override
+        public boolean equals(final @Nullable Object obj) {
+            return this == obj || obj instanceof Resolved && qname.equals(((Resolved) obj).qname);
+        }
+
+        @Override
+        public String toString() {
+            return qname.toString();
+        }
     }
 
-    public QName getQName() {
-        return qname;
+    public static final class Unresolved extends YangQNameExpr implements UnresolvedQNameReferent<Resolved> {
+        private static final long serialVersionUID = 1L;
+
+        private final AbstractQName qname;
+
+        Unresolved(final AbstractQName qname) {
+            this.qname = requireNonNull(qname);
+        }
+
+        @Override
+        public AbstractQName getQName() {
+            return qname;
+        }
+
+        @Override
+        public int hashCode() {
+            return qname.hashCode();
+        }
+
+        @Override
+        public boolean equals(final @Nullable Object obj) {
+            return this == obj || obj instanceof Unresolved && qname.equals(((Unresolved) obj).qname);
+        }
+
+        @Override
+        public String toString() {
+            return qname.toString();
+        }
     }
 
-    @Override
-    public int hashCode() {
-        return qname.hashCode();
+    private static final long serialVersionUID = 1L;
+
+    YangQNameExpr() {
+        // Prevent instantiation
     }
 
-    @Override
-    public boolean equals(final @Nullable Object obj) {
-        return this == obj || obj instanceof YangQNameExpr && qname.equals(((YangQNameExpr) obj).qname);
+    public static Unresolved of(final AbstractQName qname) {
+        return new Unresolved(qname);
     }
 
-    @Override
-    public String toString() {
-        return qname.toString();
+    public static Resolved of(final QName qname) {
+        return new Resolved(qname);
     }
 }
index 5d2473dee4f8dd8076d2621faa250b4189cf61d1..3fc7ce508ee1d458b58a505fed75b45885a6d8a2 100644 (file)
@@ -12,6 +12,7 @@ import static java.util.Objects.requireNonNull;
 import com.google.common.annotations.Beta;
 import com.google.common.collect.ImmutableSet;
 import java.util.Collection;
+import org.opendaylight.yangtools.yang.common.AbstractQName;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.AxisStep;
 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.AxisStepWithPredicates;
@@ -19,8 +20,8 @@ import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.NodeTypeStep;
 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.NodeTypeStepWithPredicates;
 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.ProcessingInstructionStep;
 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.ProcessingInstructionStepWithPredicates;
-import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.QNameStep;
-import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.QNameStepWithPredicates;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.ResolvedQNameStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.UnresolvedQNameStep;
 
 /**
  * XPath evaluation axis, as defined in <a href="https://www.w3.org/TR/1999/REC-xpath-19991116/#axes">XPath 1.0</a>.
@@ -105,17 +106,23 @@ public enum YangXPathAxis {
     }
 
     public final AxisStep asStep(final Collection<YangExpr> predicates) {
-        final ImmutableSet<YangExpr> set = ImmutableSet.copyOf(predicates);
-        return set.isEmpty() ? step : new AxisStepWithPredicates(this, set);
+        return predicates.isEmpty() ? step : new AxisStepWithPredicates(this, ImmutableSet.copyOf(predicates));
     }
 
-    public final QNameStep asStep(final QName qname) {
-        return new QNameStep(this, qname);
+    public final ResolvedQNameStep asStep(final QName qname) {
+        return new ResolvedQNameStep(this, qname);
     }
 
-    public final QNameStep asStep(final QName qname, final Collection<YangExpr> predicates) {
-        final ImmutableSet<YangExpr> set = ImmutableSet.copyOf(predicates);
-        return set.isEmpty() ? asStep(qname) : new QNameStepWithPredicates(this, qname, set);
+    public final ResolvedQNameStep asStep(final QName qname, final Collection<YangExpr> predicates) {
+        return ResolvedQNameStep.of(this, qname, predicates);
+    }
+
+    public final UnresolvedQNameStep asStep(final AbstractQName qname) {
+        return new UnresolvedQNameStep(this, qname);
+    }
+
+    public final UnresolvedQNameStep asStep(final AbstractQName qname, final Collection<YangExpr> predicates) {
+        return UnresolvedQNameStep.of(this, qname, predicates);
     }
 
     public final NodeTypeStep asStep(final YangXPathNodeType type) {
@@ -123,8 +130,8 @@ public enum YangXPathAxis {
     }
 
     public final NodeTypeStep asStep(final YangXPathNodeType type, final Collection<YangExpr> predicates) {
-        final ImmutableSet<YangExpr> set = ImmutableSet.copyOf(predicates);
-        return set.isEmpty() ? asStep(type) : new NodeTypeStepWithPredicates(this, type, set);
+        return predicates.isEmpty() ? asStep(type) : new NodeTypeStepWithPredicates(this, type,
+            ImmutableSet.copyOf(predicates));
     }
 
     public final ProcessingInstructionStep asStep(final String name) {
@@ -132,8 +139,8 @@ public enum YangXPathAxis {
     }
 
     public final ProcessingInstructionStep asStep(final String name, final Collection<YangExpr> predicates) {
-        final ImmutableSet<YangExpr> set = ImmutableSet.copyOf(predicates);
-        return set.isEmpty() ? asStep(name) : new ProcessingInstructionStepWithPredicates(this, name, set);
+        return predicates.isEmpty() ? asStep(name) : new ProcessingInstructionStepWithPredicates(this, name,
+            ImmutableSet.copyOf(predicates));
     }
 
     @Override
index da7ac0068fa3037ba012850e25f28ccfd2b487ca..3d7dc728086d275b2aef2609544aa48624ee5b75 100644 (file)
@@ -11,14 +11,52 @@ import com.google.common.annotations.Beta;
 import javax.xml.xpath.XPathExpressionException;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QualifiedQName;
+import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
 
 /**
- * An XPath expression.
+ * An XPath expression. This interface defines a parsed representation of an XPath defined in a YANG context, as
+ * specified in <a href="https://tools.ietf.org/html/rfc7950#section-6.4">RFC7950, Section 6.4</a>.
+ *
+ * <p>
+ * The specification along with rules for {@code path} statement evaluation rules end up defining four incremental
+ * levels to which an XPath expression can be bound:
+ * <ul>
+ * <li>Unbound Expressions, which is a essentially a parse tree. No namespace binding has been performed, i.e. all
+ *     node identifiers are in {@link QualifiedQName} or {@link UnqualifiedQName} form. This level is typically not used
+ *     when dealing with YANG models directly, but can be useful for validating a String conforms to XPath syntax.
+ * </li>
+ * <li>Qualified-bound Expressions, where all {@link QualifiedQName}s are resolved and bound to {@link QName}s, but
+ *     {@link UnqualifiedQName}s are still present. This level corresponds to how far a YANG parser can interpret XPath
+ *     expressions defined in {@code typedef} statements and statements which are not fully instantiated, i.e. are
+ *     descendants of a {@code grouping} statement.
+ * </li>
+ * <li>Namespace-bound Expressions, where all node identifier references are resolved to {@link QName}s. This level
+ *     corresponds to how far a YANG parser can interpret XPath expressions at their place of instantiation, either in
+ *     the data tree or an {@code action}/@{code rpc}/{@code notification} or similar context.
+ * </li>
+ * <li>Context-bound Expressions, where the expression is bound to a {code context node}, i.e. {@code current()}
+ *     function result is know. This API does not handle this state, as it is inherently bound to a particular data
+ *     object model.
+ * </li>
+ * </ul>
  *
  * @author Robert Varga
  */
 @Beta
 public interface YangXPathExpression extends Immutable {
+    /**
+     * A Qualified-bound expression. All {@link QualifiedQName}s are eliminated and replaced with {@link QName}s.
+     */
+    interface QualifiedBound extends YangXPathExpression {
+
+    }
+
+    interface UnqualifiedBound extends QualifiedBound {
+        @Override
+        YangQNameExpr.Resolved interpretAsQName(YangLiteralExpr expr) throws XPathExpressionException;
+    }
+
     /**
      * Return the root {@link YangExpr}.
      *
index 3395e93cf941f840ecd94fb46ef770c7dbc3c27c..7c06db7204a1e5db56ca273abec718f3cf172061 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.yangtools.yang.xpath.api;
 
 import com.google.common.annotations.Beta;
 import javax.xml.xpath.XPathExpressionException;
+import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
 
 /**
  * Interface for converting a String into a {@link YangXPathExpression}. Implementations of this interface are expected
@@ -18,6 +19,23 @@ import javax.xml.xpath.XPathExpressionException;
  */
 @Beta
 public interface YangXPathParser {
+    /**
+     * A {@link YangXPathParser} bound to a {@link YangNamespaceContext}, producing Qualified-bound Expressions.
+     */
+    interface QualifiedBound extends YangXPathParser {
+        @Override
+        YangXPathExpression.QualifiedBound parseExpression(String xpath) throws XPathExpressionException;
+    }
+
+    /**
+     * A {@link YangXPathParser} bound to a {@link YangNamespaceContext} and a default namespace, producing
+     * Unqualified-bound Expressions.
+     */
+    interface UnqualifiedBound extends QualifiedBound {
+        @Override
+        YangXPathExpression.UnqualifiedBound parseExpression(String xpath) throws XPathExpressionException;
+    }
+
     /**
      * Parse a string containing an XPath expression.
      *
index 2857bafee4788056c2c0b152e63b08d3356f62b0..9b5a6413a9ee5d0691c451a16ede708e88cfb857 100644 (file)
@@ -8,6 +8,8 @@
 package org.opendaylight.yangtools.yang.xpath.api;
 
 import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
 
 /**
@@ -18,23 +20,72 @@ import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
 @Beta
 public interface YangXPathParserFactory {
     /**
-     * Return a {@link YangXPathParser} compliant with {@link YangXPathMathMode#IEEE754}.
+     * Return a {@link YangXPathParser} compliant with {@link YangXPathMathMode#IEEE754}. Returned parser will not
+     * perform any namespace binding.
      *
-     * @param namespaceContext Prefix-to-namespace resolver
+     * @return An XPathParser
+     */
+    default YangXPathParser newParser() {
+        return newParser(YangXPathMathMode.IEEE754);
+    }
+
+    /**
+     * Return a {@link YangXPathParser} compliant with {@link YangXPathMathMode}. Returned parser will not perform any
+     * namespace binding.
+     *
+     * @param mathMode Requested XPath number compliance
+     * @return An XPathParser
+     * @throws NullPointerException if {@code mathMode} is null
+     */
+    YangXPathParser newParser(YangXPathMathMode mathMode);
+
+    /**
+     * Return a {@link YangXPathParser} compliant with {@link YangXPathMathMode#IEEE754}. Returned parser will bind
+     * qualified node identifiers to {@link QName}s.
+     *
+     * @param namespaceContext Prefix-to-namespace resolver, used to bind qualified node identifiers
      * @return An XPathParser
      * @throws NullPointerException if {@code namespaceContext} is null
      */
-    default YangXPathParser newParser(final YangNamespaceContext namespaceContext) {
-        return newParser(namespaceContext, YangXPathMathMode.IEEE754);
+    default YangXPathParser.QualifiedBound newParser(final YangNamespaceContext namespaceContext) {
+        return newParser(YangXPathMathMode.IEEE754, namespaceContext);
+    }
+
+    /**
+     * Return a {@link YangXPathParser} compliant with {@link YangXPathMathMode}. Returned parser will bind qualified
+     * node identifiers to {@link QName}s.
+     *
+     * @param mathMode Requested XPath number compliance
+     * @param namespaceContext Prefix-to-namespace resolver, used to bind qualified node identifiers
+     * @return An XPathParser
+     * @throws NullPointerException if any argument is null
+     */
+    YangXPathParser.QualifiedBound newParser(YangXPathMathMode mathMode, YangNamespaceContext namespaceContext);
+
+    /**
+     * Return a {@link YangXPathParser} compliant with {@link YangXPathMathMode#IEEE754}. Returned parser will bind
+     * qualified and unqualified node identifiers to {@link QName}s.
+     *
+     * @param namespaceContext Prefix-to-namespace resolver, used to bind qualified node identifiers
+     * @param defaultNamespace Default namespace, used to bind unqualified node identifiers
+     * @return An XPathParser
+     * @throws NullPointerException if any argument is null
+     */
+    default YangXPathParser.UnqualifiedBound newParser(final YangNamespaceContext namespaceContext,
+            final QNameModule defaultNamespace) {
+        return newParser(YangXPathMathMode.IEEE754, namespaceContext, defaultNamespace);
     }
 
     /**
-     * Return a {@link YangXPathParser} compliant with {@link YangXPathMathMode}.
+     * Return a {@link YangXPathParser} compliant with {@link YangXPathMathMode}. Returned parser will bind qualified
+     * and unqualified node identifiers to {@link QName}s.
      *
-     * @param namespaceContext Prefix-to-namespace resolver
      * @param mathMode Requested XPath number compliance
+     * @param namespaceContext Prefix-to-namespace resolver, used to bind qualified node identifiers
+     * @param defaultNamespace Default namespace, used to bind unqualified node identifiers
      * @return An XPathParser
      * @throws NullPointerException if any argument is null
      */
-    YangXPathParser newParser(YangNamespaceContext namespaceContext, YangXPathMathMode mathMode);
+    YangXPathParser.UnqualifiedBound newParser(YangXPathMathMode mathMode, YangNamespaceContext namespaceContext,
+            QNameModule defaultNamespace);
 }
index 82134f64dc64dd3aed5a7144277ecfeccb729f9b..dc348d9b90862cd7aaf5e28c35702e5908589ec7 100644 (file)
@@ -40,6 +40,9 @@ import org.antlr.v4.runtime.ParserRuleContext;
 import org.antlr.v4.runtime.tree.ParseTree;
 import org.antlr.v4.runtime.tree.TerminalNode;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.QualifiedQName;
+import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
 import org.opendaylight.yangtools.yang.common.YangConstants;
 import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
 import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator;
@@ -51,6 +54,8 @@ import org.opendaylight.yangtools.yang.xpath.api.YangFunctionCallExpr;
 import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr;
 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.AxisStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.QNameStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.ResolvedQNameStep;
 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step;
 import org.opendaylight.yangtools.yang.xpath.api.YangNaryExpr;
 import org.opendaylight.yangtools.yang.xpath.api.YangNaryOperator;
@@ -90,16 +95,99 @@ import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.StepContext;
 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.UnaryExprNoRootContext;
 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.UnionExprNoRootContext;
 import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.VariableReferenceContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * ANTLR-based XPath parser. Uses {@code xpath.g4} ANTLR grammar.
  *
  * @author Robert Varga
  */
-final class AntlrXPathParser implements YangXPathParser {
-    private static final Logger LOG = LoggerFactory.getLogger(AntlrXPathParser.class);
+abstract class AntlrXPathParser implements YangXPathParser {
+    static class Base extends AntlrXPathParser {
+        Base(final YangXPathMathMode mathMode) {
+            super(mathMode);
+        }
+
+        @Override
+        public YangXPathExpression parseExpression(final String xpath) throws XPathExpressionException {
+            return new AntlrYangXPathExpression.Base(mathMode, parseExpr(xpath), xpath);
+        }
+
+        @Override
+        QNameStep createStep(final YangXPathAxis axis, final String localName,
+                final List<YangExpr> predicates) {
+            return axis.asStep(UnqualifiedQName.of(localName), predicates);
+        }
+
+        @Override
+        QNameStep createStep(final YangXPathAxis axis, final String prefix, final String localName,
+                final List<YangExpr> predicates) {
+            return axis.asStep(QualifiedQName.of(prefix, localName), predicates);
+        }
+
+        @Override
+        QName createQName(final String localName) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        QName createQName(final String prefix, final String localName) {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    static class Qualified extends Base implements QualifiedBound {
+        final YangNamespaceContext namespaceContext;
+
+        Qualified(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext) {
+            super(mathMode);
+            this.namespaceContext = requireNonNull(namespaceContext);
+        }
+
+        @Override
+        public YangXPathExpression.QualifiedBound parseExpression(final String xpath) throws XPathExpressionException {
+            return new AntlrYangXPathExpression.Qualified(mathMode, parseExpr(xpath), xpath, namespaceContext);
+        }
+
+        @Override
+        final QName createQName(final String prefix, final String localName) {
+            return namespaceContext.createQName(prefix, localName);
+        }
+
+        @Override
+        ResolvedQNameStep createStep(final YangXPathAxis axis, final String prefix, final String localName,
+                final List<YangExpr> predicates) {
+            return axis.asStep(createQName(prefix, localName), predicates);
+        }
+    }
+
+    static final class Unqualified extends Qualified implements UnqualifiedBound {
+        private final QNameModule defaultNamespace;
+
+        Unqualified(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext,
+                final QNameModule defaultNamespace) {
+            super(mathMode, namespaceContext);
+            this.defaultNamespace = requireNonNull(defaultNamespace);
+        }
+
+        @Override
+        public YangXPathExpression.UnqualifiedBound parseExpression(final String xpath)
+                throws XPathExpressionException {
+            return new AntlrYangXPathExpression.Unqualified(mathMode, parseExpr(xpath), xpath, namespaceContext,
+                defaultNamespace);
+        }
+
+        @Override
+        QName createQName(final String localName) {
+            return QName.create(defaultNamespace, localName);
+        }
+
+        @Override
+        ResolvedQNameStep createStep(final YangXPathAxis axis, final String localName,
+                final List<YangExpr> predicates) {
+            return axis.asStep(QName.create(defaultNamespace, localName), predicates);
+        }
+    }
+
     private static final Map<String, YangBinaryOperator> BINARY_OPERATORS = Maps.uniqueIndex(
         Arrays.asList(YangBinaryOperator.values()), YangBinaryOperator::toString);
     private static final Map<String, YangXPathNodeType> NODE_TYPES = Maps.uniqueIndex(Arrays.asList(
@@ -112,20 +200,37 @@ final class AntlrXPathParser implements YangXPathParser {
     // Cached for checks in hot path
     private static final AxisStep SELF_STEP = YangXPathAxis.SELF.asStep();
 
-    private final YangXPathMathMode mathMode;
+    final YangXPathMathMode mathMode;
     private final YangXPathMathSupport mathSupport;
-    private final YangNamespaceContext namespaceContext;
     private final FunctionSupport functionSupport;
 
-    AntlrXPathParser(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext) {
+    AntlrXPathParser(final YangXPathMathMode mathMode) {
         this.mathMode = requireNonNull(mathMode);
         this.mathSupport = mathMode.getSupport();
-        this.namespaceContext = requireNonNull(namespaceContext);
-        this.functionSupport = new FunctionSupport(namespaceContext, mathSupport);
+        this.functionSupport = new FunctionSupport(mathSupport);
     }
 
-    @Override
-    public YangXPathExpression parseExpression(final String xpath) throws XPathExpressionException {
+    abstract QName createQName(String localName);
+
+    abstract QName createQName(String prefix, String localName);
+
+    abstract QNameStep createStep(YangXPathAxis axis, String localName, List<YangExpr> predicates);
+
+    abstract QNameStep createStep(YangXPathAxis axis, String prefix, String localName, List<YangExpr> predicates);
+
+    private QNameStep createStep(final YangXPathAxis axis, final QNameContext expr, final List<YangExpr> predicates) {
+        switch (expr.getChildCount()) {
+            case 1:
+                return createStep(axis, getChild(expr, NCNameContext.class, 0).getText(), predicates);
+            case 3:
+                return createStep(axis, getChild(expr, NCNameContext.class, 0).getText(),
+                    getChild(expr, NCNameContext.class, 2).getText(), predicates);
+            default:
+                throw illegalShape(expr);
+        }
+    }
+
+    final YangExpr parseExpr(final String xpath) throws XPathExpressionException {
         // Create a parser and disconnect it from console error output
         final xpathLexer lexer = new xpathLexer(CharStreams.fromString(xpath));
         final xpathParser parser = new xpathParser(new CommonTokenStream(lexer));
@@ -138,7 +243,7 @@ final class AntlrXPathParser implements YangXPathParser {
 
         final YangExpr expr = parseExpr(parser.main().expr());
         listener.reportError();
-        return new AntlrYangXPathExpression(namespaceContext, mathMode, expr, xpath);
+        return expr;
     }
 
     /**
@@ -204,7 +309,7 @@ final class AntlrXPathParser implements YangXPathParser {
                 parsed = QName.create(YangConstants.RFC6020_YIN_MODULE, name.getChild(0).getText());
                 break;
             case 3:
-                parsed = namespaceContext.createQName(name.getChild(0).getText(), name.getChild(2).getText());
+                parsed = createQName(name.getChild(0).getText(), name.getChild(2).getText());
                 break;
             default:
                 throw illegalShape(name);
@@ -436,18 +541,6 @@ final class AntlrXPathParser implements YangXPathParser {
         return ret;
     }
 
-    private QName parseQName(final QNameContext expr) {
-        switch (expr.getChildCount()) {
-            case 1:
-                return namespaceContext.createQName(getChild(expr, NCNameContext.class, 0).getText());
-            case 3:
-                return namespaceContext.createQName(getChild(expr, NCNameContext.class, 0).getText(),
-                    getChild(expr, NCNameContext.class, 2).getText());
-            default:
-                throw illegalShape(expr);
-        }
-    }
-
     private Step parseStep(final StepContext expr) {
         if (expr.getChildCount() == 1) {
             final AbbreviatedStepContext abbrev = getChild(expr, AbbreviatedStepContext.class, 0);
@@ -478,7 +571,7 @@ final class AntlrXPathParser implements YangXPathParser {
                     verify(((TerminalNode) first).getSymbol().getType() == xpathParser.MUL);
                     return axis.asStep(predicates);
                 }
-                return axis.asStep(parseQName(verifyTree(QNameContext.class, first)), predicates);
+                return createStep(axis, verifyTree(QNameContext.class, first), predicates);
             case 3:
                 return axis.asStep(parseNodeType(nodeTest.getChild(0)), predicates);
             case 4:
@@ -504,6 +597,18 @@ final class AntlrXPathParser implements YangXPathParser {
         }
     }
 
+    private QName parseQName(final QNameContext expr) {
+        switch (expr.getChildCount()) {
+            case 1:
+                return createQName(getChild(expr, NCNameContext.class, 0).getText());
+            case 3:
+                return createQName(getChild(expr, NCNameContext.class, 0).getText(),
+                    getChild(expr, NCNameContext.class, 2).getText());
+            default:
+                throw illegalShape(expr);
+        }
+    }
+
     private static <T extends ParserRuleContext> T nextContext(final Iterator<ParseTree> it, final Class<T> type) {
         return verifyTree(type, it.next());
     }
index 48e4f506a95c3482c712ef35de2a65f86fc35c15..d061bfcc208fd5211f680d3dc1de33ca9050ddec 100644 (file)
@@ -8,16 +8,29 @@
 package org.opendaylight.yangtools.yang.xpath.impl;
 
 import org.kohsuke.MetaInfServices;
+import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
 import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathMode;
 import org.opendaylight.yangtools.yang.xpath.api.YangXPathParser;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathParser.QualifiedBound;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathParser.UnqualifiedBound;
 import org.opendaylight.yangtools.yang.xpath.api.YangXPathParserFactory;
 
 @MetaInfServices
 public final class AntlrXPathParserFactory implements YangXPathParserFactory {
     @Override
-    public YangXPathParser newParser(final YangNamespaceContext namespaceContext,
-            final YangXPathMathMode mathMode) {
-        return new AntlrXPathParser(mathMode, namespaceContext);
+    public YangXPathParser newParser(final YangXPathMathMode mathMode) {
+        return new AntlrXPathParser.Base(mathMode);
+    }
+
+    @Override
+    public QualifiedBound newParser(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext) {
+        return new AntlrXPathParser.Qualified(mathMode, namespaceContext);
+    }
+
+    @Override
+    public UnqualifiedBound newParser(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext,
+            final QNameModule defaultNamespace) {
+        return new AntlrXPathParser.Unqualified(mathMode, namespaceContext, defaultNamespace);
     }
 }
index d46e13f56c52c5f5894df0fa7fe9bd79c8eacd9c..52560c5d4c4e1b5cac29d5101ed5b500f3c51bff 100644 (file)
@@ -10,50 +10,102 @@ package org.opendaylight.yangtools.yang.xpath.impl;
 import static java.util.Objects.requireNonNull;
 
 import javax.xml.xpath.XPathExpressionException;
+import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
 import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
 import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr;
 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
 import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr.Resolved;
 import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression;
 import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathMode;
 
-final class AntlrYangXPathExpression implements YangXPathExpression {
-    private final YangNamespaceContext namespaceContext;
+abstract class AntlrYangXPathExpression implements YangXPathExpression {
+    static final class Base extends AntlrYangXPathExpression {
+        Base(final YangXPathMathMode mathMode, final YangExpr rootExpr, final String origStr) {
+            super(mathMode, rootExpr, origStr);
+        }
+
+        @Override
+        public YangQNameExpr interpretAsQName(final YangLiteralExpr expr) throws XPathExpressionException {
+            return Utils.interpretAsQName(expr);
+        }
+
+        @Override
+        InstanceIdentifierParser createInstanceIdentifierParser() {
+            return new InstanceIdentifierParser.Base(getMathMode());
+        }
+    }
+
+    static class Qualified extends AntlrYangXPathExpression implements QualifiedBound {
+        final YangNamespaceContext namespaceContext;
+
+        Qualified(final YangXPathMathMode mathMode, final YangExpr rootExpr,
+            final String origStr, final YangNamespaceContext namespaceContext) {
+            super(mathMode, rootExpr, origStr);
+            this.namespaceContext = requireNonNull(namespaceContext);
+        }
+
+        YangNamespaceContext getNamespaceContext() {
+            return namespaceContext;
+        }
+
+        @Override
+        public YangQNameExpr interpretAsQName(final YangLiteralExpr expr) throws XPathExpressionException {
+            return Utils.interpretAsQName(namespaceContext, expr);
+        }
+
+        @Override
+        final InstanceIdentifierParser createInstanceIdentifierParser() {
+            return new InstanceIdentifierParser.Qualified(getMathMode(), namespaceContext);
+        }
+    }
+
+    static final class Unqualified extends Qualified implements UnqualifiedBound {
+        private final QNameModule defaultNamespace;
+
+        Unqualified(final YangXPathMathMode mathMode, final YangExpr rootExpr,
+            final String origStr, final YangNamespaceContext namespaceContext, final QNameModule defaultNamespace) {
+            super(mathMode, rootExpr, origStr, namespaceContext);
+            this.defaultNamespace = requireNonNull(defaultNamespace);
+        }
+
+        @Override
+        public Resolved interpretAsQName(final YangLiteralExpr expr) throws XPathExpressionException {
+            return Utils.interpretAsQName(getNamespaceContext(), defaultNamespace, expr);
+        }
+    }
+
     private final YangXPathMathMode mathMode;
     private final YangExpr rootExpr;
     private final String origStr;
 
-    AntlrYangXPathExpression(final YangNamespaceContext namespaceContext, final YangXPathMathMode mathMode,
-            final YangExpr rootExpr, final String origStr) {
-        this.namespaceContext = requireNonNull(namespaceContext);
+    AntlrYangXPathExpression(final YangXPathMathMode mathMode, final YangExpr rootExpr, final String origStr) {
         this.mathMode = requireNonNull(mathMode);
         this.rootExpr = requireNonNull(rootExpr);
         this.origStr = requireNonNull(origStr);
     }
 
     @Override
-    public YangXPathMathMode getMathMode() {
+    public final YangXPathMathMode getMathMode() {
         return mathMode;
     }
 
     @Override
-    public YangExpr getRootExpr() {
+    public final YangExpr getRootExpr() {
         return rootExpr;
     }
 
     @Override
-    public YangQNameExpr interpretAsQName(final YangLiteralExpr expr) throws XPathExpressionException {
-        return Utils.interpretAsQName(namespaceContext, expr);
+    public final YangLocationPath interpretAsInstanceIdentifier(final YangLiteralExpr expr)
+            throws XPathExpressionException {
+        return createInstanceIdentifierParser().interpretAsInstanceIdentifier(expr);
     }
 
     @Override
-    public YangLocationPath interpretAsInstanceIdentifier(final YangLiteralExpr expr) throws XPathExpressionException {
-        return new InstanceIdentifierParser(namespaceContext, mathMode).interpretAsInstanceIdentifier(expr);
-    }
-
-    @Override
-    public String toString() {
+    public final String toString() {
         return origStr;
     }
+
+    abstract InstanceIdentifierParser createInstanceIdentifierParser();
 }
index 8e3d7b0aedafac269326b39953210d4fd988c2c4..74f2dce4b9d961851da11cd12dd8e2073a4652a4 100644 (file)
@@ -11,7 +11,6 @@ import static com.google.common.base.Preconditions.checkArgument;
 import static java.util.Objects.requireNonNull;
 
 import java.util.List;
-import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
 import org.opendaylight.yangtools.yang.xpath.api.YangBooleanConstantExpr;
 import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
 import org.opendaylight.yangtools.yang.xpath.api.YangFunction;
@@ -41,10 +40,8 @@ final class FunctionSupport {
         YangFunction.STRING_LENGTH.getIdentifier());
 
     private final YangXPathMathSupport mathSupport;
-    private final YangNamespaceContext namespaceContext;
 
-    FunctionSupport(final YangNamespaceContext namespaceContext, final YangXPathMathSupport mathSupport) {
-        this.namespaceContext = requireNonNull(namespaceContext);
+    FunctionSupport(final YangXPathMathSupport mathSupport) {
         this.mathSupport = requireNonNull(mathSupport);
     }
 
index ce2944ee8d44c87229f1fd360d784d3df5bda8b8..3c28a1faf4cef3078e3e7d634fdfa4fdd6a9e20f 100644 (file)
@@ -20,13 +20,14 @@ import javax.xml.xpath.XPathExpressionException;
 import org.antlr.v4.runtime.CharStreams;
 import org.antlr.v4.runtime.CommonTokenStream;
 import org.antlr.v4.runtime.tree.ParseTree;
-import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QualifiedQName;
 import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
 import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator;
 import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
 import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr;
 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Absolute;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.QNameStep;
 import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step;
 import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr;
 import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
@@ -44,16 +45,51 @@ import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.PosCo
 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.PredicateContext;
 import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.QuotedStringContext;
 
-final class InstanceIdentifierParser {
-    private final YangNamespaceContext namespaceContext;
+abstract class InstanceIdentifierParser {
+    static final class Base extends InstanceIdentifierParser {
+        Base(final YangXPathMathMode mathMode) {
+            super(mathMode);
+        }
+
+        @Override
+        YangQNameExpr createExpr(final String prefix, final String localName) {
+            return YangQNameExpr.of(QualifiedQName.of(prefix, localName));
+        }
+
+        @Override
+        QNameStep createChildStep(final String prefix, final String localName, final Collection<YangExpr> predicates) {
+            return YangXPathAxis.CHILD.asStep(QualifiedQName.of(prefix, localName), predicates);
+        }
+    }
+
+    static final class Qualified extends InstanceIdentifierParser {
+        final YangNamespaceContext namespaceContext;
+
+        Qualified(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext) {
+            super(mathMode);
+            this.namespaceContext = requireNonNull(namespaceContext);
+        }
+
+        @Override
+        QNameStep createChildStep(final String prefix, final String localName, final Collection<YangExpr> predicates) {
+            return YangXPathAxis.CHILD.asStep(namespaceContext.createQName(prefix, localName), predicates);
+        }
+
+
+        @Override
+        YangQNameExpr createExpr(final String prefix, final String localName) {
+            return YangQNameExpr.of(namespaceContext.createQName(prefix, localName));
+        }
+
+    }
+
     private final YangXPathMathSupport mathSupport;
 
-    InstanceIdentifierParser(final YangNamespaceContext namespaceContext, final YangXPathMathMode mathMode) {
-        this.namespaceContext = requireNonNull(namespaceContext);
+    InstanceIdentifierParser(final YangXPathMathMode mathMode) {
         this.mathSupport = mathMode.getSupport();
     }
 
-    Absolute interpretAsInstanceIdentifier(final YangLiteralExpr expr) throws XPathExpressionException {
+    final Absolute interpretAsInstanceIdentifier(final YangLiteralExpr expr) throws XPathExpressionException {
         final xpathLexer lexer = new xpathLexer(CharStreams.fromString(expr.getLiteral()));
         final instanceIdentifierParser parser = new instanceIdentifierParser(new CommonTokenStream(lexer));
         final CapturingErrorListener listener = new CapturingErrorListener();
@@ -74,13 +110,20 @@ final class InstanceIdentifierParser {
         return YangLocationPath.absolute(steps);
     }
 
-    private Step parsePathArgument(final PathArgumentContext expr) {
-        final QName qname = parseQName(getChild(expr, NodeIdentifierContext.class, 0));
+    abstract YangQNameExpr createExpr(String prefix, String localName);
+
+    abstract QNameStep createChildStep(String prefix, String localName, Collection<YangExpr> predicates);
+
+    private QNameStep parsePathArgument(final PathArgumentContext expr) {
+        final NodeIdentifierContext childExpr = getChild(expr, NodeIdentifierContext.class, 0);
+        final String prefix = verifyIdentifier(childExpr, 0);
+        final String localName = verifyIdentifier(childExpr, 2);
+
         switch (expr.getChildCount()) {
             case 1:
-                return YangXPathAxis.CHILD.asStep(qname, ImmutableSet.of());
+                return createChildStep(prefix, localName, ImmutableSet.of());
             case 2:
-                return YangXPathAxis.CHILD.asStep(qname, parsePredicate(getChild(expr, PredicateContext.class, 1)));
+                return createChildStep(prefix, localName, parsePredicate(getChild(expr, PredicateContext.class, 1)));
             default:
                 throw illegalShape(expr);
         }
@@ -104,7 +147,7 @@ final class InstanceIdentifierParser {
             final KeyPredicateExprContext pred = getChild(expr, KeyPredicateContext.class, i)
                     .getChild(KeyPredicateExprContext.class, 0);
             ret.add(YangBinaryOperator.EQUALS.exprWith(
-                YangQNameExpr.of(parseQName(getChild(pred, NodeIdentifierContext.class, 0))),
+                createChildExpr(getChild(pred, NodeIdentifierContext.class, 0)),
                 parseEqStringValue(getChild(pred, EqQuotedStringContext.class, 1))));
 
         }
@@ -112,10 +155,12 @@ final class InstanceIdentifierParser {
         return ret;
     }
 
-    private QName parseQName(final NodeIdentifierContext expr) {
-        return namespaceContext.createQName(
-            verifyToken(expr, 0, instanceIdentifierParser.Identifier).getText(),
-            verifyToken(expr, 2, instanceIdentifierParser.Identifier).getText());
+    private YangQNameExpr createChildExpr(final NodeIdentifierContext expr) {
+        return createExpr(verifyIdentifier(expr, 0), verifyIdentifier(expr, 2));
+    }
+
+    private static String verifyIdentifier(final NodeIdentifierContext expr, final int child) {
+        return verifyToken(expr, child, instanceIdentifierParser.Identifier).getText();
     }
 
     private static YangLiteralExpr parseEqStringValue(final EqQuotedStringContext expr) {
index 2c2c1198e37cd2b16e88d9e887e98381e5f3ab5c..67d15cb9916f9e12b4883fc72cc0f905686d1570 100644 (file)
@@ -9,10 +9,16 @@ package org.opendaylight.yangtools.yang.xpath.impl;
 
 import javax.xml.xpath.XPathExpressionException;
 import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.AbstractQName;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.QualifiedQName;
+import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
 import org.opendaylight.yangtools.yang.common.YangNamespaceContext;
 import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr;
 import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr.Resolved;
+import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr.Unresolved;
 
 /**
  * Various simplistic utilities shared across classes.
@@ -22,24 +28,47 @@ final class Utils {
 
     }
 
+    static Unresolved interpretAsQName(final YangLiteralExpr expr) throws XPathExpressionException {
+        final String text = expr.getLiteral();
+        final int colon = text.indexOf(':');
+
+        final AbstractQName qname;
+        try {
+            qname = colon != -1 ? QualifiedQName.of(text.substring(0, colon), text.substring(colon + 1))
+                    : UnqualifiedQName.of(text);
+        } catch (IllegalArgumentException e) {
+            throw wrapException(e, "Cannot interpret %s as a QName", expr);
+        }
+
+        return YangQNameExpr.of(qname);
+    }
+
     static YangQNameExpr interpretAsQName(final YangNamespaceContext namespaceContext, final YangLiteralExpr expr)
             throws XPathExpressionException {
         final String text = expr.getLiteral();
         final int colon = text.indexOf(':');
-        final QName qname;
-        if (colon != -1) {
-            try {
-                qname = namespaceContext.createQName(text.substring(0, colon), text.substring(colon + 1));
-            } catch (IllegalArgumentException e) {
-                throw wrapException(e, "Cannot interpret %s as a QName", expr);
-            }
-        } else {
-            try {
-                // Deal with UnprefixedNames by interpreting them in implicit namespace
-                qname = namespaceContext.createQName(expr.getLiteral());
-            } catch (IllegalArgumentException | IllegalStateException e) {
-                throw wrapException(e, "Cannot interpret %s as a QName", expr);
+        try {
+            if (colon == -1) {
+                return YangQNameExpr.of(UnqualifiedQName.of(text));
             }
+
+            return YangQNameExpr.of(namespaceContext.createQName(text.substring(0, colon), text.substring(colon + 1)));
+        } catch (IllegalArgumentException e) {
+            throw wrapException(e, "Cannot interpret %s as a QName", expr);
+        }
+    }
+
+    static Resolved interpretAsQName(final YangNamespaceContext namespaceContext,
+            final QNameModule defaultNamespace, final YangLiteralExpr expr) throws XPathExpressionException {
+        final String text = expr.getLiteral();
+        final int colon = text.indexOf(':');
+        final QName qname;
+
+        try {
+            qname = colon == -1 ? QName.create(defaultNamespace, text)
+                    : namespaceContext.createQName(text.substring(0, colon), text.substring(colon + 1));
+        } catch (IllegalArgumentException e) {
+            throw wrapException(e, "Cannot interpret %s as a QName", expr);
         }
 
         return YangQNameExpr.of(qname);
index 3b363d3eb779a1fc7bd5b558b020020924ce1086..e249ede6ae8a4a7da61d421372d3f94c1c247e7b 100644 (file)
@@ -34,7 +34,7 @@ public class XPathParserTest {
 
     @Before
     public void before() {
-        parser = new AntlrXPathParser(YangXPathMathMode.IEEE754, CONTEXT);
+        parser = new AntlrXPathParser.Unqualified(YangXPathMathMode.IEEE754, CONTEXT, DEFNS);
     }
 
     @Test