*/
package org.opendaylight.yangtools.yang.data.impl.leafref;
-import org.antlr.v4.runtime.CharStreams;
-import org.antlr.v4.runtime.CommonTokenStream;
-import org.antlr.v4.runtime.tree.ParseTreeWalker;
-import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefPathParser.Path_argContext;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.AbstractQName;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
+import org.opendaylight.yangtools.yang.model.api.PathExpression;
+import org.opendaylight.yangtools.yang.model.api.PathExpression.DerefSteps;
+import org.opendaylight.yangtools.yang.model.api.PathExpression.LocationPathSteps;
+import org.opendaylight.yangtools.yang.model.api.PathExpression.Steps;
+import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.xpath.api.YangBinaryExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator;
+import org.opendaylight.yangtools.yang.xpath.api.YangExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangFunction;
+import org.opendaylight.yangtools.yang.xpath.api.YangFunctionCallExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.QNameStep;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Relative;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath.Step;
+import org.opendaylight.yangtools.yang.xpath.api.YangPathExpr;
+import org.opendaylight.yangtools.yang.xpath.api.YangQNameExpr;
final class LeafRefPathParserImpl {
- private final SchemaContext schemaContext;
- private final Module module;
- private final SchemaNode node;
-
- LeafRefPathParserImpl(final SchemaContext schemaContext, final Module currentModule, final SchemaNode currentNode) {
- this.schemaContext = schemaContext;
- this.module = currentModule;
- this.node = currentNode;
+ private final QNameModule leafrefModule;
+ private final QNameModule nodeModule;
+
+ LeafRefPathParserImpl(final LeafrefTypeDefinition leafrefType, final TypedDataSchemaNode currentNode) {
+ // FIXME: these two namespaces look not quite right:
+ // - leafrefModule is used for absolute paths, irrespective of where they occur
+ // - nodeModule is used for relative paths, irrespective of where they occur
+ //
+ // There is little in RFC7950 which would hint at such a distinction and if even if it were true, it would be
+ // the job of YANG parser to ensure that absolute paths are bound during parsing.
+ //
+ // The only distinction is relative to where the leafref is defined, namely:
+ //
+ // 1) as per section 9.9.2:
+ // o If the "path" statement is defined within a typedef, the context
+ // node is the leaf or leaf-list node in the data tree that
+ // references the typedef.
+ //
+ // o Otherwise, the context node is the node in the data tree for which
+ // the "path" statement is defined.
+ //
+ // 2) as per section 6.4.1:
+ // o Names without a namespace prefix belong to the same namespace as
+ // the identifier of the current node. Inside a grouping, that
+ // namespace is affected by where the grouping is used (see
+ // Section 7.13). Inside a typedef, that namespace is affected by
+ // where the typedef is referenced. If a typedef is defined and
+ // referenced within a grouping, the namespace is affected by where
+ // the grouping is used (see Section 7.13).
+ this.leafrefModule = getBaseModule(leafrefType);
+ this.nodeModule = currentNode.getQName().getModule();
}
- LeafRefPath parseLeafRefPath(final String path) throws LeafRefYangSyntaxErrorException {
- final Path_argContext pathCtx = parseLeafRefPathSource(path);
+ LeafRefPath parseLeafRefPath(final PathExpression path) {
+ final Steps steps = path.getSteps();
+ if (steps instanceof LocationPathSteps) {
+ return parseLocationPath(((LocationPathSteps) steps).getLocationPath());
+ } else if (steps instanceof DerefSteps) {
+ throw new UnsupportedOperationException("deref() leafrefs are not implemented yet");
+ } else {
+ throw new IllegalStateException("Unsupported steps " + steps);
+ }
+ }
- final ParseTreeWalker walker = new ParseTreeWalker();
- final LeafRefPathParserListenerImpl leafRefPathParserListenerImpl = new LeafRefPathParserListenerImpl(
- schemaContext, module, node);
- walker.walk(leafRefPathParserListenerImpl, pathCtx);
+ private LeafRefPath parseLocationPath(final YangLocationPath locationPath) {
+ return LeafRefPath.create(
+ createPathSteps(locationPath.isAbsolute() ? leafrefModule : nodeModule, locationPath.getSteps()),
+ locationPath.isAbsolute());
+ }
- return leafRefPathParserListenerImpl.getLeafRefPath();
+ private static Deque<QNameWithPredicate> createPathSteps(final QNameModule localModule,
+ final ImmutableList<Step> steps) {
+ final Deque<QNameWithPredicate> path = new ArrayDeque<>(steps.size());
+ for (Step step : steps) {
+ switch (step.getAxis()) {
+ case CHILD:
+ checkState(step instanceof QNameStep, "Unsupported step %s", step);
+ path.add(adaptChildStep((QNameStep) step, localModule));
+ break;
+ case PARENT:
+ path.add(QNameWithPredicate.UP_PARENT);
+ break;
+ default:
+ throw new IllegalStateException("Unsupported axis in step " + step);
+ }
+ }
+ return path;
}
- private Path_argContext parseLeafRefPathSource(final String path) throws LeafRefYangSyntaxErrorException {
- final LeafRefPathLexer lexer = new LeafRefPathLexer(CharStreams.fromString(path));
- final LeafRefPathParser parser = new LeafRefPathParser(new CommonTokenStream(lexer));
+ private static QNameWithPredicate adaptChildStep(final QNameStep step, final QNameModule localModule) {
+ final QName qname = resolve(step.getQName(), localModule);
+ final Set<YangExpr> predicates = step.getPredicates();
+ if (predicates.isEmpty()) {
+ return new SimpleQNameWithPredicate(qname);
+ }
+
+ final QNameWithPredicateBuilder builder = new QNameWithPredicateBuilder(qname.getModule(),
+ qname.getLocalName());
+
+ for (YangExpr pred : predicates) {
+ final QNamePredicateBuilder predBuilder = new QNamePredicateBuilder();
+
+ if (pred instanceof YangBinaryExpr) {
+ final YangBinaryExpr eqPred = (YangBinaryExpr) pred;
+ checkState(eqPred.getOperator() == YangBinaryOperator.EQUALS);
- final LeafRefPathErrorListener errorListener = new LeafRefPathErrorListener(module);
- lexer.removeErrorListeners();
- lexer.addErrorListener(errorListener);
- parser.removeErrorListeners();
- parser.addErrorListener(errorListener);
+ final YangExpr left = eqPred.getLeftExpr();
+ checkState(left instanceof YangQNameExpr, "Unsupported left expression %s", left);
+ predBuilder.setIdentifier(resolve(((YangQNameExpr) left).getQName(), localModule));
+
+ final YangExpr right = eqPred.getRightExpr();
+ if (right instanceof YangPathExpr) {
+ final YangPathExpr rightPath = (YangPathExpr) right;
+ final YangExpr filter = rightPath.getFilterExpr();
+ if (filter instanceof YangFunctionCallExpr) {
+ checkState(YangFunction.CURRENT.getIdentifier().equals(
+ ((YangFunctionCallExpr) filter).getName()));
+ } else {
+ throw new IllegalStateException("Unhandled filter " + filter);
+ }
+
+ final Relative location = rightPath.getLocationPath()
+ .orElseThrow(() -> new IllegalStateException("Missing locationPath in " + rightPath));
+ predBuilder.setPathKeyExpression(LeafRefPath.create(
+ createPathSteps(localModule, location.getSteps()), false));
+ } else {
+ throw new UnsupportedOperationException("Not implemented for " + right);
+ }
+ }
+
+ builder.addQNamePredicate(predBuilder.build());
+ }
+
+ return builder.build();
+ }
+
+ private static QName resolve(final AbstractQName qname, final QNameModule localModule) {
+ if (qname instanceof QName) {
+ return (QName) qname;
+ } else if (qname instanceof UnqualifiedQName) {
+ // Bind to namespace. Note we expect to perform frequent matching, hence we are interning the result
+ return ((UnqualifiedQName) qname).bindTo(localModule).intern();
+ } else {
+ throw new IllegalStateException("Unhandled unresolved QName " + qname);
+ }
+ }
- final Path_argContext result = parser.path_arg();
- errorListener.validate();
- return result;
+ /**
+ * Find the first definition of supplied leafref type and return the module which contains this definition.
+ */
+ private static QNameModule getBaseModule(final LeafrefTypeDefinition leafrefType) {
+ LeafrefTypeDefinition current = leafrefType;
+ while (true) {
+ final LeafrefTypeDefinition base = current.getBaseType();
+ if (base == null) {
+ return current.getQName().getModule();
+ }
+ current = base;
+ }
}
}