import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
-import com.google.common.annotations.VisibleForTesting;
+import com.google.common.annotations.Beta;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.util.HashSet;
-import java.util.List;
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.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.concepts.SemVer;
import org.opendaylight.yangtools.openconfig.model.api.OpenConfigStatements;
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.api.stmt.ImportEffectiveStatement;
import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRArgument;
+import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRKeyword;
+import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRKeyword.Unqualified;
+import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRSchemaSource;
+import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRStatement;
import org.opendaylight.yangtools.yang.parser.spi.source.DeclarationInTextSource;
import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
private static final String OPENCONFIG_VERSION = OpenConfigStatements.OPENCONFIG_VERSION.getStatementName()
.getLocalName();
+ @Deprecated
private static final Splitter COLON_SPLITTER = Splitter.on(":").omitEmptyStrings().trimResults();
private final String name;
}
/**
- * Extracts {@link YangModelDependencyInfo} from an abstract syntax tree of a YANG model.
+ * Extracts {@link YangModelDependencyInfo} from an intermediate representation root statement of a YANG model.
*
- * @param source Source identifier
- * @param tree Abstract syntax tree
+ * @param source Schema source
* @return {@link YangModelDependencyInfo}
- * @throws YangSyntaxErrorException If the AST is not a valid YANG module/submodule
+ * @throws IllegalArgumentException If the root statement is not a valid YANG module/submodule
*/
- static YangModelDependencyInfo fromAST(final SourceIdentifier source, final ParserRuleContext tree)
- throws YangSyntaxErrorException {
-
- if (tree instanceof StatementContext) {
- final StatementContext rootStatement = (StatementContext) tree;
- return parseAST(rootStatement, source);
- }
-
- throw new YangSyntaxErrorException(source, 0, 0, "Unknown YANG text type");
+ public static @NonNull YangModelDependencyInfo forIR(final IRSchemaSource source) {
+ return forIR(source.getRootStatement(), source.getIdentifier());
}
- private static YangModelDependencyInfo parseAST(final StatementContext rootStatement,
+ /**
+ * Extracts {@link YangModelDependencyInfo} from an intermediate representation root statement of a YANG model.
+ *
+ * @param source Source identifier
+ * @param rootStatement root statement
+ * @return {@link YangModelDependencyInfo}
+ * @throws IllegalArgumentException If the root statement is not a valid YANG module/submodule
+ */
+ static @NonNull YangModelDependencyInfo forIR(final IRStatement rootStatement,
final SourceIdentifier source) {
- final String keyWordText = rootStatement.keyword().getText();
- if (MODULE.equals(keyWordText)) {
+ final IRKeyword keyword = rootStatement.keyword();
+ checkArgument(keyword instanceof Unqualified, "Invalid root statement %s", keyword);
+
+ final String arg = keyword.identifier();
+ if (MODULE.equals(arg)) {
return parseModuleContext(rootStatement, source);
}
- if (SUBMODULE.equals(keyWordText)) {
+ if (SUBMODULE.equals(arg)) {
return parseSubmoduleContext(rootStatement, source);
}
throw new IllegalArgumentException("Root of parsed AST must be either module or submodule");
* @throws IOException When the resource cannot be read
* @throws IllegalArgumentException
* If input stream is not valid YANG stream
+ * @deprecated This method was used by testing framework and was deemed to be potentially useful to the outside
+ * world. With Java Platform Module System, though, the resource loading rules have changed to the point
+ * where we no longer can guarantee it working correctly, as the results depend on the resource path.
+ * Users are advised to use {@link #forYangText(YangTextSchemaSource)}.
*/
- @VisibleForTesting
+ @Deprecated(forRemoval = true)
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());
+ return forYangText(YangTextSchemaSource.forResource(refClass, resourceName));
}
- private static YangModelDependencyInfo parseModuleContext(final StatementContext module,
+ /**
+ * Extracts {@link YangModelDependencyInfo} from a {@link YangTextSchemaSource}. This parsing does not
+ * validate full YANG module, only parses header up to the revisions and imports.
+ *
+ * @param yangText {@link YangTextSchemaSource}
+ * @return {@link YangModelDependencyInfo}
+ * @throws YangSyntaxErrorException If the resource does not pass syntactic analysis
+ * @throws IOException When the resource cannot be read
+ */
+ public static YangModelDependencyInfo forYangText(final YangTextSchemaSource yangText)
+ throws IOException, YangSyntaxErrorException {
+ final YangStatementStreamSource source = YangStatementStreamSource.create(yangText);
+ return forIR(source.rootStatement(), source.getIdentifier());
+ }
+
+ private static @NonNull YangModelDependencyInfo parseModuleContext(final IRStatement module,
final SourceIdentifier source) {
- final String name = ArgumentContextUtils.stringFromStringContext(module.argument(), getReference(source,
- module));
+ final String name = safeStringArgument(source, module, "module name");
final String latestRevision = getLatestRevision(module, source);
final Optional<SemVer> semVer = Optional.ofNullable(findSemanticVersion(module, source));
final ImmutableSet<ModuleImport> imports = parseImports(module, source);
return new ModuleDependencyInfo(name, latestRevision, imports, includes, semVer);
}
- private static ImmutableSet<ModuleImport> parseImports(final StatementContext module,
+ private static ImmutableSet<ModuleImport> parseImports(final IRStatement module,
final SourceIdentifier source) {
final Set<ModuleImport> result = new HashSet<>();
- for (final StatementContext subStatementContext : module.statement()) {
- if (IMPORT.equals(subStatementContext.keyword().getText())) {
- final String revisionDateStr = getRevisionDateString(subStatementContext, source);
- final String importedModuleName = ArgumentContextUtils.stringFromStringContext(
- subStatementContext.argument(), getReference(source, subStatementContext));
+ for (final IRStatement substatement : module.statements()) {
+ if (isBuiltin(substatement, IMPORT)) {
+ final String importedModuleName = safeStringArgument(source, substatement, "imported module name");
+ final String revisionDateStr = getRevisionDateString(substatement, source);
final Revision revisionDate = Revision.ofNullable(revisionDateStr).orElse(null);
- final SemVer importSemVer = findSemanticVersion(subStatementContext, source);
+ final SemVer importSemVer = findSemanticVersion(substatement, source);
result.add(new ModuleImportImpl(importedModuleName, revisionDate, importSemVer));
}
}
return ImmutableSet.copyOf(result);
}
- private static SemVer findSemanticVersion(final StatementContext statement, final SourceIdentifier source) {
+ @Beta
+ public static SemVer findSemanticVersion(final IRStatement statement, final SourceIdentifier source) {
String semVerString = null;
- for (final StatementContext subStatement : statement.statement()) {
- final String subStatementName = trimPrefix(subStatement.keyword().getText());
- if (OPENCONFIG_VERSION.equals(subStatementName)) {
- semVerString = ArgumentContextUtils.stringFromStringContext(subStatement.argument(),
- getReference(source, subStatement));
+ for (final IRStatement substatement : statement.statements()) {
+ // FIXME: this should also check we are using a prefix
+ if (OPENCONFIG_VERSION.equals(substatement.keyword().identifier())) {
+ semVerString = safeStringArgument(source, substatement, "version string");
break;
}
}
return Strings.isNullOrEmpty(semVerString) ? null : SemVer.valueOf(semVerString);
}
-
- private static String trimPrefix(final String identifier) {
- final List<String> namesParts = COLON_SPLITTER.splitToList(identifier);
- if (namesParts.size() == 2) {
- return namesParts.get(1);
- }
- return identifier;
+ private static boolean isBuiltin(final IRStatement stmt, final String localName) {
+ final IRKeyword keyword = stmt.keyword();
+ return keyword instanceof Unqualified && localName.equals(keyword.identifier());
}
-
- private static ImmutableSet<ModuleImport> parseIncludes(final StatementContext module,
- final SourceIdentifier source) {
+ private static ImmutableSet<ModuleImport> parseIncludes(final IRStatement module, final SourceIdentifier source) {
final Set<ModuleImport> result = new HashSet<>();
- for (final StatementContext subStatementContext : module.statement()) {
- if (INCLUDE.equals(subStatementContext.keyword().getText())) {
- final String revisionDateStr = getRevisionDateString(subStatementContext, source);
- final String IncludeModuleName = ArgumentContextUtils.stringFromStringContext(
- subStatementContext.argument(), getReference(source, subStatementContext));
+ for (final IRStatement substatement : module.statements()) {
+ if (isBuiltin(substatement, INCLUDE)) {
+ final String revisionDateStr = getRevisionDateString(substatement, source);
+ final String IncludeModuleName = safeStringArgument(source, substatement, "included submodule name");
final Revision revisionDate = Revision.ofNullable(revisionDateStr).orElse(null);
result.add(new ModuleImportImpl(IncludeModuleName, revisionDate));
}
return ImmutableSet.copyOf(result);
}
- private static String getRevisionDateString(final StatementContext importStatement, final SourceIdentifier source) {
+ private static String getRevisionDateString(final IRStatement importStatement, final SourceIdentifier source) {
String revisionDateStr = null;
- for (final StatementContext importSubStatement : importStatement.statement()) {
- if (REVISION_DATE.equals(importSubStatement.keyword().getText())) {
- revisionDateStr = ArgumentContextUtils.stringFromStringContext(importSubStatement.argument(),
- getReference(source, importSubStatement));
+ for (final IRStatement substatement : importStatement.statements()) {
+ if (isBuiltin(substatement, REVISION_DATE)) {
+ revisionDateStr = safeStringArgument(source, substatement, "imported module revision-date");
}
}
return revisionDateStr;
}
- public static String getLatestRevision(final StatementContext module, final SourceIdentifier source) {
+ public static String getLatestRevision(final IRStatement module, final SourceIdentifier source) {
String latestRevision = null;
- for (final StatementContext subStatementContext : module.statement()) {
- if (REVISION.equals(subStatementContext.keyword().getText())) {
- final String currentRevision = ArgumentContextUtils.stringFromStringContext(
- subStatementContext.argument(), getReference(source, subStatementContext));
+ for (final IRStatement substatement : module.statements()) {
+ if (isBuiltin(substatement, REVISION)) {
+ final String currentRevision = safeStringArgument(source, substatement, "revision date");
if (latestRevision == null || latestRevision.compareTo(currentRevision) < 0) {
latestRevision = currentRevision;
}
return latestRevision;
}
- private static YangModelDependencyInfo parseSubmoduleContext(final StatementContext submodule,
+ private static @NonNull YangModelDependencyInfo parseSubmoduleContext(final IRStatement submodule,
final SourceIdentifier source) {
- final String name = ArgumentContextUtils.stringFromStringContext(submodule.argument(),
- getReference(source, submodule));
+ final String name = safeStringArgument(source, submodule, "submodule name");
final String belongsTo = parseBelongsTo(submodule, source);
final String latestRevision = getLatestRevision(submodule, source);
return new SubmoduleDependencyInfo(name, latestRevision, belongsTo, imports, includes);
}
- private static String parseBelongsTo(final StatementContext submodule, final SourceIdentifier source) {
- for (final StatementContext subStatementContext : submodule.statement()) {
- if (BELONGS_TO.equals(subStatementContext.keyword().getText())) {
- return ArgumentContextUtils.stringFromStringContext(subStatementContext.argument(),
- getReference(source, subStatementContext));
+ private static String parseBelongsTo(final IRStatement submodule, final SourceIdentifier source) {
+ for (final IRStatement substatement : submodule.statements()) {
+ if (isBuiltin(substatement, BELONGS_TO)) {
+ return safeStringArgument(source, substatement, "belongs-to module name");
}
}
return null;
}
- private static StatementSourceReference getReference(final SourceIdentifier source,
- final StatementContext context) {
- return DeclarationInTextSource.atPosition(source.getName(), context.getStart().getLine(),
- context.getStart().getCharPositionInLine());
+ static String safeStringArgument(final SourceIdentifier source, final IRStatement stmt, final String desc) {
+ final StatementSourceReference ref = getReference(source, stmt);
+ final IRArgument arg = stmt.argument();
+ checkArgument(arg != null, "Missing %s at %s", desc, ref);
+ // TODO: we probably need to understand yang version first....
+ return ArgumentContextUtils.rfc6020().stringFromStringContext(arg, ref);
+ }
+
+ private static StatementSourceReference getReference(final SourceIdentifier source, final IRStatement stmt) {
+ return DeclarationInTextSource.atPosition(source.getName(), stmt.startLine(), stmt.startColumn());
}
/**
* Dependency information for YANG module.
*/
- public static class ModuleDependencyInfo extends YangModelDependencyInfo {
- private ModuleDependencyInfo(final String name, final String latestRevision,
- final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
- super(name, latestRevision, imports, includes);
- }
-
- private ModuleDependencyInfo(final String name, final String latestRevision,
- final ImmutableSet<ModuleImport> imports,
- final ImmutableSet<ModuleImport> includes,
- final Optional<SemVer> semVer) {
+ public static final class ModuleDependencyInfo extends YangModelDependencyInfo {
+ ModuleDependencyInfo(final String name, final String latestRevision, final ImmutableSet<ModuleImport> imports,
+ final ImmutableSet<ModuleImport> includes, final Optional<SemVer> semVer) {
super(name, latestRevision, imports, includes, semVer);
}
/**
* Utility implementation of {@link ModuleImport} to be used by {@link YangModelDependencyInfo}.
*/
+ // FIXME: this is a rather nasty misuse of APIs :(
private static final class ModuleImportImpl implements ModuleImport {
private final Revision revision;
private final SemVer semVer;
private final String name;
- ModuleImportImpl(final String moduleName, final Revision revision) {
+ ModuleImportImpl(final @NonNull String moduleName, final @Nullable Revision revision) {
this(moduleName, revision, null);
}
- ModuleImportImpl(final String moduleName, @Nullable final Revision revision, @Nullable final SemVer semVer) {
+ ModuleImportImpl(final @NonNull String moduleName, final @Nullable Revision revision,
+ final @Nullable SemVer semVer) {
this.name = requireNonNull(moduleName, "Module name must not be null.");
this.revision = revision;
this.semVer = semVer;
return Optional.empty();
}
+ @Override
+ public ImportEffectiveStatement asEffectiveStatement() {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public int hashCode() {
final int prime = 31;
if (this == obj) {
return true;
}
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
+ if (!(obj instanceof ModuleImportImpl)) {
return false;
}
final ModuleImportImpl other = (ModuleImportImpl) obj;
- if (name == null) {
- if (other.name != null) {
- return false;
- }
- } else if (!name.equals(other.name)) {
- return false;
- }
- if (revision == null) {
- if (other.revision != null) {
- return false;
- }
- } else if (!revision.equals(other.revision)) {
- return false;
- }
-
- if (!Objects.equals(getSemanticVersion(), other.getSemanticVersion())) {
- return false;
- }
- return true;
+ return name.equals(other.name) && Objects.equals(revision, other.revision)
+ && Objects.equals(getSemanticVersion(), other.getSemanticVersion());
}
@Override