Add OnDemandSchemaTreeStorageNode
[yangtools.git] / yang / yang-parser-spi / src / main / java / org / opendaylight / yangtools / yang / parser / spi / meta / NamespaceBehaviour.java
index c24b5c107c4da02ee4f348d0e7f680c1b7e8458d..2139cd55623debd712cb18efb8a2eeeb3d00b7d8 100644 (file)
@@ -7,53 +7,85 @@
  */
 package org.opendaylight.yangtools.yang.parser.spi.meta;
 
-import com.google.common.base.Preconditions;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.Verify;
 import java.util.Map;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import org.opendaylight.yangtools.concepts.Identifiable;
+import java.util.Map.Entry;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.AbstractIdentifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
 
 /**
- * Definition / implementation of specific Identifier Namespace behaviour.
- *
- * Namespace behaviour is build on top of tree of {@link NamespaceStorageNode} which represents local context of one of
- * types defined in {@link StorageNodeType}.
+ * Definition / implementation of specific Identifier Namespace behaviour. A namespace behaviour is built on top
+ * of a tree of {@link NamespaceStorageNode} which represents local context of one of types defined
+ * n {@link StorageNodeType}.
  *
+ * <p>
  * For common behaviour models please use static factories {@link #global(Class)}, {@link #sourceLocal(Class)} and
  * {@link #treeScoped(Class)}.
  *
- * @param <K>
- *            Key type
- * @param <V>
- *            Value type
- * @param <N>
- *            Namespace Type
+ * @param <K> Key type
+ * @param <V> Value type
+ * @param <N> Namespace Type
  */
-public abstract class NamespaceBehaviour<K, V, N extends IdentifierNamespace<K, V>> implements Identifiable<Class<N>> {
+public abstract class NamespaceBehaviour<K, V, N extends IdentifierNamespace<K, V>>
+        extends AbstractIdentifiable<Class<N>> {
 
     public enum StorageNodeType {
-        GLOBAL, SOURCE_LOCAL_SPECIAL, STATEMENT_LOCAL, ROOT_STATEMENT_LOCAL
+        /**
+         * Global storage, visible from all sources.
+         */
+        GLOBAL,
+        /**
+         * Storage of the root statement of a particular source and any sources it is importing.
+         */
+        // FIXME: 6.0.0: this is a misnomer and should be renamed
+        SOURCE_LOCAL_SPECIAL,
+        /**
+         * Storage of a single statement.
+         */
+        STATEMENT_LOCAL,
+        /**
+         * Storage of the root statement of a particular source.
+         */
+        ROOT_STATEMENT_LOCAL
     }
 
     public interface Registry {
+        /**
+         * Get a namespace behavior.
+         *
+         * @param type Namespace type class
+         * @param <K> key type
+         * @param <V> value type
+         * @param <N> namespace type
+         * @return Namespace behaviour
+         * @throws NamespaceNotAvailableException when the namespace is not available
+         */
         <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviour<K, V, N> getNamespaceBehaviour(Class<N> type);
     }
 
     public interface NamespaceStorageNode {
         /**
+         * Return local namespace behaviour type.
+         *
          * @return local namespace behaviour type {@link NamespaceBehaviour}
          */
         StorageNodeType getStorageNodeType();
 
-        @Nullable
-        NamespaceStorageNode getParentNamespaceStorage();
+        @Nullable NamespaceStorageNode getParentNamespaceStorage();
 
-        @Nullable
-        <K, V, N extends IdentifierNamespace<K, V>> V getFromLocalStorage(Class<N> type, K key);
+        <K, V, N extends IdentifierNamespace<K, V>> @Nullable V getFromLocalStorage(Class<N> type, K key);
 
-        @Nullable
-        <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAllFromLocalStorage(Class<N> type);
+        <K, V, N extends IdentifierNamespace<K, V>> @Nullable Map<K, V> getAllFromLocalStorage(Class<N> type);
 
         /**
          * Populate specified namespace with a key/value pair, overwriting previous contents. Similar to
@@ -64,8 +96,7 @@ public abstract class NamespaceBehaviour<K, V, N extends IdentifierNamespace<K,
          * @param value Value
          * @return Previously-stored value, or null if the key was not present
          */
-        @Nullable
-        <K, V, N extends IdentifierNamespace<K, V>> V putToLocalStorage(Class<N> type, K key, V value);
+        <K, V, N extends IdentifierNamespace<K, V>> @Nullable V putToLocalStorage(Class<N> type, K key, V value);
 
         /**
          * Populate specified namespace with a key/value pair unless the key is already associated with a value. Similar
@@ -76,101 +107,160 @@ public abstract class NamespaceBehaviour<K, V, N extends IdentifierNamespace<K,
          * @param value Value
          * @return Preexisting value or null if there was no previous mapping
          */
-        @Nullable
-        <K, V, N extends IdentifierNamespace<K, V>> V putToLocalStorageIfAbsent(Class<N> type, K key, V value);
+        <K, V, N extends IdentifierNamespace<K, V>> @Nullable V putToLocalStorageIfAbsent(Class<N> type, K key,
+                V value);
     }
 
-    private final Class<N> identifier;
+    /**
+     * Interface implemented by {@link NamespaceStorageNode}s which support dynamic addition of child elements as they
+     * are requested. This means that such a node can, defer creation of child namespace storage nodes, in effect lazily
+     * expanding this namespace on an if-needed basis.
+     */
+    @Beta
+    public interface OnDemandSchemaTreeStorageNode extends NamespaceStorageNode {
+        /**
+         * Request that a new member of this node's schema tree statement be added. Implementations are required to
+         * perform lookup in their internal structure and create a child if tracktable. Resulting node is expected to
+         * have been registered with local storage, so that it is accessible through
+         * {@link #getFromLocalStorage(Class, Object)}.
+         *
+         * <p>
+         * This method must not change its mind about a child's presence -- once it returns non-present, it has to be
+         * always returning non-present.
+         *
+         * @param qname node identifier of the child being requested
+         * @return Requested child, if it is present.
+         */
+        <D extends DeclaredStatement<QName>, E extends EffectiveStatement<QName, D>>
+            @Nullable StmtContext<QName, D, E> requestSchemaTreeChild(QName qname);
+    }
 
     protected NamespaceBehaviour(final Class<N> identifier) {
-        this.identifier = Preconditions.checkNotNull(identifier);
+        super(identifier);
     }
 
     /**
+     * Creates a global namespace behaviour for supplied namespace type. Global behaviour stores and loads all values
+     * from root {@link NamespaceStorageNode} with type of {@link StorageNodeType#GLOBAL}.
      *
-     * Creates global namespace behaviour for supplied namespace type.
-     *
-     * Global behaviour stores and loads all values from root {@link NamespaceStorageNode} with type of
-     * {@link StorageNodeType#GLOBAL}.
-     *
-     * @param identifier
-     *            Namespace identifier.
+     * @param identifier Namespace identifier.
      * @param <K> type parameter
      * @param <V> type parameter
      * @param <N> type parameter
-     *
      * @return global namespace behaviour for supplied namespace type.
      */
-    public static @Nonnull <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviour<K, V, N> global(
+    public static <K, V, N extends IdentifierNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> global(
             final Class<N> identifier) {
         return new StorageSpecific<>(identifier, StorageNodeType.GLOBAL);
     }
 
     /**
+     * Creates source-local namespace behaviour for supplied namespace type. Source-local namespace behaviour stores
+     * and loads all values from closest {@link NamespaceStorageNode} ancestor with type
+     * of {@link StorageNodeType#SOURCE_LOCAL_SPECIAL}.
      *
-     * Creates source-local namespace behaviour for supplied namespace type.
-     *
-     * Source-local namespace behaviour stores and loads all values from closest {@link NamespaceStorageNode} ancestor
-     * with type of {@link StorageNodeType#SOURCE_LOCAL_SPECIAL}.
-     *
-     * @param identifier
-     *            Namespace identifier.
+     * @param identifier Namespace identifier.
      * @param <K> type parameter
      * @param <V> type parameter
      * @param <N> type parameter
-     *
      * @return source-local namespace behaviour for supplied namespace type.
      */
-    public static <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviour<K, V, N> sourceLocal(
+    public static <K, V, N extends IdentifierNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> sourceLocal(
             final Class<N> identifier) {
         return new StorageSpecific<>(identifier, StorageNodeType.SOURCE_LOCAL_SPECIAL);
     }
 
-    public static <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviour<K, V, N> statementLocal(
+    public static <K, V, N extends IdentifierNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> statementLocal(
            final Class<N> identifier) {
-       return new StorageSpecific<>(identifier, StorageNodeType.STATEMENT_LOCAL);
-   }
+        return new StorageSpecific<>(identifier, StorageNodeType.STATEMENT_LOCAL);
+    }
 
     /**
+     * Creates a root-statement-local namespace behaviour for supplied namespace type. Root-statement-local namespace
+     * behaviour stores and loads all values from closest {@link NamespaceStorageNode} ancestor with type
+     * of {@link StorageNodeType#ROOT_STATEMENT_LOCAL}.
      *
-     * Creates tree-scoped namespace behaviour for supplied namespace type.
-     *
-     * Tree-scoped namespace behaviour search for value in all storage nodes up to the root and stores values in
-     * supplied node.
+     * @param identifier Namespace identifier.
+     * @param <K> type parameter
+     * @param <V> type parameter
+     * @param <N> type parameter
+     * @return root-statement-local namespace behaviour for supplied namespace type.
+     */
+    public static <K, V, N extends IdentifierNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> rootStatementLocal(
+            final Class<N> identifier) {
+        return new StorageSpecific<>(identifier, StorageNodeType.ROOT_STATEMENT_LOCAL);
+    }
+
+    /**
+     * Creates tree-scoped namespace behaviour for supplied namespace type. Tree-scoped namespace behaviour searches
+     * for value in all storage nodes up to the root and stores values in supplied node.
      *
      * @param identifier
-     *            Namespace identifier.     *
+     *            Namespace identifier.
      * @param <K> type parameter
      * @param <V> type parameter
      * @param <N> type parameter
-     *
      * @return tree-scoped namespace behaviour for supplied namespace type.
      */
-    public static <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviour<K, V, N> treeScoped(final Class<N> identifier) {
+    public static <K, V, N extends IdentifierNamespace<K, V>> @NonNull NamespaceBehaviour<K, V, N> treeScoped(
+            final Class<N> identifier) {
         return new TreeScoped<>(identifier);
     }
 
     /**
-     * returns value from model namespace storage according to key param class
+     * Returns a value from model namespace storage according to key param class.
      *
      * @param storage namespace storage
      * @param key type parameter
-     *
      * @return value from model namespace storage according to key param class
      */
     public abstract V getFrom(NamespaceStorageNode storage, K key);
 
     /**
-     * returns all values of a keys of param class from model namespace storage
+     * Returns the key/value mapping best matching specified criterion.
      *
      * @param storage namespace storage
+     * @param criterion selection criterion
+     * @return Selected mapping, if available.
+     */
+    public final Optional<Entry<K, V>> getFrom(final NamespaceStorageNode storage,
+            final NamespaceKeyCriterion<K> criterion) {
+        final Map<K, V> mappings = getAllFrom(storage);
+        if (mappings == null) {
+            return Optional.empty();
+        }
+
+        Entry<K, V> match = null;
+        for (Entry<K, V> entry : mappings.entrySet()) {
+            final K key = entry.getKey();
+            if (criterion.match(key)) {
+                if (match != null) {
+                    final K selected = criterion.select(match.getKey(), key);
+                    if (selected.equals(match.getKey())) {
+                        continue;
+                    }
+
+                    Verify.verify(selected == key, "Criterion %s selected invalid key %s from candidates [%s %s]",
+                            selected, match.getKey(), key);
+                }
+
+                match = entry;
+            }
+        }
+
+        return Optional.ofNullable(match);
+    }
+
+    /**
+     * Returns all values of a keys of param class from model namespace storage.
      *
+     * @param storage namespace storage
      * @return all values of keys of param class from model namespace storage
      */
     public abstract Map<K, V> getAllFrom(NamespaceStorageNode storage);
 
     /**
-     * adds key and value to corresponding namespace storage according to param class
+     * Adds a key/value to corresponding namespace storage according to param class.
      *
      * @param storage namespace storage
      * @param key type parameter
@@ -178,11 +268,6 @@ public abstract class NamespaceBehaviour<K, V, N extends IdentifierNamespace<K,
      */
     public abstract void addTo(NamespaceStorageNode storage, K key, V value);
 
-    @Override
-    public Class<N> getIdentifier() {
-        return identifier;
-    }
-
     protected final V getFromLocalStorage(final NamespaceStorageNode storage, final K key) {
         return storage.getFromLocalStorage(getIdentifier(), key);
     }
@@ -195,13 +280,12 @@ public abstract class NamespaceBehaviour<K, V, N extends IdentifierNamespace<K,
         storage.putToLocalStorage(getIdentifier(), key, value);
     }
 
-    static class StorageSpecific<K, V, N extends IdentifierNamespace<K, V>> extends NamespaceBehaviour<K, V, N> {
+    static final class StorageSpecific<K, V, N extends IdentifierNamespace<K, V>> extends NamespaceBehaviour<K, V, N> {
+        private final StorageNodeType storageType;
 
-        StorageNodeType storageType;
-
-        public StorageSpecific(final Class<N> identifier, final StorageNodeType type) {
+        StorageSpecific(final Class<N> identifier, final StorageNodeType type) {
             super(identifier);
-            storageType = Preconditions.checkNotNull(type);
+            storageType = requireNonNull(type);
         }
 
         @Override
@@ -226,11 +310,15 @@ public abstract class NamespaceBehaviour<K, V, N extends IdentifierNamespace<K,
             addToStorage(current, key, value);
         }
 
+        @Override
+        protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+            return super.addToStringAttributes(helper.add("type", storageType));
+        }
     }
 
-    static class TreeScoped<K, V, N extends IdentifierNamespace<K, V>> extends NamespaceBehaviour<K, V, N> {
+    static final class TreeScoped<K, V, N extends IdentifierNamespace<K, V>> extends NamespaceBehaviour<K, V, N> {
 
-        public TreeScoped(final Class<N> identifier) {
+        TreeScoped(final Class<N> identifier) {
             super(identifier);
         }
 
@@ -267,11 +355,17 @@ public abstract class NamespaceBehaviour<K, V, N extends IdentifierNamespace<K,
 
     }
 
-    protected static NamespaceStorageNode findClosestTowardsRoot(final NamespaceStorageNode storage, final StorageNodeType type) {
+    protected static NamespaceStorageNode findClosestTowardsRoot(final NamespaceStorageNode storage,
+            final StorageNodeType type) {
         NamespaceStorageNode current = storage;
         while (current != null && current.getStorageNodeType() != type) {
             current = current.getParentNamespaceStorage();
         }
         return current;
     }
+
+    @Override
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return helper.add("identifier", getIdentifier().getName());
+    }
 }