From d3bd58ed1f89d2bc84c420b93529ef0f9182d277 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Tue, 26 Feb 2019 22:14:28 +0100 Subject: [PATCH] Update yang-xpath-api design This is the first round of API design update, bringing the following changes: - MathMode is now a top-level enumeration - YangXPathMathSupport is an API interface providing common functions - Prefix resolution is done through YangNamespaceContext - YangNaryExpr is final - math-specific YangNumberExprs are hidden classes - YangFunctionCallExpr.NoArgs is folded into YangFunctionCallExpr - YangLiteralExpr is now final - YangXPathExpression exposes MathMode - Literal interpretation is lazy and works on subexpressions Change-Id: Ie721678b5513dc088c1a267b98f38d9b0643cb33 Signed-off-by: Robert Varga --- .../api/AbstractYangXPathMathSupport.java | 53 ++++ .../yang/xpath/api/BigDecimalNumberExpr.java | 53 ++++ .../xpath/api/BigDecimalXPathMathSupport.java | 86 +++++++ .../yang/xpath/api/DoubleNumberExpr.java | 60 +++++ .../xpath/api/DoubleXPathMathSupport.java | 71 ++++++ .../yang/xpath/api/YangFilterExpr.java | 9 +- .../yang/xpath/api/YangFunctionCallExpr.java | 33 +-- .../yang/xpath/api/YangLiteralExpr.java | 33 +-- .../yang/xpath/api/YangNaryExpr.java | 28 ++- .../yang/xpath/api/YangNaryOperator.java | 20 +- .../yang/xpath/api/YangNumberExpr.java | 79 +----- .../yang/xpath/api/YangXPathExpression.java | 27 +- .../yang/xpath/api/YangXPathMathMode.java | 58 +++++ .../yang/xpath/api/YangXPathMathSupport.java | 45 ++++ .../yang/xpath/api/YangXPathParser.java | 14 +- .../xpath/api/YangXPathParserFactory.java | 56 +---- yang/yang-xpath-impl/pom.xml | 19 ++ .../yangtools/yang/xpath/impl/Activator.java | 36 +++ ...XPathParser.java => AntlrXPathParser.java} | 232 +++--------------- .../xpath/impl/AntlrXPathParserFactory.java | 23 ++ .../xpath/impl/AntlrYangXPathExpression.java | 37 ++- .../xpath/impl/BigDecimalXPathParser.java | 92 ------- .../yang/xpath/impl/DoubleXPathParser.java | 79 ------ .../impl/InstanceIdentifierLiteralExpr.java | 28 --- .../xpath/impl/InstanceIdentifierParser.java | 121 +++++++++ .../yang/xpath/impl/LiteralExprUtils.java | 47 ++++ .../yang/xpath/impl/ParseTreeUtils.java | 68 +++++ .../yang/xpath/impl/QNameLiteralExpr.java | 35 --- .../yang/xpath/impl/QNameSupport.java | 40 --- .../yang/xpath/impl/XPathParserTest.java | 17 +- 30 files changed, 888 insertions(+), 711 deletions(-) create mode 100644 yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/AbstractYangXPathMathSupport.java create mode 100644 yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/BigDecimalNumberExpr.java create mode 100644 yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/BigDecimalXPathMathSupport.java create mode 100644 yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/DoubleNumberExpr.java create mode 100644 yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/DoubleXPathMathSupport.java create mode 100644 yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathMathMode.java create mode 100644 yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathMathSupport.java create mode 100644 yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/Activator.java rename yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/{XPathParser.java => AntlrXPathParser.java} (70%) create mode 100644 yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrXPathParserFactory.java delete mode 100644 yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/BigDecimalXPathParser.java delete mode 100644 yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/DoubleXPathParser.java delete mode 100644 yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/InstanceIdentifierLiteralExpr.java create mode 100644 yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/InstanceIdentifierParser.java create mode 100644 yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/LiteralExprUtils.java create mode 100644 yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/ParseTreeUtils.java delete mode 100644 yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/QNameLiteralExpr.java delete mode 100644 yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/QNameSupport.java diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/AbstractYangXPathMathSupport.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/AbstractYangXPathMathSupport.java new file mode 100644 index 0000000000..0de1b7c3db --- /dev/null +++ b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/AbstractYangXPathMathSupport.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019 Pantheon Technologies, 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 static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +import java.util.Optional; + +abstract class AbstractYangXPathMathSupport> implements YangXPathMathSupport { + private final Class numberClass; + + AbstractYangXPathMathSupport(final Class numberClass) { + this.numberClass = requireNonNull(numberClass); + } + + @Override + public final N negateNumber(final YangNumberExpr number) { + checkArgument(numberClass.isInstance(requireNonNull(number)), "Expected %s have %s", numberClass, number); + return doNegate(numberClass.cast(number)); + } + + + @Override + public final Optional tryEvaluate(final YangBinaryOperator operator, + final YangNumberExpr left, final YangNumberExpr right) { + if (!numberClass.isInstance(left) || !numberClass.isInstance(right)) { + requireNonNull(operator); + requireNonNull(left); + requireNonNull(right); + return Optional.empty(); + } + + return Optional.of(evaluate(requireNonNull(operator), numberClass.cast(left), numberClass.cast(right))); + } + + abstract N doNegate(N number); + + /** + * Evaluate an operator and its left- and right-handside. + * + * @param operator Operator to apply + * @param left Left hand-side + * @param right Right hand-side + * @return Evaluation result + */ + abstract YangExpr evaluate(YangBinaryOperator operator, N left, N right); +} diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/BigDecimalNumberExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/BigDecimalNumberExpr.java new file mode 100644 index 0000000000..3fa4d202ec --- /dev/null +++ b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/BigDecimalNumberExpr.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019 Pantheon Technologies, 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 static java.util.Objects.requireNonNull; + +import java.math.BigDecimal; +import org.eclipse.jdt.annotation.Nullable; + +final class BigDecimalNumberExpr extends YangNumberExpr { + private static final long serialVersionUID = 1L; + + private final BigDecimal number; + + private BigDecimalNumberExpr(final BigDecimal number) { + this.number = requireNonNull(number); + } + + static BigDecimalNumberExpr of(final BigDecimal number) { + return new BigDecimalNumberExpr(number); + } + + @Override + public BigDecimal getNumber() { + return number; + } + + @Override + public BigDecimalXPathMathSupport getSupport() { + return BigDecimalXPathMathSupport.getInstance(); + } + + @Override + public int hashCode() { + return number.hashCode(); + } + + @Override + public boolean equals(final @Nullable Object obj) { + return this == obj || obj instanceof BigDecimalNumberExpr + && number.equals(((BigDecimalNumberExpr) obj).number); + } + + @Override + public String toString() { + return number.toString(); + } +} \ No newline at end of file diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/BigDecimalXPathMathSupport.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/BigDecimalXPathMathSupport.java new file mode 100644 index 0000000000..34d4829675 --- /dev/null +++ b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/BigDecimalXPathMathSupport.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2019 Pantheon Technologies, 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 java.math.BigDecimal; + +final class BigDecimalXPathMathSupport extends AbstractYangXPathMathSupport { + private static final BigDecimalXPathMathSupport INSTANCE = new BigDecimalXPathMathSupport(); + private static final BigDecimalNumberExpr ZERO = BigDecimalNumberExpr.of(BigDecimal.ZERO); + private static final BigDecimalNumberExpr ONE = BigDecimalNumberExpr.of(BigDecimal.ONE); + private static final BigDecimalNumberExpr TEN = BigDecimalNumberExpr.of(BigDecimal.TEN); + + private BigDecimalXPathMathSupport() { + super(BigDecimalNumberExpr.class); + } + + static BigDecimalXPathMathSupport getInstance() { + return INSTANCE; + } + + @Override + public BigDecimalNumberExpr createNumber(final String str) { + switch (str) { + case "0": + return ZERO; + case "1": + return ONE; + case "10": + return TEN; + default: + return BigDecimalNumberExpr.of(new BigDecimal(str)); + } + } + + @Override + BigDecimalNumberExpr doNegate(final BigDecimalNumberExpr number) { + return BigDecimalNumberExpr.of(number.getNumber().negate()); + } + + @Override + YangExpr evaluate(final YangBinaryOperator operator, final BigDecimalNumberExpr left, + final BigDecimalNumberExpr right) { + final BigDecimal l = left.getNumber(); + final BigDecimal r = right.getNumber(); + + final BigDecimal result; + switch (operator) { + case DIV: + result = l.divide(r); + break; + case EQUALS: + return YangBooleanConstantExpr.of(l.equals(r)); + case GT: + return YangBooleanConstantExpr.of(l.compareTo(r) > 0); + case GTE: + return YangBooleanConstantExpr.of(l.compareTo(r) >= 0); + case LT: + return YangBooleanConstantExpr.of(l.compareTo(r) < 0); + case LTE: + return YangBooleanConstantExpr.of(l.compareTo(r) <= 0); + case MINUS: + result = l.subtract(r); + break; + case MOD: + result = l.remainder(r); + break; + case MUL: + result = l.multiply(r); + break; + case NOT_EQUALS: + return YangBooleanConstantExpr.of(!l.equals(r)); + case PLUS: + result = l.add(r); + break; + default: + throw new IllegalStateException("Unhandled operator " + operator); + } + + return BigDecimalNumberExpr.of(result); + } +} diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/DoubleNumberExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/DoubleNumberExpr.java new file mode 100644 index 0000000000..52cdf0b036 --- /dev/null +++ b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/DoubleNumberExpr.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2019 Pantheon Technologies, 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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import org.eclipse.jdt.annotation.Nullable; + +final class DoubleNumberExpr extends YangNumberExpr { + private static final long serialVersionUID = 1L; + + private final double value; + + private DoubleNumberExpr(final double value) { + this.value = value; + } + + static DoubleNumberExpr of(final double value) { + return new DoubleNumberExpr(value); + } + + double getValue() { + return value; + } + + @Override + public Double getNumber() { + return value; + } + + @Override + public DoubleXPathMathSupport getSupport() { + return DoubleXPathMathSupport.getInstance(); + } + + @Override + public int hashCode() { + return Double.hashCode(value); + } + + @Override + @SuppressFBWarnings(value = "FE_FLOATING_POINT_EQUALITY", justification = "") + public boolean equals(final @Nullable Object obj) { + return this == obj || obj instanceof DoubleNumberExpr && bitEqual(((DoubleNumberExpr) obj).value); + } + + private boolean bitEqual(final double other) { + return Double.doubleToLongBits(value) == Double.doubleToLongBits(other); + } + + @Override + public String toString() { + return String.valueOf(value); + } + +} \ No newline at end of file diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/DoubleXPathMathSupport.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/DoubleXPathMathSupport.java new file mode 100644 index 0000000000..fb396efd25 --- /dev/null +++ b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/DoubleXPathMathSupport.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2019 Pantheon Technologies, 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; + +final class DoubleXPathMathSupport extends AbstractYangXPathMathSupport { + private static final DoubleXPathMathSupport INSTANCE = new DoubleXPathMathSupport(); + + private DoubleXPathMathSupport() { + super(DoubleNumberExpr.class); + } + + static DoubleXPathMathSupport getInstance() { + return INSTANCE; + } + + @Override + public DoubleNumberExpr createNumber(final String str) { + return DoubleNumberExpr.of(Double.parseDouble(str)); + } + + @Override + DoubleNumberExpr doNegate(final DoubleNumberExpr number) { + return DoubleNumberExpr.of(-number.getValue()); + } + + @Override + YangExpr evaluate(final YangBinaryOperator operator, final DoubleNumberExpr left, final DoubleNumberExpr right) { + final double l = left.getValue(); + final double r = right.getValue(); + + final double result; + switch (operator) { + case DIV: + result = l / r; + break; + case EQUALS: + return YangBooleanConstantExpr.of(left.equals(right)); + case GT: + return YangBooleanConstantExpr.of(l > r); + case GTE: + return YangBooleanConstantExpr.of(l >= r); + case LT: + return YangBooleanConstantExpr.of(l < r); + case LTE: + return YangBooleanConstantExpr.of(l <= r); + case MINUS: + result = l - r; + break; + case MOD: + result = l % r; + break; + case MUL: + result = l * r; + break; + case NOT_EQUALS: + return YangBooleanConstantExpr.of(!left.equals(right)); + case PLUS: + result = l + r; + break; + default: + throw new IllegalStateException("Unhandled operator " + operator); + } + + return DoubleNumberExpr.of(result); + } +} diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangFilterExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangFilterExpr.java index 7343e569ff..8b1291bc7e 100644 --- a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangFilterExpr.java +++ b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangFilterExpr.java @@ -22,9 +22,9 @@ public class YangFilterExpr implements YangExpr, YangPredicateAware { private static final class WithPredicates extends YangFilterExpr { private static final long serialVersionUID = 1L; - private final Set predicates; + private final ImmutableSet predicates; - WithPredicates(final YangExpr expr, final Set predicates) { + WithPredicates(final YangExpr expr, final ImmutableSet predicates) { super(expr); this.predicates = requireNonNull(predicates); } @@ -60,11 +60,6 @@ public class YangFilterExpr implements YangExpr, YangPredicateAware { return expr; } - @Override - public Set getPredicates() { - return ImmutableSet.of(); - } - @Override public final int hashCode() { return Objects.hash(expr, getPredicates()); diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangFunctionCallExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangFunctionCallExpr.java index 4cf1fbf4af..df27ca2c57 100644 --- a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangFunctionCallExpr.java +++ b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangFunctionCallExpr.java @@ -25,26 +25,11 @@ import org.opendaylight.yangtools.yang.common.YangConstants; * @author Robert Varga */ @Beta -public abstract class YangFunctionCallExpr implements YangExpr { - static class NoArgs extends YangFunctionCallExpr { +public class YangFunctionCallExpr implements YangExpr { + private static final class WithArgs extends YangFunctionCallExpr { private static final long serialVersionUID = 1L; - private final QName name; - - NoArgs(final QName name) { - this.name = requireNonNull(name); - } - - @Override - public QName getName() { - return name; - } - } - - private static final class WithArgs extends NoArgs { - private static final long serialVersionUID = 1L; - - private final List arguments; + private final ImmutableList arguments; WithArgs(final QName name, final List arguments) { super(name); @@ -59,19 +44,23 @@ public abstract class YangFunctionCallExpr implements YangExpr { private static final long serialVersionUID = 1L; - YangFunctionCallExpr() { - // Hidden + private final QName name; + + YangFunctionCallExpr(final QName name) { + this.name = requireNonNull(name); } public static YangFunctionCallExpr of(final QName name) { - return new NoArgs(name); + return new YangFunctionCallExpr(name); } public static YangFunctionCallExpr of(final QName name, final List arguments) { return arguments.isEmpty() ? of(name) : new WithArgs(name, arguments); } - public abstract QName getName(); + public final QName getName() { + return name; + } public List getArguments() { return ImmutableList.of(); diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangLiteralExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangLiteralExpr.java index 1ece060f05..efff7c6929 100644 --- a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangLiteralExpr.java +++ b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangLiteralExpr.java @@ -28,22 +28,9 @@ import org.eclipse.jdt.annotation.Nullable; * @author Robert Varga */ @Beta -public class YangLiteralExpr implements YangExpr { - private static final class Empty extends YangLiteralExpr { - private static final long serialVersionUID = 1L; - - Empty() { - super(""); - } - - @SuppressWarnings("static-method") - Object readResolve() { - return empty(); - } - } - +public final class YangLiteralExpr implements YangExpr { private static final long serialVersionUID = 1L; - private static final YangLiteralExpr EMPTY = new Empty(); + private static final YangLiteralExpr EMPTY = new YangLiteralExpr(""); private final String literal; @@ -51,30 +38,34 @@ public class YangLiteralExpr implements YangExpr { this.literal = requireNonNull(literal); } - public static final YangLiteralExpr empty() { + public static YangLiteralExpr empty() { return EMPTY; } - public static final YangLiteralExpr of(final String literal) { + public static YangLiteralExpr of(final String literal) { return literal.isEmpty() ? EMPTY : new YangLiteralExpr(literal); } - public final String getLiteral() { + public String getLiteral() { return literal; } @Override - public final int hashCode() { + public int hashCode() { return literal.hashCode(); } @Override - public final boolean equals(final @Nullable Object obj) { + public boolean equals(final @Nullable Object obj) { return this == obj || obj instanceof YangLiteralExpr && literal.equals(((YangLiteralExpr) obj).literal); } @Override - public final String toString() { + public String toString() { return literal; } + + protected Object readResolve() { + return literal.isEmpty() ? EMPTY : this; + } } diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNaryExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNaryExpr.java index fe513a8adb..982b338095 100644 --- a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNaryExpr.java +++ b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNaryExpr.java @@ -11,33 +11,41 @@ import static java.util.Objects.requireNonNull; import com.google.common.annotations.Beta; import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableSet; import java.util.Objects; import java.util.Set; import org.eclipse.jdt.annotation.Nullable; +/** + * A {@link YangExpr} combining a {@link YangNaryOperator} with a set of expressions. + */ @Beta -public abstract class YangNaryExpr implements YangExpr { +public final class YangNaryExpr implements YangExpr { private static final long serialVersionUID = 1L; - private final Set expressions; + private final YangNaryOperator operator; + private final ImmutableSet expressions; - YangNaryExpr(final Set expressions) { + YangNaryExpr(final YangNaryOperator operator, final ImmutableSet expressions) { + this.operator = requireNonNull(operator); this.expressions = requireNonNull(expressions); } - public final Set getExpressions() { + public Set getExpressions() { return expressions; } - public abstract YangNaryOperator getOperator(); + public YangNaryOperator getOperator() { + return operator; + } @Override - public final int hashCode() { + public int hashCode() { return Objects.hash(getOperator(), expressions); } @Override - public final boolean equals(final @Nullable Object obj) { + public boolean equals(final @Nullable Object obj) { if (this == obj) { return true; } @@ -49,10 +57,10 @@ public abstract class YangNaryExpr implements YangExpr { } @Override - public final String toString() { + public String toString() { return MoreObjects.toStringHelper(YangNaryExpr.class) - .add("operator", getOperator()) - .add("expressions", getExpressions()) + .add("operator", operator) + .add("expressions", expressions) .toString(); } } diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNaryOperator.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNaryOperator.java index b358b21902..a4fc4051d9 100644 --- a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNaryOperator.java +++ b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNaryOperator.java @@ -11,9 +11,7 @@ import static java.util.Objects.requireNonNull; import com.google.common.annotations.Beta; import com.google.common.collect.ImmutableSet; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.Collection; -import java.util.Set; /** * YANG XPath binary operator. @@ -41,20 +39,6 @@ public enum YangNaryOperator { */ UNION("|"); - @SuppressFBWarnings(value = "SE_INNER_CLASS", justification = "Outer class is a retained enumeration") - private final class Expr extends YangNaryExpr { - private static final long serialVersionUID = 1L; - - Expr(final Set exprs) { - super(exprs); - } - - @Override - public YangNaryOperator getOperator() { - return YangNaryOperator.this; - } - } - private final String str; YangNaryOperator(final String str) { @@ -67,7 +51,7 @@ public enum YangNaryOperator { } public YangExpr exprWith(final Collection exprs) { - final Set set = ImmutableSet.copyOf(exprs); - return set.size() == 1 ? set.iterator().next() : new Expr(set); + final ImmutableSet set = ImmutableSet.copyOf(exprs); + return set.size() == 1 ? set.iterator().next() : new YangNaryExpr(this, set); } } diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNumberExpr.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNumberExpr.java index 96f6bd7051..f5b5d1b62d 100644 --- a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNumberExpr.java +++ b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangNumberExpr.java @@ -7,95 +7,20 @@ */ package org.opendaylight.yangtools.yang.xpath.api; -import static java.util.Objects.requireNonNull; - import com.google.common.annotations.Beta; -import java.math.BigDecimal; -import org.eclipse.jdt.annotation.Nullable; /** * A number-bearing expression. */ @Beta public abstract class YangNumberExpr, N extends Number> implements YangExpr { - public static final class YangBigDecimal extends YangNumberExpr { - private static final long serialVersionUID = 1L; - - private final BigDecimal number; - - YangBigDecimal(final BigDecimal number) { - this.number = requireNonNull(number); - } - - @Override - public BigDecimal getNumber() { - return number; - } - - @Override - public int hashCode() { - return number.hashCode(); - } - - @Override - public boolean equals(final @Nullable Object obj) { - return this == obj || obj instanceof YangBigDecimal - && number.equals(((YangBigDecimal) obj).number); - } - - @Override - public String toString() { - return number.toString(); - } - } - - public static final class YangDouble extends YangNumberExpr { - private static final long serialVersionUID = 1L; - - private final double value; - - YangDouble(final double value) { - this.value = value; - } - - public double getValue() { - return value; - } - - @Override - public Double getNumber() { - return value; - } - - @Override - public int hashCode() { - return Double.hashCode(value); - } - - @Override - public boolean equals(final @Nullable Object obj) { - return this == obj || obj instanceof YangDouble && value == ((YangDouble) obj).value; - } - - @Override - public String toString() { - return String.valueOf(value); - } - } - private static final long serialVersionUID = 1L; YangNumberExpr() { // Hidden to prevent external subclassing } - public static YangBigDecimal of(final BigDecimal number) { - return new YangBigDecimal(number); - } - - public static YangDouble of(final double value) { - return new YangDouble(value); - } - public abstract N getNumber(); + + public abstract YangXPathMathSupport getSupport(); } diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathExpression.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathExpression.java index 91d3e7c37d..da7ac0068f 100644 --- a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathExpression.java +++ b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathExpression.java @@ -12,11 +12,28 @@ import javax.xml.xpath.XPathExpressionException; import org.opendaylight.yangtools.concepts.Immutable; import org.opendaylight.yangtools.yang.common.QName; +/** + * An XPath expression. + * + * @author Robert Varga + */ @Beta public interface YangXPathExpression extends Immutable { - + /** + * Return the root {@link YangExpr}. + * + * @return Root expression. + */ YangExpr getRootExpr(); + /** + * Return the {@link YangXPathMathMode} used in this expression. All {@link YangNumberExpr} objects used by this + * expression are expected to be handled by this mode's {@link YangXPathMathSupport}. + * + * @return YangXPathMathMode + */ + YangXPathMathMode getMathMode(); + /** * Attempt to interpret a {@link YangLiteralExpr} referenced by this expression as a {@link QName}. This method * is required to perform late value binding of the expression when the literal needs to be interpreted as @@ -32,12 +49,12 @@ public interface YangXPathExpression extends Immutable { * at evaluation. * * @param expr Literal to be reinterpreted - * @return QName representation of the literal + * @return YangQNameExpr result of interpretation * @throws XPathExpressionException when the literal cannot be interpreted as a QName */ - QName interpretAsQName(YangLiteralExpr expr) throws XPathExpressionException; + YangQNameExpr interpretAsQName(YangLiteralExpr expr) throws XPathExpressionException; - // API design: this really should be YangInstanceIdentifier without AugmentationIdentifier. Implementations are - // strongly encouraged to validate it as such. + // FIXME: this really should be YangInstanceIdentifier without AugmentationIdentifier. Implementations are + // strongly encouraged to validate it as such. YangLocationPath interpretAsInstanceIdentifier(YangLiteralExpr expr) throws XPathExpressionException; } diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathMathMode.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathMathMode.java new file mode 100644 index 0000000000..073ad60863 --- /dev/null +++ b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathMathMode.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 Pantheon Technologies, 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 static java.util.Objects.requireNonNull; + +import com.google.common.annotations.Beta; + +/** + * {@link YangXPathParser} number compliance knobs. This enumeration defines what assumptions the parser can make -- + * affecting its optimization properties around + * constant folding when number expressions are + * involved. + */ +@Beta +public enum YangXPathMathMode { + /** + * All number expressions are treated as {@code double}. This in spirit of XPath 1.0 -- any number expression + * starts its life as a double, making all operations subject to IEEE754 rounding and range rules. + */ + IEEE754(DoubleXPathMathSupport.getInstance()), + + /** + * All number expressions are treated as infinite-precision numbers. This follows the spirit of YANG 1.1 -- + * where mostly have integral types and decimal64 mapped to BigDecimal. Non-decimal numbers are mapped either to + * {@code int}, {@code long} or {@code BigInteger}. + */ + EXACT(BigDecimalXPathMathSupport.getInstance()); + + /* + * FIXME: 3.0.0: specify and implement this: + * + * All number expressions are treated either as {@code org.opendaylight.yangtools.yang.common} types with + * precision required to hold them. Decimal types are mapped to {@link Decimal64} with all range restrictions + * and rounding error implied by it. + */ + // ODL_COMMON; + + private YangXPathMathSupport support; + + YangXPathMathMode(final YangXPathMathSupport support) { + this.support = requireNonNull(support); + } + + /** + * Return {@link YangXPathMathSupport} which provides support for the this mode. + * + * @return YangXPathMathSupport supporting this mode. + */ + public YangXPathMathSupport getSupport() { + return support; + } +} diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathMathSupport.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathMathSupport.java new file mode 100644 index 0000000000..20ee96b31b --- /dev/null +++ b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathMathSupport.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 Pantheon Technologies, 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 java.util.Optional; + +@Beta +public interface YangXPathMathSupport> { + /** + * Create a {@link YangNumberExpr} backed by specified string. + * + * @param str String + * @return number expression + * @throws NullPointerException if {@code str} is null + * @throws NumberFormatException if the string does not represent a valid number + */ + N createNumber(String str); + + /** + * Create a {@link YangNumberExpr} representing the negated value of a number. + * + * @param number input number + * @return negated number expression + * @throws NullPointerException if {@code number} is null + * @throws IllegalArgumentException if {@code number} has unrecognized type + */ + N negateNumber(YangNumberExpr number); + + /** + * Attempt to evaluate an operator and its left- and right-handside. + * + * @param operator Operator to apply + * @param left Left hand-side + * @param right Right hand-side + * @return Evaluation result, if evaluation succeeded + * @throws NullPointerException if any of the arguments is null + */ + Optional tryEvaluate(YangBinaryOperator operator, YangNumberExpr left, YangNumberExpr right); +} diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathParser.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathParser.java index f0e594909a..0545366427 100644 --- a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathParser.java +++ b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathParser.java @@ -11,9 +11,21 @@ import com.google.common.annotations.Beta; import javax.annotation.concurrent.NotThreadSafe; import javax.xml.xpath.XPathExpressionException; +/** + * Interface for converting a String into a {@link YangXPathExpression}. + * + * @author Robert Varga + */ @Beta @NotThreadSafe public interface YangXPathParser { - + /** + * Parse a string containing an XPath expression. + * + * @param xpath XPath expression string + * @return A parsed {@link YangXPathExpression} + * @throws NullPointerException if {@code xpath} is null + * @throws XPathExpressionException when the expression cannot be parsed + */ YangXPathExpression parseExpression(String xpath) throws XPathExpressionException; } diff --git a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathParserFactory.java b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathParserFactory.java index 77a02ff01b..49e29ad438 100644 --- a/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathParserFactory.java +++ b/yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangXPathParserFactory.java @@ -8,10 +8,8 @@ package org.opendaylight.yangtools.yang.xpath.api; import com.google.common.annotations.Beta; -import java.util.Set; -import java.util.function.Function; import javax.annotation.concurrent.ThreadSafe; -import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.common.YangNamespaceContext; /** * Factory for creating {@link YangXPathParser}s. @@ -22,57 +20,23 @@ import org.opendaylight.yangtools.yang.common.QNameModule; @ThreadSafe public interface YangXPathParserFactory { /** - * {@link YangXPathParser} number compliance knobs. This enumeration defines what assumptions the parser can make -- - * affecting its optimization properties around - * constant folding when number expressions are - * involved. - */ - @Beta - enum MathMode { - /** - * All number expressions are treated as {@code double}. This in spirit of XPath 1.0 -- any number expression - * starts its life as a double, making all operations subject to IEEE754 rounding and range rules. - */ - IEEE754, - - /** - * All number expressions are treated as infinite-precision numbers. This follows the spirit of YANG 1.1 -- - * where mostly have integral types and decimal64 mapped to BigDecimal. Non-decimal numbers are mapped either to - * {@code int}, {@code long} or {@code BigInteger}. - */ - EXACT, - - /* - * FIXME: 3.0.0: specify and implement this: - * - * All number expressions are treated either as {@code org.opendaylight.yangtools.yang.common} types with - * precision required to hold them. Decimal types are mapped to {@link Decimal64} with all range restrictions - * and rounding error implied by it. - */ - // ODL_COMMON, - } - - Set getSupportedMathModes(); - - /** - * Return a {@link YangXPathParser} compliant with {@link MathMode#IEEE754}. + * Return a {@link YangXPathParser} compliant with {@link YangXPathMathMode#IEEE754}. * - * @param prefixResolver Prefix-to-namespace resolver function + * @param namespaceContext Prefix-to-namespace resolver * @return An XPathParser - * @throws IllegalArgumentException if {@code IEEE754} is not supported. + * @throws NullPointerException if {@code namespaceContext} is null */ - default YangXPathParser newParser(final Function prefixResolver) { - return newParser(prefixResolver, MathMode.IEEE754); + default YangXPathParser newParser(final YangNamespaceContext namespaceContext) { + return newParser(namespaceContext, YangXPathMathMode.IEEE754); } /** - * Return a {@link YangXPathParser} compliant with {@link MathMode}. + * Return a {@link YangXPathParser} compliant with {@link YangXPathMathMode}. * - * @param prefixResolver Prefix-to-namespace resolver function + * @param namespaceContext Prefix-to-namespace resolver * @param mathMode Requested XPath number compliance * @return An XPathParser - * @throws NullPointerException if {@code mathMode} is null - * @throws IllegalArgumentException if {@code mathMode} is not supported. + * @throws NullPointerException if any argument is null */ - YangXPathParser newParser(Function prefixResolver, MathMode mathMode); + YangXPathParser newParser(YangNamespaceContext namespaceContext, YangXPathMathMode mathMode); } diff --git a/yang/yang-xpath-impl/pom.xml b/yang/yang-xpath-impl/pom.xml index f4661ca63d..e9aded9ba4 100644 --- a/yang/yang-xpath-impl/pom.xml +++ b/yang/yang-xpath-impl/pom.xml @@ -44,6 +44,14 @@ org.antlr antlr4-runtime + + org.kohsuke.metainf-services + metainf-services + + + org.osgi + org.osgi.core + @@ -65,6 +73,17 @@ false + + org.apache.felix + maven-bundle-plugin + true + + + {META-INF/services=${project.build.directory}/classes/META-INF/services} + org.opendaylight.yangtools.yang.xpath.impl.Activator + + + org.apache.maven.plugins maven-checkstyle-plugin diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/Activator.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/Activator.java new file mode 100644 index 0000000000..665631b51a --- /dev/null +++ b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/Activator.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 Pantheon Technologies, s.r.o. and others. 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.impl; + +import org.eclipse.jdt.annotation.Nullable; +import org.opendaylight.yangtools.yang.xpath.api.YangXPathParserFactory; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +/** + * YANG XPath implementation activator. Publishes a {@link YangXPathParserFactory} implementation on bundle start. + * + * @author Robert Varga + */ +public final class Activator implements BundleActivator { + private @Nullable ServiceRegistration registration; + + @Override + public void start(final @Nullable BundleContext context) throws Exception { + registration = context.registerService(YangXPathParserFactory.class, new AntlrXPathParserFactory(), null); + } + + @Override + public void stop(final @Nullable BundleContext context) throws Exception { + if (registration != null) { + registration.unregister(); + registration = null; + } + } +} diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/XPathParser.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrXPathParser.java similarity index 70% rename from yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/XPathParser.java rename to yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrXPathParser.java index b035c696e8..2b53a3b2f9 100644 --- a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/XPathParser.java +++ b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrXPathParser.java @@ -10,17 +10,22 @@ package org.opendaylight.yangtools.yang.xpath.impl; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Verify.verify; import static com.google.common.base.Verify.verifyNotNull; +import static java.util.Objects.requireNonNull; +import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.getChild; +import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.illegalShape; +import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyAtLeastChildren; +import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyChildCount; +import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyTerminal; +import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyToken; +import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyTree; -import com.google.common.base.VerifyException; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Deque; import java.util.Iterator; import java.util.LinkedHashSet; @@ -28,7 +33,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.function.Function; import javax.xml.xpath.XPathExpressionException; import org.antlr.v4.runtime.BaseErrorListener; import org.antlr.v4.runtime.CharStreams; @@ -36,13 +40,12 @@ import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.Recognizer; -import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.common.YangConstants; +import org.opendaylight.yangtools.yang.common.YangNamespaceContext; import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator; import org.opendaylight.yangtools.yang.xpath.api.YangBooleanConstantExpr; import org.opendaylight.yangtools.yang.xpath.api.YangExpr; @@ -57,22 +60,13 @@ import org.opendaylight.yangtools.yang.xpath.api.YangNaryExpr; import org.opendaylight.yangtools.yang.xpath.api.YangNaryOperator; import org.opendaylight.yangtools.yang.xpath.api.YangNegateExpr; import org.opendaylight.yangtools.yang.xpath.api.YangNumberExpr; -import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr; import org.opendaylight.yangtools.yang.xpath.api.YangVariableReferenceExpr; import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis; import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression; +import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathMode; +import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathSupport; import org.opendaylight.yangtools.yang.xpath.api.YangXPathNodeType; import org.opendaylight.yangtools.yang.xpath.api.YangXPathParser; -import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.EqQuotedStringContext; -import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.InstanceIdentifierContext; -import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.KeyPredicateContext; -import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.KeyPredicateExprContext; -import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.LeafListPredicateContext; -import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.LeafListPredicateExprContext; -import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.NodeIdentifierContext; -import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.PathArgumentContext; -import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.PosContext; -import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.QuotedStringContext; import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.AbbreviatedStepContext; import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.AbsoluteLocationPathNorootContext; import org.opendaylight.yangtools.yang.xpath.impl.xpathParser.AdditiveExprContext; @@ -107,8 +101,8 @@ import org.slf4j.LoggerFactory; * * @author Robert Varga */ -abstract class XPathParser> implements YangXPathParser { - private static final Logger LOG = LoggerFactory.getLogger(XPathParser.class); +final class AntlrXPathParser implements YangXPathParser { + private static final Logger LOG = LoggerFactory.getLogger(AntlrXPathParser.class); private static final Map BINARY_OPERATORS = Maps.uniqueIndex( Arrays.asList(YangBinaryOperator.values()), YangBinaryOperator::toString); private static final Map NODE_TYPES = Maps.uniqueIndex(Arrays.asList( @@ -121,10 +115,14 @@ abstract class XPathParser> implements YangXPathP // Cached for checks in hot path private static final AxisStep SELF_STEP = YangXPathAxis.SELF.asStep(); - private final QNameSupport qnameSupport; + private final YangXPathMathMode mathMode; + private final YangXPathMathSupport mathSupport; + private final YangNamespaceContext namespaceContext; - XPathParser(final QNameModule implicitNamespace, final Function prefixes) { - qnameSupport = new QNameSupport(implicitNamespace, prefixes); + AntlrXPathParser(final YangXPathMathMode mathMode, final YangNamespaceContext namespaceContext) { + this.mathMode = requireNonNull(mathMode); + this.mathSupport = mathMode.getSupport(); + this.namespaceContext = requireNonNull(namespaceContext); } @Override @@ -159,7 +157,7 @@ abstract class XPathParser> implements YangXPathP throw errors.get(0); } - return new AntlrYangXPathExpression(qnameSupport, expr, xpath); + return new AntlrYangXPathExpression(namespaceContext, mathMode, expr, xpath); } /** @@ -183,24 +181,6 @@ abstract class XPathParser> implements YangXPathP return YangNaryOperator.OR.exprWith(tmp); } - /** - * Create a {@link YangNumberExpr} backed by specified string. - * - * @param str String, matching {@link xpathParser#Number} production. - * @return number expression - * @throws NullPointerException if {@code str} is null - */ - abstract N createNumber(String str); - - /** - * Create a {@link YangNumberExpr} representing the negated value of a number. - * - * @param number input number - * @return negated number expression - * @throws NullPointerException if {@code number} is null - */ - abstract N negateNumber(N number); - private YangExpr parseAdditive(final AdditiveExprContext expr) { final Iterator it = expr.children.iterator(); final YangExpr first = parseMultiplicative(nextContext(it, MultiplicativeExprContext.class)); @@ -243,7 +223,7 @@ abstract class XPathParser> implements YangXPathP parsed = QName.create(YangConstants.RFC6020_YIN_MODULE, name.getChild(0).getText()); break; case 3: - parsed = qnameSupport.createQName(name.getChild(0).getText(), name.getChild(2).getText()); + parsed = namespaceContext.createQName(name.getChild(0).getText(), name.getChild(2).getText()); break; default: throw illegalShape(name); @@ -385,114 +365,14 @@ abstract class XPathParser> implements YangXPathP switch (term.getSymbol().getType()) { case xpathParser.Literal: // We have to strip quotes - return parseLiteral(text.substring(1, text.length() - 1)); + return YangLiteralExpr.of(text.substring(1, text.length() - 1)); case xpathParser.Number: - return createNumber(text); + return mathSupport.createNumber(text); default: throw illegalShape(term); } } - private YangLiteralExpr parseLiteral(final String text) { - if (text.isEmpty()) { - return YangLiteralExpr.empty(); - } - if (text.charAt(0) == '/') { - return parseLocationLiteral(text); - } - return parseQNameLiteral(text); - } - - private YangLiteralExpr parseLocationLiteral(final String text) { - final xpathLexer lexer = new xpathLexer(CharStreams.fromString(text)); - final instanceIdentifierParser parser = new instanceIdentifierParser(new CommonTokenStream(lexer)); - lexer.removeErrorListeners(); - parser.removeErrorListeners(); - - // FIXME: add listeners - - final InstanceIdentifierContext id = parser.instanceIdentifier(); - final int length = id.getChildCount(); - final List steps = new ArrayList<>(length / 2); - for (int i = 1; i < length; i += 2) { - steps.add(parsePathArgument(getChild(id, PathArgumentContext.class, i))); - } - - return new InstanceIdentifierLiteralExpr(text, steps); - } - - private Step parsePathArgument(final PathArgumentContext expr) { - final QName qname = parseInstanceIdentifierQName(getChild(expr, NodeIdentifierContext.class, 0)); - switch (expr.getChildCount()) { - case 1: - return YangXPathAxis.CHILD.asStep(qname, ImmutableSet.of()); - case 2: - return YangXPathAxis.CHILD.asStep(qname, - parsePathArgumentPredicate(getChild(expr, instanceIdentifierParser.PredicateContext.class, 1))); - default: - throw illegalShape(expr); - } - } - - private Collection parsePathArgumentPredicate(final instanceIdentifierParser.PredicateContext expr) { - final ParseTree first = expr.getChild(0); - if (first instanceof LeafListPredicateContext) { - return ImmutableSet.of(YangBinaryOperator.EQUALS.exprWith(YangLocationPath.self(), - parseEqStringValue(getChild(((LeafListPredicateContext) first) - .getChild(LeafListPredicateExprContext.class, 0), EqQuotedStringContext.class, 1)))); - } else if (first instanceof PosContext) { - return ImmutableSet.of(YangBinaryOperator.EQUALS.exprWith(Functions.POSITION, - createNumber(((PosContext) first).getToken(instanceIdentifierParser.PositiveIntegerValue, 0) - .getText()))); - } - - final int length = expr.getChildCount(); - final List ret = new ArrayList<>(length); - for (int i = 0; i < length; ++i) { - final KeyPredicateExprContext pred = getChild(expr, KeyPredicateContext.class, i) - .getChild(KeyPredicateExprContext.class, 0); - ret.add(YangBinaryOperator.EQUALS.exprWith( - YangQNameExpr.of(parseInstanceIdentifierQName(getChild(pred, NodeIdentifierContext.class, 0))), - parseEqStringValue(getChild(pred, EqQuotedStringContext.class, 1)))); - - } - - return ret; - } - - private YangLiteralExpr parseEqStringValue(final EqQuotedStringContext expr) { - return parseLiteral(verifyToken(getChild(expr, QuotedStringContext.class, expr.getChildCount() - 1), 1, - instanceIdentifierParser.STRING).getText()); - } - - private QName parseInstanceIdentifierQName(final NodeIdentifierContext expr) { - return qnameSupport.createQName(verifyToken(expr, 0, instanceIdentifierParser.Identifier).getText(), - verifyToken(expr, 2, instanceIdentifierParser.Identifier).getText()); - } - - private YangLiteralExpr parseQNameLiteral(final String text) { - final int firstColon = text.indexOf(':'); - if (firstColon != -1) { - // If we have two colons this node cannot be interpreted as a QName -- this may explode at evaluation-time, - // but that's fine as it will just result in evaluation error. Users do have unit tests, right? - final int secondColon = text.indexOf(':', firstColon + 1); - if (secondColon == -1) { - final Optional optNamespace = qnameSupport.resolvePrefix(text.substring(0, firstColon)); - // If we cannot resolve the namespace at evaluation-time has to deal with it. - if (optNamespace.isPresent()) { - try { - return new QNameLiteralExpr(text, QName.create(optNamespace.get(), - text.substring(firstColon + 1))); - } catch (IllegalArgumentException e) { - LOG.trace("Cannot interpret {} as a QName", text, e); - return YangLiteralExpr.of(text); - } - } - } - } - return YangLiteralExpr.of(text); - } - private YangExpr parseUnary(final UnaryExprNoRootContext expr) { // any number of '-' and an union expr final int size = verifyAtLeastChildren(expr, 1); @@ -502,7 +382,8 @@ abstract class XPathParser> implements YangXPathP return ret; } - return ret instanceof YangNumberExpr ? negateNumber((N) ret) : YangNegateExpr.of(ret); + return ret instanceof YangNumberExpr ? mathSupport.negateNumber((YangNumberExpr) ret) + : YangNegateExpr.of(ret); } private YangExpr parseUnion(final UnionExprNoRootContext expr) { @@ -554,22 +435,11 @@ abstract class XPathParser> implements YangXPathP final YangExpr right) { if (left instanceof YangNumberExpr && right instanceof YangNumberExpr) { // Constant folding on numbers -- precision plays a role here - return simplifyNumbers(operator, (N) left, (N) right); + return mathSupport.tryEvaluate(operator, (YangNumberExpr)left, (YangNumberExpr)right); } return Optional.empty(); } - Optional simplifyNumbers(final YangBinaryOperator operator, final N left, final N right) { - switch (operator) { - case EQUALS: - return Optional.of(YangBooleanConstantExpr.of(left.getNumber().equals(right.getNumber()))); - case NOT_EQUALS: - return Optional.of(YangBooleanConstantExpr.of(!left.getNumber().equals(right.getNumber()))); - default: - return Optional.empty(); - } - } - private YangExpr parseEqualityExpr(final YangExpr left, final Iterator it) { YangExpr ret = left; do { @@ -610,9 +480,9 @@ abstract class XPathParser> implements YangXPathP private QName parseQName(final QNameContext expr) { switch (expr.getChildCount()) { case 1: - return qnameSupport.createQName(getChild(expr, NCNameContext.class, 0).getText()); + return namespaceContext.createQName(getChild(expr, NCNameContext.class, 0).getText()); case 3: - return qnameSupport.createQName(getChild(expr, NCNameContext.class, 0).getText(), + return namespaceContext.createQName(getChild(expr, NCNameContext.class, 0).getText(), getChild(expr, NCNameContext.class, 2).getText()); default: throw illegalShape(expr); @@ -683,18 +553,6 @@ abstract class XPathParser> implements YangXPathP return parseOperator(it.next()); } - private static T getChild(final ParseTree parent, final Class type, final int offset) { - return verifyTree(type, parent.getChild(offset)); - } - - private static Token verifyToken(final ParseTree parent, final int offset, final int expected) { - final TerminalNode node = verifyTerminal(parent.getChild(offset)); - final Token ret = node.getSymbol(); - final int type = ret.getType(); - verify(type == expected, "Item %s has type %s, expected %s", node, type, expected); - return ret; - } - private static int getTerminalType(final ParseTree parent, final int offset) { return verifyTerminal(parent.getChild(offset)).getSymbol().getType(); } @@ -708,36 +566,4 @@ abstract class XPathParser> implements YangXPathP final String str = verifyTerminal(tree).getText(); return verifyNotNull(BINARY_OPERATORS.get(str), "Unhandled operator %s", str); } - - private static void verifyChildCount(final ParseTree tree, final int expected) { - if (tree.getChildCount() != expected) { - throw illegalShape(tree); - } - } - - private static int verifyAtLeastChildren(final ParseTree tree, final int expected) { - final int count = tree.getChildCount(); - if (count < expected) { - throw illegalShape(tree); - } - return count; - } - - private static TerminalNode verifyTerminal(final ParseTree tree) { - if (tree instanceof TerminalNode) { - return (TerminalNode) tree; - } - throw new VerifyException(String.format("'%s' is not a terminal node", tree.getText())); - } - - private static T verifyTree(final Class type, final ParseTree tree) { - if (type.isInstance(tree)) { - return type.cast(tree); - } - throw new VerifyException(String.format("'%s' does not have expected type %s", tree.getText(), type)); - } - - private static VerifyException illegalShape(final ParseTree tree) { - return new VerifyException(String.format("Invalid parser shape of '%s'", tree.getText())); - } } diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrXPathParserFactory.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrXPathParserFactory.java new file mode 100644 index 0000000000..48e4f506a9 --- /dev/null +++ b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrXPathParserFactory.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018 Pantheon Technologies, 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.impl; + +import org.kohsuke.MetaInfServices; +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.YangXPathParserFactory; + +@MetaInfServices +public final class AntlrXPathParserFactory implements YangXPathParserFactory { + @Override + public YangXPathParser newParser(final YangNamespaceContext namespaceContext, + final YangXPathMathMode mathMode) { + return new AntlrXPathParser(mathMode, namespaceContext); + } +} diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrYangXPathExpression.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrYangXPathExpression.java index fa663d2ded..5d14aeaf7d 100644 --- a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrYangXPathExpression.java +++ b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/AntlrYangXPathExpression.java @@ -10,49 +10,46 @@ package org.opendaylight.yangtools.yang.xpath.impl; import static java.util.Objects.requireNonNull; import javax.xml.xpath.XPathExpressionException; -import org.opendaylight.yangtools.yang.common.QName; +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.YangXPathExpression; +import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathMode; final class AntlrYangXPathExpression implements YangXPathExpression { - private final QNameSupport qnameSupport; + private final YangNamespaceContext namespaceContext; + private final YangXPathMathMode mathMode; private final YangExpr rootExpr; private final String origStr; - AntlrYangXPathExpression(final QNameSupport qnameSupport, final YangExpr rootExpr, final String origStr) { - this.qnameSupport = requireNonNull(qnameSupport); + AntlrYangXPathExpression(final YangNamespaceContext namespaceContext, final YangXPathMathMode mathMode, + final YangExpr rootExpr, final String origStr) { + this.namespaceContext = requireNonNull(namespaceContext); + this.mathMode = requireNonNull(mathMode); this.rootExpr = requireNonNull(rootExpr); this.origStr = requireNonNull(origStr); } + @Override + public YangXPathMathMode getMathMode() { + return mathMode; + } + @Override public YangExpr getRootExpr() { return rootExpr; } @Override - public QName interpretAsQName(final YangLiteralExpr expr) throws XPathExpressionException { - // We are eagerly interpreting PrefixedName-compliant strings, hence they have a specific subclass - if (expr instanceof QNameLiteralExpr) { - return ((QNameLiteralExpr) expr).getQName(); - } - - try { - // Deal with UnprefixedNames by interpreting them in implicit namespace - return qnameSupport.createQName(expr.getLiteral()); - } catch (IllegalArgumentException e) { - throw new XPathExpressionException(e); - } + public YangQNameExpr interpretAsQName(final YangLiteralExpr expr) throws XPathExpressionException { + return LiteralExprUtils.interpretAsQName(namespaceContext, expr); } @Override public YangLocationPath interpretAsInstanceIdentifier(final YangLiteralExpr expr) throws XPathExpressionException { - if (expr instanceof InstanceIdentifierLiteralExpr) { - return YangLocationPath.of(true, ((InstanceIdentifierLiteralExpr)expr).getSteps()); - } - throw new XPathExpressionException("Invalid instance-identifier " + expr); + return new InstanceIdentifierParser(namespaceContext, mathMode).interpretAsInstanceIdentifier(expr); } @Override diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/BigDecimalXPathParser.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/BigDecimalXPathParser.java deleted file mode 100644 index 37a6131cbb..0000000000 --- a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/BigDecimalXPathParser.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2018 Pantheon Technologies, 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.impl; - -import java.math.BigDecimal; -import java.util.Optional; -import java.util.function.Function; -import org.opendaylight.yangtools.yang.common.QNameModule; -import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator; -import org.opendaylight.yangtools.yang.xpath.api.YangBooleanConstantExpr; -import org.opendaylight.yangtools.yang.xpath.api.YangExpr; -import org.opendaylight.yangtools.yang.xpath.api.YangNumberExpr.YangBigDecimal; - -final class BigDecimalXPathParser extends XPathParser { - private static final YangBigDecimal ZERO = YangBigDecimal.of(BigDecimal.ZERO); - private static final YangBigDecimal ONE = YangBigDecimal.of(BigDecimal.ONE); - private static final YangBigDecimal TEN = YangBigDecimal.of(BigDecimal.TEN); - - BigDecimalXPathParser(final QNameModule implicitNamespace, final Function prefixes) { - super(implicitNamespace, prefixes); - } - - @Override - YangBigDecimal createNumber(final String str) { - switch (str) { - case "0": - return ZERO; - case "1": - return ONE; - case "10": - return TEN; - default: - return YangBigDecimal.of(new BigDecimal(str)); - } - } - - @Override - YangBigDecimal negateNumber(final YangBigDecimal number) { - return YangBigDecimal.of(number.getNumber().negate()); - } - - @Override - Optional simplifyNumbers(final YangBinaryOperator operator, final YangBigDecimal left, - final YangBigDecimal right) { - final BigDecimal l = left.getNumber(); - final BigDecimal r = right.getNumber(); - - final BigDecimal result; - switch (operator) { - case DIV: - result = l.divide(r); - break; - case EQUALS: - return of(l.equals(r)); - case GT: - return of(l.compareTo(r) > 0); - case GTE: - return of(l.compareTo(r) >= 0); - case LT: - return of(l.compareTo(r) < 0); - case LTE: - return of(l.compareTo(r) <= 0); - case MINUS: - result = l.subtract(r); - break; - case MOD: - result = l.remainder(r); - break; - case MUL: - result = l.multiply(r); - break; - case NOT_EQUALS: - return of(!l.equals(r)); - case PLUS: - result = l.add(r); - break; - default: - throw new IllegalStateException("Unhandled operator " + operator); - } - - return Optional.of(YangBigDecimal.of(result)); - } - - private static Optional of(final boolean value) { - return Optional.of(YangBooleanConstantExpr.of(value)); - } -} diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/DoubleXPathParser.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/DoubleXPathParser.java deleted file mode 100644 index 40e459c6bb..0000000000 --- a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/DoubleXPathParser.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2018 Pantheon Technologies, 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.impl; - -import java.util.Optional; -import java.util.function.Function; -import org.opendaylight.yangtools.yang.common.QNameModule; -import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator; -import org.opendaylight.yangtools.yang.xpath.api.YangBooleanConstantExpr; -import org.opendaylight.yangtools.yang.xpath.api.YangExpr; -import org.opendaylight.yangtools.yang.xpath.api.YangNumberExpr.YangDouble; - -final class DoubleXPathParser extends XPathParser { - - DoubleXPathParser(final QNameModule implicitNamespace, final Function prefixes) { - super(implicitNamespace, prefixes); - } - - @Override - YangDouble createNumber(final String str) { - return YangDouble.of(Double.parseDouble(str)); - } - - @Override - YangDouble negateNumber(final YangDouble number) { - return YangDouble.of(-number.getValue()); - } - - @Override - Optional simplifyNumbers(final YangBinaryOperator operator, final YangDouble left, - final YangDouble right) { - final double l = left.getValue(); - final double r = right.getValue(); - - final double result; - switch (operator) { - case DIV: - result = l / r; - break; - case EQUALS: - return of(l == r); - case GT: - return of(l > r); - case GTE: - return of(l >= r); - case LT: - return of(l < r); - case LTE: - return of(l <= r); - case MINUS: - result = l - r; - break; - case MOD: - result = l % r; - break; - case MUL: - result = l * r; - break; - case NOT_EQUALS: - return of(l != r); - case PLUS: - result = l + r; - break; - default: - throw new IllegalStateException("Unhandled operator " + operator); - } - - return Optional.of(YangDouble.of(result)); - } - - private static Optional of(final boolean value) { - return Optional.of(YangBooleanConstantExpr.of(value)); - } -} diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/InstanceIdentifierLiteralExpr.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/InstanceIdentifierLiteralExpr.java deleted file mode 100644 index d9362861e6..0000000000 --- a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/InstanceIdentifierLiteralExpr.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2018 Pantheon Technologies, 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.impl; - -import com.google.common.collect.ImmutableList; -import java.util.List; -import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr; -import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step; - -final class InstanceIdentifierLiteralExpr extends YangLiteralExpr { - private static final long serialVersionUID = 1L; - - private final List steps; - - InstanceIdentifierLiteralExpr(final String str, final List steps) { - super(str); - this.steps = ImmutableList.copyOf(steps); - } - - List getSteps() { - return steps; - } -} diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/InstanceIdentifierParser.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/InstanceIdentifierParser.java new file mode 100644 index 0000000000..cc6b3d8d05 --- /dev/null +++ b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/InstanceIdentifierParser.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2019 Pantheon Technologies, 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.impl; + +import static java.util.Objects.requireNonNull; +import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.getChild; +import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.illegalShape; +import static org.opendaylight.yangtools.yang.xpath.impl.ParseTreeUtils.verifyToken; + +import com.google.common.collect.ImmutableSet; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +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.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.Step; +import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr; +import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis; +import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathMode; +import org.opendaylight.yangtools.yang.xpath.api.YangXPathMathSupport; +import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.EqQuotedStringContext; +import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.InstanceIdentifierContext; +import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.KeyPredicateContext; +import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.KeyPredicateExprContext; +import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.LeafListPredicateContext; +import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.LeafListPredicateExprContext; +import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.NodeIdentifierContext; +import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.PathArgumentContext; +import org.opendaylight.yangtools.yang.xpath.impl.instanceIdentifierParser.PosContext; +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; + private final YangXPathMathSupport mathSupport; + + InstanceIdentifierParser(final YangNamespaceContext namespaceContext, final YangXPathMathMode mathMode) { + this.namespaceContext = requireNonNull(namespaceContext); + this.mathSupport = mathMode.getSupport(); + } + + YangLocationPath interpretAsInstanceIdentifier(final YangLiteralExpr expr) throws XPathExpressionException { + final xpathLexer lexer = new xpathLexer(CharStreams.fromString(expr.getLiteral())); + final instanceIdentifierParser parser = new instanceIdentifierParser(new CommonTokenStream(lexer)); + lexer.removeErrorListeners(); + parser.removeErrorListeners(); + + // FIXME: add listeners + + final InstanceIdentifierContext id = parser.instanceIdentifier(); + final int length = id.getChildCount(); + final List steps = new ArrayList<>(length / 2); + for (int i = 1; i < length; i += 2) { + steps.add(parsePathArgument(getChild(id, PathArgumentContext.class, i))); + } + + return YangLocationPath.of(true, steps); + } + + private Step parsePathArgument(final PathArgumentContext expr) { + final QName qname = parseQName(getChild(expr, NodeIdentifierContext.class, 0)); + switch (expr.getChildCount()) { + case 1: + return YangXPathAxis.CHILD.asStep(qname, ImmutableSet.of()); + case 2: + return YangXPathAxis.CHILD.asStep(qname, parsePredicate(getChild(expr, PredicateContext.class, 1))); + default: + throw illegalShape(expr); + } + } + + private Collection parsePredicate(final PredicateContext expr) { + final ParseTree first = expr.getChild(0); + if (first instanceof LeafListPredicateContext) { + return ImmutableSet.of(YangBinaryOperator.EQUALS.exprWith(YangLocationPath.self(), + parseEqStringValue(getChild(((LeafListPredicateContext) first) + .getChild(LeafListPredicateExprContext.class, 0), EqQuotedStringContext.class, 1)))); + } else if (first instanceof PosContext) { + return ImmutableSet.of(YangBinaryOperator.EQUALS.exprWith(Functions.POSITION, mathSupport.createNumber( + ((PosContext) first).getToken(instanceIdentifierParser.PositiveIntegerValue, 0).getText()))); + } + + final int length = expr.getChildCount(); + final List ret = new ArrayList<>(length); + for (int i = 0; i < length; ++i) { + 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))), + parseEqStringValue(getChild(pred, EqQuotedStringContext.class, 1)))); + + } + + return ret; + } + + private QName parseQName(final NodeIdentifierContext expr) { + return namespaceContext.createQName( + verifyToken(expr, 0, instanceIdentifierParser.Identifier).getText(), + verifyToken(expr, 2, instanceIdentifierParser.Identifier).getText()); + } + + private static YangLiteralExpr parseEqStringValue(final EqQuotedStringContext expr) { + return YangLiteralExpr.of(verifyToken(getChild(expr, QuotedStringContext.class, expr.getChildCount() - 1), 1, + instanceIdentifierParser.STRING).getText()); + } +} diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/LiteralExprUtils.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/LiteralExprUtils.java new file mode 100644 index 0000000000..2d0ed0c906 --- /dev/null +++ b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/LiteralExprUtils.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Pantheon Technologies, 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.impl; + +import javax.xml.xpath.XPathExpressionException; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.YangNamespaceContext; +import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr; +import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath; +import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr; + +/** + * Utilities for interpreting {@link YangLiteralExpr}s as {@link YangQNameExpr}s and {@link YangLocationPath}s. + */ +final class LiteralExprUtils { + private LiteralExprUtils() { + + } + + 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 new XPathExpressionException(e); + } + } else { + try { + // Deal with UnprefixedNames by interpreting them in implicit namespace + qname = namespaceContext.createQName(expr.getLiteral()); + } catch (IllegalArgumentException | IllegalStateException e) { + throw new XPathExpressionException(e); + } + } + + return YangQNameExpr.of(qname); + } +} diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/ParseTreeUtils.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/ParseTreeUtils.java new file mode 100644 index 0000000000..8e6c035899 --- /dev/null +++ b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/ParseTreeUtils.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019 Pantheon Technologies, 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.impl; + +import static com.google.common.base.Verify.verify; + +import com.google.common.base.VerifyException; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.TerminalNode; + +/** + * Utility methods for dealing with {@link ParseTree}s. + */ +final class ParseTreeUtils { + private ParseTreeUtils() { + + } + + static T getChild(final ParseTree parent, final Class type, final int offset) { + return verifyTree(type, parent.getChild(offset)); + } + + static void verifyChildCount(final ParseTree tree, final int expected) { + if (tree.getChildCount() != expected) { + throw illegalShape(tree); + } + } + + static int verifyAtLeastChildren(final ParseTree tree, final int expected) { + final int count = tree.getChildCount(); + if (count < expected) { + throw illegalShape(tree); + } + return count; + } + + static TerminalNode verifyTerminal(final ParseTree tree) { + if (tree instanceof TerminalNode) { + return (TerminalNode) tree; + } + throw new VerifyException(String.format("'%s' is not a terminal node", tree.getText())); + } + + static Token verifyToken(final ParseTree parent, final int offset, final int expected) { + final TerminalNode node = verifyTerminal(parent.getChild(offset)); + final Token ret = node.getSymbol(); + final int type = ret.getType(); + verify(type == expected, "Item %s has type %s, expected %s", node, type, expected); + return ret; + } + + static T verifyTree(final Class type, final ParseTree tree) { + if (type.isInstance(tree)) { + return type.cast(tree); + } + throw new VerifyException(String.format("'%s' does not have expected type %s", tree.getText(), type)); + } + + static VerifyException illegalShape(final ParseTree tree) { + return new VerifyException(String.format("Invalid parser shape of '%s'", tree.getText())); + } +} diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/QNameLiteralExpr.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/QNameLiteralExpr.java deleted file mode 100644 index 6f8d9a2b0e..0000000000 --- a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/QNameLiteralExpr.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2018 Pantheon Technologies, 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.impl; - -import static com.google.common.base.Preconditions.checkArgument; -import static java.util.Objects.requireNonNull; - -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.xpath.api.YangLiteralExpr; - -/** - * Eagerly-bound literal interpreted a PrefixName. - * - * @author Robert Varga - */ -final class QNameLiteralExpr extends YangLiteralExpr { - private static final long serialVersionUID = 1L; - - private final QName qname; - - QNameLiteralExpr(final String str, final QName qname) { - super(str); - checkArgument(str.endsWith(qname.getLocalName())); - this.qname = requireNonNull(qname); - } - - QName getQName() { - return qname; - } -} diff --git a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/QNameSupport.java b/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/QNameSupport.java deleted file mode 100644 index 68a2f8dfcb..0000000000 --- a/yang/yang-xpath-impl/src/main/java/org/opendaylight/yangtools/yang/xpath/impl/QNameSupport.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2018 Pantheon Technologies, 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.impl; - -import static com.google.common.base.Preconditions.checkArgument; -import static java.util.Objects.requireNonNull; - -import java.util.Optional; -import java.util.function.Function; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.common.QNameModule; - -final class QNameSupport { - private final Function prefixes; - private final QNameModule implicitNamespace; - - QNameSupport(final QNameModule implicitNamespace, final Function prefixes) { - this.implicitNamespace = requireNonNull(implicitNamespace); - this.prefixes = requireNonNull(prefixes); - } - - QName createQName(final String localName) { - return QName.create(implicitNamespace, localName); - } - - QName createQName(final String prefix, final String localName) { - final QNameModule namespace = prefixes.apply(prefix); - checkArgument(namespace != null, "Failed to lookup namespace for prefix %s", prefix); - return QName.create(namespace, localName); - } - - Optional resolvePrefix(final String prefix) { - return Optional.ofNullable(prefixes.apply(requireNonNull(prefix))); - } -} diff --git a/yang/yang-xpath-impl/src/test/java/org/opendaylight/yangtools/yang/xpath/impl/XPathParserTest.java b/yang/yang-xpath-impl/src/test/java/org/opendaylight/yangtools/yang/xpath/impl/XPathParserTest.java index 2a38822356..3fe300bd0f 100644 --- a/yang/yang-xpath-impl/src/test/java/org/opendaylight/yangtools/yang/xpath/impl/XPathParserTest.java +++ b/yang/yang-xpath-impl/src/test/java/org/opendaylight/yangtools/yang/xpath/impl/XPathParserTest.java @@ -9,29 +9,32 @@ package org.opendaylight.yangtools.yang.xpath.impl; import static org.junit.Assert.assertEquals; -import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableBiMap; import java.net.URI; -import java.util.Map; import javax.xml.xpath.XPathExpressionException; import org.eclipse.jdt.annotation.Nullable; import org.junit.Before; import org.junit.Test; +import org.opendaylight.yangtools.yang.common.BiMapYangNamespaceContext; import org.opendaylight.yangtools.yang.common.QNameModule; +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.YangXPathMathMode; @SuppressWarnings("null") public class XPathParserTest { - private static final QNameModule DEF_NS = QNameModule.create(URI.create("defaultns")); - private static final Map NAMESPACES = ImmutableMap.of( + private static final QNameModule DEFNS = QNameModule.create(URI.create("defaultns")); + private static final YangNamespaceContext CONTEXT = new BiMapYangNamespaceContext(ImmutableBiMap.of( + "def", DEFNS, "foo", QNameModule.create(URI.create("foo")), - "bar", QNameModule.create(URI.create("bar"))); + "bar", QNameModule.create(URI.create("bar"))), DEFNS); - private @Nullable XPathParser parser; + private @Nullable AntlrXPathParser parser; @Before public void before() { - parser = new BigDecimalXPathParser(DEF_NS, NAMESPACES::get); + parser = new AntlrXPathParser(YangXPathMathMode.IEEE754, CONTEXT); } @Test -- 2.36.6