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.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;
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;
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;
42 public final class FilteringSchemaContextProxy extends AbstractSchemaContext {
44 //collection to be filled with filtered modules
45 private final Set<Module> filteredModules;
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;
53 * Filters SchemaContext for yang modules
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
60 public FilteringSchemaContextProxy(final SchemaContext delegate, final Collection<ModuleId> rootModules, final Set<ModuleId> additionalModuleIds) {
62 Preconditions.checkArgument(rootModules!=null,"Base modules cannot be null.");
63 Preconditions.checkArgument(additionalModuleIds!=null,"Additional modules cannot be null.");
65 final Builder<Module> filteredModulesBuilder = new Builder<>();
67 final SetMultimap<URI, Module> nsMap = Multimaps.newSetMultimap(new TreeMap<URI, Collection<Module>>(), MODULE_SET_SUPPLIER);
68 final SetMultimap<String, Module> nameMap = Multimaps.newSetMultimap(new TreeMap<String, Collection<Module>>(), MODULE_SET_SUPPLIER);
70 ImmutableMap.Builder<ModuleIdentifier, String> identifiersToSourcesBuilder = ImmutableMap.builder();
72 //preparing map to get all modules with one name but difference in revision
73 final TreeMultimap<String, Module> nameToModulesAll = getStringModuleTreeMultimap();
75 nameToModulesAll.putAll(getStringModuleMap(delegate));
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);
81 //adding additional modules
82 processForAdditionalModules(delegate, additionalModuleIds, filteredModulesBuilder);
84 filteredModulesBuilder.addAll(getImportedModules(
85 Maps.uniqueIndex(delegate.getModules(), ModuleId.MODULE_TO_MODULE_ID), filteredModulesBuilder.build(), nameToModulesAll));
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.
91 this.filteredModules = filteredModulesBuilder.build();
93 for (final Module module :filteredModules) {
94 nameMap.put(module.getName(), module);
95 nsMap.put(module.getNamespace(), module);
96 identifiersToSourcesBuilder.put(module, module.getSource());
99 namespaceToModules = ImmutableSetMultimap.copyOf(nsMap);
100 nameToModules = ImmutableSetMultimap.copyOf(nameMap);
101 identifiersToSources = identifiersToSourcesBuilder.build();
104 private static TreeMultimap<String, Module> getStringModuleTreeMultimap() {
105 return TreeMultimap.create(new Comparator<String>() {
107 public int compare(String o1, String o2) {
108 return o1.compareTo(o2);
110 }, REVISION_COMPARATOR);
113 private void processForAdditionalModules(SchemaContext delegate, final Set<ModuleId> additionalModuleIds, Builder<Module> filteredModulesBuilder) {
114 filteredModulesBuilder.addAll(Collections2.filter(delegate.getModules(), new Predicate<Module>() {
116 public boolean apply(@Nullable Module module) {
117 return selectAdditionalModules(module, additionalModuleIds);
122 private void processForRootModules(SchemaContext delegate, final Collection<ModuleId> rootModules, Builder<Module> filteredModulesBuilder) {
123 filteredModulesBuilder.addAll(Collections2.filter(delegate.getModules(), new Predicate<Module>() {
125 public boolean apply(@Nullable Module module) {
126 return checkModuleDependency(module, rootModules);
131 private Multimap<String, Module> getStringModuleMap(SchemaContext delegate) {
132 return Multimaps.index(delegate.getModules(), new Function<Module, String>() {
134 public String apply(Module input) {
135 return input.getName();
140 //dealing with imported module other than root and directly importing root
141 private static Collection<Module> getImportedModules(Map<ModuleId, Module> allModules, Set<Module> baseModules, TreeMultimap<String, Module> nameToModulesAll) {
143 List<Module> relatedModules = Lists.newLinkedList();
145 for (Module module : baseModules) {
146 for (ModuleImport moduleImport : module.getImports()) {
148 Date revisionDate = moduleImport.getRevision() == null ?
149 nameToModulesAll.get(moduleImport.getModuleName()).first().getRevision() : moduleImport.getRevision();
151 ModuleId key = new ModuleId(moduleImport.getModuleName(),revisionDate);
152 Module importedModule = allModules.get(key);
154 Preconditions.checkArgument(importedModule != null, "Invalid schema, cannot find imported module: %s from module: %s, %s, modules:%s", key, module.getQNameModule(), module.getName() );
155 relatedModules.add(importedModule);
157 //calling imports recursive
158 relatedModules.addAll(getImportedModules(allModules, Collections.singleton(importedModule), nameToModulesAll));
163 return relatedModules;
167 protected Map<ModuleIdentifier, String> getIdentifiersToSources() {
168 return identifiersToSources;
171 public Set<Module> getModules() {
172 return filteredModules;
176 protected SetMultimap<URI, Module> getNamespaceToModules() {
177 return namespaceToModules;
181 protected SetMultimap<String, Module> getNameToModules() {
182 return nameToModules;
185 private boolean selectAdditionalModules(Module module, Set<ModuleId> additionalModules){
187 if(additionalModules.contains(new ModuleId(module.getName(), module.getRevision()))){
195 //check for any dependency regarding given string
196 private boolean checkModuleDependency(Module module, Collection<ModuleId> rootModules) {
198 for (ModuleId rootModule : rootModules) {
200 if(rootModule.equals(new ModuleId(module.getName(), module.getRevision()))) {
204 //handling/checking imports regarding root modules
205 for (ModuleImport moduleImport : module.getImports()) {
207 if(moduleImport.getModuleName().equals(rootModule.getName())) {
209 if(moduleImport.getRevision() != null && !moduleImport.getRevision().equals(rootModule.getRev())) {
217 //submodules handling
218 for (Module moduleSub : module.getSubmodules()) {
219 return checkModuleDependency(moduleSub, rootModules);
227 public String toString() {
228 return String.format("SchemaContextProxyImpl{filteredModules=%s}", filteredModules);
231 public static final class ModuleId {
232 private final String name;
233 private final Date rev;
235 public ModuleId(String name, Date rev) {
236 Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "No module dependency name given. Nothing to do.");
238 this.rev = Preconditions.checkNotNull(rev, "No revision date given. Nothing to do.");
241 public String getName() {
245 public Date getRev() {
249 public static final Function<Module, ModuleId> MODULE_TO_MODULE_ID = new Function<Module, ModuleId>() {
251 public ModuleId apply(Module input) {
252 return new ModuleId(input.getName(), input.getRevision());
257 public boolean equals(Object o) {
261 if (!(o instanceof ModuleId)) {
265 ModuleId moduleId = (ModuleId) o;
266 if (name != null ? !name.equals(moduleId.name) : moduleId.name != null) {
269 if (rev != null ? !rev.equals(moduleId.rev) : moduleId.rev != null) {
277 public int hashCode() {
278 int result = name != null ? name.hashCode() : 0;
279 result = 31 * result + (rev != null ? rev.hashCode() : 0);
284 public String toString() {
286 return String.format("ModuleId{name='%s', rev=%s}",name,rev);