05c1eb6687e25345b8d377d5ea9e3175a9d34408
[yangtools.git] / model / yang-model-spi / src / main / java / org / opendaylight / yangtools / yang / model / spi / AbstractSchemaContext.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.model.spi;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.collect.ImmutableMap;
13 import com.google.common.collect.ImmutableSet;
14 import com.google.common.collect.Multimaps;
15 import com.google.common.collect.SetMultimap;
16 import java.lang.invoke.MethodHandles;
17 import java.lang.invoke.VarHandle;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.Comparator;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.LinkedHashSet;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Optional;
28 import java.util.Set;
29 import java.util.TreeSet;
30 import org.eclipse.jdt.annotation.NonNull;
31 import org.opendaylight.yangtools.yang.common.QName;
32 import org.opendaylight.yangtools.yang.common.QNameModule;
33 import org.opendaylight.yangtools.yang.common.Revision;
34 import org.opendaylight.yangtools.yang.common.XMLNamespace;
35 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
37 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
38 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.Module;
40 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
41 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
42 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
43 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
44 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
45
46 public abstract class AbstractSchemaContext implements SchemaContext {
47     /**
48      * A {@link Module} comparator based on {@link Module#getRevision()}, placing latest revision first. Note this
49      * comparator does not take into account module name and so two modules with different names but same revisions
50      * compare as equal.
51      */
52     public static final Comparator<Module> REVISION_COMPARATOR =
53         (first, second) -> Revision.compare(second.getRevision(), first.getRevision());
54
55     /**
56      * A {@link Module} comparator based on {@link Module#getName()} and {@link Module#getRevision()}, ordering modules
57      * lexicographically by their name and then in order of descending revision. This comparator assumes that
58      * the combination of these two attributes is sufficient to be consistent with hashCode/equals.
59      */
60     public static final Comparator<Module> NAME_REVISION_COMPARATOR = (first, second) -> {
61         final int cmp = first.getName().compareTo(second.getName());
62         return cmp != 0 ? cmp : REVISION_COMPARATOR.compare(first, second);
63     };
64
65     /**
66      * Create a TreeSet for containing Modules with the same name, such that the set is ordered
67      * by {@link #REVISION_COMPARATOR}.
68      *
69      * @return A fresh TreeSet instance.
70      */
71     protected static final TreeSet<Module> createModuleSet() {
72         return new TreeSet<>(REVISION_COMPARATOR);
73     }
74
75     private static final VarHandle DERIVED_IDENTITIES;
76
77     static {
78         try {
79             DERIVED_IDENTITIES = MethodHandles.lookup().findVarHandle(AbstractSchemaContext.class, "derivedIdentities",
80                 ImmutableMap.class);
81         } catch (NoSuchFieldException | IllegalAccessException e) {
82             throw new ExceptionInInitializerError(e);
83         }
84     }
85
86     // Accessed via DERIVED_IDENTITIES
87     @SuppressWarnings("unused")
88     private volatile ImmutableMap<IdentitySchemaNode, ImmutableSet<IdentitySchemaNode>> derivedIdentities = null;
89
90     /**
91      * Returns the namespace-to-module mapping.
92      *
93      * @return Map of modules where key is namespace
94      */
95     protected abstract SetMultimap<XMLNamespace, Module> getNamespaceToModules();
96
97     /**
98      * Returns the module name-to-module mapping.
99      *
100      * @return Map of modules where key is name of module
101      */
102     protected abstract SetMultimap<String, Module> getNameToModules();
103
104     /**
105      * Returns the namespace+revision-to-module mapping.
106      *
107      * @return Map of modules where key is Module's QNameModule.
108      */
109     protected abstract Map<QNameModule, Module> getModuleMap();
110
111     @Override
112     public Collection<? extends DataSchemaNode> getDataDefinitions() {
113         final Set<DataSchemaNode> dataDefs = new HashSet<>();
114         for (Module m : getModules()) {
115             dataDefs.addAll(m.getChildNodes());
116         }
117         return dataDefs;
118     }
119
120     @Override
121     public Collection<? extends NotificationDefinition> getNotifications() {
122         final Set<NotificationDefinition> notifications = new HashSet<>();
123         for (Module m : getModules()) {
124             notifications.addAll(m.getNotifications());
125         }
126         return notifications;
127     }
128
129     @Override
130     public Collection<? extends RpcDefinition> getOperations() {
131         final Set<RpcDefinition> rpcs = new HashSet<>();
132         for (Module m : getModules()) {
133             rpcs.addAll(m.getRpcs());
134         }
135         return rpcs;
136     }
137
138     @Override
139     public Collection<? extends ExtensionDefinition> getExtensions() {
140         final Set<ExtensionDefinition> extensions = new HashSet<>();
141         for (Module m : getModules()) {
142             extensions.addAll(m.getExtensionSchemaNodes());
143         }
144         return extensions;
145     }
146
147     @Override
148     public Optional<? extends Module> findModule(final String name, final Optional<Revision> revision) {
149         for (final Module module : getNameToModules().get(name)) {
150             if (revision.equals(module.getRevision())) {
151                 return Optional.of(module);
152             }
153         }
154
155         return Optional.empty();
156     }
157
158     @Override
159     public Optional<Module> findModule(final QNameModule qnameModule) {
160         return Optional.ofNullable(getModuleMap().get(qnameModule));
161     }
162
163     @Override
164     public Collection<? extends Module> findModules(final XMLNamespace namespace) {
165         return getNamespaceToModules().get(namespace);
166     }
167
168     @Override
169     public Collection<? extends Module> findModules(final String name) {
170         return getNameToModules().get(name);
171     }
172
173     @Override
174     public Collection<? extends UnknownSchemaNode> getUnknownSchemaNodes() {
175         final List<UnknownSchemaNode> result = new ArrayList<>();
176         for (Module module : getModules()) {
177             result.addAll(module.getUnknownSchemaNodes());
178         }
179         return Collections.unmodifiableList(result);
180     }
181
182     @Override
183     public Collection<? extends TypeDefinition<?>> getTypeDefinitions() {
184         final Set<TypeDefinition<?>> result = new LinkedHashSet<>();
185         for (Module module : getModules()) {
186             result.addAll(module.getTypeDefinitions());
187         }
188         return Collections.unmodifiableSet(result);
189     }
190
191     @Override
192     public Collection<? extends DataSchemaNode> getChildNodes() {
193         final Set<DataSchemaNode> result = new LinkedHashSet<>();
194         for (Module module : getModules()) {
195             result.addAll(module.getChildNodes());
196         }
197         return Collections.unmodifiableSet(result);
198     }
199
200     @Override
201     public Collection<? extends GroupingDefinition> getGroupings() {
202         final Set<GroupingDefinition> result = new LinkedHashSet<>();
203         for (Module module : getModules()) {
204             result.addAll(module.getGroupings());
205         }
206         return Collections.unmodifiableSet(result);
207     }
208
209     @Override
210     public DataSchemaNode dataChildByName(final QName name) {
211         requireNonNull(name);
212         for (Module module : getModules()) {
213             final DataSchemaNode result = module.dataChildByName(name);
214             if (result != null) {
215                 return result;
216             }
217         }
218         return null;
219     }
220
221     @Override
222     public Collection<? extends IdentitySchemaNode> getDerivedIdentities(final IdentitySchemaNode identity) {
223         ImmutableMap<IdentitySchemaNode, ImmutableSet<IdentitySchemaNode>> local =
224                 (ImmutableMap<IdentitySchemaNode, ImmutableSet<IdentitySchemaNode>>)
225                 DERIVED_IDENTITIES.getAcquire(this);
226         if (local == null) {
227             local = loadDerivedIdentities();
228         }
229         final ImmutableSet<IdentitySchemaNode> result = local.get(requireNonNull(identity));
230         if (result == null) {
231             throw new IllegalArgumentException("Identity " + identity + " not found");
232         }
233         return result;
234     }
235
236     private ImmutableMap<IdentitySchemaNode, ImmutableSet<IdentitySchemaNode>> loadDerivedIdentities() {
237         final SetMultimap<IdentitySchemaNode, IdentitySchemaNode> tmp =
238                 Multimaps.newSetMultimap(new HashMap<>(), HashSet::new);
239         final List<IdentitySchemaNode> identities = new ArrayList<>();
240         for (Module module : getModules()) {
241             final Collection<? extends @NonNull IdentitySchemaNode> ids = module.getIdentities();
242             for (IdentitySchemaNode identity : ids) {
243                 for (IdentitySchemaNode base : identity.getBaseIdentities()) {
244                     tmp.put(base, identity);
245                 }
246             }
247             identities.addAll(ids);
248         }
249
250         final ImmutableMap.Builder<IdentitySchemaNode, ImmutableSet<IdentitySchemaNode>> builder =
251                 ImmutableMap.builderWithExpectedSize(identities.size());
252         for (IdentitySchemaNode identity : identities) {
253             builder.put(identity, ImmutableSet.copyOf(tmp.get(identity)));
254         }
255
256         final ImmutableMap<IdentitySchemaNode, ImmutableSet<IdentitySchemaNode>> result = builder.build();
257         final Object witness = DERIVED_IDENTITIES.compareAndExchangeRelease(this, null, result);
258         return witness == null ? result : (ImmutableMap<IdentitySchemaNode, ImmutableSet<IdentitySchemaNode>>) witness;
259     }
260 }