Merge "Bug 560: Fixed incoherent API implementation."
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / impl / util / YangSourceContext.java
index eccd60dd1a99c3a1d49747129c9fed793f86b0da..3e34a32a5ebac650edd69ff1827eb74c40151db1 100644 (file)
 /*
- * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
- *
+ * 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.ImmutableList;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.ByteSource;
+import java.io.Closeable;
+import java.io.IOException;
 import java.io.InputStream;
-import java.util.Collections;
-import java.util.HashMap;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.yangtools.concepts.Delegator;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 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.SchemaSourceProviders;
 import org.opendaylight.yangtools.yang.model.util.repo.SourceIdentifier;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.ImmutableSet;
+import org.opendaylight.yangtools.yang.parser.builder.impl.BuilderUtils;
 
-public class YangSourceContext implements AdvancedSchemaSourceProvider<InputStream>, AutoCloseable {
+/**
+ *
+ * Context of YANG model sources
+ *
+ * YANG sources context represent information learned about set of model sources
+ * which could be derived from dependency information only.
+ *
+ * Contains following information:
+ * <ul>
+ * <li>{@link #getValidSources()} - set of {@link SourceIdentifier} which have
+ * their dependencies present and are safe to be used by full blown parser.
+ * <li>{@link #getMissingSources()} - set of {@link SourceIdentifier} which have
+ * been referenced by other YANG sources, but source code for them is missing.
+ * <li>{@link #getMissingDependencies()} - map of {@link SourceIdentifier} and
+ * their imports for which source codes was not available.
+ *
+ * {@link YangSourceContext} may be associated with {@link SchemaSourceProvider}
+ * (see {@link #getDelegate()}, which was used for retrieval of sources during
+ * context computation.
+ *
+ * {@link YangSourceContext} may be used as schema source provider to retrieve
+ * this sources.
+ *
+ *
+ */
+// FIXME: for some reason this class is Closeable even though close is never called and no resources are leaked
+@ThreadSafe
+public class YangSourceContext implements AdvancedSchemaSourceProvider<InputStream>, Closeable,
+        Delegator<AdvancedSchemaSourceProvider<InputStream>> {
 
     private final ImmutableSet<SourceIdentifier> validSources;
 
     private final ImmutableSet<SourceIdentifier> missingSources;
     private final ImmutableMultimap<SourceIdentifier, ModuleImport> missingDependencies;
-    private AdvancedSchemaSourceProvider<InputStream> sourceProvider;
-
-    private YangSourceContext(ImmutableSet<SourceIdentifier> validSourcesSet,
-            ImmutableSet<SourceIdentifier> missingSourcesSet,
-            ImmutableMultimap<SourceIdentifier, ModuleImport> missingDependenciesMap,
-            AdvancedSchemaSourceProvider<InputStream> sourceProvicer) {
-        validSources = validSourcesSet;
-        missingSources = missingSourcesSet;
-        missingDependencies = missingDependenciesMap;
-        sourceProvider = sourceProvicer;
-    }
-
+    private final AdvancedSchemaSourceProvider<InputStream> sourceProvider;
+    private final AtomicBoolean isClosed = new AtomicBoolean();
+
+    /**
+     * Construct YANG Source Context
+     * 
+     * @param validSourcesSet Set of identifiers of valid sources
+     * @param missingSourcesSet Set of identifiers of missing sources
+     * @param missingDependenciesMap Map of identifiers of resolved sources and their missing imports.
+     * @param sourceProvider Source provider which was used for context resolution or 
+     *          null if provider was not used.
+     */
+    YangSourceContext(final ImmutableSet<SourceIdentifier> validSourcesSet,
+            final ImmutableSet<SourceIdentifier> missingSourcesSet,
+            final ImmutableMultimap<SourceIdentifier, ModuleImport> missingDependenciesMap,
+            final AdvancedSchemaSourceProvider<InputStream> sourceProvider) {
+        validSources = checkNotNull(validSourcesSet, "Valid source set must not be null");
+        missingSources = checkNotNull(missingSourcesSet, "Missing sources set must not be null");
+        missingDependencies = checkNotNull(missingDependenciesMap, "Missing dependencies map must not be null");
+        this.sourceProvider = checkNotNull(sourceProvider, "Missing sourceProvider");
+    }
+
+    /**
+     * Returns set of valid source identifiers.
+     *
+     * Source identifier is considered valid if it's source
+     * was present during resolution and sources
+     * for all known dependencies was present at the time of creation
+     * of {@link YangSourceContext}.
+     *
+     * @return Set of valid source identifiers.
+     */
     public ImmutableSet<SourceIdentifier> getValidSources() {
         return validSources;
     }
 
+    /**
+     * Returns set of source identifiers, whom sources was not resolved.
+     *
+     * Source is considered missing if the source was not present
+     * during resolution of {@link YangSourceContext}.
+     *
+     * @return Set of missing sources.
+     */
     public ImmutableSet<SourceIdentifier> getMissingSources() {
         return missingSources;
     }
 
+    /**
+     * Returns a multimap of Source Identifier and imports which had missing
+     * sources.
+     *
+     * Maps a source identifier to its imports, which was not resolved
+     * during resolution of this context, so it is unable to fully
+     * processed source identifier.
+     *
+     *
+     * @return Multi-map of source identifier to it's unresolved dependencies.
+     */
     public ImmutableMultimap<SourceIdentifier, ModuleImport> getMissingDependencies() {
         return missingDependencies;
     }
 
     @Override
-    public Optional<InputStream> getSchemaSource(String moduleName, Optional<String> revision) {
+    public Optional<InputStream> getSchemaSource(final String moduleName, final Optional<String> revision) {
         return getSchemaSource(SourceIdentifier.create(moduleName, revision));
     }
 
     @Override
-    public Optional<InputStream> getSchemaSource(SourceIdentifier sourceIdentifier) {
+    public Optional<InputStream> getSchemaSource(final SourceIdentifier sourceIdentifier) {
         if (validSources.contains(sourceIdentifier)) {
             return getDelegateChecked().getSchemaSource(sourceIdentifier);
         }
@@ -79,245 +141,99 @@ public class YangSourceContext implements AdvancedSchemaSourceProvider<InputStre
     }
 
     private AdvancedSchemaSourceProvider<InputStream> getDelegateChecked() {
-        Preconditions.checkState(sourceProvider != null, "Instance is already closed.");
+        assertNotClosed();
         return sourceProvider;
     }
 
     @Override
-    public void close() {
-        if (sourceProvider != null) {
-            sourceProvider = null;
+    public AdvancedSchemaSourceProvider<InputStream> getDelegate() {
+        assertNotClosed();
+        return sourceProvider;
+    }
+
+    private void assertNotClosed() {
+        if (isClosed.get()) {
+            throw new IllegalStateException("Instance already closed");
         }
     }
 
-    public static final YangSourceContext createFrom(Iterable<QName> capabilities,
-            SchemaSourceProvider<InputStream> schemaSourceProvider) {
+    @Override
+    public void close() {
+        isClosed.set(true);
+    }
+
+    /**
+     * Creates YANG Source context from supplied capabilities and schema source
+     * provider.
+     *
+     * @param capabilities
+     *            Set of QName representing module capabilities,
+     *            {@link QName#getLocalName()} represents
+     *            source name and {@link QName#getRevision()} represents
+     *            revision of source.
+     *
+     * @param schemaSourceProvider
+     *            - {@link SchemaSourceProvider} which should be used to resolve
+     *            sources.
+     * @return YANG source context which describes resolution of capabilities
+     *         and their dependencies
+     *         against supplied schema source provider.
+     */
+    public static YangSourceContext createFrom(final Iterable<QName> capabilities,
+            final SchemaSourceProvider<InputStream> schemaSourceProvider) {
         YangSourceContextResolver resolver = new YangSourceFromCapabilitiesResolver(capabilities, schemaSourceProvider);
         return resolver.resolveContext();
     }
 
-    public static final YangSourceContext createFrom(Map<SourceIdentifier, YangModelDependencyInfo> moduleDependencies) {
-        YangSourceContextResolver resolver = new YangSourceFromDependencyInfoResolver(moduleDependencies);
+    public static YangSourceContext createFrom(final Map<SourceIdentifier, YangModelDependencyInfo> moduleDependencies,
+            AdvancedSchemaSourceProvider<InputStream> sourceProvider) {
+        YangSourceFromDependencyInfoResolver resolver = new YangSourceFromDependencyInfoResolver(
+                moduleDependencies, sourceProvider);
         return resolver.resolveContext();
     }
 
-    public static final SchemaContext toSchemaContext(YangSourceContext context) {
-        List<InputStream> inputStreams = getValidInputStreams(context);
-        YangParserImpl parser = new YangParserImpl();
-        Set<Module> models = parser.parseYangModelsFromStreams(inputStreams);
-        return parser.resolveSchemaContext(models);
+    /**
+     * Returns a list of valid input streams from YANG Source Context
+     * using supplied schema source provider.
+     *
+     * @return List of input streams.
+     * @deprecated Use {@link #getValidByteSources()}
+     */
+    @Deprecated
+    public List<InputStream> getValidInputStreams() {
+        return getValidInputStreamsInternal();
     }
 
-    public static List<InputStream> getValidInputStreams(YangSourceContext context) {
-        return getValidInputStreams(context, context.sourceProvider);
-    }
-
-    public static List<InputStream> getValidInputStreams(YangSourceContext context,
-            AdvancedSchemaSourceProvider<InputStream> provider) {
+    private List<InputStream> getValidInputStreamsInternal() {
+        assertNotClosed();
         final HashSet<SourceIdentifier> sourcesToLoad = new HashSet<>();
-        sourcesToLoad.addAll(context.getValidSources());
-        for (SourceIdentifier source : context.getValidSources()) {
+        sourcesToLoad.addAll(this.getValidSources());
+        for (SourceIdentifier source : this.getValidSources()) {
             if (source.getRevision() != null) {
                 SourceIdentifier sourceWithoutRevision = SourceIdentifier.create(source.getName(),
                         Optional.<String> absent());
-                sourcesToLoad.removeAll(Collections.singleton(sourceWithoutRevision));
+                sourcesToLoad.remove(sourceWithoutRevision);
             }
         }
 
-        ImmutableList.Builder<InputStream> ret = ImmutableList.<InputStream> builder();
+        ImmutableList.Builder<InputStream> ret = ImmutableList.<InputStream>builder();
         for (SourceIdentifier sourceIdentifier : sourcesToLoad) {
-            Optional<InputStream> source = provider.getSchemaSource(sourceIdentifier);
+            Optional<InputStream> source = sourceProvider.getSchemaSource(sourceIdentifier);
             ret.add(source.get());
         }
         return ret.build();
     }
 
-    public static abstract class YangSourceContextResolver {
-
-        private static final Logger LOG = LoggerFactory.getLogger(YangSourceContextResolver.class);
-
-        private final AdvancedSchemaSourceProvider<InputStream> sourceProvider;
-
-        public AdvancedSchemaSourceProvider<InputStream> getSourceProvider() {
-            return sourceProvider;
-        }
-
-        private final HashMap<SourceIdentifier, ResolutionState> alreadyProcessed = new HashMap<>();
-
-        private final ImmutableSet.Builder<SourceIdentifier> missingSources = ImmutableSet.builder();
-
-        private ImmutableMultimap.Builder<SourceIdentifier, ModuleImport> missingDependencies = ImmutableMultimap
-                .builder();
-
-        private final ImmutableSet.Builder<SourceIdentifier> validSources = ImmutableSet.builder();
-
-        public YangSourceContextResolver() {
-            sourceProvider = null;
-        }
-
-        public YangSourceContextResolver(AdvancedSchemaSourceProvider<InputStream> sourceProvicer) {
-            super();
-            this.sourceProvider = sourceProvicer;
-        }
-
-        public abstract YangSourceContext resolveContext();
-
-        public ResolutionState resolveSource(String name, Optional<String> formattedRevision) {
-            return resolveSource(new SourceIdentifier(name, formattedRevision));
-        }
-
-        public ResolutionState resolveSource(SourceIdentifier identifier) {
-
-            if (alreadyProcessed.containsKey(identifier)) {
-                return alreadyProcessed.get(identifier);
-            }
-            LOG.trace("Resolving source: {}", identifier);
-            ResolutionState potentialState = ResolutionState.EVERYTHING_OK;
-            try {
-                Optional<YangModelDependencyInfo> 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);
-                        ResolutionState dependencyState = resolveDependency(dependency);
-                        if (dependencyState != ResolutionState.EVERYTHING_OK) {
-                            potentialState = ResolutionState.MISSING_DEPENDENCY;
-                            missingDependencies.put(identifier, dependency);
-                        }
-                    }
-                } else {
-                    missingSources.add(identifier);
-                    return ResolutionState.MISSING_SOURCE;
-                }
-            } catch (Exception e) {
-                potentialState = ResolutionState.OTHER_ERROR;
-            }
-            updateResolutionState(identifier, potentialState);
-            return potentialState;
-        }
-
-        public abstract Optional<YangModelDependencyInfo> getDependencyInfo(SourceIdentifier identifier);
-
-        private boolean checkValidSource(SourceIdentifier identifier, 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(SourceIdentifier identifier, 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 ResolutionState resolveDependency(ModuleImport dependency) {
-            String name = dependency.getModuleName();
-            Optional<String> formattedRevision = Optional
-                    .fromNullable(QName.formattedRevision(dependency.getRevision()));
-            return resolveSource(new SourceIdentifier(name, formattedRevision));
-        }
-
-        protected YangSourceContext createSourceContext() {
-
-            ImmutableSet<SourceIdentifier> missingSourcesSet = missingSources.build();
-            ImmutableMultimap<SourceIdentifier, ModuleImport> missingDependenciesMap = missingDependencies.build();
-            ImmutableSet<SourceIdentifier> validSourcesSet = validSources.build();
-
-            return new YangSourceContext(validSourcesSet, missingSourcesSet, missingDependenciesMap, sourceProvider);
-
-        }
-    }
-
-    private enum ResolutionState {
-        MISSING_SOURCE, MISSING_DEPENDENCY, OTHER_ERROR, EVERYTHING_OK
-    }
-
-    public static final class YangSourceFromCapabilitiesResolver extends YangSourceContextResolver {
 
-        private Iterable<QName> capabilities;
-
-        public YangSourceFromCapabilitiesResolver(Iterable<QName> capabilities,
-                SchemaSourceProvider<InputStream> schemaSourceProvider) {
-            super(SchemaSourceProviders.toAdvancedSchemaSourceProvider(schemaSourceProvider));
-            this.capabilities = capabilities;
-        }
-
-        @Override
-        public YangSourceContext resolveContext() {
-            for (QName capability : capabilities) {
-                resolveCapability(capability);
-            }
-            return createSourceContext();
-        }
-
-        private void resolveCapability(QName capability) {
-            super.resolveSource(capability.getLocalName(), Optional.fromNullable(capability.getFormattedRevision()));
-        }
-
-        @Override
-        public Optional<YangModelDependencyInfo> getDependencyInfo(SourceIdentifier identifier) {
-            Optional<InputStream> source = getSchemaSource(identifier);
-            if (source.isPresent()) {
-                return Optional.of(YangModelDependencyInfo.fromInputStream(source.get()));
-            }
-            return Optional.absent();
-        }
-
-        private Optional<InputStream> getSchemaSource(SourceIdentifier identifier) {
-            return getSourceProvider().getSchemaSource(identifier.getName(),
-                    Optional.fromNullable(identifier.getRevision()));
-        }
 
+    public Collection<ByteSource> getValidByteSources() throws IOException {
+        List<InputStream> yangModelStreams = getValidInputStreamsInternal();
+        return BuilderUtils.streamsToByteSources(yangModelStreams);
     }
 
-    public static final class YangSourceFromDependencyInfoResolver extends YangSourceContextResolver {
-
-        private final Map<SourceIdentifier, YangModelDependencyInfo> dependencyInfo;
-
-        public YangSourceFromDependencyInfoResolver(Map<SourceIdentifier, YangModelDependencyInfo> moduleDependencies) {
-            dependencyInfo = ImmutableMap.copyOf(moduleDependencies);
-        }
-
-        @Override
-        public Optional<YangModelDependencyInfo> getDependencyInfo(SourceIdentifier identifier) {
-            if (identifier.getRevision() != null) {
-                return Optional.fromNullable(dependencyInfo.get(identifier));
-            }
-            YangModelDependencyInfo potential = dependencyInfo.get(identifier);
-            if (potential == null) {
-                for (Entry<SourceIdentifier, YangModelDependencyInfo> newPotential : dependencyInfo.entrySet()) {
-                    String newPotentialName = newPotential.getKey().getName();
-
-                    if (newPotentialName.equals(identifier.getName())) {
-                        String newPotentialRevision = newPotential.getKey().getRevision();
-                        if (potential == null || 1 == newPotentialRevision.compareTo(potential.getFormattedRevision())) {
-                            potential = newPotential.getValue();
-                        }
-                    }
-                }
-            }
-            return Optional.fromNullable(potential);
-        }
-
-        @Override
-        public YangSourceContext resolveContext() {
-            for (SourceIdentifier source : dependencyInfo.keySet()) {
-                resolveSource(source);
-            }
-            return createSourceContext();
-        }
+    @Deprecated
+    public static List<InputStream> getValidInputStreams(final YangSourceContext context) {
+        return context.getValidInputStreams();
     }
 
 }