/* * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/eplv10.html */ package org.opendaylight.yangtools.yang.parser.impl.util; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.base.Optional; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; import java.io.InputStream; import java.util.HashMap; import javax.annotation.concurrent.NotThreadSafe; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.model.api.ModuleImport; import org.opendaylight.yangtools.yang.model.util.repo.AdvancedSchemaSourceProvider; import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider; import org.opendaylight.yangtools.yang.model.util.repo.SourceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * Resolution task for YANG Source Context * * {@link YangSourceContextResolver} and its subclasses are responsible for * resolving {@link YangSourceContext} based on provided * {@link SchemaSourceProvider} and set of modules to process. * * *

Implementation notes

* * In order to customize resolution of {@link YangSourceContext} implementators * of this class are required to implement following methods: * * * This abstract class provides utility methods for implementators which may be * used in {@link #resolveContext()} to create {@link YangSourceContext}: * * */ @Deprecated @NotThreadSafe public abstract class YangSourceContextResolver { /** * * State of source code resolution * */ public enum ResolutionState { /** * * Source was missing during source resolution * */ MISSING_SOURCE, /** * * One or multiple of dependencies of source are missing * */ MISSING_DEPENDENCY, /** * Other error ocurred during resolution * */ OTHER_ERROR, /** * Source, its dependencies and its transient dependencies * are resolved. * */ EVERYTHING_OK, } private static final Logger LOG = LoggerFactory.getLogger(YangSourceContextResolver.class); private final HashMap alreadyProcessed = new HashMap<>(); private final ImmutableSet.Builder missingSources = ImmutableSet.builder(); private final ImmutableMultimap.Builder missingDependencies = ImmutableMultimap .builder(); private final ImmutableSet.Builder validSources = ImmutableSet.builder(); private final AdvancedSchemaSourceProvider sourceProvider; public YangSourceContextResolver(final AdvancedSchemaSourceProvider sourceProvider) { this.sourceProvider = checkNotNull(sourceProvider, "Missing sourceProvider"); } /** * Resolves {@link YangSourceContext} * * Implementators of this method should invoke * {@link #resolveSource(SourceIdentifier)} for sources which should be * present in {@link YangSourceContext} and {@link #createSourceContext()} * to create resulting {@link YangSourceContext} which will contain state * derived by callbacks to {@link #getDependencyInfo(SourceIdentifier)}. * * @return Resolved {@link YangSourceContext}. */ public abstract YangSourceContext resolveContext(); /** * Returns dependency information for provided identifier * * Implementations are required to: *
    *
  • return {@link Optional#absent()} If source code for source is not * present
  • *
  • return same dependency information for multiple invocations of this * method for same source identifier.
  • *
  • return latest available revision if {@link SourceIdentifier} does not * specify revision. If no revision is available {@link Optional#absent()} * MUST be returned.
  • *
* * * Internal state of this object (and resulting {@link YangSourceContext} * will be updated as following: *
    *
  • If {@link Optional#absent()} is returned: *
      *
    • source will be marked as {@link ResolutionState#MISSING_SOURCE} and * source identifier will be contained in - * {@link YangSourceContext#getMissingSources()}
    • *
    • All sources which imported or included this source will be present in * {@link YangSourceContext#getMissingDependencies()}
    • *
    *
* * * @param identifier * Source identifier * @return Dependency Information for {@link SourceIdentifier}, * {@link Optional#absent()} if no source is present. */ abstract Optional getDependencyInfo(SourceIdentifier identifier); /** * Return Source provider against which YANG source context was computed * * @return Source provider against which YANG source context was computed or null, if source provider * is not associated with computation. */ public AdvancedSchemaSourceProvider getSourceProvider() { return sourceProvider; } /** * * Resolves resolution state for provided name and formated revision * * This method is shorthand for {@link #resolveSource(SourceIdentifier)} * with argument new SourceIdentifier(name, formattedRevision) * * @see #resolveSource(SourceIdentifier) * @param name * Name of YANG model * @param formattedRevision * revision of YANG model * @return Resolution context of YANG Source */ public final YangSourceContextResolver.ResolutionState resolveSource(final String name, final Optional formattedRevision) { return resolveSource(new SourceIdentifier(name, formattedRevision)); } /** * Resolves state of source and updates internal state accordingly. * *

* Resolves state of source and updates internal state based on resolution. * This method tries to get module dependency info via user implementation * of {@link #getDependencyInfo(SourceIdentifier)} and then is recursively * called for each announced dependency in * {@link YangModelDependencyInfo#getDependencies()}. * *

* Resolution state of resolveSource is internally cached and is used in * subsequent resolution of dependent modules and in creation of * YANGSourceContext via {@link #createSourceContext()}. * *

* Possible resolution state for sources are: *

    *
  • {@link ResolutionState#EVERYTHING_OK} - If sources for module and its * dependencies are available
  • *
  • {@link ResolutionState#MISSING_DEPENDENCY} - If dependency of source * is missing (call to {@link #getDependencyInfo(SourceIdentifier)} for * imported / included model returned returned {@link Optional#absent()}.
  • *
  • {@link ResolutionState#MISSING_SOURCE} - If source is missing. (call * of {@link #getDependencyInfo(SourceIdentifier)} returned * {@link Optional#absent()}.
  • *
  • {@link ResolutionState#OTHER_ERROR} - If other runtime error * prevented resolution of informations.
  • *
* * Note: Multiple invocations of this method returns cached result, since * {@link #getDependencyInfo(SourceIdentifier)} contract requires * implementors to return same information during life of this object. * * * @param identifier * Source Identifier * @return Returns resolution state for source. */ public final YangSourceContextResolver.ResolutionState resolveSource(final SourceIdentifier identifier) { if (alreadyProcessed.containsKey(identifier)) { return alreadyProcessed.get(identifier); } LOG.trace("Resolving source: {}", identifier); YangSourceContextResolver.ResolutionState potentialState = YangSourceContextResolver.ResolutionState.EVERYTHING_OK; try { Optional potentialInfo = getDependencyInfo(identifier); if (potentialInfo.isPresent()) { YangModelDependencyInfo info = potentialInfo.get(); checkValidSource(identifier, info); for (ModuleImport dependency : info.getDependencies()) { LOG.trace("Source: {} Resolving dependency: {}", identifier, dependency); YangSourceContextResolver.ResolutionState dependencyState = resolveDependency(dependency); if (dependencyState != YangSourceContextResolver.ResolutionState.EVERYTHING_OK) { potentialState = YangSourceContextResolver.ResolutionState.MISSING_DEPENDENCY; missingDependencies.put(identifier, dependency); } } } else { missingSources.add(identifier); return YangSourceContextResolver.ResolutionState.MISSING_SOURCE; } } catch (Exception e) { potentialState = YangSourceContextResolver.ResolutionState.OTHER_ERROR; } updateResolutionState(identifier, potentialState); return potentialState; } private boolean checkValidSource(final SourceIdentifier identifier, final YangModelDependencyInfo info) { if (!identifier.getName().equals(info.getName())) { LOG.warn("Incorrect model returned. Identifier name was: {}, source contained: {}", identifier.getName(), info.getName()); throw new IllegalStateException("Incorrect source was returned"); } return true; } private void updateResolutionState(final SourceIdentifier identifier, final YangSourceContextResolver.ResolutionState potentialState) { alreadyProcessed.put(identifier, potentialState); switch (potentialState) { case MISSING_SOURCE: missingSources.add(identifier); break; case EVERYTHING_OK: validSources.add(identifier); break; default: break; } } private YangSourceContextResolver.ResolutionState resolveDependency(final ModuleImport dependency) { String name = dependency.getModuleName(); Optional formattedRevision = Optional.fromNullable(QName.formattedRevision(dependency.getRevision())); return resolveSource(new SourceIdentifier(name, formattedRevision)); } protected YangSourceContext createSourceContext() { ImmutableSet missingSourcesSet = missingSources.build(); ImmutableMultimap missingDependenciesMap = missingDependencies.build(); ImmutableSet validSourcesSet = validSources.build(); return new YangSourceContext(validSourcesSet, missingSourcesSet, missingDependenciesMap, sourceProvider); } }