Drop unneeded generic type specifiers
[yangtools.git] / yang / yang-model-util / src / main / java / org / opendaylight / yangtools / yang / model / util / FilteringSchemaContextProxy.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
9 package org.opendaylight.yangtools.yang.model.util;
10
11 import com.google.common.base.Function;
12 import com.google.common.base.Preconditions;
13 import com.google.common.base.Predicate;
14 import com.google.common.base.Strings;
15 import com.google.common.collect.Collections2;
16 import com.google.common.collect.ImmutableMap;
17 import com.google.common.collect.ImmutableSet.Builder;
18 import com.google.common.collect.ImmutableSetMultimap;
19 import com.google.common.collect.Lists;
20 import com.google.common.collect.Maps;
21 import com.google.common.collect.Multimap;
22 import com.google.common.collect.Multimaps;
23 import com.google.common.collect.SetMultimap;
24 import com.google.common.collect.TreeMultimap;
25 import java.net.URI;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.Comparator;
29 import java.util.Date;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Set;
33 import java.util.TreeMap;
34 import javax.annotation.Nullable;
35 import javax.annotation.concurrent.Immutable;
36 import org.opendaylight.yangtools.yang.model.api.Module;
37 import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
38 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
39 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
40
41 @Immutable
42 public final class FilteringSchemaContextProxy extends AbstractSchemaContext {
43
44     //collection to be filled with filtered modules
45     private final Set<Module> filteredModules;
46
47     //collections to be filled in with filtered data
48     private final Map<ModuleIdentifier, String> identifiersToSources;
49     private final SetMultimap<URI, Module> namespaceToModules;
50     private final SetMultimap<String, Module> nameToModules;
51
52     /**
53      * Filters SchemaContext for yang modules
54      *
55      * @param delegate original SchemaContext
56      * @param rootModules modules (yang schemas) to be available and all their dependencies (modules importing rootModule and whole chain of their imports)
57      * @param additionalModuleIds (additional) modules (yang schemas) to be available and whole chain of their imports
58      *
59      */
60     public FilteringSchemaContextProxy(final SchemaContext delegate, final Collection<ModuleId> rootModules, final Set<ModuleId> additionalModuleIds) {
61
62         Preconditions.checkArgument(rootModules!=null,"Base modules cannot be null.");
63         Preconditions.checkArgument(additionalModuleIds!=null,"Additional modules cannot be null.");
64
65         final Builder<Module> filteredModulesBuilder = new Builder<>();
66
67         final SetMultimap<URI, Module> nsMap = Multimaps.newSetMultimap(new TreeMap<>(), MODULE_SET_SUPPLIER);
68         final SetMultimap<String, Module> nameMap = Multimaps.newSetMultimap(new TreeMap<>(), MODULE_SET_SUPPLIER);
69
70         ImmutableMap.Builder<ModuleIdentifier, String> identifiersToSourcesBuilder = ImmutableMap.builder();
71
72         //preparing map to get all modules with one name but difference in revision
73         final TreeMultimap<String, Module> nameToModulesAll = getStringModuleTreeMultimap();
74
75         nameToModulesAll.putAll(getStringModuleMap(delegate));
76
77         //in case there is a particular dependancy to view filteredModules/yang models
78         //dependancy is checked for module name and imports
79         processForRootModules(delegate, rootModules, filteredModulesBuilder);
80
81         //adding additional modules
82         processForAdditionalModules(delegate, additionalModuleIds, filteredModulesBuilder);
83
84         filteredModulesBuilder.addAll(getImportedModules(
85                 Maps.uniqueIndex(delegate.getModules(), ModuleId.MODULE_TO_MODULE_ID), filteredModulesBuilder.build(), nameToModulesAll));
86
87         /**
88          * Instead of doing this on each invocation of getModules(), pre-compute
89          * it once and keep it around -- better than the set we got in.
90          */
91         this.filteredModules = filteredModulesBuilder.build();
92
93         for (final Module module :filteredModules) {
94             nameMap.put(module.getName(), module);
95             nsMap.put(module.getNamespace(), module);
96             identifiersToSourcesBuilder.put(module, module.getSource());
97         }
98
99         namespaceToModules = ImmutableSetMultimap.copyOf(nsMap);
100         nameToModules = ImmutableSetMultimap.copyOf(nameMap);
101         identifiersToSources = identifiersToSourcesBuilder.build();
102     }
103
104     private static TreeMultimap<String, Module> getStringModuleTreeMultimap() {
105         return TreeMultimap.create(new Comparator<String>() {
106                 @Override
107                 public int compare(final String o1, final String o2) {
108                     return o1.compareTo(o2);
109                 }
110             }, REVISION_COMPARATOR);
111     }
112
113     private static void processForAdditionalModules(final SchemaContext delegate,
114             final Set<ModuleId> additionalModuleIds, final Builder<Module> filteredModulesBuilder) {
115         filteredModulesBuilder.addAll(Collections2.filter(delegate.getModules(), new Predicate<Module>() {
116             @Override
117             public boolean apply(@Nullable final Module module) {
118                 return selectAdditionalModules(module, additionalModuleIds);
119             }
120         }));
121     }
122
123     private void processForRootModules(final SchemaContext delegate, final Collection<ModuleId> rootModules, final Builder<Module> filteredModulesBuilder) {
124         filteredModulesBuilder.addAll(Collections2.filter(delegate.getModules(), new Predicate<Module>() {
125             @Override
126             public boolean apply(@Nullable final Module module) {
127                 return checkModuleDependency(module, rootModules);
128             }
129         }));
130     }
131
132     private static Multimap<String, Module> getStringModuleMap(final SchemaContext delegate) {
133         return Multimaps.index(delegate.getModules(), new Function<Module, String>() {
134             @Override
135             public String apply(final Module input) {
136                 return input.getName();
137             }
138         });
139     }
140
141     //dealing with imported module other than root and directly importing root
142     private static Collection<Module> getImportedModules(final Map<ModuleId, Module> allModules, final Set<Module> baseModules, final TreeMultimap<String, Module> nameToModulesAll) {
143
144         List<Module> relatedModules = Lists.newLinkedList();
145
146         for (Module module : baseModules) {
147             for (ModuleImport moduleImport : module.getImports()) {
148
149                 Date revisionDate = moduleImport.getRevision() == null ?
150                         nameToModulesAll.get(moduleImport.getModuleName()).first().getRevision() : moduleImport.getRevision();
151
152                 ModuleId key = new ModuleId(moduleImport.getModuleName(),revisionDate);
153                 Module importedModule = allModules.get(key);
154
155                 Preconditions.checkArgument(importedModule != null,  "Invalid schema, cannot find imported module: %s from module: %s, %s, modules:%s", key, module.getQNameModule(), module.getName() );
156                 relatedModules.add(importedModule);
157
158                 //calling imports recursive
159                 relatedModules.addAll(getImportedModules(allModules, Collections.singleton(importedModule), nameToModulesAll));
160
161             }
162         }
163
164         return relatedModules;
165     }
166
167     @Override
168     protected Map<ModuleIdentifier, String> getIdentifiersToSources() {
169         return identifiersToSources;
170     }
171
172     @Override
173     public Set<Module> getModules() {
174         return filteredModules;
175     }
176
177     @Override
178     protected SetMultimap<URI, Module> getNamespaceToModules() {
179         return namespaceToModules;
180     }
181
182     @Override
183     protected SetMultimap<String, Module> getNameToModules() {
184         return nameToModules;
185     }
186
187     private static boolean selectAdditionalModules(final Module module, final Set<ModuleId> additionalModules){
188         return additionalModules.contains(new ModuleId(module.getName(), module.getRevision()));
189     }
190
191     //check for any dependency regarding given string
192     private boolean checkModuleDependency(final Module module, final Collection<ModuleId> rootModules) {
193
194         for (ModuleId rootModule : rootModules) {
195
196             if(rootModule.equals(new ModuleId(module.getName(), module.getRevision()))) {
197                 return true;
198             }
199
200             //handling/checking imports regarding root modules
201             for (ModuleImport moduleImport : module.getImports()) {
202
203                 if(moduleImport.getModuleName().equals(rootModule.getName())) {
204
205                     if(moduleImport.getRevision() != null && !moduleImport.getRevision().equals(rootModule.getRev())) {
206                         return false;
207                     }
208
209                     return true;
210                 }
211             }
212
213             //submodules handling
214             for (Module moduleSub : module.getSubmodules()) {
215                 return checkModuleDependency(moduleSub, rootModules);
216             }
217         }
218
219         return false;
220     }
221
222     @Override
223     public String toString() {
224         return String.format("SchemaContextProxyImpl{filteredModules=%s}", filteredModules);
225     }
226
227     public static final class ModuleId {
228         private final String name;
229         private final Date rev;
230
231         public ModuleId(final String name, final Date rev) {
232             Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "No module dependency name given. Nothing to do.");
233             this.name = name;
234             this.rev = Preconditions.checkNotNull(rev, "No revision date given. Nothing to do.");
235         }
236
237         public String getName() {
238             return name;
239         }
240
241         public Date getRev() {
242             return rev;
243         }
244
245         public static final Function<Module, ModuleId> MODULE_TO_MODULE_ID = new Function<Module, ModuleId>() {
246             @Override
247             public ModuleId apply(final Module input) {
248                 return new ModuleId(input.getName(), input.getRevision());
249             }
250         };
251
252         @Override
253         public boolean equals(final Object o) {
254             if (this == o) {
255                 return true;
256             }
257             if (!(o instanceof ModuleId)) {
258                 return false;
259             }
260
261             ModuleId moduleId = (ModuleId) o;
262             if (name != null ? !name.equals(moduleId.name) : moduleId.name != null) {
263                 return false;
264             }
265             if (rev != null ? !rev.equals(moduleId.rev) : moduleId.rev != null) {
266                 return false;
267             }
268
269             return true;
270         }
271
272         @Override
273         public int hashCode() {
274             int result = name != null ? name.hashCode() : 0;
275             result = 31 * result + (rev != null ? rev.hashCode() : 0);
276             return result;
277         }
278
279         @Override
280         public String toString() {
281
282             return String.format("ModuleId{name='%s', rev=%s}",name,rev);
283         }
284     }
285 }