X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=yang%2Fyang-parser-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fparser%2Frepo%2FSharedSchemaContextFactory.java;h=7b40342003c575eca5692f0f88d4ca6ad827a441;hb=e403e34bcb508c48aa606a1cd81a386fb73c5db6;hp=3c8a45e26dca59f7f100b8f7fbc4f4289af54e73;hpb=8c1ac2024eb319b2b914c2510192bda06a1bf03e;p=yangtools.git diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java index 3c8a45e26d..7b40342003 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java @@ -8,40 +8,44 @@ package org.opendaylight.yangtools.yang.parser.repo; import com.google.common.base.Function; -import com.google.common.base.Optional; import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import java.net.URI; import java.util.Collection; -import java.util.Date; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.TreeMap; +import java.util.Set; +import javax.annotation.Nullable; import org.antlr.v4.runtime.ParserRuleContext; -import org.antlr.v4.runtime.tree.ParseTreeWalker; +import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.StatementContext; import org.opendaylight.yangtools.util.concurrent.ExceptionMapper; import org.opendaylight.yangtools.util.concurrent.ReflectiveExceptionMapper; -import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory; import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException; import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter; import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; -import org.opendaylight.yangtools.yang.parser.builder.impl.BuilderUtils; -import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder; -import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; -import org.opendaylight.yangtools.yang.parser.impl.YangParserListenerImpl; +import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode; import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; +import org.opendaylight.yangtools.yang.parser.spi.source.SourceException; +import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor; +import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline; +import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl; import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,71 +60,44 @@ final class SharedSchemaContextFactory implements SchemaContextFactory { return repository.getSchemaSource(input, ASTSchemaSource.class); } }; - private final Cache, SchemaContext> cache = CacheBuilder.newBuilder().softValues().build(); - - private final AsyncFunction, SchemaContext> assembleSources = new AsyncFunction, SchemaContext>() { - @Override - public ListenableFuture apply(final List sources) throws SchemaResolutionException { - final Map srcs = - Maps.uniqueIndex(sources, ASTSchemaSource.GET_IDENTIFIER); - final Map deps = - Maps.transformValues(srcs, ASTSchemaSource.GET_DEPINFO); - - LOG.debug("Resolving dependency reactor {}", deps); - - final DependencyResolver res = DependencyResolver.create(deps); - if (!res.getUnresolvedSources().isEmpty()) { - LOG.debug("Omitting models {} due to unsatisfied imports {}", res.getUnresolvedSources(), res.getUnsatisfiedImports()); - - // FIXME: push into DependencyResolver - - throw new SchemaResolutionException("Failed to resolve required models", - res.getResolvedSources(), res.getUnsatisfiedImports()); - } - - final Map asts = - Maps.transformValues(srcs, ASTSchemaSource.GET_AST); - final Map> namespaceContext = BuilderUtils.createYangNamespaceContext( - asts.values(), Optional. absent()); - - final ParseTreeWalker walker = new ParseTreeWalker(); - final Map sourceToBuilder = new LinkedHashMap<>(); - - for (Entry entry : asts.entrySet()) { - ModuleBuilder moduleBuilder = YangParserListenerImpl.create(namespaceContext, entry.getKey().getName(), - walker, entry.getValue()).getModuleBuilder(); - - moduleBuilder.setSource(srcs.get(entry.getKey()).getYangText()); - sourceToBuilder.put(entry.getKey(), moduleBuilder); - } - LOG.debug("Modules ready for integration"); - - final YangParserImpl parser = YangParserImpl.getInstance(); - final Collection modules = parser.buildModules(sourceToBuilder.values()); - LOG.debug("Integrated cross-references modules"); - return Futures.immediateCheckedFuture(parser.assembleContext(modules)); - } - }; - + private final Cache, SchemaContext> cache = CacheBuilder.newBuilder().weakValues().build(); + private final Cache, SchemaContext> semVerCache = CacheBuilder.newBuilder().weakValues().build(); private final SharedSchemaRepository repository; // FIXME: ignored right now private final SchemaSourceFilter filter; + // FIXME SchemaRepository should be the type for repository parameter instead of SharedSchemaRepository (final implementation) public SharedSchemaContextFactory(final SharedSchemaRepository repository, final SchemaSourceFilter filter) { this.repository = Preconditions.checkNotNull(repository); this.filter = Preconditions.checkNotNull(filter); } @Override - public CheckedFuture createSchemaContext(final Collection requiredSources) { - final SchemaContext existing = cache.getIfPresent(requiredSources); + public CheckedFuture createSchemaContext( + final Collection requiredSources, final StatementParserMode statementParserMode, + final java.util.function.Predicate isFeatureSupported) { + return createSchemaContext(requiredSources, + statementParserMode == StatementParserMode.SEMVER_MODE ? this.semVerCache : this.cache, + new AssembleSources(isFeatureSupported, statementParserMode)); + } + + private CheckedFuture createSchemaContext(final Collection requiredSources, final Cache, SchemaContext> cache, final AsyncFunction, SchemaContext> assembleSources) { + // Make sources unique + final List uniqueSourceIdentifiers = deDuplicateSources(requiredSources); + + final SchemaContext existing = cache.getIfPresent(uniqueSourceIdentifiers); if (existing != null) { LOG.debug("Returning cached context {}", existing); return Futures.immediateCheckedFuture(existing); } // Request all sources be loaded - final ListenableFuture> sf = Futures.allAsList(Collections2.transform(requiredSources, requestSources)); + ListenableFuture> sf = Futures.allAsList(Collections2.transform(uniqueSourceIdentifiers, requestSources)); + + // Detect mismatch between requested Source IDs and IDs that are extracted from parsed source + // Also remove duplicates if present + // We are relying on preserved order of uniqueSourceIdentifiers as well as sf + sf = Futures.transform(sf, new SourceIdMismatchDetector(uniqueSourceIdentifiers)); // Assemble sources into a schema context final ListenableFuture cf = Futures.transform(sf, assembleSources); @@ -129,7 +106,7 @@ final class SharedSchemaContextFactory implements SchemaContextFactory { Futures.addCallback(cf, new FutureCallback() { @Override public void onSuccess(final SchemaContext result) { - cache.put(requiredSources, result); + cache.put(uniqueSourceIdentifiers, result); } @Override @@ -140,4 +117,111 @@ final class SharedSchemaContextFactory implements SchemaContextFactory { return Futures.makeChecked(cf, MAPPER); } + + /** + * @return set (preserving ordering) from the input collection + */ + private static List deDuplicateSources(final Collection requiredSources) { + final Set uniqueSourceIdentifiers = new LinkedHashSet<>(requiredSources); + if (uniqueSourceIdentifiers.size() == requiredSources.size()) { + // Can potentially reuse input + return ImmutableList.copyOf(requiredSources); + } + + LOG.warn("Duplicate sources requested for schema context, removed duplicate sources: {}", + Collections2.filter(uniqueSourceIdentifiers, new Predicate() { + @Override + public boolean apply(@Nullable final SourceIdentifier input) { + return Iterables.frequency(requiredSources, input) > 1; + } + })); + return ImmutableList.copyOf(uniqueSourceIdentifiers); + } + + private static final class SourceIdMismatchDetector implements Function, List> { + private final List sourceIdentifiers; + + public SourceIdMismatchDetector(final List sourceIdentifiers) { + this.sourceIdentifiers = Preconditions.checkNotNull(sourceIdentifiers); + } + + @Override + public List apply(final List input) { + final Map filtered = new LinkedHashMap<>(); + + for (int i = 0; i < input.size(); i++) { + + final SourceIdentifier expectedSId = sourceIdentifiers.get(i); + final ASTSchemaSource astSchemaSource = input.get(i); + final SourceIdentifier realSId = astSchemaSource.getIdentifier(); + + if (!expectedSId.equals(realSId)) { + LOG.warn("Source identifier mismatch for module \"{}\", requested as {} but actually is {}. Using actual id", + expectedSId.getName(), expectedSId, realSId); + } + + if (filtered.containsKey(realSId)) { + LOG.warn("Duplicate source for module {} detected in reactor", realSId); + } + + filtered.put(realSId, astSchemaSource); + + } + return ImmutableList.copyOf(filtered.values()); + } + } + + private static final class AssembleSources implements AsyncFunction, SchemaContext> { + + private final java.util.function.Predicate isFeatureSupported; + private final StatementParserMode statementParserMode; + private final Function getIdentifier; + + private AssembleSources(final java.util.function.Predicate isFeatureSupported, + final StatementParserMode statementParserMode) { + this.isFeatureSupported = Preconditions.checkNotNull(isFeatureSupported); + this.statementParserMode = Preconditions.checkNotNull(statementParserMode); + switch (statementParserMode) { + case SEMVER_MODE: + this.getIdentifier = ASTSchemaSource.GET_SEMVER_IDENTIFIER; + break; + default: + this.getIdentifier = ASTSchemaSource.GET_IDENTIFIER; + } + } + + @Override + public ListenableFuture apply(final List sources) throws SchemaResolutionException, + SourceException, ReactorException { + final Map srcs = Maps.uniqueIndex(sources, getIdentifier); + final Map deps = + Maps.transformValues(srcs, ASTSchemaSource.GET_DEPINFO); + + LOG.debug("Resolving dependency reactor {}", deps); + + final DependencyResolver res = this.statementParserMode == StatementParserMode.SEMVER_MODE ? SemVerDependencyResolver + .create(deps) : RevisionDependencyResolver.create(deps); + if (!res.getUnresolvedSources().isEmpty()) { + LOG.debug("Omitting models {} due to unsatisfied imports {}", res.getUnresolvedSources(), res.getUnsatisfiedImports()); + throw new SchemaResolutionException("Failed to resolve required models", + res.getResolvedSources(), res.getUnsatisfiedImports()); + } + + final Map asts = Maps.transformValues(srcs, ASTSchemaSource.GET_AST); + final CrossSourceStatementReactor.BuildAction reactor = + YangInferencePipeline.RFC6020_REACTOR.newBuild(statementParserMode, isFeatureSupported); + + for (final Entry e : asts.entrySet()) { + final ParserRuleContext parserRuleCtx = e.getValue(); + Preconditions.checkArgument(parserRuleCtx instanceof StatementContext, + "Unsupported context class %s for source %s", parserRuleCtx.getClass(), e.getKey()); + + reactor.addSource(new YangStatementSourceImpl(e.getKey(), (StatementContext) parserRuleCtx)); + } + + final SchemaContext schemaContext = reactor.buildEffective(); + + return Futures.immediateCheckedFuture(schemaContext); + } + } }