/* * 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.model.spi; import static java.util.Objects.requireNonNull; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimaps; import com.google.common.collect.SetMultimap; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.TreeSet; import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.common.Revision; import org.opendaylight.yangtools.yang.common.XMLNamespace; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition; import org.opendaylight.yangtools.yang.model.api.GroupingDefinition; import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; import org.opendaylight.yangtools.yang.model.api.RpcDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode; public abstract class AbstractSchemaContext implements SchemaContext { /** * A {@link Module} comparator based on {@link Module#getRevision()}, placing latest revision first. Note this * comparator does not take into account module name and so two modules with different names but same revisions * compare as equal. */ public static final Comparator REVISION_COMPARATOR = (first, second) -> Revision.compare(second.getRevision(), first.getRevision()); /** * A {@link Module} comparator based on {@link Module#getName()} and {@link Module#getRevision()}, ordering modules * lexicographically by their name and then in order of descending revision. This comparator assumes that * the combination of these two attributes is sufficient to be consistent with hashCode/equals. */ public static final Comparator NAME_REVISION_COMPARATOR = (first, second) -> { final int cmp = first.getName().compareTo(second.getName()); return cmp != 0 ? cmp : REVISION_COMPARATOR.compare(first, second); }; /** * Create a TreeSet for containing Modules with the same name, such that the set is ordered * by {@link #REVISION_COMPARATOR}. * * @return A fresh TreeSet instance. */ protected static final TreeSet createModuleSet() { return new TreeSet<>(REVISION_COMPARATOR); } private static final VarHandle DERIVED_IDENTITIES; static { try { DERIVED_IDENTITIES = MethodHandles.lookup().findVarHandle(AbstractSchemaContext.class, "derivedIdentities", ImmutableMap.class); } catch (NoSuchFieldException | IllegalAccessException e) { throw new ExceptionInInitializerError(e); } } // Accessed via DERIVED_IDENTITIES @SuppressWarnings("unused") private volatile ImmutableMap> derivedIdentities = null; /** * Returns the namespace-to-module mapping. * * @return Map of modules where key is namespace */ protected abstract SetMultimap getNamespaceToModules(); /** * Returns the module name-to-module mapping. * * @return Map of modules where key is name of module */ protected abstract SetMultimap getNameToModules(); /** * Returns the namespace+revision-to-module mapping. * * @return Map of modules where key is Module's QNameModule. */ protected abstract Map getModuleMap(); @Override public Collection getDataDefinitions() { final Set dataDefs = new HashSet<>(); for (Module m : getModules()) { dataDefs.addAll(m.getChildNodes()); } return dataDefs; } @Override public Collection getNotifications() { final Set notifications = new HashSet<>(); for (Module m : getModules()) { notifications.addAll(m.getNotifications()); } return notifications; } @Override public Collection getOperations() { final Set rpcs = new HashSet<>(); for (Module m : getModules()) { rpcs.addAll(m.getRpcs()); } return rpcs; } @Override public Collection getExtensions() { final Set extensions = new HashSet<>(); for (Module m : getModules()) { extensions.addAll(m.getExtensionSchemaNodes()); } return extensions; } @Override public Optional findModule(final String name, final Optional revision) { for (final Module module : getNameToModules().get(name)) { if (revision.equals(module.getRevision())) { return Optional.of(module); } } return Optional.empty(); } @Override public Optional findModule(final QNameModule qnameModule) { return Optional.ofNullable(getModuleMap().get(qnameModule)); } @Override public Collection findModules(final XMLNamespace namespace) { return getNamespaceToModules().get(namespace); } @Override public Collection findModules(final String name) { return getNameToModules().get(name); } @Override public Collection getUnknownSchemaNodes() { final List result = new ArrayList<>(); for (Module module : getModules()) { result.addAll(module.getUnknownSchemaNodes()); } return Collections.unmodifiableList(result); } @Override public Collection> getTypeDefinitions() { final Set> result = new LinkedHashSet<>(); for (Module module : getModules()) { result.addAll(module.getTypeDefinitions()); } return Collections.unmodifiableSet(result); } @Override public Collection getChildNodes() { final Set result = new LinkedHashSet<>(); for (Module module : getModules()) { result.addAll(module.getChildNodes()); } return Collections.unmodifiableSet(result); } @Override public Collection getGroupings() { final Set result = new LinkedHashSet<>(); for (Module module : getModules()) { result.addAll(module.getGroupings()); } return Collections.unmodifiableSet(result); } @Override public DataSchemaNode dataChildByName(final QName name) { requireNonNull(name); for (Module module : getModules()) { final DataSchemaNode result = module.dataChildByName(name); if (result != null) { return result; } } return null; } @Override public Collection getDerivedIdentities(final IdentitySchemaNode identity) { ImmutableMap> local = (ImmutableMap>) DERIVED_IDENTITIES.getAcquire(this); if (local == null) { local = loadDerivedIdentities(); } final ImmutableSet result = local.get(requireNonNull(identity)); if (result == null) { throw new IllegalArgumentException("Identity " + identity + " not found"); } return result; } private ImmutableMap> loadDerivedIdentities() { final SetMultimap tmp = Multimaps.newSetMultimap(new HashMap<>(), HashSet::new); final List identities = new ArrayList<>(); for (Module module : getModules()) { final Collection ids = module.getIdentities(); for (IdentitySchemaNode identity : ids) { for (IdentitySchemaNode base : identity.getBaseIdentities()) { tmp.put(base, identity); } } identities.addAll(ids); } final ImmutableMap.Builder> builder = ImmutableMap.builderWithExpectedSize(identities.size()); for (IdentitySchemaNode identity : identities) { builder.put(identity, ImmutableSet.copyOf(tmp.get(identity))); } final ImmutableMap> result = builder.build(); final Object witness = DERIVED_IDENTITIES.compareAndExchangeRelease(this, null, result); return witness == null ? result : (ImmutableMap>) witness; } }