2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
5 * and is available at http://www.eclipse.org/legal/eplv10.html
7 package org.opendaylight.yangtools.yang.parser.impl.util;
9 import static com.google.common.base.Preconditions.checkNotNull;
11 import com.google.common.base.Optional;
12 import com.google.common.collect.ImmutableMultimap;
13 import com.google.common.collect.ImmutableSet;
14 import java.io.InputStream;
15 import java.util.HashMap;
16 import javax.annotation.concurrent.NotThreadSafe;
17 import org.opendaylight.yangtools.yang.common.QName;
18 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
19 import org.opendaylight.yangtools.yang.model.util.repo.AdvancedSchemaSourceProvider;
20 import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
21 import org.opendaylight.yangtools.yang.model.util.repo.SourceIdentifier;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
27 * Resolution task for YANG Source Context
29 * {@link YangSourceContextResolver} and its subclasses are responsible for
30 * resolving {@link YangSourceContext} based on provided
31 * {@link SchemaSourceProvider} and set of modules to process.
34 * <h3>Implementation notes</h3>
36 * In order to customize resolution of {@link YangSourceContext} implementators
37 * of this class are required to implement following methods:
39 * <li>{@link #getDependencyInfo(SourceIdentifier)} - Retrieval of dependency
41 * <li>{@link #resolveContext()} - Main resolution algorithm
45 * This abstract class provides utility methods for implementators which may be
46 * used in {@link #resolveContext()} to create {@link YangSourceContext}:
48 * <li>{@link #resolveSource(SourceIdentifier)} and
49 * {@link #resolveSource(String, Optional)} - Tries to resolve state for
50 * supplied model identifier and updates internal state. If state was not
51 * already resolved for identifier it invokes
52 * {@link #getDependencyInfo(SourceIdentifier)} for particular identifier. This
53 * method is recursively invoked for all dependencies.</li>
54 * <li>{@link #createSourceContext()} - Creates {@link YangSourceContext} based
55 * on previous invocations of {@link #resolveSource(SourceIdentifier)} methods.</li>
60 public abstract class YangSourceContextResolver {
64 * State of source code resolution
67 public enum ResolutionState {
70 * Source was missing during source resolution
76 * One or multiple of dependencies of source are missing
81 * Other error ocurred during resolution
86 * Source, its dependencies and its transient dependencies
93 private static final Logger LOG = LoggerFactory.getLogger(YangSourceContextResolver.class);
94 private final HashMap<SourceIdentifier, YangSourceContextResolver.ResolutionState> alreadyProcessed = new HashMap<>();
95 private final ImmutableSet.Builder<SourceIdentifier> missingSources = ImmutableSet.builder();
96 private final ImmutableMultimap.Builder<SourceIdentifier, ModuleImport> missingDependencies = ImmutableMultimap
98 private final ImmutableSet.Builder<SourceIdentifier> validSources = ImmutableSet.builder();
99 private final AdvancedSchemaSourceProvider<InputStream> sourceProvider;
101 public YangSourceContextResolver(final AdvancedSchemaSourceProvider<InputStream> sourceProvider) {
102 this.sourceProvider = checkNotNull(sourceProvider, "Missing sourceProvider");
106 * Resolves {@link YangSourceContext}
108 * Implementators of this method should invoke
109 * {@link #resolveSource(SourceIdentifier)} for sources which should be
110 * present in {@link YangSourceContext} and {@link #createSourceContext()}
111 * to create resulting {@link YangSourceContext} which will contain state
112 * derived by callbacks to {@link #getDependencyInfo(SourceIdentifier)}.
114 * @return Resolved {@link YangSourceContext}.
116 public abstract YangSourceContext resolveContext();
119 * Returns dependency information for provided identifier
121 * Implementations are required to:
123 * <li>return {@link Optional#absent()} If source code for source is not
125 * <li>return same dependency information for multiple invocations of this
126 * method for same source identifier.</li>
127 * <li>return latest available revision if {@link SourceIdentifier} does not
128 * specify revision. If no revision is available {@link Optional#absent()}
129 * MUST be returned.</li>
133 * Internal state of this object (and resulting {@link YangSourceContext}
134 * will be updated as following:
136 * <li>If {@link Optional#absent()} is returned:
138 * <li>source will be marked as {@link ResolutionState#MISSING_SOURCE} and
139 * source identifier will be contained in -
140 * {@link YangSourceContext#getMissingSources()}</li>
141 * <li>All sources which imported or included this source will be present in
142 * {@link YangSourceContext#getMissingDependencies()}</li>
149 * @return Dependency Information for {@link SourceIdentifier},
150 * {@link Optional#absent()} if no source is present.
152 abstract Optional<YangModelDependencyInfo> getDependencyInfo(SourceIdentifier identifier);
155 * Return Source provider against which YANG source context was computed
157 * @return Source provider against which YANG source context was computed or null, if source provider
158 * is not associated with computation.
160 public AdvancedSchemaSourceProvider<InputStream> getSourceProvider() {
161 return sourceProvider;
166 * Resolves resolution state for provided name and formated revision
168 * This method is shorthand for {@link #resolveSource(SourceIdentifier)}
169 * with argument <code>new SourceIdentifier(name, formattedRevision)</code>
171 * @see #resolveSource(SourceIdentifier)
174 * @param formattedRevision
175 * revision of YANG model
176 * @return Resolution context of YANG Source
178 public final YangSourceContextResolver.ResolutionState resolveSource(final String name,
179 final Optional<String> formattedRevision) {
180 return resolveSource(new SourceIdentifier(name, formattedRevision));
184 * Resolves state of source and updates internal state accordingly.
187 * Resolves state of source and updates internal state based on resolution.
188 * This method tries to get module dependency info via user implementation
189 * of {@link #getDependencyInfo(SourceIdentifier)} and then is recursively
190 * called for each announced dependency in
191 * {@link YangModelDependencyInfo#getDependencies()}.
194 * Resolution state of resolveSource is internally cached and is used in
195 * subsequent resolution of dependent modules and in creation of
196 * YANGSourceContext via {@link #createSourceContext()}.
199 * Possible resolution state for sources are:
201 * <li>{@link ResolutionState#EVERYTHING_OK} - If sources for module and its
202 * dependencies are available</li>
203 * <li>{@link ResolutionState#MISSING_DEPENDENCY} - If dependency of source
204 * is missing (call to {@link #getDependencyInfo(SourceIdentifier)} for
205 * imported / included model returned returned {@link Optional#absent()}.</li>
206 * <li>{@link ResolutionState#MISSING_SOURCE} - If source is missing. (call
207 * of {@link #getDependencyInfo(SourceIdentifier)} returned
208 * {@link Optional#absent()}.</li>
209 * <li>{@link ResolutionState#OTHER_ERROR} - If other runtime error
210 * prevented resolution of informations.</li>
213 * Note: Multiple invocations of this method returns cached result, since
214 * {@link #getDependencyInfo(SourceIdentifier)} contract requires
215 * implementors to return same information during life of this object.
220 * @return Returns resolution state for source.
222 public final YangSourceContextResolver.ResolutionState resolveSource(final SourceIdentifier identifier) {
224 if (alreadyProcessed.containsKey(identifier)) {
225 return alreadyProcessed.get(identifier);
227 LOG.trace("Resolving source: {}", identifier);
228 YangSourceContextResolver.ResolutionState potentialState = YangSourceContextResolver.ResolutionState.EVERYTHING_OK;
230 Optional<YangModelDependencyInfo> potentialInfo = getDependencyInfo(identifier);
231 if (potentialInfo.isPresent()) {
232 YangModelDependencyInfo info = potentialInfo.get();
233 checkValidSource(identifier, info);
234 for (ModuleImport dependency : info.getDependencies()) {
235 LOG.trace("Source: {} Resolving dependency: {}", identifier, dependency);
236 YangSourceContextResolver.ResolutionState dependencyState = resolveDependency(dependency);
237 if (dependencyState != YangSourceContextResolver.ResolutionState.EVERYTHING_OK) {
238 potentialState = YangSourceContextResolver.ResolutionState.MISSING_DEPENDENCY;
239 missingDependencies.put(identifier, dependency);
243 missingSources.add(identifier);
244 return YangSourceContextResolver.ResolutionState.MISSING_SOURCE;
246 } catch (Exception e) {
247 potentialState = YangSourceContextResolver.ResolutionState.OTHER_ERROR;
249 updateResolutionState(identifier, potentialState);
250 return potentialState;
253 private boolean checkValidSource(final SourceIdentifier identifier, final YangModelDependencyInfo info) {
254 if (!identifier.getName().equals(info.getName())) {
255 LOG.warn("Incorrect model returned. Identifier name was: {}, source contained: {}", identifier.getName(),
257 throw new IllegalStateException("Incorrect source was returned");
262 private void updateResolutionState(final SourceIdentifier identifier,
263 final YangSourceContextResolver.ResolutionState potentialState) {
264 alreadyProcessed.put(identifier, potentialState);
265 switch (potentialState) {
267 missingSources.add(identifier);
270 validSources.add(identifier);
277 private YangSourceContextResolver.ResolutionState resolveDependency(final ModuleImport dependency) {
278 String name = dependency.getModuleName();
279 Optional<String> formattedRevision = Optional.fromNullable(QName.formattedRevision(dependency.getRevision()));
280 return resolveSource(new SourceIdentifier(name, formattedRevision));
283 protected YangSourceContext createSourceContext() {
284 ImmutableSet<SourceIdentifier> missingSourcesSet = missingSources.build();
285 ImmutableMultimap<SourceIdentifier, ModuleImport> missingDependenciesMap = missingDependencies.build();
286 ImmutableSet<SourceIdentifier> validSourcesSet = validSources.build();
287 return new YangSourceContext(validSourcesSet, missingSourcesSet, missingDependenciesMap, sourceProvider);