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%2Fimpl%2Futil%2FYangModelDependencyInfo.java;h=6b1abbc91158fe1089f5681c6c2a00e166a4196d;hb=b212baa59f859732bd3a799425bb420035fe6154;hp=6530c92510c8d6aabd43eb5640a4ae146b5c71ca;hpb=72d620c6fb5312f594474a992ec65154c2562b07;p=yangtools.git diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/util/YangModelDependencyInfo.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/util/YangModelDependencyInfo.java index 6530c92510..6b1abbc911 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/util/YangModelDependencyInfo.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/util/YangModelDependencyInfo.java @@ -3,77 +3,143 @@ * * 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 + * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.yangtools.yang.parser.impl.util; -import static org.opendaylight.yangtools.yang.parser.util.ParserListenerUtils.getArgumentString; -import static org.opendaylight.yangtools.yang.parser.util.ParserListenerUtils.getFirstContext; - -import java.io.InputStream; -import java.util.Date; -import java.util.List; - -import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Belongs_to_stmtContext; -import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Import_stmtContext; -import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Include_stmtContext; -import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Module_stmtContext; -import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_date_stmtContext; -import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtContext; -import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtsContext; -import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_stmtContext; -import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.YangContext; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.model.api.ModuleImport; -import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; -import com.google.common.base.Optional; +import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; - +import java.io.IOException; +import java.util.HashSet; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import javax.annotation.Nullable; +import org.antlr.v4.runtime.ParserRuleContext; +import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.StatementContext; +import org.opendaylight.yangtools.concepts.SemVer; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.model.api.ModuleImport; +import org.opendaylight.yangtools.yang.model.api.YangStmtMapping; +import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException; +import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; +import org.opendaylight.yangtools.yang.parser.rfc6020.repo.YangStatementStreamSource; +import org.opendaylight.yangtools.yang.parser.spi.source.DeclarationInTextSource; +import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference; +import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.SupportedExtensionsMapping; +import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils; + +/** + * Helper transfer object which holds basic and dependency information for YANG + * model. + * + *

+ * There are two concrete implementations of this interface: + *

+ * + * @see ModuleDependencyInfo + * @see SubmoduleDependencyInfo + */ public abstract class YangModelDependencyInfo { + private static final String BELONGS_TO = YangStmtMapping.BELONGS_TO.getStatementName().getLocalName(); + private static final String IMPORT = YangStmtMapping.IMPORT.getStatementName().getLocalName(); + private static final String INCLUDE = YangStmtMapping.INCLUDE.getStatementName().getLocalName(); + private static final String MODULE = YangStmtMapping.MODULE.getStatementName().getLocalName(); + private static final String REVISION = YangStmtMapping.REVISION.getStatementName().getLocalName(); + private static final String REVISION_DATE = YangStmtMapping.REVISION_DATE.getStatementName().getLocalName(); + private static final String SUBMODULE = YangStmtMapping.SUBMODULE.getStatementName().getLocalName(); + + private static final String OPENCONFIG_VERSION = SupportedExtensionsMapping.OPENCONFIG_VERSION.getStatementName() + .getLocalName(); private final String name; private final String formattedRevision; - private final Date revision; + private final Revision revision; + private final Optional semVer; private final ImmutableSet submoduleIncludes; private final ImmutableSet moduleImports; private final ImmutableSet dependencies; - public YangModelDependencyInfo(final String name, final String formattedRevision, final ImmutableSet imports, + YangModelDependencyInfo(final String name, final String formattedRevision, + final ImmutableSet imports, final ImmutableSet includes) { + this(name, formattedRevision, imports, includes, Optional.empty()); + } + + YangModelDependencyInfo(final String name, final String formattedRevision, + final ImmutableSet imports, + final ImmutableSet includes, + final Optional semVer) { this.name = name; this.formattedRevision = formattedRevision; - this.revision = QName.parseRevision(formattedRevision); + this.revision = formattedRevision == null ? null : Revision.valueOf(formattedRevision); this.moduleImports = imports; this.submoduleIncludes = includes; - this.dependencies = ImmutableSet. builder() // - .addAll(moduleImports) // - .addAll(submoduleIncludes) // - .build(); + this.dependencies = ImmutableSet.builder() + .addAll(moduleImports).addAll(submoduleIncludes).build(); + this.semVer = semVer; } + /** + * Returns immutable collection of all module imports. This collection contains both import statements + * and include statements for submodules. + * + * @return Immutable collection of imports. + */ public ImmutableSet getDependencies() { return dependencies; } + /** + * Returns model name. + * + * @return model name + */ public String getName() { return name; } + /** + * Returns formatted revision string. + * + * @return formatted revision string + */ public String getFormattedRevision() { return formattedRevision; } - public Date getRevision() { + /** + * Returns revision. + * + * @return revision + */ + Revision getRevision() { return revision; } + /** + * Returns semantic version of module. + * + * @return semantic version + */ + public Optional getSemanticVersion() { + return semVer; + } + @Override public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((formattedRevision == null) ? 0 : formattedRevision.hashCode()); - result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + Objects.hashCode(formattedRevision); + result = prime * result + Objects.hashCode(name); + result = prime * result + Objects.hashCode(semVer); return result; } @@ -88,7 +154,7 @@ public abstract class YangModelDependencyInfo { if (!(obj instanceof YangModelDependencyInfo)) { return false; } - YangModelDependencyInfo other = (YangModelDependencyInfo) obj; + final YangModelDependencyInfo other = (YangModelDependencyInfo) obj; if (formattedRevision == null) { if (other.formattedRevision != null) { return false; @@ -103,140 +169,249 @@ public abstract class YangModelDependencyInfo { } else if (!name.equals(other.name)) { return false; } - return true; + return Objects.equals(semVer, other.semVer); } - public static YangModelDependencyInfo fromInputStream(final InputStream yangStream) { - YangContext yangContext = YangParserImpl.parseStreamWithoutErrorListeners(yangStream); + /** + * Extracts {@link YangModelDependencyInfo} from an abstract syntax tree of a YANG model. + * + * @param tree Abstract syntax tree + * @return {@link YangModelDependencyInfo} + * @throws YangSyntaxErrorException If the AST is not a valid YANG module/submodule + */ + public static YangModelDependencyInfo fromAST(final String name, + final ParserRuleContext tree) throws YangSyntaxErrorException { + + if (tree instanceof StatementContext) { + final StatementContext rootStatement = (StatementContext) tree; + return parseAST(rootStatement, name); + } + + throw new YangSyntaxErrorException(name, 0, 0, "Unknown YANG text type"); + } - Optional moduleCtx = getFirstContext(yangContext, Module_stmtContext.class); - if (moduleCtx.isPresent()) { - return fromModuleContext(moduleCtx.get()); + private static YangModelDependencyInfo parseAST(final StatementContext rootStatement, final String sourceName) { + final String keyWordText = rootStatement.keyword().getText(); + if (MODULE.equals(keyWordText)) { + return parseModuleContext(rootStatement, sourceName); } - Optional submoduleCtx = getFirstContext(yangContext, Submodule_stmtContext.class); - if (submoduleCtx.isPresent()) { - return fromSubmoduleContext(submoduleCtx.get()); + if (SUBMODULE.equals(keyWordText)) { + return parseSubmoduleContext(rootStatement, sourceName); + } + throw new IllegalArgumentException("Root of parsed AST must be either module or submodule"); + } + + /** + * Extracts {@link YangModelDependencyInfo} from input stream containing a YANG model. This parsing does not + * validate full YANG module, only parses header up to the revisions and imports. + * + * @param refClass Base search class + * @param resourceName resource name, relative to refClass + * @return {@link YangModelDependencyInfo} + * @throws YangSyntaxErrorException If the resource does not pass syntactic analysis + * @throws IOException When the resource cannot be read + * @throws IllegalArgumentException + * If input stream is not valid YANG stream + */ + public static YangModelDependencyInfo forResource(final Class refClass, final String resourceName) + throws IOException, YangSyntaxErrorException { + final YangStatementStreamSource source = YangStatementStreamSource.create( + YangTextSchemaSource.forResource(refClass, resourceName)); + final ParserRuleContext ast = source.getYangAST(); + checkArgument(ast instanceof StatementContext); + return parseAST((StatementContext) ast, source.getIdentifier().toYangFilename()); + } + + private static YangModelDependencyInfo parseModuleContext(final StatementContext module, final String sourceName) { + final String name = Utils.stringFromStringContext(module.argument(), getReference(sourceName, module)); + final String latestRevision = getLatestRevision(module, sourceName); + final Optional semVer = Optional.ofNullable(findSemanticVersion(module, sourceName)); + final ImmutableSet imports = parseImports(module, sourceName); + final ImmutableSet includes = parseIncludes(module, sourceName); + + return new ModuleDependencyInfo(name, latestRevision, imports, includes, semVer); + } + + private static ImmutableSet parseImports(final StatementContext module, final String sourceName) { + final Set result = new HashSet<>(); + for (final StatementContext subStatementContext : module.statement()) { + if (IMPORT.equals(subStatementContext.keyword().getText())) { + final String revisionDateStr = getRevisionDateString(subStatementContext, sourceName); + final String importedModuleName = Utils.stringFromStringContext(subStatementContext.argument(), + getReference(sourceName, subStatementContext)); + final Revision revisionDate = revisionDateStr == null ? null : Revision.valueOf(revisionDateStr); + final SemVer importSemVer = findSemanticVersion(subStatementContext, sourceName); + result.add(new ModuleImportImpl(importedModuleName, revisionDate, importSemVer)); + } } - throw new IllegalArgumentException("Supplied stream is not valid yang file."); + return ImmutableSet.copyOf(result); } - private static YangModelDependencyInfo fromModuleContext(final Module_stmtContext module) { - String name = getArgumentString(module); - // String prefix = - // getArgumentString(module.module_header_stmts().prefix_stmt(0)); - String namespace = getArgumentString(module.module_header_stmts().namespace_stmt(0)); - String latestRevision = getLatestRevision(module.revision_stmts()); - ImmutableSet imports = getImports(module.linkage_stmts().import_stmt()); - ImmutableSet includes = getIncludes(module.linkage_stmts().include_stmt()); + private static SemVer findSemanticVersion(final StatementContext statement, final String sourceName) { + String semVerString = null; + for (final StatementContext subStatement : statement.statement()) { + final String subStatementName = Utils.trimPrefix(subStatement.keyword().getText()); + if (OPENCONFIG_VERSION.equals(subStatementName)) { + semVerString = Utils.stringFromStringContext(subStatement.argument(), + getReference(sourceName, subStatement)); + break; + } + } + + return Strings.isNullOrEmpty(semVerString) ? null : SemVer.valueOf(semVerString); + } - return new ModuleDependencyInfo(name, latestRevision, namespace, imports, includes); + private static ImmutableSet parseIncludes(final StatementContext module, final String sourceName) { + final Set result = new HashSet<>(); + for (final StatementContext subStatementContext : module.statement()) { + if (INCLUDE.equals(subStatementContext.keyword().getText())) { + final String revisionDateStr = getRevisionDateString(subStatementContext, sourceName); + final String IncludeModuleName = Utils.stringFromStringContext(subStatementContext.argument(), + getReference(sourceName, subStatementContext)); + final Revision revisionDate = revisionDateStr == null ? null : Revision.valueOf(revisionDateStr); + result.add(new ModuleImportImpl(IncludeModuleName, revisionDate)); + } + } + return ImmutableSet.copyOf(result); } - private static ImmutableSet getImports(final List importStatements) { - ImmutableSet.Builder builder = ImmutableSet.builder(); - for (Import_stmtContext importStmt : importStatements) { - String moduleName = getArgumentString(importStmt); - Date revision = getRevision(importStmt.revision_date_stmt()); - builder.add(new ModuleImportImpl(moduleName, revision)); + private static String getRevisionDateString(final StatementContext importStatement, final String sourceName) { + String revisionDateStr = null; + for (final StatementContext importSubStatement : importStatement.statement()) { + if (REVISION_DATE.equals(importSubStatement.keyword().getText())) { + revisionDateStr = Utils.stringFromStringContext(importSubStatement.argument(), + getReference(sourceName, importSubStatement)); + } } - return builder.build(); + return revisionDateStr; } - private static String getLatestRevision(final Revision_stmtsContext revision_stmts) { - List revisions = revision_stmts.getRuleContexts(Revision_stmtContext.class); + public static String getLatestRevision(final StatementContext module, final String sourceName) { String latestRevision = null; - for (Revision_stmtContext revisionStmt : revisions) { - String currentRevision = getArgumentString(revisionStmt); - if (latestRevision == null || latestRevision.compareTo(currentRevision) == -1) { - latestRevision = currentRevision; + for (final StatementContext subStatementContext : module.statement()) { + if (REVISION.equals(subStatementContext.keyword().getText())) { + final String currentRevision = Utils.stringFromStringContext(subStatementContext.argument(), + getReference(sourceName, subStatementContext)); + if (latestRevision == null || latestRevision.compareTo(currentRevision) == -1) { + latestRevision = currentRevision; + } } } return latestRevision; } - private static YangModelDependencyInfo fromSubmoduleContext(final Submodule_stmtContext submodule) { - String name = getArgumentString(submodule); - Belongs_to_stmtContext belongsToStmt = submodule.submodule_header_stmts().belongs_to_stmt(0); - String belongsTo = getArgumentString(belongsToStmt); + private static YangModelDependencyInfo parseSubmoduleContext(final StatementContext submodule, + final String sourceName) { + final String name = Utils.stringFromStringContext(submodule.argument(), getReference(sourceName, submodule)); + final String belongsTo = parseBelongsTo(submodule, sourceName); - String latestRevision = getLatestRevision(submodule.revision_stmts()); - ImmutableSet imports = getImports(submodule.linkage_stmts().import_stmt()); - ImmutableSet includes = getIncludes(submodule.linkage_stmts().include_stmt()); + final String latestRevision = getLatestRevision(submodule, sourceName); + final ImmutableSet imports = parseImports(submodule, sourceName); + final ImmutableSet includes = parseIncludes(submodule, sourceName); return new SubmoduleDependencyInfo(name, latestRevision, belongsTo, imports, includes); } - private static ImmutableSet getIncludes(final List importStatements) { - ImmutableSet.Builder builder = ImmutableSet.builder(); - for (Include_stmtContext importStmt : importStatements) { - String moduleName = getArgumentString(importStmt); - Date revision = getRevision(importStmt.revision_date_stmt()); - builder.add(new ModuleImportImpl(moduleName, revision)); + private static String parseBelongsTo(final StatementContext submodule, final String sourceName) { + for (final StatementContext subStatementContext : submodule.statement()) { + if (BELONGS_TO.equals(subStatementContext.keyword().getText())) { + return Utils.stringFromStringContext(subStatementContext.argument(), + getReference(sourceName, subStatementContext)); + } } - return builder.build(); + return null; } - private static Date getRevision(final Revision_date_stmtContext revision_date_stmt) { - if (revision_date_stmt == null) { - return null; - } - String formatedDate = getArgumentString(revision_date_stmt); - return QName.parseRevision(formatedDate); + private static StatementSourceReference getReference(final String sourceName, + final StatementContext context) { + return DeclarationInTextSource.atPosition(sourceName, context.getStart().getLine(), + context.getStart().getCharPositionInLine()); } - public static final class ModuleDependencyInfo extends YangModelDependencyInfo { - - private ModuleDependencyInfo(final String name, final String latestRevision, final String namespace, + /** + * Dependency information for YANG module. + */ + public static class ModuleDependencyInfo extends YangModelDependencyInfo { + private ModuleDependencyInfo(final String name, final String latestRevision, final ImmutableSet imports, final ImmutableSet includes) { super(name, latestRevision, imports, includes); } + private ModuleDependencyInfo(final String name, final String latestRevision, + final ImmutableSet imports, + final ImmutableSet includes, + final Optional semVer) { + super(name, latestRevision, imports, includes, semVer); + } + @Override public String toString() { return "Module [name=" + getName() + ", revision=" + getRevision() - + ", dependencies=" + getDependencies() + "]"; + + ", semanticVersion=" + getSemanticVersion().orElse(null) + + ", dependencies=" + getDependencies() + + "]"; } } + /** + * Dependency information for submodule, also provides name for parent module. + */ public static final class SubmoduleDependencyInfo extends YangModelDependencyInfo { - private final String belongsTo; - public String getParentModule() { - return belongsTo; - } - private SubmoduleDependencyInfo(final String name, final String latestRevision, final String belongsTo, final ImmutableSet imports, final ImmutableSet includes) { super(name, latestRevision, imports, includes); this.belongsTo = belongsTo; } + /** + * Returns name of parent module. + */ + public String getParentModule() { + return belongsTo; + } + @Override public String toString() { - return "Submodule [name=" + getName() + ", revision=" + getRevision() - + ", dependencies=" + getDependencies() + "]"; + return "Submodule [name=" + getName() + ", revision=" + + getRevision() + ", dependencies=" + getDependencies() + + "]"; } } + /** + * Utility implementation of {@link ModuleImport} to be used by {@link YangModelDependencyInfo}. + */ private static final class ModuleImportImpl implements ModuleImport { - private final Date revision; + private final Revision revision; + private final SemVer semVer; private final String name; - public ModuleImportImpl(final String moduleName, final Date revision) { - this.name = moduleName; + ModuleImportImpl(final String moduleName, final Revision revision) { + this(moduleName, revision, null); + } + + ModuleImportImpl(final String moduleName, @Nullable final Revision revision, @Nullable final SemVer semVer) { + this.name = requireNonNull(moduleName, "Module name must not be null."); this.revision = revision; + this.semVer = semVer; } @Override public String getModuleName() { - return this.name; + return name; + } + + @Override + public Optional getRevision() { + return Optional.ofNullable(revision); } @Override - public Date getRevision() { - return this.revision; + public Optional getSemanticVersion() { + return Optional.ofNullable(semVer); } @Override @@ -248,8 +423,9 @@ public abstract class YangModelDependencyInfo { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((revision == null) ? 0 : revision.hashCode()); + result = prime * result + Objects.hashCode(name); + result = prime * result + Objects.hashCode(revision); + result = prime * result + Objects.hashCode(semVer); return result; } @@ -264,7 +440,7 @@ public abstract class YangModelDependencyInfo { if (getClass() != obj.getClass()) { return false; } - ModuleImportImpl other = (ModuleImportImpl) obj; + final ModuleImportImpl other = (ModuleImportImpl) obj; if (name == null) { if (other.name != null) { return false; @@ -279,12 +455,17 @@ public abstract class YangModelDependencyInfo { } else if (!revision.equals(other.revision)) { return false; } + + if (!Objects.equals(getSemanticVersion(), other.getSemanticVersion())) { + return false; + } return true; } @Override public String toString() { - return "ModuleImportImpl [name=" + name + ", revision=" + QName.formattedRevision(revision) + "]"; + return "ModuleImportImpl [name=" + name + ", revision=" + + QName.formattedRevision(Optional.ofNullable(revision)) + ", semanticVersion=" + semVer + "]"; } } }