BUG-1281: fix SchemaContext module ordering
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / impl / SchemaContextImpl.java
1 /*
2  * Copyright (c) 2013 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.parser.impl;
9
10 import com.google.common.base.Optional;
11 import com.google.common.base.Supplier;
12 import com.google.common.collect.ImmutableMap;
13 import com.google.common.collect.ImmutableSet;
14 import com.google.common.collect.ImmutableSetMultimap;
15 import com.google.common.collect.Multimaps;
16 import com.google.common.collect.SetMultimap;
17
18 import java.net.URI;
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.Comparator;
23 import java.util.Date;
24 import java.util.HashSet;
25 import java.util.LinkedHashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.TreeMap;
30 import java.util.TreeSet;
31
32 import javax.annotation.concurrent.Immutable;
33
34 import org.opendaylight.yangtools.yang.common.QName;
35 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
36 import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
37 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
39 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
40 import org.opendaylight.yangtools.yang.model.api.Module;
41 import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
42 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
43 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
44 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
45 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
46 import org.opendaylight.yangtools.yang.model.api.Status;
47 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
48 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
49 import org.opendaylight.yangtools.yang.model.api.UsesNode;
50 import org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort;
51
52 @Immutable
53 final class SchemaContextImpl implements SchemaContext {
54     private static final Comparator<Module> REVISION_COMPARATOR = new Comparator<Module>() {
55         @Override
56         public int compare(final Module o1, final Module o2) {
57             if (o2.getRevision() == null) {
58                 return -1;
59             }
60
61             return o2.getRevision().compareTo(o1.getRevision());
62         }
63     };
64
65     private static final Supplier<TreeSet<Module>> MODULE_SET_SUPPLIER = new Supplier<TreeSet<Module>>() {
66         @Override
67         public TreeSet<Module> get() {
68             return new TreeSet<>(REVISION_COMPARATOR);
69         }
70     };
71
72     private final Map<ModuleIdentifier, String> identifiersToSources;
73     private final SetMultimap<URI, Module> namespaceToModules;
74     private final SetMultimap<String, Module> nameToModules;
75     private final Set<Module> modules;
76
77     SchemaContextImpl(final Set<Module> modules, final Map<ModuleIdentifier, String> identifiersToSources) {
78         this.identifiersToSources = ImmutableMap.copyOf(identifiersToSources);
79
80         /*
81          * Instead of doing this on each invocation of getModules(), pre-compute
82          * it once and keep it around -- better than the set we got in.
83          */
84         this.modules = ImmutableSet.copyOf(ModuleDependencySort.sort(modules.toArray(new Module[modules.size()])));
85
86         /*
87          * The most common lookup is from Namespace->Module.
88          *
89          * RESTCONF performs lookups based on module name only, where it wants
90          * to receive the latest revision
91          *
92          * Invest some quality time in building up lookup tables for both.
93          */
94         final SetMultimap<URI, Module> nsMap = Multimaps.newSetMultimap(
95                 new TreeMap<URI, Collection<Module>>(), MODULE_SET_SUPPLIER);
96         final SetMultimap<String, Module> nameMap = Multimaps.newSetMultimap(
97                 new TreeMap<String, Collection<Module>>(), MODULE_SET_SUPPLIER);
98         for (Module m : modules) {
99             nameMap.put(m.getName(), m);
100             nsMap.put(m.getNamespace(), m);
101         }
102
103         namespaceToModules = ImmutableSetMultimap.copyOf(nsMap);
104         nameToModules = ImmutableSetMultimap.copyOf(nameMap);
105     }
106
107     @Override
108     public Set<DataSchemaNode> getDataDefinitions() {
109         final Set<DataSchemaNode> dataDefs = new HashSet<>();
110         for (Module m : modules) {
111             dataDefs.addAll(m.getChildNodes());
112         }
113         return dataDefs;
114     }
115
116     @Override
117     public Set<Module> getModules() {
118         return modules;
119     }
120
121     @Override
122     public Set<NotificationDefinition> getNotifications() {
123         final Set<NotificationDefinition> notifications = new HashSet<>();
124         for (Module m : modules) {
125             notifications.addAll(m.getNotifications());
126         }
127         return notifications;
128     }
129
130     @Override
131     public Set<RpcDefinition> getOperations() {
132         final Set<RpcDefinition> rpcs = new HashSet<>();
133         for (Module m : modules) {
134             rpcs.addAll(m.getRpcs());
135         }
136         return rpcs;
137     }
138
139     @Override
140     public Set<ExtensionDefinition> getExtensions() {
141         final Set<ExtensionDefinition> extensions = new HashSet<>();
142         for (Module m : modules) {
143             extensions.addAll(m.getExtensionSchemaNodes());
144         }
145         return extensions;
146     }
147
148     @Override
149     public Module findModuleByName(final String name, final Date revision) {
150         for (final Module module : nameToModules.get(name)) {
151             if (revision == null || revision.equals(module.getRevision())) {
152                 return module;
153             }
154         }
155
156         return null;
157     }
158
159     @Override
160     public Set<Module> findModuleByNamespace(final URI namespace) {
161         final Set<Module> ret = namespaceToModules.get(namespace);
162         return ret == null ? Collections.<Module>emptySet() : ret;
163     }
164
165     @Override
166     public Module findModuleByNamespaceAndRevision(final URI namespace, final Date revision) {
167         if (namespace == null) {
168             return null;
169         }
170         for (Module module : findModuleByNamespace(namespace)) {
171             if (revision == null || revision.equals(module.getRevision())) {
172                 return module;
173             }
174         }
175         return null;
176     }
177
178     @Override
179     public boolean isAugmenting() {
180         return false;
181     }
182
183     @Override
184     public boolean isAddedByUses() {
185         return false;
186     }
187
188     @Override
189     public boolean isConfiguration() {
190         return false;
191     }
192
193     @Override
194     public ConstraintDefinition getConstraints() {
195         return null;
196     }
197
198     @Override
199     public QName getQName() {
200         return SchemaContext.NAME;
201     }
202
203     @Override
204     public SchemaPath getPath() {
205         return null;
206     }
207
208     @Override
209     public String getDescription() {
210         return null;
211     }
212
213     @Override
214     public String getReference() {
215         return null;
216     }
217
218     @Override
219     public Status getStatus() {
220         return Status.CURRENT;
221     }
222
223     @Override
224     public List<UnknownSchemaNode> getUnknownSchemaNodes() {
225         final List<UnknownSchemaNode> result = new ArrayList<>();
226         for (Module module : modules) {
227             result.addAll(module.getUnknownSchemaNodes());
228         }
229         return Collections.unmodifiableList(result);
230     }
231
232     @Override
233     public Set<TypeDefinition<?>> getTypeDefinitions() {
234         final Set<TypeDefinition<?>> result = new LinkedHashSet<>();
235         for (Module module : modules) {
236             result.addAll(module.getTypeDefinitions());
237         }
238         return Collections.unmodifiableSet(result);
239     }
240
241     @Override
242     public Set<DataSchemaNode> getChildNodes() {
243         final Set<DataSchemaNode> result = new LinkedHashSet<>();
244         for (Module module : modules) {
245             result.addAll(module.getChildNodes());
246         }
247         return Collections.unmodifiableSet(result);
248     }
249
250     @Override
251     public Set<GroupingDefinition> getGroupings() {
252         final Set<GroupingDefinition> result = new LinkedHashSet<>();
253         for (Module module : modules) {
254             result.addAll(module.getGroupings());
255         }
256         return Collections.unmodifiableSet(result);
257     }
258
259     @Override
260     public DataSchemaNode getDataChildByName(final QName name) {
261         for (Module module : modules) {
262             final DataSchemaNode result = module.getDataChildByName(name);
263             if (result != null) {
264                 return result;
265             }
266         }
267         return null;
268     }
269
270     @Override
271     public DataSchemaNode getDataChildByName(final String name) {
272         for (Module module : modules) {
273             final DataSchemaNode result = module.getDataChildByName(name);
274             if (result != null) {
275                 return result;
276             }
277         }
278         return null;
279     }
280
281     @Override
282     public Set<UsesNode> getUses() {
283         return Collections.emptySet();
284     }
285
286     @Override
287     public boolean isPresenceContainer() {
288         return false;
289     }
290
291     @Override
292     public Set<AugmentationSchema> getAvailableAugmentations() {
293         return Collections.emptySet();
294     }
295
296     //FIXME: should work for submodules too
297     @Override
298     public Set<ModuleIdentifier> getAllModuleIdentifiers() {
299         return identifiersToSources.keySet();
300     }
301
302     @Override
303     public Optional<String> getModuleSource(final ModuleIdentifier moduleIdentifier) {
304         String maybeSource = identifiersToSources.get(moduleIdentifier);
305         return Optional.fromNullable(maybeSource);
306     }
307
308     @Override
309     public String toString() {
310         return "SchemaContextImpl{" +
311                 "modules=" + modules +
312                 '}';
313     }
314 }