Clean up DataSchemaContext{Node,Tree} 14/71514/1
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 27 Apr 2018 14:49:29 +0000 (16:49 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Fri, 27 Apr 2018 15:00:51 +0000 (17:00 +0200)
This patch fixes a potential NPE when a YangInstanceIdentifier does
not match the schema tree and adds alternative Optional-based methods
for finding children based on relative/absolute YangInstanceIdentifier.

Change-Id: Icdc08c02817ad9d59434a060b6da27dfe2b5ea67
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/DataSchemaContextNode.java
yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/DataSchemaContextTree.java

index e51f1a60619b02356126354aec06411b9d9f8f25..22b93d8621aca0727d02a63c8da448a1593fcff2 100644 (file)
@@ -9,13 +9,15 @@ package org.opendaylight.yangtools.yang.data.util;
 
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableSet;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Optional;
 import java.util.Set;
 import javax.annotation.Nullable;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.concepts.Identifiable;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
@@ -34,23 +36,15 @@ import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
 
 /**
- * Schema derived data providing necessary information for mapping
- * between {@link org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode}
- * and serialization format defined in RFC6020, since the mapping
- * is not one-to-one.
+ * Schema derived data providing necessary information for mapping between
+ * {@link org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode} and serialization format defined in RFC6020,
+ * since the mapping is not one-to-one.
  *
  * @param <T> Path Argument type
- *
  */
 public abstract class DataSchemaContextNode<T extends PathArgument> implements Identifiable<T> {
-
-    private final T identifier;
     private final DataSchemaNode dataSchemaNode;
-
-    @Override
-    public T getIdentifier() {
-        return identifier;
-    }
+    private final T identifier;
 
     protected DataSchemaContextNode(final T identifier, final SchemaNode schema) {
         this.identifier = identifier;
@@ -61,6 +55,11 @@ public abstract class DataSchemaContextNode<T extends PathArgument> implements I
         }
     }
 
+    @Override
+    public T getIdentifier() {
+        return identifier;
+    }
+
     public boolean isMixin() {
         return false;
     }
@@ -69,21 +68,45 @@ public abstract class DataSchemaContextNode<T extends PathArgument> implements I
         return false;
     }
 
+    public abstract boolean isLeaf();
+
     protected Set<QName> getQNameIdentifiers() {
-        return Collections.singleton(identifier.getNodeType());
+        return ImmutableSet.of(identifier.getNodeType());
     }
 
+    /**
+     * Find a child node identifier by its {@link PathArgument}.
+     *
+     * @param child Child path argument
+     * @return
+     */
     @Nullable public abstract DataSchemaContextNode<?> getChild(PathArgument child);
 
     @Nullable public abstract DataSchemaContextNode<?> getChild(QName child);
 
-    public abstract boolean isLeaf();
-
-
     @Nullable public DataSchemaNode getDataSchemaNode() {
         return dataSchemaNode;
     }
 
+    /**
+     * Find a child node as identified by a {@link YangInstanceIdentifier} relative to this node.
+     *
+     * @param path Path towards the child node
+     * @return Child node if present, or empty when corresponding child is not found.
+     * @throws NullPointerException if {@code path} is null
+     */
+    public final @NonNull Optional<@NonNull DataSchemaContextNode<?>> findChild(
+            final @NonNull YangInstanceIdentifier path) {
+        DataSchemaContextNode<?> currentOp = this;
+        for (PathArgument arg : path.getPathArguments()) {
+            currentOp = currentOp.getChild(arg);
+            if (currentOp == null) {
+                return Optional.empty();
+            }
+        }
+        return Optional.of(currentOp);
+    }
+
     static DataSchemaNode findChildSchemaNode(final DataNodeContainer parent, final QName child) {
         DataSchemaNode potential = parent.getDataChildByName(child);
         if (potential == null) {
index e012e29fef435b4d419b96cde4e752032d620946..dc0407afd2aef9d0ad98c23c235ec2139d1078c3 100644 (file)
@@ -10,11 +10,22 @@ package org.opendaylight.yangtools.yang.data.util;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
+import java.util.Optional;
 import javax.annotation.Nonnull;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
+/**
+ * Semantic tree binding a {@link SchemaContext} to a {@link NormalizedNode} tree. Since the layout of the schema
+ * and data has differences, the mapping is not trivial -- which is where this class comes in.
+ *
+ * @author Robert Varga
+ */
+// FIXME: 3.0.0: @NonNullByDefault
 public final class DataSchemaContextTree {
     private static final LoadingCache<SchemaContext, DataSchemaContextTree> TREES = CacheBuilder.newBuilder()
             .weakKeys().weakValues().build(new CacheLoader<SchemaContext, DataSchemaContextTree>() {
@@ -34,10 +45,34 @@ public final class DataSchemaContextTree {
         return TREES.getUnchecked(ctx);
     }
 
-    public DataSchemaContextNode<?> getChild(final YangInstanceIdentifier path) {
+    /**
+     * Find a child node as identified by an absolute {@link YangInstanceIdentifier}.
+     *
+     * @param path Path towards the child node
+     * @return Child node if present, or empty when corresponding child is not found.
+     * @throws NullPointerException if {@code path} is null
+     */
+    public @NonNull Optional<@NonNull DataSchemaContextNode<?>> findChild(final @NonNull YangInstanceIdentifier path) {
+        return getRoot().findChild(path);
+    }
+
+    /**
+     * Get a child node as identified by an absolute {@link YangInstanceIdentifier}.
+     *
+     * @param path Path towards the child node
+     * @return Child node if present, or null when corresponding child is not found.
+     * @throws NullPointerException if {@code path} is null
+     *
+     * @deprecated Use {@link #findChild(YangInstanceIdentifier)} instead.
+     */
+    @Deprecated
+    public @Nullable DataSchemaContextNode<?> getChild(final YangInstanceIdentifier path) {
         DataSchemaContextNode<?> currentOp = root;
         for (PathArgument arg : path.getPathArguments()) {
             currentOp = currentOp.getChild(arg);
+            if (currentOp == null) {
+                return null;
+            }
         }
         return currentOp;
     }