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