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