Allow Errata 5617 expressions in leafref 58/86558/1
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 24 Jun 2019 16:01:16 +0000 (18:01 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 24 Dec 2019 20:00:46 +0000 (21:00 +0100)
This updates PathExpression interface to allow expressing
leafref paths which start with a deref(...) function call,
as proposed by https://www.rfc-editor.org/errata/eid5617.

This unfortunately necessitates breaking the API contract of this
interface, as the original object model could not express these.

JIRA: YANGTOOLS-968
Change-Id: I3a193d338de5a82b95d2dd6cc5200e77032e67d9
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
Signed-off-by: Peter Suna <peter.suna@pantheon.tech>
(cherry picked from commit 2788395b6edecb26c9c2e59b2ab94275e8276987)

yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/PathExpression.java
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/PathExpressionImpl.java
yang/yang-parser-rfc7950/src/main/antlr/org/opendaylight/yangtools/antlrv4/code/gen/LeafRefPathLexer.g4
yang/yang-parser-rfc7950/src/main/antlr/org/opendaylight/yangtools/antlrv4/code/gen/LeafRefPathParser.g4
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/ParsedPathExpression.java
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathExpressionParser.java
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/UnparsedPathExpression.java
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/parser/rfc7950/stmt/path/PathExpressionParserTest.java
yang/yang-parser-rfc7950/src/test/java/org/opendaylight/yangtools/yang/stmt/EffectiveStatementTypeTest.java
yang/yang-parser-rfc7950/src/test/resources/type-tests/types.yang
yang/yang-xpath-api/src/main/java/org/opendaylight/yangtools/yang/xpath/api/YangFunctionCallExpr.java

index fef07e744dc8efede4cf45a38d2e5005855fdbbb..5673a241172bbb3b24a551a5d1fbaa3804306dad 100644 (file)
@@ -7,13 +7,20 @@
  */
 package org.opendaylight.yangtools.yang.model.api;
 
+import static java.util.Objects.requireNonNull;
+
 import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
 import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.yang.xpath.api.YangBinaryOperator;
 import org.opendaylight.yangtools.yang.xpath.api.YangFunction;
 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.YangPathExpr;
 import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
 import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression;
 
@@ -39,6 +46,103 @@ import org.opendaylight.yangtools.yang.xpath.api.YangXPathExpression;
 @Beta
 @NonNullByDefault
 public interface PathExpression extends Immutable {
+    /**
+     * Abstract base class for expressing steps of a PathExpression.
+     */
+    abstract class Steps {
+        Steps() {
+            // Prevent external subclassing
+        }
+
+        @Override
+        public abstract int hashCode();
+
+        @Override
+        public abstract boolean equals(@Nullable Object obj);
+
+        @Override
+        public final String toString() {
+            return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+        }
+
+        abstract ToStringHelper addToStringAttributes(ToStringHelper helper);
+    }
+
+    /**
+     * Steps of a PathExpression which is a LocationPath, corresponding to RFC7950 base specification.
+     */
+    final class LocationPathSteps extends Steps {
+        private final YangLocationPath locationPath;
+
+        public LocationPathSteps(final YangLocationPath locationPath) {
+            this.locationPath = requireNonNull(locationPath);
+        }
+
+        public YangLocationPath getLocationPath() {
+            return locationPath;
+        }
+
+        @Override
+        public int hashCode() {
+            return locationPath.hashCode();
+        }
+
+        @Override
+        public boolean equals(final @Nullable Object obj) {
+            return this == obj
+                    || obj instanceof LocationPathSteps && locationPath.equals(((LocationPathSteps) obj).locationPath);
+        }
+
+        @Override
+        ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+            return helper.add("locationPath", locationPath);
+        }
+    }
+
+    /**
+     * Steps of a PathExpression which is a combination of {@code deref()} function call and a relative path,
+     * corresponding to Errata 5617.
+     */
+    final class DerefSteps extends Steps {
+        private final Relative derefArgument;
+        private final Relative relativePath;
+
+        public DerefSteps(final Relative derefArgument, final Relative relativePath) {
+            this.derefArgument = requireNonNull(derefArgument);
+            this.relativePath = requireNonNull(relativePath);
+        }
+
+        public Relative getDerefArgument() {
+            return derefArgument;
+        }
+
+        public Relative getRelativePath() {
+            return relativePath;
+        }
+
+        @Override
+        public int hashCode() {
+            return 31 * derefArgument.hashCode() + relativePath.hashCode();
+        }
+
+        @Override
+        public boolean equals(@Nullable final Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof DerefSteps)) {
+                return false;
+            }
+            final DerefSteps other = (DerefSteps) obj;
+            return derefArgument.equals(other.derefArgument) && relativePath.equals(other.relativePath);
+        }
+
+        @Override
+        ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+            return helper.add("derefArgument", derefArgument).add("relativePath", relativePath);
+        }
+    }
+
     /**
      * Returns the path expression formatted string as is defined in model. For example:
      * {@code /prefix:container/prefix:container::cond[when()=foo]/prefix:leaf}
@@ -48,13 +152,14 @@ public interface PathExpression extends Immutable {
     String getOriginalString();
 
     /**
-     * Return the {@link YangLocationPath} of this expression.
+     * Return the path of this expression, which can either be a {@link YangLocationPath} (compliant to RFC7950) or
+     * a {@link YangPathExpr} with filter being an invocation of {@link YangFunction#DEREF}.
      *
-     * @return The location path
+     * @return The path's steps
      * @throws UnsupportedOperationException if the implementation has not parsed the string. Implementations are
      *         strongly encouraged to perform proper parsing.
      */
-    YangLocationPath getLocation();
+    Steps getSteps();
 
     /**
      * Returns <code>true</code> if the XPapth starts in root of YANG model, otherwise returns <code>false</code>.
@@ -62,6 +167,7 @@ public interface PathExpression extends Immutable {
      * @return <code>true</code> if the XPapth starts in root of YANG model, otherwise returns <code>false</code>
      */
     default boolean isAbsolute() {
-        return getLocation().isAbsolute();
+        final Steps steps = getSteps();
+        return steps instanceof LocationPathSteps && ((LocationPathSteps) steps).getLocationPath().isAbsolute();
     }
 }
index ca35335b2d619c1af1914ca48aa826cf30d19153..70782bdd1ed168d6264847fe72ae896954d33dce 100644 (file)
@@ -7,11 +7,12 @@
  */
 package org.opendaylight.yangtools.yang.model.util;
 
+import static java.util.Objects.requireNonNull;
+
 import com.google.common.base.MoreObjects.ToStringHelper;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
 
 /**
  * A simple XPathExpression implementation.
@@ -22,20 +23,21 @@ import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
 @Deprecated
 @NonNullByDefault
 public final class PathExpressionImpl extends AbstractPathExpression {
-    private final @Nullable YangLocationPath location;
+    private final @Nullable Steps steps;
     private final boolean absolute;
 
     @SuppressFBWarnings(value = "NP_STORE_INTO_NONNULL_FIELD", justification = "Non-grok on SpotBugs part")
     public PathExpressionImpl(final String xpath, final boolean absolute) {
         super(xpath);
         this.absolute = absolute;
-        this.location = null;
+        this.steps = null;
     }
 
-    public PathExpressionImpl(final String xpath, final YangLocationPath location) {
+    public PathExpressionImpl(final String xpath, final Steps steps) {
         super(xpath);
-        this.absolute = location.isAbsolute();
-        this.location = location;
+        this.steps = requireNonNull(steps);
+        this.absolute = steps instanceof LocationPathSteps
+                && ((LocationPathSteps) steps).getLocationPath().isAbsolute();
     }
 
     @Override
@@ -44,16 +46,16 @@ public final class PathExpressionImpl extends AbstractPathExpression {
     }
 
     @Override
-    public YangLocationPath getLocation() {
-        final YangLocationPath loc = location;
+    public Steps getSteps() {
+        final Steps loc = steps;
         if (loc == null) {
-            throw new UnsupportedOperationException("Location has not been provided");
+            throw new UnsupportedOperationException("Steps have not been provided");
         }
         return loc;
     }
 
     @Override
     protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
-        return super.addToStringAttributes(helper.add("absolute", absolute).add("location", location));
+        return super.addToStringAttributes(helper.add("absolute", absolute).add("steps", steps));
     }
 }
index 3d63ee9eeaa42e8e3e55fc99d7bff30711fb639e..7b88efe3524c00940e9e947abda3a44485b76d04 100644 (file)
@@ -10,6 +10,7 @@ LEFT_PARENTHESIS : '(' ;
 RIGHT_PARENTHESIS : ')' ;
 
 CURRENT_KEYWORD : 'current';
+DEREF_KEYWORD : 'deref';
 
 SEP: [ \n\r\t]+ ;
 IDENTIFIER : [a-zA-Z_][a-zA-Z0-9_\-.]*;
index 1ae7f9ef6767130dcebc551a4e2556b850d41d43..816f638f0468fcc73b296414004327716b2a8dfa 100644 (file)
@@ -4,7 +4,11 @@ options {
     tokenVocab = LeafRefPathLexer;
 }
 
-path_arg : (absolute_path | relative_path) EOF;
+path_arg : (deref_expr | path_str) EOF;
+
+deref_expr : deref_function_invocation SEP? SLASH SEP? relative_path;
+
+path_str : absolute_path | relative_path;
 
 absolute_path : (SLASH node_identifier (path_predicate)*)+;
 
@@ -24,7 +28,9 @@ node_identifier : (prefix COLON)? identifier;
 
 current_function_invocation : CURRENT_KEYWORD SEP? LEFT_PARENTHESIS SEP? RIGHT_PARENTHESIS;
 
+deref_function_invocation : DEREF_KEYWORD SEP? LEFT_PARENTHESIS SEP? relative_path SEP? RIGHT_PARENTHESIS;
+
 prefix : identifier;
 
-identifier: IDENTIFIER | CURRENT_KEYWORD;
+identifier: IDENTIFIER | CURRENT_KEYWORD | DEREF_KEYWORD;
 
index 21ee7f757f97a6579133206ff7ecc32e563f9a78..6c482a6254579d1a638bfee73caf76ef3ea8504f 100644 (file)
@@ -10,25 +10,25 @@ package org.opendaylight.yangtools.yang.parser.rfc7950.stmt.path;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.base.MoreObjects.ToStringHelper;
-import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.opendaylight.yangtools.yang.model.util.AbstractPathExpression;
-import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
 
+@NonNullByDefault
 final class ParsedPathExpression extends AbstractPathExpression {
-    private final @NonNull YangLocationPath location;
+    private final Steps steps;
 
-    ParsedPathExpression(final YangLocationPath location, final String originalString) {
+    ParsedPathExpression(final Steps steps, final String originalString) {
         super(originalString);
-        this.location = requireNonNull(location);
+        this.steps = requireNonNull(steps);
     }
 
     @Override
-    public YangLocationPath getLocation() {
-        return location;
+    public Steps getSteps() {
+        return steps;
     }
 
     @Override
     protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
-        return super.addToStringAttributes(helper.add("location", location));
+        return super.addToStringAttributes(helper.add("steps", steps));
     }
 }
index e02e6fce25d1cc1c5e1add405266ab2678c26cd6..83d3d122ee56a04c94db6a3c058f144c805454ae 100644 (file)
@@ -20,17 +20,23 @@ import org.antlr.v4.runtime.tree.TerminalNode;
 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathLexer;
 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser;
 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Absolute_pathContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Deref_exprContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Deref_function_invocationContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Descendant_pathContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Node_identifierContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Path_argContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Path_equality_exprContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Path_key_exprContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Path_predicateContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Path_strContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Rel_path_keyexprContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.LeafRefPathParser.Relative_pathContext;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.UnqualifiedQName;
 import org.opendaylight.yangtools.yang.model.api.PathExpression;
+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.parser.rfc7950.antlr.SourceExceptionParser;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
@@ -76,16 +82,30 @@ class PathExpressionParser {
             ctx.getStatementSourceReference());
 
         final ParseTree childPath = path.getChild(0);
-        final YangLocationPath location;
+        final Steps steps;
+        if (childPath instanceof Path_strContext) {
+            steps = new LocationPathSteps(parsePathStr(ctx, pathArg, (Path_strContext) childPath));
+        } else if (childPath instanceof Deref_exprContext) {
+            final Deref_exprContext deref = (Deref_exprContext) childPath;
+            steps = new DerefSteps(parseRelative(ctx, pathArg,
+                getChild(deref, 0, Deref_function_invocationContext.class).getChild(Relative_pathContext.class, 0)),
+                parseRelative(ctx, pathArg, getChild(deref, deref.getChildCount() - 1, Relative_pathContext.class)));
+        } else {
+            throw new IllegalStateException("Unsupported child " + childPath);
+        }
+        return new ParsedPathExpression(steps, pathArg);
+    }
+
+    private static YangLocationPath parsePathStr(final StmtContext<?, ?, ?> ctx, final String pathArg,
+            final Path_strContext path) {
+        final ParseTree childPath = path.getChild(0);
         if (childPath instanceof Absolute_pathContext) {
-            location = parseAbsolute(ctx, pathArg, (Absolute_pathContext) childPath);
+            return parseAbsolute(ctx, pathArg, (Absolute_pathContext) childPath);
         } else if (childPath instanceof Relative_pathContext) {
-            location = parseRelative(ctx, pathArg, (Relative_pathContext) childPath);
+            return parseRelative(ctx, pathArg, (Relative_pathContext) childPath);
         } else {
             throw new IllegalStateException("Unsupported child " + childPath);
         }
-
-        return new ParsedPathExpression(location, pathArg);
     }
 
     private static Absolute parseAbsolute(final StmtContext<?, ?, ?> ctx, final String pathArg,
@@ -97,14 +117,20 @@ class PathExpressionParser {
 
     private static Relative parseRelative(final StmtContext<?, ?, ?> ctx, final String pathArg,
             final Relative_pathContext relative) {
-        final List<Step> steps = new ArrayList<>();
-
         final int relativeChildren = relative.getChildCount();
         verify(relativeChildren % 2 != 0, "Unexpected child count %s", relativeChildren);
-        for (int i = 0; i < relativeChildren / 2; ++i) {
+
+        final int stepCount = relativeChildren / 2;
+        final List<Step> steps = new ArrayList<>(stepCount);
+        for (int i = 0; i < stepCount; ++i) {
             steps.add(YangXPathAxis.PARENT.asStep());
         }
+        return parseRelative(ctx, pathArg, relative, steps);
+    }
 
+    private static Relative parseRelative(final StmtContext<?, ?, ?> ctx, final String pathArg,
+            final Relative_pathContext relative, final List<Step> steps) {
+        final int relativeChildren = relative.getChildCount();
         final Descendant_pathContext descendant = getChild(relative, relativeChildren - 1,
             Descendant_pathContext.class);
         final Node_identifierContext qname = getChild(descendant, 0, Node_identifierContext.class);
index 643691d9331a597f434bfcfc17e099a84b1828aa..1f363a86877193d8ab0fa9a2d0211aae12207205 100644 (file)
@@ -12,7 +12,6 @@ import static java.util.Objects.requireNonNull;
 import com.google.common.base.MoreObjects.ToStringHelper;
 import org.opendaylight.yangtools.yang.model.util.AbstractPathExpression;
 import org.opendaylight.yangtools.yang.parser.rfc7950.stmt.ArgumentUtils;
-import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
 
 final class UnparsedPathExpression extends AbstractPathExpression {
     private final RuntimeException cause;
@@ -30,7 +29,7 @@ final class UnparsedPathExpression extends AbstractPathExpression {
     }
 
     @Override
-    public YangLocationPath getLocation() {
+    public Steps getSteps() {
         throw new UnsupportedOperationException("Expression '" + getOriginalString() + "' was not parsed", cause);
     }
 
index 07ee8c34865c67467bb1ec6703cdba5ba66ca8d4..b1d4d6471cc3f1eb051b7a2d79202b157c3c0422 100644 (file)
@@ -8,7 +8,9 @@
 package org.opendaylight.yangtools.yang.parser.rfc7950.stmt.path;
 
 import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.isA;
 import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
@@ -19,9 +21,15 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
+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.Steps;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
+import org.opendaylight.yangtools.yang.xpath.api.YangLocationPath;
+import org.opendaylight.yangtools.yang.xpath.api.YangXPathAxis;
 
 @RunWith(MockitoJUnitRunner.StrictStubs.class)
 public class PathExpressionParserTest {
@@ -29,6 +37,7 @@ public class PathExpressionParserTest {
     private StmtContext<?, ?, ?> ctx;
     @Mock
     private StatementSourceReference ref;
+    private final PathExpressionParser parser = new PathExpressionParser();
 
     @Before
     public void before() {
@@ -37,21 +46,23 @@ public class PathExpressionParserTest {
 
     @Test
     public void testDerefPath() {
-        try {
-            // deref() is not valid as per RFC7950, but YANGTOOLS-968 would allow it
-            new PathExpressionParser().parseExpression(ctx, "deref(../id)/../type");
-            fail("SourceException should have been thrown");
-        } catch (SourceException e) {
-            assertSame(ref, e.getSourceReference());
-            assertThat(e.getMessage(), startsWith("mismatched input '(' expecting "));
-            assertThat(e.getMessage(), containsString(" at 1:5 [at "));
-        }
+        // deref() is not valid as per RFC7950, but we tolarate it.
+        final PathExpression deref = parser.parseExpression(ctx, "deref(../id)/../type");
+
+        final Steps steps = deref.getSteps();
+        assertThat(steps, isA(DerefSteps.class));
+
+        final DerefSteps derefSteps = (DerefSteps) steps;
+        assertEquals(YangLocationPath.relative(YangXPathAxis.PARENT.asStep(),
+            YangXPathAxis.CHILD.asStep(UnqualifiedQName.of("type"))), derefSteps.getRelativePath());
+        assertEquals(YangLocationPath.relative(YangXPathAxis.PARENT.asStep(),
+            YangXPathAxis.CHILD.asStep(UnqualifiedQName.of("id"))), derefSteps.getDerefArgument());
     }
 
     @Test
     public void testInvalidLeftParent() {
         try {
-            new PathExpressionParser().parseExpression(ctx, "foo(");
+            parser.parseExpression(ctx, "foo(");
             fail("SourceException should have been thrown");
         } catch (SourceException e) {
             assertSame(ref, e.getSourceReference());
@@ -63,7 +74,7 @@ public class PathExpressionParserTest {
     @Test
     public void testInvalidRightParent() {
         try {
-            new PathExpressionParser().parseExpression(ctx, "foo)");
+            parser.parseExpression(ctx, "foo)");
             fail("SourceException should have been thrown");
         } catch (SourceException e) {
             assertSame(ref, e.getSourceReference());
@@ -75,7 +86,7 @@ public class PathExpressionParserTest {
     @Test
     public void testInvalidIdentifier() {
         try {
-            new PathExpressionParser().parseExpression(ctx, "foo%");
+            parser.parseExpression(ctx, "foo%");
             fail("SourceException should have been thrown");
         } catch (SourceException e) {
             assertSame(ref, e.getSourceReference());
index 75c31dcb8c4fa266ed9a1334a7fb3ee5c7e63f01..ee76fdc092d742c8a89d451c5c64d66decc8c33b 100644 (file)
@@ -321,6 +321,34 @@ public class EffectiveStatementTypeTest {
         assertTrue(leafrefEff.equals(leafrefEff));
     }
 
+    @Test
+    public void testLeafrefWithDeref() {
+        currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName
+                .create(types.getQNameModule(), "leaf-leafref-deref"));
+        assertNotNull(currentLeaf.getType());
+
+        final LeafrefTypeDefinition leafrefEff = ((LeafrefSpecificationEffectiveStatement)
+                ((LeafEffectiveStatement) currentLeaf).effectiveSubstatements().iterator().next())
+                .getTypeDefinition();
+
+        assertEquals("deref(../container-test)/leaf-test",
+                leafrefEff.getPathStatement().getOriginalString());
+        assertNull(leafrefEff.getBaseType());
+        assertEquals(Optional.empty(), leafrefEff.getUnits());
+        assertEquals(Optional.empty(), leafrefEff.getDefaultValue());
+        assertNotNull(leafrefEff.toString());
+        assertEquals("leafref", leafrefEff.getQName().getLocalName());
+        assertEquals(Status.CURRENT, leafrefEff.getStatus());
+        assertNotNull(leafrefEff.getUnknownSchemaNodes());
+        assertEquals("leafref", leafrefEff.getPath().getLastComponent().getLocalName());
+        assertFalse(leafrefEff.getDescription().isPresent());
+        assertFalse(leafrefEff.getReference().isPresent());
+        assertNotNull(leafrefEff.hashCode());
+        assertFalse(leafrefEff.equals(null));
+        assertFalse(leafrefEff.equals("test"));
+        assertTrue(leafrefEff.equals(leafrefEff));
+    }
+
     @Test
     public void testIntAll() {
         currentLeaf = (LeafSchemaNode) types.getDataChildByName(QName.create(types.getQNameModule(), "leaf-int8"));
index 7f8ed0cc96440f8b656ac0f867e066b95b4936bb..4e62f014a5f58401c77e261fd4aaf915168daf3c 100644 (file)
@@ -135,6 +135,12 @@ module types {
          }
     }
 
+    leaf leaf-leafref-deref {
+      type leafref {
+        path "deref(../container-test)/leaf-test";
+      }
+    }
+
     container container-test {
         leaf leaf-test {
             type int8;
index df27ca2c5798ccaa6fb19209acd1729f3299d94e..2049c805fce6e1930292b0f4e86f1f6b33f33fdd 100644 (file)
@@ -54,6 +54,10 @@ public class YangFunctionCallExpr implements YangExpr {
         return new YangFunctionCallExpr(name);
     }
 
+    public static YangFunctionCallExpr of(final QName name, final YangExpr argument) {
+        return new WithArgs(name, ImmutableList.of(argument));
+    }
+
     public static YangFunctionCallExpr of(final QName name, final List<YangExpr> arguments) {
         return arguments.isEmpty() ? of(name) : new WithArgs(name, arguments);
     }