/* * Copyright (c) 2015 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.stmt.rfc6020; import static org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.SOURCE_LINKAGE; import static org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase.SOURCE_PRE_LINKAGE; import static org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils.firstAttributeOf; import com.google.common.base.Verify; import java.net.URI; import java.util.Collection; import java.util.Date; import java.util.Map; import java.util.Map.Entry; import java.util.NavigableMap; import java.util.Optional; import org.opendaylight.yangtools.concepts.SemVer; import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier; import org.opendaylight.yangtools.yang.model.api.YangStmtMapping; import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.ImportStatement; import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement; import org.opendaylight.yangtools.yang.model.api.stmt.NamespaceStatement; import org.opendaylight.yangtools.yang.model.api.stmt.PrefixStatement; import org.opendaylight.yangtools.yang.model.api.stmt.RevisionDateStatement; import org.opendaylight.yangtools.yang.model.util.ModuleIdentifierImpl; import org.opendaylight.yangtools.yang.parser.spi.ModuleNamespace; import org.opendaylight.yangtools.yang.parser.spi.PreLinkageModuleNamespace; import org.opendaylight.yangtools.yang.parser.spi.SubstatementValidator; import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport; import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException; import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder; import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.InferenceAction; import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder.Prerequisite; import org.opendaylight.yangtools.yang.parser.spi.meta.SemanticVersionModuleNamespace; import org.opendaylight.yangtools.yang.parser.spi.meta.SemanticVersionNamespace; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable; import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToModuleIdentifier; import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToNamespace; import org.opendaylight.yangtools.yang.parser.spi.source.ImpPrefixToSemVerModuleIdentifier; import org.opendaylight.yangtools.yang.parser.spi.source.ImportedModuleContext; import org.opendaylight.yangtools.yang.parser.spi.source.ModuleCtxToModuleIdentifier; import org.opendaylight.yangtools.yang.parser.spi.source.ModuleNameToNamespace; import org.opendaylight.yangtools.yang.parser.spi.source.SourceException; import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.ImportEffectiveStatementImpl; public class ImportStatementDefinition extends AbstractStatementSupport<String, ImportStatement, EffectiveStatement<String, ImportStatement>> { private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator .builder(YangStmtMapping.IMPORT) .addMandatory(YangStmtMapping.PREFIX) .addOptional(YangStmtMapping.REVISION_DATE) .addOptional(SupportedExtensionsMapping.SEMANTIC_VERSION) .build(); public ImportStatementDefinition() { super(YangStmtMapping.IMPORT); } @Override public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, final String value) { return value; } @Override public ImportStatement createDeclared(final StmtContext<String, ImportStatement, ?> ctx) { return new ImportStatementImpl(ctx); } @Override public EffectiveStatement<String, ImportStatement> createEffective( final StmtContext<String, ImportStatement, EffectiveStatement<String, ImportStatement>> ctx) { return new ImportEffectiveStatementImpl(ctx); } @Override public void onPreLinkageDeclared(final Mutable<String, ImportStatement, EffectiveStatement<String, ImportStatement>> stmt) { /* * Add ModuleIdentifier of a module which is required by this module. * Based on this information, required modules are searched from library * sources. */ stmt.addRequiredModule(RevisionImport.getImportedModuleIdentifier(stmt)); final String moduleName = stmt.getStatementArgument(); final ModelActionBuilder importAction = stmt.newInferenceAction(SOURCE_PRE_LINKAGE); final Prerequisite<StmtContext<?, ?, ?>> imported = importAction.requiresCtx(stmt, PreLinkageModuleNamespace.class, moduleName, SOURCE_PRE_LINKAGE); final Prerequisite<Mutable<?, ?, ?>> linkageTarget = importAction .mutatesCtx(stmt.getRoot(), SOURCE_PRE_LINKAGE); importAction.apply(new InferenceAction() { @Override public void apply() { final StmtContext<?, ?, ?> importedModuleContext = imported.get(); Verify.verify(moduleName.equals(importedModuleContext.getStatementArgument())); final URI importedModuleNamespace = importedModuleContext.getFromNamespace(ModuleNameToNamespace.class, moduleName); Verify.verifyNotNull(importedModuleNamespace); final String impPrefix = SourceException.throwIfNull( firstAttributeOf(stmt.declaredSubstatements(), PrefixStatement.class), stmt.getStatementSourceReference(), "Missing prefix statement"); stmt.addToNs(ImpPrefixToNamespace.class, impPrefix, importedModuleNamespace); } @Override public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) { InferenceException.throwIf(failed.contains(imported), stmt.getStatementSourceReference(), "Imported module [%s] was not found.", moduleName); } }); } @Override public void onLinkageDeclared( final Mutable<String, ImportStatement, EffectiveStatement<String, ImportStatement>> stmt) { if (stmt.isEnabledSemanticVersioning()) { SemanticVersionImport.onLinkageDeclared(stmt); } else { RevisionImport.onLinkageDeclared(stmt); } } @Override protected SubstatementValidator getSubstatementValidator() { return SUBSTATEMENT_VALIDATOR; } private static class RevisionImport { private RevisionImport() { throw new UnsupportedOperationException("Utility class"); } private static void onLinkageDeclared( final Mutable<String, ImportStatement, EffectiveStatement<String, ImportStatement>> stmt) { final ModuleIdentifier impIdentifier = getImportedModuleIdentifier(stmt); final ModelActionBuilder importAction = stmt.newInferenceAction(SOURCE_LINKAGE); final Prerequisite<StmtContext<?, ?, ?>> imported = importAction.requiresCtx(stmt, ModuleNamespace.class, impIdentifier, SOURCE_LINKAGE); final Prerequisite<Mutable<?, ?, ?>> linkageTarget = importAction .mutatesCtx(stmt.getRoot(), SOURCE_LINKAGE); importAction.apply(new InferenceAction() { @Override public void apply() { StmtContext<?, ?, ?> importedModule = null; ModuleIdentifier importedModuleIdentifier = null; if (impIdentifier.getRevision() == SimpleDateFormatUtil.DEFAULT_DATE_IMP) { final Entry<ModuleIdentifier, StmtContext<?, ModuleStatement, EffectiveStatement<String, ModuleStatement>>> recentModuleEntry = findRecentModule( impIdentifier, stmt.getAllFromNamespace(ModuleNamespace.class)); if (recentModuleEntry != null) { importedModuleIdentifier = recentModuleEntry.getKey(); importedModule = recentModuleEntry.getValue(); } } if (importedModule == null || importedModuleIdentifier == null) { importedModule = imported.get(); importedModuleIdentifier = impIdentifier; } linkageTarget.get().addToNs(ImportedModuleContext.class, importedModuleIdentifier, importedModule); final String impPrefix = firstAttributeOf(stmt.declaredSubstatements(), PrefixStatement.class); stmt.addToNs(ImpPrefixToModuleIdentifier.class, impPrefix, importedModuleIdentifier); final URI modNs = firstAttributeOf(importedModule.declaredSubstatements(), NamespaceStatement.class); stmt.addToNs(URIStringToImpPrefix.class, modNs.toString(), impPrefix); } @Override public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) { if (failed.contains(imported)) { throw new InferenceException(stmt.getStatementSourceReference(), "Imported module [%s] was not found.", impIdentifier); } } }); } private static Entry<ModuleIdentifier, StmtContext<?, ModuleStatement, EffectiveStatement<String, ModuleStatement>>> findRecentModule( final ModuleIdentifier impIdentifier, final Map<ModuleIdentifier, StmtContext<?, ModuleStatement, EffectiveStatement<String, ModuleStatement>>> allModules) { ModuleIdentifier recentModuleIdentifier = impIdentifier; Entry<ModuleIdentifier, StmtContext<?, ModuleStatement, EffectiveStatement<String, ModuleStatement>>> recentModuleEntry = null; for (final Entry<ModuleIdentifier, StmtContext<?, ModuleStatement, EffectiveStatement<String, ModuleStatement>>> moduleEntry : allModules .entrySet()) { final ModuleIdentifier id = moduleEntry.getKey(); if (id.getName().equals(impIdentifier.getName()) && id.getRevision().compareTo(recentModuleIdentifier.getRevision()) > 0) { recentModuleIdentifier = id; recentModuleEntry = moduleEntry; } } return recentModuleEntry; } private static ModuleIdentifier getImportedModuleIdentifier(final Mutable<String, ImportStatement, ?> stmt) { Date revision = firstAttributeOf(stmt.declaredSubstatements(), RevisionDateStatement.class); if (revision == null) { revision = SimpleDateFormatUtil.DEFAULT_DATE_IMP; } return ModuleIdentifierImpl.create(stmt.getStatementArgument(), Optional.empty(), Optional.of(revision)); } } private static class SemanticVersionImport { private SemanticVersionImport() { throw new UnsupportedOperationException("Utility class"); } private static void onLinkageDeclared( final Mutable<String, ImportStatement, EffectiveStatement<String, ImportStatement>> stmt) { final ModuleIdentifier impIdentifier = getImportedModuleIdentifier(stmt); final ModelActionBuilder importAction = stmt.newInferenceAction(SOURCE_LINKAGE); final Prerequisite<StmtContext<?, ?, ?>> imported = importAction.requiresCtx(stmt, ModuleNamespace.class, impIdentifier, SOURCE_LINKAGE); final Prerequisite<Mutable<?, ?, ?>> linkageTarget = importAction .mutatesCtx(stmt.getRoot(), SOURCE_LINKAGE); importAction.apply(new InferenceAction() { @Override public void apply() { final Entry<SemVer, StmtContext<?, ?, ?>> importedModuleEntry= findRecentCompatibleModuleEntry( impIdentifier.getName(), stmt); StmtContext<?, ?, ?> importedModule = null; ModuleIdentifier importedModuleIdentifier = null; ModuleIdentifier semVerModuleIdentifier = null; if (importedModuleEntry != null) { importedModule = importedModuleEntry.getValue(); importedModuleIdentifier = importedModule.getFromNamespace(ModuleCtxToModuleIdentifier.class, importedModule); semVerModuleIdentifier = createSemVerModuleIdentifier(importedModuleIdentifier, importedModuleEntry.getKey()); } else { throw new InferenceException(stmt.getStatementSourceReference(), "Unable to find module compatible with requested import [%s(%s)].", impIdentifier .getName(), getRequestedImportVersion(stmt)); } linkageTarget.get().addToNs(ImportedModuleContext.class, importedModuleIdentifier, importedModule); final String impPrefix = firstAttributeOf(stmt.declaredSubstatements(), PrefixStatement.class); stmt.addToNs(ImpPrefixToModuleIdentifier.class, impPrefix, importedModuleIdentifier); stmt.addToNs(ImpPrefixToSemVerModuleIdentifier.class, impPrefix, semVerModuleIdentifier); final URI modNs = firstAttributeOf(importedModule.declaredSubstatements(), NamespaceStatement.class); stmt.addToNs(URIStringToImpPrefix.class, modNs.toString(), impPrefix); } @Override public void prerequisiteFailed(final Collection<? extends Prerequisite<?>> failed) { if (failed.contains(imported)) { throw new InferenceException(stmt.getStatementSourceReference(), "Unable to find module compatible with requested import [%s(%s)].", impIdentifier .getName(), getRequestedImportVersion(stmt)); } } }); } private static SemVer getRequestedImportVersion(final Mutable<?, ?, ?> impStmt) { SemVer requestedImportVersion = impStmt.getFromNamespace(SemanticVersionNamespace.class, impStmt); if (requestedImportVersion == null) { requestedImportVersion = Module.DEFAULT_SEMANTIC_VERSION; } return requestedImportVersion; } private static Entry<SemVer, StmtContext<?, ?, ?>> findRecentCompatibleModuleEntry(final String moduleName, final Mutable<String, ImportStatement, EffectiveStatement<String, ImportStatement>> impStmt) { NavigableMap<SemVer, StmtContext<?, ?, ?>> allRelevantModulesMap = impStmt.getFromNamespace( SemanticVersionModuleNamespace.class, moduleName); if (allRelevantModulesMap == null) { return null; } final SemVer requestedImportVersion = getRequestedImportVersion(impStmt); allRelevantModulesMap = allRelevantModulesMap.subMap(requestedImportVersion, true, SemVer.create(requestedImportVersion.getMajor() + 1), false); if (!allRelevantModulesMap.isEmpty()) { return allRelevantModulesMap.lastEntry(); } return null; } private static ModuleIdentifier getImportedModuleIdentifier(final Mutable<String, ImportStatement, ?> impStmt) { return ModuleIdentifierImpl.create(impStmt.getStatementArgument(), Optional.empty(), Optional.of(SimpleDateFormatUtil.DEFAULT_DATE_IMP)); } private static ModuleIdentifier createSemVerModuleIdentifier(final ModuleIdentifier importedModuleIdentifier, final SemVer semVer) { return ModuleIdentifierImpl.create(importedModuleIdentifier.getName(), Optional.ofNullable(importedModuleIdentifier.getNamespace()), Optional.of(importedModuleIdentifier.getRevision()), semVer); } } }