2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.yangtools.yang.model.util;
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;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.Date;
27 import java.util.List;
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;
39 public final class FilteringSchemaContextProxy extends AbstractSchemaContext {
41 //collection to be filled with filtered modules
42 private final Set<Module> filteredModules;
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;
50 * Filters SchemaContext for yang modules.
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
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.");
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);
68 ImmutableMap.Builder<ModuleIdentifier, String> identifiersToSourcesBuilder = ImmutableMap.builder();
70 //preparing map to get all modules with one name but difference in revision
71 final TreeMultimap<String, Module> nameToModulesAll = getStringModuleTreeMultimap();
73 nameToModulesAll.putAll(getStringModuleMap(delegate));
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);
79 //adding additional modules
80 processForAdditionalModules(delegate, additionalModuleIds, filteredModulesBuilder);
82 filteredModulesBuilder.addAll(getImportedModules(
83 Maps.uniqueIndex(delegate.getModules(), ModuleId.MODULE_TO_MODULE_ID::apply),
84 filteredModulesBuilder.build(), nameToModulesAll));
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.
90 this.filteredModules = filteredModulesBuilder.build();
92 for (final Module module :filteredModules) {
93 nameMap.put(module.getName(), module);
94 nsMap.put(module.getNamespace(), module);
95 identifiersToSourcesBuilder.put(module, module.getSource());
98 namespaceToModules = ImmutableSetMultimap.copyOf(nsMap);
99 nameToModules = ImmutableSetMultimap.copyOf(nameMap);
100 identifiersToSources = identifiersToSourcesBuilder.build();
103 private static TreeMultimap<String, Module> getStringModuleTreeMultimap() {
104 return TreeMultimap.create(String::compareTo, REVISION_COMPARATOR);
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)));
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)));
119 private static Multimap<String, Module> getStringModuleMap(final SchemaContext delegate) {
120 return Multimaps.index(delegate.getModules(), Module::getName);
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) {
127 List<Module> relatedModules = Lists.newLinkedList();
129 for (Module module : baseModules) {
130 for (ModuleImport moduleImport : module.getImports()) {
132 Date revisionDate = moduleImport.getRevision() == null
133 ? nameToModulesAll.get(moduleImport.getModuleName()).first().getRevision()
134 : moduleImport.getRevision();
136 ModuleId key = new ModuleId(moduleImport.getModuleName(),revisionDate);
137 Module importedModule = allModules.get(key);
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);
144 //calling imports recursive
145 relatedModules.addAll(getImportedModules(allModules, Collections.singleton(importedModule),
150 return relatedModules;
154 protected Map<ModuleIdentifier, String> getIdentifiersToSources() {
155 return identifiersToSources;
159 public Set<Module> getModules() {
160 return filteredModules;
164 protected SetMultimap<URI, Module> getNamespaceToModules() {
165 return namespaceToModules;
169 protected SetMultimap<String, Module> getNameToModules() {
170 return nameToModules;
173 private static boolean selectAdditionalModules(final Module module, final Set<ModuleId> additionalModules) {
174 return additionalModules.contains(new ModuleId(module.getName(), module.getRevision()));
177 //check for any dependency regarding given string
178 private boolean checkModuleDependency(final Module module, final Collection<ModuleId> rootModules) {
180 for (ModuleId rootModule : rootModules) {
182 if (rootModule.equals(new ModuleId(module.getName(), module.getRevision()))) {
186 //handling/checking imports regarding root modules
187 for (ModuleImport moduleImport : module.getImports()) {
189 if (moduleImport.getModuleName().equals(rootModule.getName())) {
191 if (moduleImport.getRevision() != null && !moduleImport.getRevision().equals(rootModule.getRev())) {
199 //submodules handling
200 for (Module moduleSub : module.getSubmodules()) {
201 return checkModuleDependency(moduleSub, rootModules);
209 public String toString() {
210 return String.format("SchemaContextProxyImpl{filteredModules=%s}", filteredModules);
213 public static final class ModuleId {
214 private final String name;
215 private final Date rev;
217 public ModuleId(final String name, final Date rev) {
218 Preconditions.checkArgument(!Strings.isNullOrEmpty(name),
219 "No module dependency name given. Nothing to do.");
221 this.rev = Preconditions.checkNotNull(rev, "No revision date given. Nothing to do.");
224 public String getName() {
228 public Date getRev() {
232 public static final Function<Module, ModuleId> MODULE_TO_MODULE_ID = input -> new ModuleId(input.getName(),
233 input.getRevision());
236 public boolean equals(final Object obj) {
240 if (!(obj instanceof ModuleId)) {
244 ModuleId moduleId = (ModuleId) obj;
245 if (name != null ? !name.equals(moduleId.name) : moduleId.name != null) {
248 if (rev != null ? !rev.equals(moduleId.rev) : moduleId.rev != null) {
256 public int hashCode() {
257 int result = name != null ? name.hashCode() : 0;
258 result = 31 * result + (rev != null ? rev.hashCode() : 0);
263 public String toString() {
265 return String.format("ModuleId{name='%s', rev=%s}",name,rev);