/* * 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/epl-v10.html */ 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.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.collect.Collections2; 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.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.ParseTreeWalker; 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.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.parser.impl.util.YangModelDependencyInfo; import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; final class SharedSchemaContextFactory implements SchemaContextFactory { private static final ExceptionMapper MAPPER = ReflectiveExceptionMapper.create("resolve sources", SchemaResolutionException.class); private static final Logger LOG = LoggerFactory.getLogger(SharedSchemaContextFactory.class); private final Function> requestSources = new Function>() { @Override public ListenableFuture apply(final SourceIdentifier input) { 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 SharedSchemaRepository repository; // FIXME: ignored right now private final SchemaSourceFilter filter; 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); 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)); // Assemble sources into a schema context final ListenableFuture cf = Futures.transform(sf, assembleSources); // Populate cache when successful Futures.addCallback(cf, new FutureCallback() { @Override public void onSuccess(final SchemaContext result) { cache.put(requiredSources, result); } @Override public void onFailure(final Throwable t) { LOG.debug("Failed to assemble sources", t); } }); return Futures.makeChecked(cf, MAPPER); } }