/* * 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 static java.util.Objects.requireNonNull; import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects.ToStringHelper; import com.google.common.base.Verify; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.concepts.Identifiable; import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace; /** * 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}. * *

* For common behaviour models please use static factories {@link #global(Class)}, {@link #sourceLocal(Class)} and * {@link #treeScoped(Class)}. * * @param * Key type * @param * Value type * @param * Namespace Type */ public abstract class NamespaceBehaviour> implements Identifiable> { public enum StorageNodeType { /** * Global storage, visible from all sources. */ GLOBAL, /** * Storage of the root statement of a particular source and any sources it is importing. */ // FIXME: 3.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 key type * @param value type * @param namespace type * @return Namespace behaviour * @throws NamespaceNotAvailableException when the namespace is not available */ > NamespaceBehaviour getNamespaceBehaviour(Class type); } public interface NamespaceStorageNode { /** * Return local namespace behaviour type. * * @return local namespace behaviour type {@link NamespaceBehaviour} */ StorageNodeType getStorageNodeType(); @Nullable NamespaceStorageNode getParentNamespaceStorage(); @Nullable > V getFromLocalStorage(Class type, K key); @Nullable > Map getAllFromLocalStorage(Class 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 > V putToLocalStorage(Class 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 > V putToLocalStorageIfAbsent(Class type, K key, V value); } private final @NonNull Class identifier; protected NamespaceBehaviour(final Class identifier) { this.identifier = requireNonNull(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}. * * @param identifier * Namespace identifier. * @param type parameter * @param type parameter * @param type parameter * @return global namespace behaviour for supplied namespace type. */ public static @Nonnull > NamespaceBehaviour global( final Class 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 type parameter * @param type parameter * @param type parameter * @return source-local namespace behaviour for supplied namespace type. */ public static > NamespaceBehaviour sourceLocal( final Class identifier) { return new StorageSpecific<>(identifier, StorageNodeType.SOURCE_LOCAL_SPECIAL); } public static > NamespaceBehaviour statementLocal( final Class identifier) { 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}. * * @param identifier * Namespace identifier. * @param type parameter * @param type parameter * @param type parameter * @return root-statement-local namespace behaviour for supplied namespace type. */ public static > NamespaceBehaviour rootStatementLocal( final Class 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. * @param type parameter * @param type parameter * @param type parameter * @return tree-scoped namespace behaviour for supplied namespace type. */ public static > NamespaceBehaviour treeScoped( final Class identifier) { return new TreeScoped<>(identifier); } /** * 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 the key/value mapping best matching specified criterion. * * @param storage namespace storage * @param criterion selection criterion * @return Selected mapping, if available. */ public final Optional> getFrom(final NamespaceStorageNode storage, final NamespaceKeyCriterion criterion) { final Map mappings = getAllFrom(storage); if (mappings == null) { return Optional.empty(); } Entry match = null; for (Entry 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 getAllFrom(NamespaceStorageNode storage); /** * Adds a key/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 getIdentifier() { return identifier; } protected final V getFromLocalStorage(final NamespaceStorageNode storage, final K key) { return storage.getFromLocalStorage(getIdentifier(), key); } protected final Map 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 final class StorageSpecific> extends NamespaceBehaviour { StorageNodeType storageType; StorageSpecific(final Class identifier, final StorageNodeType type) { super(identifier); storageType = requireNonNull(type); } @Override public V getFrom(final NamespaceStorageNode storage, final K key) { NamespaceStorageNode current = findClosestTowardsRoot(storage, storageType); return getFromLocalStorage(current, key); } @Override public Map 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); } @Override protected ToStringHelper addToStringAttributes(final ToStringHelper helper) { return super.addToStringAttributes(helper.add("type", storageType)); } } static final class TreeScoped> extends NamespaceBehaviour { TreeScoped(final Class 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 getAllFrom(final NamespaceStorageNode storage) { NamespaceStorageNode current = storage; while (current != null) { final Map 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; } @Override // FIXME: 3.0.0: make this final public String toString() { return addToStringAttributes(MoreObjects.toStringHelper(this)).toString(); } protected ToStringHelper addToStringAttributes(final ToStringHelper helper) { return helper.add("identifier", identifier.getName()); } }