Add SchemaTreeInference 40/95140/7
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 12 Feb 2021 15:20:56 +0000 (16:20 +0100)
committerRobert Varga <nite@hq.sk>
Fri, 12 Feb 2021 18:49:27 +0000 (18:49 +0000)
SchemaTreeInference is a nice inference point, firmly rooted in
RFC7950 concepts. Also wire it to SchemaInferenceStack, which can
support it quite easily.

JIRA: YANGTOOLS-1240
Change-Id: I746e5a4c17af3e4ca17c24dc15b7cb3533b48d8f
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
yang/yang-model-api/pom.xml
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/EffectiveStatementInference.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaTreeInference.java [new file with mode: 0644]
yang/yang-model-spi/src/main/java/org/opendaylight/yangtools/yang/model/spi/AbstractEffectiveStatementInference.java
yang/yang-model-spi/src/main/java/org/opendaylight/yangtools/yang/model/spi/DefaultSchemaTreeInference.java [new file with mode: 0644]
yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/SchemaInferenceStack.java

index bfb946bab17c209975588e4ebbc9d7149ed9cc9a..d3589f4bb4c05c947d3a45dedb53a0f5dca3c2ef 100644 (file)
             <artifactId>guava</artifactId>
         </dependency>
     </dependencies>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <!-- FIXME: remove this when odlparent provides this -->
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-javadoc-plugin</artifactId>
+                    <configuration>
+                        <tags>
+                            <tag>
+                                <name>apiNote</name>
+                                <placement>a</placement>
+                                <head>API Note:</head>
+                            </tag>
+                            <tag>
+                                <name>implSpec</name>
+                                <placement>a</placement>
+                                <head>Implementation Requirements:</head>
+                            </tag>
+                            <tag>
+                                <name>implNote</name>
+                                <placement>a</placement>
+                                <head>Implementation Note:</head>
+                            </tag>
+                        </tags>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
 </project>
index fa4af328866cc5e664673c892fcaacdb8d1a7364..95e2e6416f196d33d61266bf0c47f314e7026e9e 100644 (file)
@@ -21,9 +21,11 @@ import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 @Beta
 public interface EffectiveStatementInference extends EffectiveModelContextProvider, Immutable {
     /**
-     * An {@code Unmodifiable} {@link List} of {@link EffectiveStatement}s, ordered in some meaningful way.
+     * An {@code Unmodifiable} {@link List} of {@link EffectiveStatement}s, ordered in some meaningful way. Precise
+     * semantics of the statement order is clarified by individual {@link EffectiveStatementInference} specializations.
      *
+     * @see SchemaTreeInference
      * @return A List of EffectiveStatements
      */
-    @NonNull List<@NonNull EffectiveStatement<?, ?>> statementPath();
+    @NonNull List<? extends @NonNull EffectiveStatement<?, ?>> statementPath();
 }
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaTreeInference.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaTreeInference.java
new file mode 100644 (file)
index 0000000..004749b
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
+
+/**
+ * An {@link EffectiveStatementInference} consisting purely of steps along the {@code schema tree} axis, so that it
+ * represents a {@code schema tree node} based on resolution of {@code absolute-schema-nodeid} as specified by
+ * <a href="https://tools.ietf.org/html/rfc7950#section-6.5">RFC7950 section 6.5</a>.
+ */
+public interface SchemaTreeInference extends EffectiveStatementInference {
+    /**
+     * {@inheritDoc}
+     *
+     * <p>
+     * The statement path is always composed on {@link SchemaTreeEffectiveStatement}s and contains at least one element.
+     * The path is ordered from conceptual YANG root, i.e. the first element corresponds to the first element in
+     * {@link SchemaNodeIdentifier.Absolute#firstNodeIdentifier()}.
+     */
+    @Override
+    List<@NonNull SchemaTreeEffectiveStatement<?>> statementPath();
+
+    /**
+     * Return the {@link SchemaNodeIdentifier.Absolute} which resulted in this inference.
+     *
+     * @implSpec
+     *      Default implementation interprets {@link #statementPath()}'s arguments as the ordered source of
+     *      {@link SchemaNodeIdentifier.Absolute} steps.
+     *
+     * @return An absolute SchemaNodeIdentifier
+     */
+    default SchemaNodeIdentifier.Absolute toSchemaNodeIdentifier() {
+        return SchemaNodeIdentifier.Absolute.of(statementPath().stream()
+            .map(SchemaTreeEffectiveStatement::argument)
+            .collect(ImmutableList.toImmutableList()));
+    }
+}
index 529f058fd1c68995a2dc778fefa1bc76aff6c1e3..8dd25e29e01e5f545e97e8c152276b16cc503a09 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.yangtools.yang.model.spi;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects.ToStringHelper;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
 import org.eclipse.jdt.annotation.NonNull;
@@ -18,27 +19,35 @@ import org.opendaylight.yangtools.yang.model.api.EffectiveStatementInference;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 
 /**
- * A simple capture of an {@link EffectiveModelContext} and a list of paths. No further guarantees are made.
+ * A simple capture of an {@link EffectiveModelContext} and a list of {@link EffectiveStatement}s. No further guarantees
+ * are made.
+ *
+ * @param T constituent {@link EffectiveStatement} type
  */
 @Beta
-public abstract class AbstractEffectiveStatementInference extends AbstractEffectiveModelContextProvider
-        implements EffectiveStatementInference {
-    private final @NonNull List<EffectiveStatement<?, ?>> path;
+public abstract class AbstractEffectiveStatementInference<T extends EffectiveStatement<?, ?>>
+        extends AbstractEffectiveModelContextProvider implements EffectiveStatementInference {
+    private final @NonNull List<T> path;
 
     protected AbstractEffectiveStatementInference(final @NonNull EffectiveModelContext modelContext,
-            final @NonNull ImmutableList<EffectiveStatement<?, ?>> path) {
+            final @NonNull ImmutableList<T> path) {
         super(modelContext);
         this.path = requireNonNull(path);
     }
 
     protected AbstractEffectiveStatementInference(final @NonNull EffectiveModelContext modelContext,
-            final @NonNull List<? extends EffectiveStatement<?, ?>> path) {
+            final @NonNull List<? extends T> path) {
         super(modelContext);
         this.path = ImmutableList.copyOf(path);
     }
 
     @Override
-    public final List<EffectiveStatement<?, ?>> statementPath() {
+    public final List<T> statementPath() {
         return path;
     }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return super.addToStringAttributes(helper).add("statements", path);
+    }
 }
diff --git a/yang/yang-model-spi/src/main/java/org/opendaylight/yangtools/yang/model/spi/DefaultSchemaTreeInference.java b/yang/yang-model-spi/src/main/java/org/opendaylight/yangtools/yang/model/spi/DefaultSchemaTreeInference.java
new file mode 100644 (file)
index 0000000..84c3f5f
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import java.util.Iterator;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaTreeInference;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeAwareEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
+
+/**
+ * Default implementation of a a {@link SchemaTreeInference}. Guaranteed to be consistent with its
+ * {@link #getEffectiveModelContext()}.
+ */
+@Beta
+@NonNullByDefault
+public final class DefaultSchemaTreeInference
+        extends AbstractEffectiveStatementInference<SchemaTreeEffectiveStatement<?>> implements SchemaTreeInference {
+    private DefaultSchemaTreeInference(final EffectiveModelContext modelContext,
+            final ImmutableList<SchemaTreeEffectiveStatement<?>> path) {
+        super(modelContext, path);
+    }
+
+    /**
+     * Create a new instance.
+     *
+     * @param modelContext Associated {@link EffectiveModelContext}
+     * @param path An absolute schema node identifier
+     * @return A new instance
+     */
+    public static DefaultSchemaTreeInference of(final EffectiveModelContext modelContext, final Absolute path) {
+        final List<QName> steps = path.getNodeIdentifiers();
+        final QName first = steps.get(0);
+        final ModuleEffectiveStatement module = modelContext.findModuleStatement(first.getModule()).orElseThrow(
+            () -> new IllegalArgumentException("No module for " + first));
+
+        final ImmutableList.Builder<SchemaTreeEffectiveStatement<?>> builder =
+            ImmutableList.builderWithExpectedSize(steps.size());
+        SchemaTreeAwareEffectiveStatement<?, ?> parent = module;
+        final Iterator<QName> it = steps.iterator();
+        while (true) {
+            final QName qname = it.next();
+            final SchemaTreeEffectiveStatement<?> found = parent.findSchemaTreeNode(qname).orElseThrow(
+                () -> new IllegalArgumentException("Cannot resolve step " + qname + " in " + builder.build()));
+            if (it.hasNext()) {
+                checkArgument(found instanceof SchemaTreeAwareEffectiveStatement, "Cannot resolve steps %s past %s",
+                    steps, found);
+                parent = (SchemaTreeAwareEffectiveStatement<?, ?>) found;
+            } else {
+                break;
+            }
+        }
+
+        return new DefaultSchemaTreeInference(modelContext, builder.build());
+    }
+}
index 1ca82186d905ab3a3ab7c5220a30c64c409d47a4..bbf05d54fe5e1d27ba406be15ba9124a25729fe6 100644 (file)
@@ -31,6 +31,7 @@ import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextProvider;
 import org.opendaylight.yangtools.yang.model.api.EffectiveStatementInference;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.SchemaTreeInference;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.CaseEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement;
@@ -42,6 +43,7 @@ import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absol
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeAwareEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.spi.AbstractEffectiveStatementInference;
+import org.opendaylight.yangtools.yang.model.spi.DefaultSchemaTreeInference;
 
 /**
  * A state tracking utility for walking {@link EffectiveModelContext}'s contents along schema/grouping namespaces. This
@@ -61,7 +63,7 @@ public final class SchemaInferenceStack implements Mutable, EffectiveModelContex
      * {@link #statementPath()} is implementation-specific.
      */
     @Beta
-    public static final class Inference extends AbstractEffectiveStatementInference {
+    public static final class Inference extends AbstractEffectiveStatementInference<EffectiveStatement<?, ?>> {
         private final ModuleEffectiveStatement currentModule;
         private final int groupingDepth;
         private final boolean clean;
@@ -161,9 +163,25 @@ public final class SchemaInferenceStack implements Mutable, EffectiveModelContex
     public static @NonNull SchemaInferenceStack ofInference(final EffectiveStatementInference inference) {
         if (inference.statementPath().isEmpty()) {
             return new SchemaInferenceStack(inference.getEffectiveModelContext());
+        } else if (inference instanceof SchemaTreeInference) {
+            return ofInference((SchemaTreeInference) inference);
+        } else if (inference instanceof Inference) {
+            return ((Inference) inference).toSchemaInferenceStack();
+        } else {
+            throw new IllegalArgumentException("Unsupported Inference " + inference);
         }
-        checkArgument(inference instanceof Inference, "Inference %s not supported", inference);
-        return ((Inference) inference).toSchemaInferenceStack();
+    }
+
+    /**
+     * Create a new stack from an {@link SchemaTreeInference}.
+     *
+     * @param inference SchemaTreeInference to use for initialization
+     * @return A new stack
+     * @throws NullPointerException if {@code inference} is null
+     * @throws IllegalArgumentException if {@code inference} cannot be resolved to a valid stack
+     */
+    public static @NonNull SchemaInferenceStack ofInference(final SchemaTreeInference inference) {
+        return of(inference.getEffectiveModelContext(), inference.toSchemaNodeIdentifier());
     }
 
     /**
@@ -356,6 +374,16 @@ public final class SchemaInferenceStack implements Mutable, EffectiveModelContex
         return new Inference(effectiveModel, deque.descendingIterator(), currentModule, groupingDepth, clean);
     }
 
+    /**
+     * Return an {@link SchemaTreeInference} equivalent of current state.
+     *
+     * @return An {@link SchemaTreeInference}
+     * @throws IllegalStateException if current state cannot be converted to a {@link SchemaTreeInference}
+     */
+    public @NonNull SchemaTreeInference toSchemaTreeInference() {
+        return DefaultSchemaTreeInference.of(getEffectiveModelContext(), toSchemaNodeIdentifier());
+    }
+
     /**
      * Convert current state into an absolute schema node identifier.
      *