/* * 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.stmt.reactor; import static com.google.common.base.Verify.verifyNotNull; import com.google.common.collect.ImmutableMap; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour; import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode; import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.Registry; import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceKeyCriterion; import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceNotAvailableException; import org.opendaylight.yangtools.yang.parser.spi.meta.ParserNamespace; import org.opendaylight.yangtools.yang.parser.spi.meta.StatementNamespace; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext; import org.opendaylight.yangtools.yang.parser.spi.source.SourceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; abstract class NamespaceStorageSupport implements NamespaceStorageNode { private static final Logger LOG = LoggerFactory.getLogger(NamespaceStorageSupport.class); private Map, Map> namespaces = ImmutableMap.of(); /** * {@inheritDoc} * *

* This method override provides bimorphic invocation on this method invocation between * {@link SourceSpecificContext} and the more general {@link NamespaceStorageSupport}. We typically do not expect * the two accesses to overlap. */ @Override public abstract NamespaceStorageNode getParentNamespaceStorage(); /** * Return the registry of a source context. * * @return registry of source context */ public abstract @NonNull Registry getBehaviourRegistry(); // FIXME: 8.0.0: do we really need this method? final void checkLocalNamespaceAllowed(final Class> type) { // Always no-op. We used to route this towards StatementDefinitionContext, but this method remained // unimplemented even there. } /** * Occurs when an item is added to model namespace. * * @throws SourceException instance of SourceException */ protected > void onNamespaceElementAdded(final Class type, final K key, final V value) { // NOOP } public final > Optional> getFromNamespace( final Class type, final NamespaceKeyCriterion criterion) { return getBehaviourRegistry().getNamespaceBehaviour(type).getFrom(this, criterion); } public final > Map getNamespace(final Class type) { return getBehaviourRegistry().getNamespaceBehaviour(type).getAllFrom(this); } @SuppressWarnings("unchecked") final > Map getLocalNamespace(final Class type) { return (Map) accessNamespaces().get(type); } final > void addToNamespace( final Class type, final T key, final U value) { getBehaviourRegistry().getNamespaceBehaviour(type).addTo(this, key, value); } final > void addToNamespace( final Class type, final Map map) { final NamespaceBehaviour behavior = getBehaviourRegistry().getNamespaceBehaviour(type); for (final Entry validationBundle : map.entrySet()) { behavior.addTo(this, validationBundle.getKey(), validationBundle.getValue()); } } /** * Associate a context with a key within a namespace. * * @param type Namespace type * @param key Key * @param value Context value * @param namespace key type * @param namespace type * @throws NamespaceNotAvailableException when the namespace is not available. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public final > void addContextToNamespace(final Class type, final K key, final StmtContext value) { getBehaviourRegistry().getNamespaceBehaviour((Class)type).addTo(this, key, value); } @SuppressWarnings("unchecked") @Override public > V getFromLocalStorage(final Class type, final K key) { final Map localNamespace = (Map) accessNamespaces().get(type); return localNamespace == null ? null : localNamespace.get(key); } @Override public > Map getAllFromLocalStorage(final Class type) { @SuppressWarnings("unchecked") final Map localNamespace = (Map) accessNamespaces().get(type); return localNamespace; } @Override public > V putToLocalStorage(final Class type, final K key, final V value) { final V ret = ensureLocalNamespace(type).put(key, value); onNamespaceElementAdded(type, key, value); return ret; } @Override public > V putToLocalStorageIfAbsent(final Class type, final K key, final V value) { final V ret = ensureLocalNamespace(type).putIfAbsent(key, value); if (ret == null) { onNamespaceElementAdded(type, key, value); } return ret; } void sweepNamespaces() { namespaces = null; LOG.trace("Swept namespace storages of {}", this); } void sweepNamespaces(final Map, SweptNamespace> toWipe) { switch (namespaces.size()) { case 0: namespaces = ImmutableMap.copyOf(toWipe); return; case 1: namespaces = new HashMap<>(namespaces); break; default: // No-op, we are ready } namespaces.putAll(toWipe); LOG.trace("Trimmed namespace storages of {} to {}", this, namespaces.keySet()); } private Map, Map> accessNamespaces() { return verifyNotNull(namespaces, "Attempted to access swept namespaces of %s", this); } private > Map ensureLocalNamespace(final Class type) { @SuppressWarnings("unchecked") Map ret = (Map) accessNamespaces().get(type); if (ret == null) { checkLocalNamespaceAllowed(type); ret = new HashMap<>(1); switch (namespaces.size()) { case 0: // We typically have small population of namespaces, use a singleton map namespaces = ImmutableMap.of(type, ret); break; case 1: // Alright, time to grow to a full HashMap final Map, Map> newNamespaces = new HashMap<>(4); final Entry, Map> entry = namespaces.entrySet().iterator().next(); newNamespaces.put(entry.getKey(), entry.getValue()); namespaces = newNamespaces; // fall through default: // Already expanded, just put the new namespace namespaces.put(type, ret); } } return ret; } }