/* * 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.meta; import com.google.common.base.Preconditions; import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opendaylight.yangtools.concepts.Identifiable; 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}. * * 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 */ public abstract class NamespaceBehaviour<K, V, N extends IdentifierNamespace<K, V>> implements Identifiable<Class<N>> { public enum StorageNodeType { GLOBAL, SOURCE_LOCAL_SPECIAL, STATEMENT_LOCAL, ROOT_STATEMENT_LOCAL } public interface Registry { <K, V, N extends IdentifierNamespace<K, V>> NamespaceBehaviour<K, V, N> getNamespaceBehaviour(Class<N> type); } public interface NamespaceStorageNode { /** * @return local namespace behaviour type {@link NamespaceBehaviour} */ StorageNodeType getStorageNodeType(); @Nullable NamespaceStorageNode getParentNamespaceStorage(); @Nullable <K, V, N extends IdentifierNamespace<K, V>> V getFromLocalStorage(Class<N> type, K key); @Nullable <K, V, N extends IdentifierNamespace<K, V>> Map<K, V> getAllFromLocalStorage(Class<N> type); /** * Populate specified namespace with a key/value pair, overwriting previous contents. Similar to * {@link Map#put(Object, Object)}. * * @param type Namespace identifier * @param key Key * @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); /** * Populate specified namespace with a key/value pair unless the key is already associated with a value. Similar * to {@link Map#putIfAbsent(Object, Object)}. * * @param type Namespace identifier * @param key Key * @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); } private final Class<N> identifier; protected NamespaceBehaviour(final Class<N> identifier) { this.identifier = Preconditions.checkNotNull(identifier); } /** * * 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 <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( 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}. * * @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( 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( final Class<N> identifier) { return new StorageSpecific<>(identifier, StorageNodeType.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 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) { return new TreeScoped<>(identifier); } /** * returns 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 * * @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 * * @param storage namespace storage * @param key type parameter * @param value type parameter */ 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); } protected final Map<K, V> getAllFromLocalStorage(final NamespaceStorageNode storage) { return storage.getAllFromLocalStorage(getIdentifier()); } protected final void addToStorage(final NamespaceStorageNode storage, final K key, final V value) { storage.putToLocalStorage(getIdentifier(), key, value); } static class StorageSpecific<K, V, N extends IdentifierNamespace<K, V>> extends NamespaceBehaviour<K, V, N> { StorageNodeType storageType; public StorageSpecific(final Class<N> identifier, final StorageNodeType type) { super(identifier); storageType = Preconditions.checkNotNull(type); } @Override public V getFrom(final NamespaceStorageNode storage, final K key) { NamespaceStorageNode current = findClosestTowardsRoot(storage, storageType); return getFromLocalStorage(current, key); } @Override public Map<K, V> getAllFrom(final NamespaceStorageNode storage) { NamespaceStorageNode current = storage; while (current.getStorageNodeType() != storageType) { current = current.getParentNamespaceStorage(); } return getAllFromLocalStorage(current); } @Override public void addTo(final NamespaceBehaviour.NamespaceStorageNode storage, final K key, final V value) { NamespaceStorageNode current = findClosestTowardsRoot(storage, storageType); addToStorage(current, key, value); } } static class TreeScoped<K, V, N extends IdentifierNamespace<K, V>> extends NamespaceBehaviour<K, V, N> { public TreeScoped(final Class<N> identifier) { super(identifier); } @Override public V getFrom(final NamespaceStorageNode storage, final K key) { NamespaceStorageNode current = storage; while (current != null) { final V val = getFromLocalStorage(current, key); if (val != null) { return val; } current = current.getParentNamespaceStorage(); } return null; } @Override public Map<K, V> getAllFrom(final NamespaceStorageNode storage) { NamespaceStorageNode current = storage; while (current != null) { final Map<K, V> val = getAllFromLocalStorage(current); if (val != null) { return val; } current = current.getParentNamespaceStorage(); } return null; } @Override public void addTo(final NamespaceStorageNode storage, final K key, final V value) { addToStorage(storage, key, value); } } protected static NamespaceStorageNode findClosestTowardsRoot(final NamespaceStorageNode storage, final StorageNodeType type) { NamespaceStorageNode current = storage; while (current != null && current.getStorageNodeType() != type) { current = current.getParentNamespaceStorage(); } return current; } }