Update {Data,Schema}TreeAwareEffectiveStatemnt 04/95204/3
authorRobert Varga <robert.varga@pantheon.tech>
Wed, 17 Feb 2021 12:04:12 +0000 (13:04 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 17 Feb 2021 12:26:44 +0000 (13:26 +0100)
Use of wildcards here is dangerous, potentially leading to
ClassCastExceptions due to how the APIs a designed. Update them
with explicit Class references, so we end up filtering results
properly.

Change-Id: I804670a24ea0de4268605c7fcdd93e00d96bb422
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DataTreeAwareEffectiveStatement.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DefaultMethodHelpers.java [new file with mode: 0644]
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/SchemaTreeAwareEffectiveStatement.java
yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/SchemaTreeRoot.java

index 455744a302d3c6076e57c5bc1a37e92f75bb770d..7e2a54feb565fef19f0cb4747ca4715d5da137c8 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.yangtools.yang.model.api.stmt;
 
 import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.model.api.stmt.DefaultMethodHelpers.filterOptional;
 
 import com.google.common.annotations.Beta;
 import java.util.Optional;
@@ -26,7 +27,6 @@ import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
 @Beta
 public interface DataTreeAwareEffectiveStatement<A, D extends DeclaredStatement<A>>
         extends SchemaTreeAwareEffectiveStatement<A, D> {
-
     /**
      * Namespace of {@code data node}s. This is a subtree of {@link SchemaTreeAwareEffectiveStatement.Namespace} in that
      * all data nodes are also schema nodes. The structure of the tree is different, though, as {@code choice}
@@ -48,13 +48,24 @@ public interface DataTreeAwareEffectiveStatement<A, D extends DeclaredStatement<
     /**
      * Find a {@code data tree} child {@link DataTreeEffectiveStatement}, as identified by its QName argument.
      *
-     * @param <E> Effective substatement type
      * @param qname Child identifier
      * @return Data tree child, or empty
      * @throws NullPointerException if {@code qname} is null
      */
-    default <E extends DataTreeEffectiveStatement<?>> @NonNull Optional<E> findDataTreeNode(
-            final @NonNull QName qname) {
+    default @NonNull Optional<DataTreeEffectiveStatement<?>> findDataTreeNode(final @NonNull QName qname) {
         return get(Namespace.class, requireNonNull(qname));
     }
+
+    /**
+     * Find a {@code data tree} child {@link DataTreeEffectiveStatement}, as identified by its QName argument.
+     *
+     * @param <E> Effective substatement type
+     * @param type Effective substatement class
+     * @param qname Child identifier
+     * @return Data tree child, or empty
+     * @throws NullPointerException if any argument is null
+     */
+    default <E> @NonNull Optional<E> findDataTreeNode(final Class<E> type, final @NonNull QName qname) {
+        return filterOptional(type, findDataTreeNode(qname));
+    }
 }
diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DefaultMethodHelpers.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/stmt/DefaultMethodHelpers.java
new file mode 100644 (file)
index 0000000..63c4bc8
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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.stmt;
+
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Simple helper methods used in default implementations.
+ */
+final class DefaultMethodHelpers {
+    private DefaultMethodHelpers() {
+        // Hidden on purpose
+    }
+
+    static <E> @NonNull Optional<E> filterOptional(final @NonNull Class<E> type, final @NonNull Optional<?> optional) {
+        return optional.filter(type::isInstance).map(type::cast);
+    }
+}
index d7a8e1afac9e9a8f04d8e62432f520cf6f56268e..144473a6907f757363b467e31eaf0acd16802a01 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.yangtools.yang.model.api.stmt;
 
 import static java.util.Objects.requireNonNull;
+import static org.opendaylight.yangtools.yang.model.api.stmt.DefaultMethodHelpers.filterOptional;
 
 import com.google.common.annotations.Beta;
 import java.util.Arrays;
@@ -49,13 +50,11 @@ public interface SchemaTreeAwareEffectiveStatement<A, D extends DeclaredStatemen
     /**
      * Find a {@code schema tree} child {@link SchemaTreeEffectiveStatement}, as identified by its QName argument.
      *
-     * @param <E> Effective substatement type
      * @param qname Child identifier
      * @return Schema tree child, or empty
      * @throws NullPointerException if {@code qname} is null
      */
-    default <E extends SchemaTreeEffectiveStatement<?>> @NonNull Optional<E> findSchemaTreeNode(
-            final @NonNull QName qname) {
+    default @NonNull Optional<SchemaTreeEffectiveStatement<?>> findSchemaTreeNode(final @NonNull QName qname) {
         return get(Namespace.class, requireNonNull(qname));
     }
 
@@ -63,13 +62,24 @@ public interface SchemaTreeAwareEffectiveStatement<A, D extends DeclaredStatemen
      * Find a {@code schema tree} child {@link SchemaTreeEffectiveStatement}, as identified by its QName argument.
      *
      * @param <E> Effective substatement type
+     * @param type Effective substatement class
+     * @param qname Child identifier
+     * @return Schema tree child, or empty
+     * @throws NullPointerException if any argument is null
+     */
+    default <E> @NonNull Optional<E> findSchemaTreeNode(final @NonNull Class<E> type, final @NonNull QName qname) {
+        return filterOptional(type, findSchemaTreeNode(qname));
+    }
+
+    /**
+     * Find a {@code schema tree} child {@link SchemaTreeEffectiveStatement}, as identified by its QName argument.
+     *
      * @param qnames Child identifiers
      * @return Schema tree child, or empty
      * @throws NullPointerException if {@code qnames} is null or contains a null element
      * @throws NoSuchElementException if {@code qnames} is empty
      */
-    default <E extends SchemaTreeEffectiveStatement<?>> @NonNull Optional<E> findSchemaTreeNode(
-            final @NonNull QName... qnames) {
+    default @NonNull Optional<SchemaTreeEffectiveStatement<?>> findSchemaTreeNode(final @NonNull QName... qnames) {
         return findSchemaTreeNode(Arrays.asList(qnames));
     }
 
@@ -77,21 +87,33 @@ public interface SchemaTreeAwareEffectiveStatement<A, D extends DeclaredStatemen
      * Find a {@code schema tree} child {@link SchemaTreeEffectiveStatement}, as identified by its QName argument.
      *
      * @param <E> Effective substatement type
+     * @param type Effective substatement class
+     * @param qnames Child identifiers
+     * @return Schema tree child, or empty
+     * @throws NullPointerException if any argument is null or if {@code qnames} contains a null element
+     * @throws NoSuchElementException if {@code qnames} is empty
+     */
+    default <E> @NonNull Optional<E> findSchemaTreeNode(final @NonNull Class<E> type, final @NonNull QName... qnames) {
+        return filterOptional(type, findSchemaTreeNode(Arrays.asList(qnames)));
+    }
+
+    /**
+     * Find a {@code schema tree} child {@link SchemaTreeEffectiveStatement}, as identified by its QName argument.
+     *
      * @param qnames Child identifiers
      * @return Schema tree child, or empty
      * @throws NullPointerException if {@code qnames} is null or contains a null element
      * @throws NoSuchElementException if {@code qnames} is empty
      */
-    default <E extends SchemaTreeEffectiveStatement<?>> @NonNull Optional<E> findSchemaTreeNode(
-            final @NonNull List<QName> qnames) {
+    default @NonNull Optional<SchemaTreeEffectiveStatement<?>> findSchemaTreeNode(final @NonNull List<QName> qnames) {
         final Iterator<QName> it = qnames.iterator();
         SchemaTreeAwareEffectiveStatement<?, ?> parent = this;
         while (true) {
-            final Optional<E> found = parent.findSchemaTreeNode(it.next());
+            final Optional<SchemaTreeEffectiveStatement<?>> found = parent.findSchemaTreeNode(it.next());
             if (!it.hasNext() || found.isEmpty()) {
                 return found;
             }
-            final E node = found.orElseThrow();
+            final SchemaTreeEffectiveStatement<?> node = found.orElseThrow();
             if (node instanceof SchemaTreeAwareEffectiveStatement) {
                 parent = (SchemaTreeAwareEffectiveStatement<?, ?>) node;
             } else {
@@ -100,6 +122,21 @@ public interface SchemaTreeAwareEffectiveStatement<A, D extends DeclaredStatemen
         }
     }
 
+    /**
+     * Find a {@code schema tree} child {@link SchemaTreeEffectiveStatement}, as identified by its QName argument.
+     *
+     * @param <E> Effective substatement type
+     * @param type Effective substatement class
+     * @param qnames Child identifiers
+     * @return Schema tree child, or empty
+     * @throws NullPointerException if {@code qnames} is null or contains a null element
+     * @throws NoSuchElementException if {@code qnames} is empty
+     */
+    default <E> @NonNull Optional<E> findSchemaTreeNode(final @NonNull Class<E> type,
+            final @NonNull List<QName> qnames) {
+        return filterOptional(type, findSchemaTreeNode(qnames));
+    }
+
     /**
      * Find a {@code schema tree} child {@link SchemaTreeEffectiveStatement}, as identified by its
      * {@link Descendant descendant schema node identifier}.
@@ -107,13 +144,30 @@ public interface SchemaTreeAwareEffectiveStatement<A, D extends DeclaredStatemen
      * @implSpec
      *     Default implementation defers to {@link #findSchemaTreeNode(List)}.
      *
-     * @param <E> Effective substatement type
      * @param descendant Descendant schema node identifier
      * @return Schema tree child, or empty
      * @throws NullPointerException if {@code descendant} is null
      */
-    default <E extends SchemaTreeEffectiveStatement<?>> @NonNull Optional<E> findSchemaTreeNode(
+    default @NonNull Optional<SchemaTreeEffectiveStatement<?>> findSchemaTreeNode(
             final @NonNull Descendant descendant) {
         return findSchemaTreeNode(descendant.getNodeIdentifiers());
     }
+
+    /**
+     * Find a {@code schema tree} child {@link SchemaTreeEffectiveStatement}, as identified by its
+     * {@link Descendant descendant schema node identifier}.
+     *
+     * @implSpec
+     *     Default implementation defers to {@link #findSchemaTreeNode(Class, List)}.
+     *
+     * @param <E> Effective substatement type
+     * @param type Effective substatement class
+     * @param descendant Descendant schema node identifier
+     * @return Schema tree child, or empty
+     * @throws NullPointerException if {@code descendant} is null
+     */
+    default <E> @NonNull Optional<E> findSchemaTreeNode(final @NonNull Class<E> type,
+            final @NonNull Descendant descendant) {
+        return findSchemaTreeNode(type, descendant.getNodeIdentifiers());
+    }
 }
index cc1dfc7acdb231483bfe1f813b5d57400a3fdcdf..3dcaede4d0cb87db257ba032e4d2dbbcc7d67a09 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.yangtools.yang.model.api.stmt;
 
+import static org.opendaylight.yangtools.yang.model.api.stmt.DefaultMethodHelpers.filterOptional;
+
 import com.google.common.annotations.Beta;
 import java.util.Optional;
 import org.eclipse.jdt.annotation.NonNull;
@@ -25,4 +27,22 @@ public interface SchemaTreeRoot {
      * @throws NullPointerException if {@code path} is null
      */
     @NonNull Optional<SchemaTreeEffectiveStatement<?>> findSchemaTreeNode(@NonNull SchemaNodeIdentifier path);
+
+    /**
+     * Find a {@code schema tree} node based on its schema node identifier.
+     *
+     * @implSpec
+     *     Default implementation defers to {@link #findSchemaTreeNode(SchemaNodeIdentifier)} and filters the result
+     *     using provided class.
+     *
+     * @param <T> requested node type
+     * @param type Request node class
+     * @param path Absolute schema node identifier
+     * @return Found node, or empty
+     * @throws NullPointerException if any argument is null
+     */
+    default <T> @NonNull Optional<T> findSchemaTreeNode(final @NonNull Class<T> type,
+            final @NonNull SchemaNodeIdentifier path) {
+        return filterOptional(type, findSchemaTreeNode(path));
+    }
 }