Add the ability to retain DeclarationReference 60/95160/19
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 13 Feb 2021 15:12:19 +0000 (16:12 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 21 Apr 2021 06:26:07 +0000 (08:26 +0200)
There are use cases where we would like to retain information about
where a statement was originally declared -- for example for use in
IDEs.

We already have a parser-internal representation of most of this
information in the form of StatementSourceReference, but that is not
retained in DeclaredStatements nor is it a public API.

Introduce DeclarationReference and its two specializations,
DeclarationInText and DeclarationInFile. These can be acquired via
DeclaredStatement.declarationReference().

Also provide integration between DeclarationReference and
StatementSourceReference, which will allow yang-parser-reactor to turn a
source reference into a declaration reference.

Finally add the basic substrate needed for forwarding decorators
that attach DeclarationReference to a delegate.

JIRA: YANGTOOLS-1193
Change-Id: I4e4d9567c83c08aab9de2a2685470835fadcd0d7
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
15 files changed:
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/DeclarationInFile.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/DeclarationInText.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/DeclarationReference.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/DeclaredStatement.java
yang/yang-model-spi/src/main/java/org/opendaylight/yangtools/yang/model/spi/meta/AbstractRefStatement.java [new file with mode: 0644]
yang/yang-model-spi/src/main/java/org/opendaylight/yangtools/yang/model/spi/meta/ForwardingDeclaredStatement.java [new file with mode: 0644]
yang/yang-model-spi/src/main/java/org/opendaylight/yangtools/yang/model/spi/meta/ForwardingModelStatement.java [new file with mode: 0644]
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/StatementContextVisitor.java
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/StatementSourceReferenceHandler.java
yang/yang-parser-rfc7950/src/main/java/org/opendaylight/yangtools/yang/parser/rfc7950/repo/YangModelDependencyInfo.java
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/DeclarationInTextSource.java [deleted file]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ExplicitStatement.java [new file with mode: 0644]
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ImplicitSubstatement.java
yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/StatementSourceReference.java
yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/source/ExplicitStatementTest.java [moved from yang/yang-parser-spi/src/test/java/org/opendaylight/yangtools/yang/parser/spi/source/DeclarationInTextSourceTest.java with 60% similarity]

diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/DeclarationInFile.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/DeclarationInFile.java
new file mode 100644 (file)
index 0000000..0ef1714
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2021 PANTHEON.tech, 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.model.api.meta;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * A {@link DeclarationReference} to something which resembles file.
+ */
+@Beta
+@NonNullByDefault
+public interface DeclarationInFile extends DeclarationReference {
+    /**
+     * Return the file where the declaration occurred. Returned is guaranteed to be non-empty and reference a file-like
+     * resource. No further guarantees on structure of the string are made.
+     *
+     * @return A non-empty file name.
+     */
+    String fileName();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/DeclarationInText.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/DeclarationInText.java
new file mode 100644 (file)
index 0000000..27d9607
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2021 PANTHEON.tech, 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.model.api.meta;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * A {@link DeclarationReference} to a position within a some text document.
+ */
+@Beta
+public interface DeclarationInText extends DeclarationReference {
+    /**
+     * Return the line where the declaration starts.
+     *
+     * @return A positive line number.
+     */
+    int startLine();
+
+    /**
+     * Return the column where the declaration starts.
+     *
+     * @return A positive column number.
+     */
+    int startColumn();
+}
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/DeclarationReference.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/meta/DeclarationReference.java
new file mode 100644 (file)
index 0000000..deca701
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2021 PANTHEON.tech, 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.model.api.meta;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Immutable;
+
+/**
+ * Reference to a statement declaration. This interface serves to provide additional information as to where a
+ * particular statement comes from. More accurate information may be available through further subclasses of this
+ * interface such as {@link DeclarationInText} and/or {@link DeclarationInFile}.
+ */
+@Beta
+public interface DeclarationReference extends Immutable {
+    /**
+     * Returns human readable representation of this reference. This method does not prescribe any format of the
+     * returned string.
+     *
+     * @return human readable representation of this reference.
+     */
+    @NonNull String toHumanReadable();
+}
index 94521207bcf6cec73fb2dab7b437955f52b90708..11e5bb12651fa78dfa90c068fb8a4b7a9a3053be 100644 (file)
@@ -53,6 +53,23 @@ public interface DeclaredStatement<A> extends ModelStatement<A> {
         return Collections2.transform(Collections2.filter(declaredSubstatements(), type::isInstance), type::cast);
     }
 
+    /**
+     * Returns a {@link DeclarationReference} associated with this statement, if available.
+     *
+     * @apiNote
+     *     This method does not contribute any semantic information and is provided purely as a conduit for
+     *     implementation-specific information where a statement instance came from.
+     *
+     * @implSpec
+     *     The default implementation returns {@link Optional#empty()}.
+     *
+     * @return A {@link DeclarationReference} associated with this statement or {@link Optional#empty()}.
+     */
+    @Beta
+    default @NonNull Optional<DeclarationReference> declarationReference() {
+        return Optional.empty();
+    }
+
     /**
      * Find the first effective substatement of specified type.
      *
diff --git a/yang/yang-model-spi/src/main/java/org/opendaylight/yangtools/yang/model/spi/meta/AbstractRefStatement.java b/yang/yang-model-spi/src/main/java/org/opendaylight/yangtools/yang/model/spi/meta/AbstractRefStatement.java
new file mode 100644 (file)
index 0000000..5b994e6
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2021 PANTHEON.tech, 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.model.spi.meta;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.Empty;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclarationReference;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+/**
+ * Abstract base class for {@link DeclaredStatement} implementations which decorate a statement with a
+ * {@link DeclarationReference}.
+ *
+ * @param <A> Argument type ({@link Empty} if statement does not have argument.)
+ * @param <D> Class representing declared version of this statement.
+ */
+public abstract class AbstractRefStatement<A, D extends DeclaredStatement<A>>
+        extends ForwardingDeclaredStatement<A, D> {
+    private final @NonNull DeclarationReference ref;
+    private final @NonNull D delegate;
+
+    protected AbstractRefStatement(final D delegate, final DeclarationReference ref) {
+        this.delegate = requireNonNull(delegate);
+        this.ref = requireNonNull(ref);
+    }
+
+    @Override
+    public final Optional<DeclarationReference> declarationReference() {
+        return Optional.of(ref);
+    }
+
+    @Override
+    protected final D delegate() {
+        return delegate;
+    }
+}
diff --git a/yang/yang-model-spi/src/main/java/org/opendaylight/yangtools/yang/model/spi/meta/ForwardingDeclaredStatement.java b/yang/yang-model-spi/src/main/java/org/opendaylight/yangtools/yang/model/spi/meta/ForwardingDeclaredStatement.java
new file mode 100644 (file)
index 0000000..6c50fdf
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2021 PANTHEON.tech, 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.model.spi.meta;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclarationReference;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+/**
+ * Common base class for forwarding implementations of {@link DeclaredStatement}.
+ */
+@Beta
+public abstract class ForwardingDeclaredStatement<A, D extends DeclaredStatement<A>>
+        extends ForwardingModelStatement<A, D> implements DeclaredStatement<A> {
+    @Override
+    public String rawArgument() {
+        return delegate().rawArgument();
+    }
+
+    @Override
+    public Collection<? extends DeclaredStatement<?>> declaredSubstatements() {
+        return delegate().declaredSubstatements();
+    }
+
+    @Override
+    public Optional<DeclarationReference> declarationReference() {
+        return delegate().declarationReference();
+    }
+
+    @Override
+    protected abstract @NonNull D delegate();
+}
diff --git a/yang/yang-model-spi/src/main/java/org/opendaylight/yangtools/yang/model/spi/meta/ForwardingModelStatement.java b/yang/yang-model-spi/src/main/java/org/opendaylight/yangtools/yang/model/spi/meta/ForwardingModelStatement.java
new file mode 100644 (file)
index 0000000..245ede5
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2021 PANTHEON.tech, 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.model.spi.meta;
+
+import com.google.common.collect.ForwardingObject;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.meta.ModelStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementOrigin;
+
+/**
+ * Common base class for forwarding implementations of {@link ModelStatement}.
+ */
+public abstract class ForwardingModelStatement<A, S extends ModelStatement<A>> extends ForwardingObject
+        implements ModelStatement<A> {
+    @Override
+    public StatementDefinition statementDefinition() {
+        return delegate().statementDefinition();
+    }
+
+    @Override
+    public A argument() {
+        return delegate().argument();
+    }
+
+    @Override
+    public StatementOrigin statementOrigin() {
+        return delegate().statementOrigin();
+    }
+
+    @Override
+    protected abstract @NonNull S delegate();
+}
index a60b040ad67d8fa916c96d7be7ef6396d1212812..0637c3c1500b8b26ff5b574eaf67c17afcaf4a3c 100644 (file)
@@ -19,7 +19,7 @@ import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRArgument;
 import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRKeyword;
 import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRKeyword.Qualified;
 import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRStatement;
-import org.opendaylight.yangtools.yang.parser.spi.source.DeclarationInTextSource;
+import org.opendaylight.yangtools.yang.parser.spi.source.ExplicitStatement;
 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
 import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
@@ -98,8 +98,8 @@ class StatementContextVisitor {
 
     // Slow-path allocation of a new statement
     private boolean processNewStatement(final int myOffset, final IRStatement stmt) {
-        final StatementSourceReference ref = DeclarationInTextSource.atPosition(sourceName, stmt.startLine(),
-            stmt.startColumn());
+        final StatementSourceReference ref = ExplicitStatement.atPosition(sourceName, stmt.startLine(),
+            stmt.startColumn() + 1);
         final QName def = getValidStatementDefinition(stmt.keyword(), ref);
         if (def == null) {
             return false;
index de13c70bdf4a3c30b1986bd702d60f68a6d22f9c..e0702d53fb999277bca8e1f63211729ef05f9518 100644 (file)
@@ -11,7 +11,7 @@ import static java.util.Objects.requireNonNull;
 
 import java.util.ArrayDeque;
 import java.util.Deque;
-import org.opendaylight.yangtools.yang.parser.spi.source.DeclarationInTextSource;
+import org.opendaylight.yangtools.yang.parser.spi.source.ExplicitStatement;
 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -67,7 +67,7 @@ final class StatementSourceReferenceHandler extends DefaultHandler {
             el.setAttributeNS(attributes.getURI(i), attributes.getQName(i), attributes.getValue(i));
         }
 
-        final StatementSourceReference ref = DeclarationInTextSource.atPosition(file, documentLocator.getLineNumber(),
+        final StatementSourceReference ref = ExplicitStatement.atPosition(file, documentLocator.getLineNumber(),
             documentLocator.getColumnNumber());
         el.setUserData(USER_DATA_KEY, ref, null);
         stack.push(el);
index 0e380901eecd1ad4cea877f93c902bb1f5b5753d..34fd2786f68cfb5b8721c3bfa3ea321c22e05945 100644 (file)
@@ -36,7 +36,7 @@ import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRKeyword;
 import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRKeyword.Unqualified;
 import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRSchemaSource;
 import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRStatement;
-import org.opendaylight.yangtools.yang.parser.spi.source.DeclarationInTextSource;
+import org.opendaylight.yangtools.yang.parser.spi.source.ExplicitStatement;
 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
 
 /**
@@ -347,7 +347,7 @@ public abstract class YangModelDependencyInfo {
     }
 
     private static StatementSourceReference getReference(final SourceIdentifier source, final IRStatement stmt) {
-        return DeclarationInTextSource.atPosition(source.getName(), stmt.startLine(), stmt.startColumn());
+        return ExplicitStatement.atPosition(source.getName(), stmt.startLine(), stmt.startColumn() + 1);
     }
 
     /**
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/DeclarationInTextSource.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/DeclarationInTextSource.java
deleted file mode 100644 (file)
index 90fe135..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (c) 2015 Cisco Systems, Inc. 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.parser.spi.source;
-
-import java.util.Objects;
-import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.yangtools.yang.model.api.meta.StatementOrigin;
-
-/**
- * Reference of statement source present in textual source format. Utility implementation
- * of {@link StatementSourceReference} for textual sources, this is preferred {@link StatementSourceReference}
- * for implementations of YANG / YIN statement stream sources.
- *
- * <p>
- * To create source reference use one of this static factories:
- * <ul>
- *   <li>{@link #atPosition(String, int, int)} - provides most specific reference of statement location, this is most
- *       preferred since it provides most context to debug YANG model.</li>
- *   <li>{@link #atLine(String, int)}- provides source and line of statement location.</li>
- *   <li>{@link #inSource(String)} - least specific reference, should be used only if any of previous references are
- *       unable to create / derive from source.</li>
- * </ul>
- */
-public abstract class DeclarationInTextSource implements StatementSourceReference {
-    private static class InSource extends DeclarationInTextSource {
-        InSource(final String sourceName) {
-            super(sourceName);
-        }
-    }
-
-    private static class AtLine extends InSource {
-        private final int line;
-
-        AtLine(final String sourceName, final int line) {
-            super(sourceName);
-            this.line = line;
-        }
-
-        @Override
-        int hashCodeImpl() {
-            return super.hashCodeImpl() * 31 + line;
-        }
-
-        @Override
-        boolean equalsImpl(final DeclarationInTextSource obj) {
-            return line == ((AtLine) obj).line && super.equalsImpl(obj);
-        }
-
-        @Override
-        public String toString() {
-            return super.toString() + ':' + line;
-        }
-    }
-
-    private static final class AtPosition extends AtLine {
-        private final int character;
-
-        AtPosition(final String sourceName, final int line, final int character) {
-            super(sourceName, line);
-            this.character = character;
-        }
-
-        @Override
-        int hashCodeImpl() {
-            return super.hashCodeImpl() * 31 + character;
-        }
-
-        @Override
-        boolean equalsImpl(final DeclarationInTextSource obj) {
-            return character == ((AtPosition) obj).character && super.equalsImpl(obj);
-        }
-
-        @Override
-        public String toString() {
-            return super.toString() + ':' + character;
-        }
-    }
-
-    private final String sourceName;
-
-    DeclarationInTextSource(final String sourceName) {
-        this.sourceName = sourceName;
-    }
-
-    public static @NonNull DeclarationInTextSource inSource(final String sourceName) {
-        return new InSource(sourceName);
-    }
-
-    public static @NonNull DeclarationInTextSource atLine(final String sourceName, final int line) {
-        return new AtLine(sourceName, line);
-    }
-
-    public static @NonNull DeclarationInTextSource atPosition(final String sourceName, final int line,
-            final int position) {
-        return new AtPosition(sourceName, line, position);
-    }
-
-    public final String getSourceName() {
-        return sourceName;
-    }
-
-    @Override
-    public final StatementOrigin statementOrigin() {
-        return StatementOrigin.DECLARATION;
-    }
-
-    @Override
-    public final int hashCode() {
-        return hashCodeImpl();
-    }
-
-    @Override
-    public final boolean equals(final Object obj) {
-        return this == obj
-                || obj != null && getClass().equals(obj.getClass()) && equalsImpl((DeclarationInTextSource) obj);
-    }
-
-    @Override
-    public String toString() {
-        return sourceName == null ? "null" : sourceName;
-    }
-
-    int hashCodeImpl() {
-        return Objects.hashCode(sourceName);
-    }
-
-    boolean equalsImpl(final DeclarationInTextSource obj) {
-        return Objects.equals(sourceName, obj.sourceName);
-    }
-}
diff --git a/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ExplicitStatement.java b/yang/yang-parser-spi/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/ExplicitStatement.java
new file mode 100644 (file)
index 0000000..f043233
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ * Copyright (c) 2021 PANTHEON.tech, s.r.o.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.spi.source;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verifyNotNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclarationInFile;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclarationInText;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclarationReference;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementOrigin;
+
+/**
+ * Reference of statement source present in textual source format. Utility implementation
+ * of {@link StatementSourceReference} for textual sources, this is preferred {@link StatementSourceReference}
+ * for implementations of YANG / YIN statement stream sources.
+ *
+ * <p>
+ * To create source reference use one of this static factories:
+ * <ul>
+ *   <li>{@link #atPosition(String, int, int)} - provides most specific reference of statement location, this is most
+ *       preferred since it provides most context to debug YANG model.</li>
+ *   <li>{@link #atPosition(int, int)}- provides location in text without knowing the name of the text file.</li>
+ *   <li>{@link #inFile(String)}- provides a source name.</li>
+ * </ul>
+ */
+@Beta
+public abstract class ExplicitStatement extends StatementSourceReference implements DeclarationReference {
+    private static final class InFile extends ExplicitStatement implements DeclarationInFile {
+        InFile(final String fileName) {
+            super(fileName, -1, -1);
+            checkArgument(!fileName.isEmpty(), "Invalid empty file name");
+        }
+
+        @Override
+        public String fileName() {
+            return verifyNotNull(file());
+        }
+    }
+
+    private static class InText extends ExplicitStatement implements DeclarationInText {
+        InText(final String file, final int line, final int column) {
+            super(file, line, column);
+            checkArgument(line > 0, "Invalid start line %s", line);
+            checkArgument(column > 0, "Invalid start column %s", column);
+        }
+
+        @Override
+        public int startLine() {
+            return line();
+        }
+
+        @Override
+        public int startColumn() {
+            return column();
+        }
+    }
+
+    private static final class InTextFile extends InText implements DeclarationInFile {
+        InTextFile(final String fileName, final int line, final int column) {
+            super(fileName, line, column);
+            checkArgument(!fileName.isEmpty(), "Invalid empty file name");
+        }
+
+        @Override
+        public String fileName() {
+            return verifyNotNull(file());
+        }
+    }
+
+    private final String file;
+    private final int line;
+    private final int column;
+
+    ExplicitStatement(final String file, final int line, final int column) {
+        this.file = file;
+        this.line = line;
+        this.column = column;
+    }
+
+    public static @NonNull ExplicitStatement atPosition(final int line, final int column) {
+        return new InText(null, line, column);
+    }
+
+    public static @NonNull ExplicitStatement atPosition(final @Nullable String fileName, final int line,
+            final int column) {
+        return fileName == null ? atPosition(line, column) : new InTextFile(fileName, line, column);
+    }
+
+    public static @NonNull ExplicitStatement inFile(final @NonNull String fileName) {
+        return new InFile(fileName);
+    }
+
+    @Override
+    public final StatementOrigin statementOrigin() {
+        return StatementOrigin.DECLARATION;
+    }
+
+    @Override
+    public final DeclarationReference declarationReference() {
+        return this;
+    }
+
+    @Override
+    public final String toHumanReadable() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append(file != null ? file : "<UNKNOWN>");
+        if (line > 0) {
+            sb.append(':').append(line);
+        }
+        if (column > 0) {
+            sb.append(':').append(column);
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(file(), line(), column());
+    }
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || !getClass().equals(obj.getClass())) {
+            return false;
+        }
+        final ExplicitStatement other = (ExplicitStatement) obj;
+        return line == other.line && column == other.column && Objects.equals(file, other.file);
+    }
+
+    @Override
+    public final String toString() {
+        return toHumanReadable();
+    }
+
+    final String file() {
+        return file;
+    }
+
+    final int line() {
+        return line;
+    }
+
+    final int column() {
+        return column;
+    }
+}
index 998a0731387344f9b5327b139aea20554e44b33f..276f54d7eabe40f8791062ea07b778a4a152c60b 100644 (file)
@@ -10,17 +10,15 @@ package org.opendaylight.yangtools.yang.parser.spi.source;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclarationReference;
 import org.opendaylight.yangtools.yang.model.api.meta.StatementOrigin;
 
 /**
- * An implicit sub-statement, which is implied to be always present in its parent, even if it does not appear
- * in model source.
- *
- * @author Robert Varga
+ * An implicit sub-statement, which is implied to be always present in its parent, even if it does not appear in model
+ * source.
  */
 @Beta
-public final class ImplicitSubstatement implements StatementSourceReference {
-
+public final class ImplicitSubstatement extends StatementSourceReference {
     private final StatementSourceReference parentRef;
 
     private ImplicitSubstatement(final StatementSourceReference parentRef) {
@@ -43,6 +41,11 @@ public final class ImplicitSubstatement implements StatementSourceReference {
         return StatementOrigin.CONTEXT;
     }
 
+    @Override
+    public DeclarationReference declarationReference() {
+        return null;
+    }
+
     @Override
     public int hashCode() {
         return parentRef.hashCode();
index f0527c7fbc70383356d6df4dba9db7359f919b3e..18ff710225ec46a0071f467b38c989c52465ff89 100644 (file)
@@ -8,6 +8,9 @@
 package org.opendaylight.yangtools.yang.parser.spi.source;
 
 import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclarationReference;
 import org.opendaylight.yangtools.yang.model.api.meta.StatementOrigin;
 
 /**
@@ -16,16 +19,23 @@ import org.opendaylight.yangtools.yang.model.api.meta.StatementOrigin;
  *
  * <p>
  * Reasons for introduction of statement could be various, but most obvious one is explicit declaration in model source
- * text such as {@link DeclarationInTextSource}.
+ * text such as {@link ExplicitStatement}.
  */
-public interface StatementSourceReference {
+public abstract class StatementSourceReference implements Immutable {
     /**
-     * Returns source type.
+     * Returns the {@link StatementOrigin} associated with this reference.
      *
      * @return {@link StatementOrigin#DECLARATION} if statement was explicitly declared in YANG model source,
      *         {@link StatementOrigin#CONTEXT} if statement was inferred.
      */
-    @NonNull StatementOrigin statementOrigin();
+    public abstract @NonNull StatementOrigin statementOrigin();
+
+    /**
+     * Returns the {@link DeclarationReference} associated with this reference, if available.
+     *
+     * @return A {@link DeclarationReference} or null.
+     */
+    public abstract @Nullable DeclarationReference declarationReference();
 
     /**
      * Returns human readable representation of statement source.
@@ -37,5 +47,5 @@ public interface StatementSourceReference {
      * @return human readable representation of statement source.
      */
     @Override
-    String toString();
+    public abstract @NonNull String toString();
 }
@@ -12,16 +12,16 @@ import static org.junit.Assert.assertEquals;
 import org.junit.Test;
 import org.opendaylight.yangtools.yang.model.api.meta.StatementOrigin;
 
-public class DeclarationInTextSourceTest {
+public class ExplicitStatementTest {
     @Test
     public void testStatementSource() {
-        assertEquals(StatementOrigin.DECLARATION, DeclarationInTextSource.atLine("foo", 5).statementOrigin());
+        assertEquals(StatementOrigin.DECLARATION, ExplicitStatement.inFile("foo").statementOrigin());
     }
 
     @Test
     public void testToString() {
-        assertEquals("foo", DeclarationInTextSource.inSource("foo").toString());
-        assertEquals("foo:5", DeclarationInTextSource.atLine("foo", 5).toString());
-        assertEquals("foo:5:10", DeclarationInTextSource.atPosition("foo", 5, 10).toString());
+        assertEquals("foo", ExplicitStatement.inFile("foo").toString());
+        assertEquals("<UNKNOWN>:5:10", ExplicitStatement.atPosition(5, 10).toString());
+        assertEquals("foo:5:10", ExplicitStatement.atPosition("foo", 5, 10).toString());
     }
 }