Add basic support for lenient path expression parsing 59/81159/2
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 26 Mar 2019 08:50:46 +0000 (09:50 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 26 Mar 2019 09:39:44 +0000 (10:39 +0100)
This promotes getLocation() to full PathExpression member, allowing
implementations to throw na exception. It also splits path expression
support into lenient and strict, allowing reactor builders to choose
their implementation.

Change-Id: Ia32a22f3de1b3997795400499c7847e1d91ba59e
JIRA: YANGTOOLS-969
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/PathExpression.java
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/AbstractPathExpression.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/PathExpressionImpl.java
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/reactor/RFC7950Reactors.java
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/ArgumentUtils.java
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/ParsedPathExpression.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathExpressionParser.java [moved from yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/AntlrPathExpression.java with 91% similarity]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathStatementSupport.java
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/UnparsedPathExpression.java [new file with mode: 0644]

index a875ed0985265144f85ec4ad5471d11063dae458..fef07e744dc8efede4cf45a38d2e5005855fdbbb 100644 (file)
@@ -21,6 +21,19 @@ import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression;
  * An expression as defined in <a href="https://tools.ietf.org/html/rfc7950#section-9.9.2">RFC7950 Section 9.9.2</a>,
  * i.e. the argument of a {@code path} statement.
  *
+ * <p>
+ * Semantically a {@link PathExpression} is similar to a {@link YangXPathExpression} with guarantees around what
+ * subexpressions it can contain:
+ * <ul>
+ * <li>the root expression must be a {@link YangLocationPath}</li>
+ * <li>it can contain steps only along {@link YangXPathAxis#CHILD} and {@link YangXPathAxis#PARENT} axis</li>
+ * <li>all steps along {@link YangXPathAxis#CHILD} axis are {@link QNameStep}</li>
+ * <li>the only function invocation is {@link YangFunction#CURRENT}</li>
+ * <li>only {@link YangBinaryOperator#EQUALS} is allowed</li>
+ * <li>no literals nor numbers are allowed</li>
+ * <li>all qualified node identifiers must me resolved</li>
+ * </ul>
+ *
  * @author Robert Varga
  */
 @Beta
@@ -35,41 +48,20 @@ public interface PathExpression extends Immutable {
     String getOriginalString();
 
     /**
-     * Returns <code>true</code> if the XPapth starts in root of YANG model, otherwise returns <code>false</code>.
+     * Return the {@link YangLocationPath} of this expression.
      *
-     * @return <code>true</code> if the XPapth starts in root of YANG model, otherwise returns <code>false</code>
+     * @return The location path
+     * @throws UnsupportedOperationException if the implementation has not parsed the string. Implementations are
+     *         strongly encouraged to perform proper parsing.
      */
-    boolean isAbsolute();
+    YangLocationPath getLocation();
 
     /**
-     * XPath-aware extension of PathExpression. Additionally to exposing {@link #getOriginalString()}, implementations
-     * of this interface expose a parsed {@link YangLocationPath}.
+     * Returns <code>true</code> if the XPapth starts in root of YANG model, otherwise returns <code>false</code>.
      *
-     * <p>
-     * Semantically a {@link PathExpression} is similar to a {@link YangXPathExpression} with guarantees around what
-     * subexpressions it can contain:
-     * <ul>
-     * <li>the root expression must be a {@link YangLocationPath}</li>
-     * <li>it can contain steps only along {@link YangXPathAxis#CHILD} and {@link YangXPathAxis#PARENT} axis</li>
-     * <li>all steps along {@link YangXPathAxis#CHILD} axis are {@link QNameStep}</li>
-     * <li>the only function invocation is {@link YangFunction#CURRENT}</li>
-     * <li>only {@link YangBinaryOperator#EQUALS} is allowed</li>
-     * <li>no literals nor numbers are allowed</li>
-     * <li>all qualified node identifiers must me resolved</li>
-     * </ul>
+     * @return <code>true</code> if the XPapth starts in root of YANG model, otherwise returns <code>false</code>
      */
-    // FIXME: 4.0.0: this is a transitional interface and needs to be integrated directly in PathExpression
-    interface WithLocation extends PathExpression {
-        /**
-         * Return the {@link YangLocationPath} of this expression.
-         *
-         * @return The location path
-         */
-        YangLocationPath getLocation();
-
-        @Override
-        default boolean isAbsolute() {
-            return getLocation().isAbsolute();
-        }
+    default boolean isAbsolute() {
+        return getLocation().isAbsolute();
     }
 }
diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/AbstractPathExpression.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/AbstractPathExpression.java
new file mode 100644 (file)
index 0000000..a097612
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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.model.util;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+
+@Beta
+@NonNullByDefault
+public abstract class AbstractPathExpression implements PathExpression {
+    private final String originalString;
+
+    protected AbstractPathExpression(final String originalString) {
+        this.originalString = requireNonNull(originalString);
+    }
+
+    @Override
+    public final String getOriginalString() {
+        return originalString;
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this).omitNullValues()).toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return helper.add("originalString", originalString);
+    }
+}
index aec319cc78b2552939b79818f72ada9939cd934f..ca35335b2d619c1af1914ca48aa826cf30d19153 100644 (file)
@@ -7,11 +7,11 @@
  */
 package org.opendaylight.yangtools.yang.model.util;
 
-import static java.util.Objects.requireNonNull;
-
-import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
 
 /**
  * A simple XPathExpression implementation.
@@ -21,18 +21,21 @@ import org.opendaylight.yangtools.yang.model.api.PathExpression;
  */
 @Deprecated
 @NonNullByDefault
-public final class PathExpressionImpl implements PathExpression {
-    private final String originalString;
+public final class PathExpressionImpl extends AbstractPathExpression {
+    private final @Nullable YangLocationPath location;
     private final boolean absolute;
 
+    @SuppressFBWarnings(value = "NP_STORE_INTO_NONNULL_FIELD", justification = "Non-grok on SpotBugs part")
     public PathExpressionImpl(final String xpath, final boolean absolute) {
-        this.originalString = requireNonNull(xpath);
+        super(xpath);
         this.absolute = absolute;
+        this.location = null;
     }
 
-    @Override
-    public String getOriginalString() {
-        return originalString;
+    public PathExpressionImpl(final String xpath, final YangLocationPath location) {
+        super(xpath);
+        this.absolute = location.isAbsolute();
+        this.location = location;
     }
 
     @Override
@@ -41,8 +44,16 @@ public final class PathExpressionImpl implements PathExpression {
     }
 
     @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this).add("absolute", absolute).add("originalString", originalString)
-                .toString();
+    public YangLocationPath getLocation() {
+        final YangLocationPath loc = location;
+        if (loc == null) {
+            throw new UnsupportedOperationException("Location has not been provided");
+        }
+        return loc;
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return super.addToStringAttributes(helper.add("absolute", absolute).add("location", location));
     }
 }
index 0cee95d41ab28c49b0208faccc023cc144cc9bdf..8d84e46a34d80f63457a1ccf7548a7c1e1c2e589 100644 (file)
@@ -236,7 +236,7 @@ public final class RFC7950Reactors {
             .addSupport(RequireInstanceStatementSupport.getInstance())
             .addVersionSpecificSupport(VERSION_1, BitStatementRFC6020Support.getInstance())
             .addVersionSpecificSupport(VERSION_1_1, BitStatementRFC7950Support.getInstance())
-            .addSupport(PathStatementSupport.getInstance())
+            .addSupport(PathStatementSupport.strictInstance())
             .addVersionSpecificSupport(VERSION_1, EnumStatementRFC6020Support.getInstance())
             .addVersionSpecificSupport(VERSION_1_1, EnumStatementRFC7950Support.getInstance())
             .addSupport(LengthStatementSupport.getInstance())
index a8ad2201bab6c503b6f7fe117018682a499eac36..28538ee98e9eabefd6db08bf61b0cfdb58b34b4e 100644 (file)
@@ -122,7 +122,11 @@ public final class ArgumentUtils {
             LOG.warn("Argument \"{}\" is not valid XPath string at \"{}\"", path, ctx.getStatementSourceReference(), e);
         }
 
-        return new RevisionAwareXPathImpl(path, PATH_ABS.matcher(path).matches());
+        return new RevisionAwareXPathImpl(path, isAbsoluteXPath(path));
+    }
+
+    public static boolean isAbsoluteXPath(final String path) {
+        return PATH_ABS.matcher(path).matches();
     }
 
     @SuppressWarnings("checkstyle:illegalCatch")
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/ParsedPathExpression.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/ParsedPathExpression.java
new file mode 100644 (file)
index 0000000..21ee7f7
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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.parser.rfc7950.stmt.path;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.util.AbstractPathExpression;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
+
+final class ParsedPathExpression extends AbstractPathExpression {
+    private final @NonNull YangLocationPath location;
+
+    ParsedPathExpression(final YangLocationPath location, final String originalString) {
+        super(originalString);
+        this.location = requireNonNull(location);
+    }
+
+    @Override
+    public YangLocationPath getLocation() {
+        return location;
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return super.addToStringAttributes(helper.add("location", location));
+    }
+}
@@ -9,7 +9,6 @@ package org.opendaylight.yangtools.yang.parser.rfc7950.stmt.path;
 
 import static com.google.common.base.Verify.verify;
 import static com.google.common.base.Verify.verifyNotNull;
-import static java.util.Objects.requireNonNull;
 
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
@@ -18,7 +17,6 @@ import org.antlr.v4.runtime.CharStreams;
 import org.antlr.v4.runtime.CommonTokenStream;
 import org.antlr.v4.runtime.tree.ParseTree;
 import org.antlr.v4.runtime.tree.TerminalNode;
-import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathLexer;
 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser;
 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Absolute_pathContext;
@@ -32,10 +30,11 @@ import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Rel_path_ke
 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Relative_pathContext;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
-import org.opendaylight.yangtools.yang.model.api.PathExpression.WithLocation;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
 import org.opendaylight.yangtools.yang.parser.rfc7950.antlr.SourceExceptionParser;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
 import org.opendaylight.yangtools.yang.xpath.api.YangBinaryExpr;
 import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator;
 import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
@@ -49,30 +48,28 @@ import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step;
 import org.opendaylight.yangtools.yang.xpath.api.YangPathExpr;
 import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr;
 import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class PathExpressionParser {
+    static final class Lenient extends PathExpressionParser {
+        private static final Logger LOG = LoggerFactory.getLogger(Lenient.class);
+
+        @Override
+        PathExpression parseExpression(final StmtContext<?, ?, ?> ctx, final String pathArg) {
+            try {
+                return super.parseExpression(ctx, pathArg);
+            } catch (IllegalStateException | SourceException e) {
+                LOG.warn("Failed to parse expression '{}'", pathArg, e);
+                return new UnparsedPathExpression(pathArg, e);
+            }
+        }
+    }
 
-final class AntlrPathExpression implements WithLocation {
     private static final YangFunctionCallExpr CURRENT_CALL = YangFunctionCallExpr.of(
         YangFunction.CURRENT.getIdentifier());
 
-    private final @NonNull YangLocationPath location;
-    private final @NonNull String originalString;
-
-    private AntlrPathExpression(final YangLocationPath location, final String originalString) {
-        this.location = requireNonNull(location);
-        this.originalString = requireNonNull(originalString);
-    }
-
-    @Override
-    public YangLocationPath getLocation() {
-        return location;
-    }
-
-    @Override
-    public String getOriginalString() {
-        return originalString;
-    }
-
-    static AntlrPathExpression parse(final StmtContext<?, ?, ?> ctx, final String pathArg) {
+    PathExpression parseExpression(final StmtContext<?, ?, ?> ctx, final String pathArg) {
         final LeafRefPathLexer lexer = new LeafRefPathLexer(CharStreams.fromString(pathArg));
         final LeafRefPathParser parser = new LeafRefPathParser(new CommonTokenStream(lexer));
         final Path_argContext path = SourceExceptionParser.parse(lexer, parser, parser::path_arg,
@@ -88,7 +85,7 @@ final class AntlrPathExpression implements WithLocation {
             throw new IllegalStateException("Unsupported child " + childPath);
         }
 
-        return new AntlrPathExpression(location, pathArg);
+        return new ParsedPathExpression(location, pathArg);
     }
 
     private static Absolute parseAbsolute(final StmtContext<?, ?, ?> ctx, final String pathArg,
index b4de8a3bbd9169111f304b4f3eedd13f12470801..2bf55f76bf195fda6c189e3f6e47914ef9b82f67 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.yangtools.yang.parser.rfc7950.stmt.path;
 
+import static java.util.Objects.requireNonNull;
+
 import org.opendaylight.yangtools.yang.model.api.PathExpression;
 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
@@ -17,22 +19,31 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.SubstatementValidator;
 
 public final class PathStatementSupport extends AbstractStatementSupport<PathExpression, PathStatement,
         EffectiveStatement<PathExpression, PathStatement>> {
-    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(YangStmtMapping
-        .PATH)
-        .build();
-    private static final PathStatementSupport INSTANCE = new PathStatementSupport();
+    private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+        YangStmtMapping.PATH).build();
+    private static final PathStatementSupport LENIENT_INSTANCE = new PathStatementSupport(
+        new PathExpressionParser.Lenient());
+    private static final PathStatementSupport STRICT_INSTANCE = new PathStatementSupport(
+        new PathExpressionParser());
+
+    private final PathExpressionParser parser;
 
-    private PathStatementSupport() {
+    private PathStatementSupport(final PathExpressionParser parser) {
         super(YangStmtMapping.PATH);
+        this.parser = requireNonNull(parser);
+    }
+
+    public static PathStatementSupport lenientInstance() {
+        return LENIENT_INSTANCE;
     }
 
-    public static PathStatementSupport getInstance() {
-        return INSTANCE;
+    public static PathStatementSupport strictInstance() {
+        return STRICT_INSTANCE;
     }
 
     @Override
     public PathExpression parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) {
-        return AntlrPathExpression.parse(ctx, value);
+        return parser.parseExpression(ctx, value);
     }
 
     @Override
diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/UnparsedPathExpression.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/UnparsedPathExpression.java
new file mode 100644 (file)
index 0000000..643691d
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.parser.rfc7950.stmt.path;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.opendaylight.yangtools.yang.model.util.AbstractPathExpression;
+import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.ArgumentUtils;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
+
+final class UnparsedPathExpression extends AbstractPathExpression {
+    private final RuntimeException cause;
+    private final boolean absolute;
+
+    UnparsedPathExpression(final String originalString, final RuntimeException cause) {
+        super(originalString);
+        this.cause = requireNonNull(cause);
+        absolute = ArgumentUtils.isAbsoluteXPath(originalString);
+    }
+
+    @Override
+    public boolean isAbsolute() {
+        return absolute;
+    }
+
+    @Override
+    public YangLocationPath getLocation() {
+        throw new UnsupportedOperationException("Expression '" + getOriginalString() + "' was not parsed", cause);
+    }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return super.addToStringAttributes(helper.add("absolute", absolute)).add("cause", cause);
+    }
+}