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