--- /dev/null
+/*
+ * 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<N extends YangNumberExpr<N, ?>> implements YangXPathMathSupport<N> {
+ private final Class<N> numberClass;
+
+ AbstractYangXPathMathSupport(final Class<N> 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<YangExpr> 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);
+}
--- /dev/null
+/*
+ * 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<BigDecimalNumberExpr, BigDecimal> {
+ 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
--- /dev/null
+/*
+ * 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<BigDecimalNumberExpr> {
+ 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);
+ }
+}
--- /dev/null
+/*
+ * 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<DoubleNumberExpr, Double> {
+ 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
--- /dev/null
+/*
+ * 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<DoubleNumberExpr> {
+ 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);
+ }
+}
private static final class WithPredicates extends YangFilterExpr {
private static final long serialVersionUID = 1L;
- private final Set<YangExpr> predicates;
+ private final ImmutableSet<YangExpr> predicates;
- WithPredicates(final YangExpr expr, final Set<YangExpr> predicates) {
+ WithPredicates(final YangExpr expr, final ImmutableSet<YangExpr> predicates) {
super(expr);
this.predicates = requireNonNull(predicates);
}
return expr;
}
- @Override
- public Set<YangExpr> getPredicates() {
- return ImmutableSet.of();
- }
-
@Override
public final int hashCode() {
return Objects.hash(expr, getPredicates());
* @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<YangExpr> arguments;
+ private final ImmutableList<YangExpr> arguments;
WithArgs(final QName name, final List<YangExpr> arguments) {
super(name);
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<YangExpr> arguments) {
return arguments.isEmpty() ? of(name) : new WithArgs(name, arguments);
}
- public abstract QName getName();
+ public final QName getName() {
+ return name;
+ }
public List<YangExpr> getArguments() {
return ImmutableList.of();
* @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;
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;
+ }
}
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<YangExpr> expressions;
+ private final YangNaryOperator operator;
+ private final ImmutableSet<YangExpr> expressions;
- YangNaryExpr(final Set<YangExpr> expressions) {
+ YangNaryExpr(final YangNaryOperator operator, final ImmutableSet<YangExpr> expressions) {
+ this.operator = requireNonNull(operator);
this.expressions = requireNonNull(expressions);
}
- public final Set<YangExpr> getExpressions() {
+ public Set<YangExpr> 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;
}
}
@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();
}
}
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.
*/
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<YangExpr> exprs) {
- super(exprs);
- }
-
- @Override
- public YangNaryOperator getOperator() {
- return YangNaryOperator.this;
- }
- }
-
private final String str;
YangNaryOperator(final String str) {
}
public YangExpr exprWith(final Collection<YangExpr> exprs) {
- final Set<YangExpr> set = ImmutableSet.copyOf(exprs);
- return set.size() == 1 ? set.iterator().next() : new Expr(set);
+ final ImmutableSet<YangExpr> set = ImmutableSet.copyOf(exprs);
+ return set.size() == 1 ? set.iterator().next() : new YangNaryExpr(this, set);
}
}
*/
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<T extends YangNumberExpr<T, N>, N extends Number> implements YangExpr {
- public static final class YangBigDecimal extends YangNumberExpr<YangBigDecimal, BigDecimal> {
- 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<YangDouble, Double> {
- 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<T> getSupport();
}
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
* 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;
}
--- /dev/null
+/*
+ * 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
+ * <a href="https://en.wikipedia.org/wiki/Constant_folding">constant folding</a> 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;
+ }
+}
--- /dev/null
+/*
+ * 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<N extends YangNumberExpr<N, ?>> {
+ /**
+ * 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<YangExpr> tryEvaluate(YangBinaryOperator operator, YangNumberExpr<?, ?> left, YangNumberExpr<?, ?> right);
+}
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;
}
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.
@ThreadSafe
public interface YangXPathParserFactory {
/**
- * {@link YangXPathParser} number compliance knobs. This enumeration defines what assumptions the parser can make --
- * affecting its optimization properties around
- * <a href="https://en.wikipedia.org/wiki/Constant_folding">constant folding</a> 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<MathMode> 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<String, QNameModule> 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<String, QNameModule> prefixResolver, MathMode mathMode);
+ YangXPathParser newParser(YangNamespaceContext namespaceContext, YangXPathMathMode mathMode);
}
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.kohsuke.metainf-services</groupId>
+ <artifactId>metainf-services</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
</dependencies>
<build>
<listener>false</listener>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Include-Resource>{META-INF/services=${project.build.directory}/classes/META-INF/services}</Include-Resource>
+ <Bundle-Activator>org.opendaylight.yangtools.yang.xpath.impl.Activator</Bundle-Activator>
+ </instructions>
+ </configuration>
+ </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
--- /dev/null
+/*
+ * 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<YangXPathParserFactory> 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;
+ }
+ }
+}
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;
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;
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;
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;
*
* @author Robert Varga
*/
-abstract class XPathParser<N extends YangNumberExpr<N, ?>> 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<String, YangBinaryOperator> BINARY_OPERATORS = Maps.uniqueIndex(
Arrays.asList(YangBinaryOperator.values()), YangBinaryOperator::toString);
private static final Map<String, YangXPathNodeType> NODE_TYPES = Maps.uniqueIndex(Arrays.asList(
// 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<String, QNameModule> 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
throw errors.get(0);
}
- return new AntlrYangXPathExpression(qnameSupport, expr, xpath);
+ return new AntlrYangXPathExpression(namespaceContext, mathMode, expr, xpath);
}
/**
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<ParseTree> it = expr.children.iterator();
final YangExpr first = parseMultiplicative(nextContext(it, MultiplicativeExprContext.class));
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);
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<Step> 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<YangExpr> 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<YangExpr> 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<QNameModule> 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);
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) {
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<YangExpr> 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<ParseTree> it) {
YangExpr ret = left;
do {
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);
return parseOperator(it.next());
}
- private static <T extends ParseTree> T getChild(final ParseTree parent, final Class<T> 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();
}
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 extends ParseTree> T verifyTree(final Class<T> 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()));
- }
}
--- /dev/null
+/*
+ * 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);
+ }
+}
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
+++ /dev/null
-/*
- * 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<YangBigDecimal> {
- 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<String, QNameModule> 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<YangExpr> 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<YangExpr> of(final boolean value) {
- return Optional.of(YangBooleanConstantExpr.of(value));
- }
-}
+++ /dev/null
-/*
- * 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<YangDouble> {
-
- DoubleXPathParser(final QNameModule implicitNamespace, final Function<String, QNameModule> 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<YangExpr> 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<YangExpr> of(final boolean value) {
- return Optional.of(YangBooleanConstantExpr.of(value));
- }
-}
+++ /dev/null
-/*
- * 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<Step> steps;
-
- InstanceIdentifierLiteralExpr(final String str, final List<Step> steps) {
- super(str);
- this.steps = ImmutableList.copyOf(steps);
- }
-
- List<Step> getSteps() {
- return steps;
- }
-}
--- /dev/null
+/*
+ * 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<Step> 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<YangExpr> 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<YangExpr> 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());
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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 extends ParseTree> T getChild(final ParseTree parent, final Class<T> 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 extends ParseTree> T verifyTree(final Class<T> 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()));
+ }
+}
+++ /dev/null
-/*
- * 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;
- }
-}
+++ /dev/null
-/*
- * 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<String, QNameModule> prefixes;
- private final QNameModule implicitNamespace;
-
- QNameSupport(final QNameModule implicitNamespace, final Function<String, QNameModule> 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<QNameModule> resolvePrefix(final String prefix) {
- return Optional.ofNullable(prefixes.apply(requireNonNull(prefix)));
- }
-}
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<String, QNameModule> 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