From 8f92eac51fa72dd2ebd98a5f1a9ca0799491633f Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Tue, 26 Mar 2019 09:50:46 +0100 Subject: [PATCH] Add basic support for lenient path expression parsing 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 --- .../yang/model/api/PathExpression.java | 52 ++++++++----------- .../model/util/AbstractPathExpression.java | 40 ++++++++++++++ .../yang/model/util/PathExpressionImpl.java | 37 ++++++++----- .../rfc7950/reactor/RFC7950Reactors.java | 2 +- .../parser/rfc7950/stmt/ArgumentUtils.java | 6 ++- .../stmt/path/ParsedPathExpression.java | 34 ++++++++++++ ...ression.java => PathExpressionParser.java} | 45 ++++++++-------- .../stmt/path/PathStatementSupport.java | 27 +++++++--- .../stmt/path/UnparsedPathExpression.java | 41 +++++++++++++++ 9 files changed, 207 insertions(+), 77 deletions(-) create mode 100644 yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/AbstractPathExpression.java create mode 100644 yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/ParsedPathExpression.java rename yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/{AntlrPathExpression.java => PathExpressionParser.java} (91%) create mode 100644 yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/UnparsedPathExpression.java diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/PathExpression.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/PathExpression.java index a875ed0985..fef07e744d 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/PathExpression.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/PathExpression.java @@ -21,6 +21,19 @@ import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression; * An expression as defined in RFC7950 Section 9.9.2, * i.e. the argument of a {@code path} statement. * + *

+ * Semantically a {@link PathExpression} is similar to a {@link YangXPathExpression} with guarantees around what + * subexpressions it can contain: + *

+ * * @author Robert Varga */ @Beta @@ -35,41 +48,20 @@ public interface PathExpression extends Immutable { String getOriginalString(); /** - * Returns true if the XPapth starts in root of YANG model, otherwise returns false. + * Return the {@link YangLocationPath} of this expression. * - * @return true if the XPapth starts in root of YANG model, otherwise returns false + * @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 true if the XPapth starts in root of YANG model, otherwise returns false. * - *

- * Semantically a {@link PathExpression} is similar to a {@link YangXPathExpression} with guarantees around what - * subexpressions it can contain: - *

+ * @return true if the XPapth starts in root of YANG model, otherwise returns false */ - // 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 index 0000000000..a097612ece --- /dev/null +++ b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/AbstractPathExpression.java @@ -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); + } +} diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/PathExpressionImpl.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/PathExpressionImpl.java index aec319cc78..ca35335b2d 100644 --- a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/PathExpressionImpl.java +++ b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/PathExpressionImpl.java @@ -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)); } } diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/reactor/RFC7950Reactors.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/reactor/RFC7950Reactors.java index 0cee95d41a..8d84e46a34 100644 --- a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/reactor/RFC7950Reactors.java +++ b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/reactor/RFC7950Reactors.java @@ -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()) diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/ArgumentUtils.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/ArgumentUtils.java index a8ad2201ba..28538ee98e 100644 --- a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/ArgumentUtils.java +++ b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/ArgumentUtils.java @@ -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 index 0000000000..21ee7f757f --- /dev/null +++ b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/ParsedPathExpression.java @@ -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)); + } +} diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/AntlrPathExpression.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathExpressionParser.java similarity index 91% rename from yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/AntlrPathExpression.java rename to yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathExpressionParser.java index 7cf9a594bc..b42d41d995 100644 --- a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/AntlrPathExpression.java +++ b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathExpressionParser.java @@ -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, diff --git a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathStatementSupport.java b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathStatementSupport.java index b4de8a3bbd..2bf55f76bf 100644 --- a/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathStatementSupport.java +++ b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathStatementSupport.java @@ -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> { - 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 index 0000000000..643691d933 --- /dev/null +++ b/yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/UnparsedPathExpression.java @@ -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); + } +} -- 2.36.6