Added support for parsing submodules & added dependency utility parser
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / impl / util / YangSourceContext.java
1 package org.opendaylight.yangtools.yang.parser.impl.util;
2
3 import java.io.InputStream;
4 import java.util.Collections;
5 import java.util.HashMap;
6 import java.util.HashSet;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Set;
10
11 import org.opendaylight.yangtools.yang.common.QName;
12 import org.opendaylight.yangtools.yang.model.api.Module;
13 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
14 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
15 import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
16 import org.opendaylight.yangtools.yang.model.util.repo.SourceIdentifier;
17 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
20
21 import com.google.common.base.Optional;
22 import com.google.common.base.Preconditions;
23 import com.google.common.collect.ImmutableList;
24 import com.google.common.collect.ImmutableMap;
25 import com.google.common.collect.ImmutableMultimap;
26 import com.google.common.collect.ImmutableSet;
27 import com.google.common.collect.ImmutableSet.Builder;
28
29 public class YangSourceContext implements SchemaSourceProvider<InputStream>,AutoCloseable {
30
31     private final ImmutableSet<SourceIdentifier> validSources;
32
33
34     private final ImmutableSet<SourceIdentifier> missingSources;
35     private final ImmutableMultimap<SourceIdentifier, ModuleImport> missingDependencies;
36     private SchemaSourceProvider<InputStream> sourceProvider;
37
38     private YangSourceContext(ImmutableSet<SourceIdentifier> validSourcesSet,
39             ImmutableSet<SourceIdentifier> missingSourcesSet,
40             ImmutableMultimap<SourceIdentifier, ModuleImport> missingDependenciesMap, SchemaSourceProvider<InputStream> sourceProvicer) {
41         validSources = validSourcesSet;
42         missingSources = missingSourcesSet;
43         missingDependencies = missingDependenciesMap;
44         sourceProvider = sourceProvicer;
45     }
46
47     public ImmutableSet<SourceIdentifier> getValidSources() {
48         return validSources;
49     }
50
51     public ImmutableSet<SourceIdentifier> getMissingSources() {
52         return missingSources;
53     }
54
55     public ImmutableMultimap<SourceIdentifier, ModuleImport> getMissingDependencies() {
56         return missingDependencies;
57     }
58     
59     @Override
60     public Optional<InputStream> getSchemaSource(String moduleName, Optional<String> revision) {
61         return getSchemaSource(SourceIdentifier.create(moduleName,revision));
62     }
63     
64     @Override
65     public Optional<InputStream> getSchemaSource(SourceIdentifier sourceIdentifier) {
66         if(validSources.contains(sourceIdentifier)) {
67             return getDelegateChecked().getSchemaSource(sourceIdentifier);
68         }
69         return Optional.absent();
70     }
71     
72     private SchemaSourceProvider<InputStream> getDelegateChecked() {
73         Preconditions.checkState(sourceProvider != null,"Instance is already closed.");
74         return sourceProvider;
75     }
76
77     @Override
78     public void close() {
79         if(sourceProvider != null) {
80             sourceProvider = null;
81         }
82     }
83     
84     public static final YangSourceContext createFrom(Iterable<QName> capabilities,
85             SchemaSourceProvider<InputStream> schemaSourceProvider) {
86         YangSourceContextResolver resolver = new YangSourceFromCapabilitiesResolver(capabilities, schemaSourceProvider);
87         return resolver.resolveContext();
88     }
89     
90     public static final SchemaContext toSchemaContext(YangSourceContext context) {
91         List<InputStream> inputStreams =  getValidInputStreams(context);
92         YangParserImpl parser = new YangParserImpl();
93         Set<Module> models = parser.parseYangModelsFromStreams(inputStreams);
94         return parser.resolveSchemaContext(models);
95     }
96     
97     public static List<InputStream> getValidInputStreams(YangSourceContext context) {
98         return getValidInputStreams(context, context.sourceProvider);
99     }
100     
101     public static List<InputStream> getValidInputStreams(YangSourceContext context, SchemaSourceProvider<InputStream> provider) {
102         // TODO Auto-generated method stub
103         final HashSet<SourceIdentifier> sourcesToLoad = new HashSet<>();
104         sourcesToLoad.addAll(context.getValidSources());
105         for(SourceIdentifier source : context.getValidSources()) {
106             if(source.getRevision() != null) {
107                 SourceIdentifier sourceWithoutRevision = SourceIdentifier.create(source.getName(), Optional.<String>absent());
108                 sourcesToLoad.removeAll(Collections.singleton(sourceWithoutRevision));
109             }
110         }
111         
112         ImmutableList.Builder<InputStream> ret = ImmutableList.<InputStream>builder();
113         for(SourceIdentifier sourceIdentifier : sourcesToLoad) {
114             Optional<InputStream> source = provider.getSchemaSource(sourceIdentifier);
115             ret.add(source.get());
116         }
117         return ret.build();
118     }
119
120
121     public static abstract class YangSourceContextResolver {
122         
123         private static final Logger LOG = LoggerFactory.getLogger(YangSourceContextResolver.class);
124
125         private SchemaSourceProvider<InputStream> sourceProvicer;
126
127         private HashMap<SourceIdentifier, ResolutionState> alreadyProcessed = new HashMap<>();
128
129         private ImmutableSet.Builder<SourceIdentifier> missingSources = ImmutableSet.builder();
130
131         private ImmutableMultimap.Builder<SourceIdentifier, ModuleImport> missingDependencies = ImmutableMultimap
132                 .builder();
133
134         private ImmutableSet.Builder<SourceIdentifier> validSources = ImmutableSet.builder();
135
136         public YangSourceContextResolver(SchemaSourceProvider<InputStream> schemaSourceProvider) {
137             sourceProvicer = schemaSourceProvider;
138         }
139
140         public abstract YangSourceContext resolveContext();
141
142         public ResolutionState resolveSource(String name, Optional<String> formattedRevision) {
143             return resolveSource(new SourceIdentifier(name, formattedRevision));
144         }
145
146         private ResolutionState resolveSource(SourceIdentifier identifier) {
147             
148             if (alreadyProcessed.containsKey(identifier)) {
149                 return alreadyProcessed.get(identifier);
150             }
151             LOG.info("Resolving source: {}",identifier);
152             ResolutionState potentialState = ResolutionState.EVERYTHING_OK;
153             try {
154                 Optional<InputStream> source = getSchemaSource(identifier);
155                 if (source.isPresent()) {
156
157                     YangModelDependencyInfo info = YangModelDependencyInfo.fromInputStream(source.get());
158
159                     checkValidSource(identifier,info);
160
161                     for (ModuleImport dependency : info.getDependencies()) {
162                         LOG.debug("Source: {} Resolving dependency: {}",identifier,dependency);
163                         ResolutionState dependencyState = resolveDependency(dependency);
164                         if (dependencyState == ResolutionState.MISSING_SOURCE) {
165                             potentialState = ResolutionState.MISSING_DEPENDENCY;
166                             missingDependencies.put(identifier, dependency);
167                         }
168                     }
169                 } else {
170                     missingSources.add(identifier);
171                     return ResolutionState.MISSING_SOURCE;
172                 } 
173             } catch (Exception e) {
174                 potentialState = ResolutionState.OTHER_ERROR;
175             }
176             updateResolutionState(identifier, potentialState);
177             return potentialState;
178         }
179
180         private boolean checkValidSource(SourceIdentifier identifier, YangModelDependencyInfo info) {
181             if(!identifier.getName().equals(info.getName())) {
182                 LOG.warn("Incorrect model returned. Identifier name was: {}, source contained: {}", identifier.getName(),info.getName());
183                 throw new IllegalStateException("Incorrect source was returned");
184             }
185             return true;
186         }
187
188         private void updateResolutionState(SourceIdentifier identifier, ResolutionState potentialState) {
189             alreadyProcessed.put(identifier, potentialState);
190             switch (potentialState) {
191             case MISSING_SOURCE:
192                 missingSources.add(identifier);
193                 break;
194             case EVERYTHING_OK:
195                 validSources.add(identifier);
196                 break;
197            default:
198                 break;
199             }
200         }
201
202         private ResolutionState resolveDependency(ModuleImport dependency) {
203             String name = dependency.getModuleName();
204             Optional<String> formattedRevision = Optional
205                     .fromNullable(QName.formattedRevision(dependency.getRevision()));
206             return resolveSource(new SourceIdentifier(name, formattedRevision));
207         }
208
209         private Optional<InputStream> getSchemaSource(SourceIdentifier identifier) {
210             return sourceProvicer
211                     .getSchemaSource(identifier.getName(), Optional.fromNullable(identifier.getRevision()));
212         }
213
214         protected YangSourceContext createSourceContext() {
215             
216             ImmutableSet<SourceIdentifier> missingSourcesSet = missingSources.build();
217             ImmutableMultimap<SourceIdentifier, ModuleImport> missingDependenciesMap = missingDependencies.build();
218             ImmutableSet<SourceIdentifier> validSourcesSet = validSources.build();
219             
220             
221             return new YangSourceContext(validSourcesSet,missingSourcesSet,missingDependenciesMap,sourceProvicer);
222             
223         }
224     }
225
226     private enum ResolutionState {
227         MISSING_SOURCE, MISSING_DEPENDENCY, OTHER_ERROR, EVERYTHING_OK
228     }
229
230     public static final class YangSourceFromCapabilitiesResolver extends YangSourceContextResolver {
231
232         private Iterable<QName> capabilities;
233
234         public YangSourceFromCapabilitiesResolver(Iterable<QName> capabilities,
235                 SchemaSourceProvider<InputStream> schemaSourceProvider) {
236             super(schemaSourceProvider);
237             this.capabilities = capabilities;
238         }
239
240         @Override
241         public YangSourceContext resolveContext() {
242             for (QName capability : capabilities) {
243                 resolveCapability(capability);
244             }
245             return createSourceContext();
246         }
247
248         private void resolveCapability(QName capability) {
249             super.resolveSource(capability.getLocalName(), Optional.fromNullable(capability.getFormattedRevision()));
250         }
251     }
252
253
254 }