2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.yangtools.yang.parser.impl.util;
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.base.Strings;
13 import com.google.common.collect.ImmutableSet;
14 import java.io.InputStream;
15 import java.util.Date;
16 import java.util.HashSet;
17 import java.util.List;
18 import java.util.Objects;
20 import org.antlr.v4.runtime.ParserRuleContext;
21 import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.StatementContext;
22 import org.opendaylight.yangtools.concepts.SemVer;
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.model.api.Module;
25 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
26 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
27 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
28 import org.opendaylight.yangtools.yang.parser.spi.source.DeclarationInTextSource;
29 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
30 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.SupportedExtensionsMapping;
31 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils;
32 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
33 import org.opendaylight.yangtools.yang.parser.util.NamedInputStream;
36 * Helper transfer object which holds basic and dependency information for YANG
41 * There are two concrete implementations of this interface:
43 * <li>{@link ModuleDependencyInfo} - Dependency information for module</li>
44 * <li>{@link SubmoduleDependencyInfo} - Dependency information for submodule</li>
47 * @see ModuleDependencyInfo
48 * @see SubmoduleDependencyInfo
52 public abstract class YangModelDependencyInfo {
54 private final String name;
55 private final String formattedRevision;
56 private final Date revision;
57 private final Optional<SemVer> semVer;
58 private final ImmutableSet<ModuleImport> submoduleIncludes;
59 private final ImmutableSet<ModuleImport> moduleImports;
60 private final ImmutableSet<ModuleImport> dependencies;
62 YangModelDependencyInfo(final String name, final String formattedRevision,
63 final ImmutableSet<ModuleImport> imports,
64 final ImmutableSet<ModuleImport> includes) {
65 this(name, formattedRevision, imports, includes, Optional.absent());
68 YangModelDependencyInfo(final String name, final String formattedRevision,
69 final ImmutableSet<ModuleImport> imports,
70 final ImmutableSet<ModuleImport> includes,
71 final Optional<SemVer> semVer) {
73 this.formattedRevision = formattedRevision;
74 this.revision = formattedRevision == null ? null : QName
75 .parseRevision(formattedRevision);
76 this.moduleImports = imports;
77 this.submoduleIncludes = includes;
78 this.dependencies = ImmutableSet.<ModuleImport> builder()
79 .addAll(moduleImports).addAll(submoduleIncludes).build();
84 * Returns immutable collection of all module imports.
86 * This collection contains both <code>import</code> statements and
87 * <code>include</code> statements for submodules.
89 * @return Immutable collection of imports.
91 public ImmutableSet<ModuleImport> getDependencies() {
100 public String getName() {
105 * Returns formatted revision string
107 * @return formatted revision string
109 public String getFormattedRevision() {
110 return formattedRevision;
123 * Returns semantic version of module
125 * @return semantic version
127 public Optional<SemVer> getSemanticVersion() {
132 public int hashCode() {
133 final int prime = 31;
135 result = prime * result + Objects.hashCode(formattedRevision);
136 result = prime * result + Objects.hashCode(name);
137 result = prime * result + Objects.hashCode(semVer);
142 public boolean equals(final Object obj) {
149 if (!(obj instanceof YangModelDependencyInfo)) {
152 final YangModelDependencyInfo other = (YangModelDependencyInfo) obj;
153 if (formattedRevision == null) {
154 if (other.formattedRevision != null) {
157 } else if (!formattedRevision.equals(other.formattedRevision)) {
161 if (other.name != null) {
164 } else if (!name.equals(other.name)) {
167 if(!Objects.equals(semVer, other.semVer)) {
175 * Extracts {@link YangModelDependencyInfo} from an abstract syntax tree of
179 * Abstract syntax tree
180 * @return {@link YangModelDependencyInfo}
181 * @throws YangSyntaxErrorException
182 * If the AST is not a valid YANG module/submodule
184 public static YangModelDependencyInfo fromAST(final String name,
185 final ParserRuleContext tree) throws YangSyntaxErrorException {
187 if (tree instanceof StatementContext) {
188 final StatementContext rootStatement = (StatementContext) tree;
189 return parseAST(rootStatement, name);
192 throw new YangSyntaxErrorException(name, 0, 0, "Unknown YANG text type");
195 private static YangModelDependencyInfo parseAST(final StatementContext rootStatement, final String sourceName) {
196 final String keyWordText = rootStatement.keyword().getText();
197 if (YangStmtMapping.MODULE.getStatementName().getLocalName().equals(keyWordText)) {
198 return parseModuleContext(rootStatement, sourceName);
200 if (YangStmtMapping.SUBMODULE.getStatementName().getLocalName().equals(keyWordText)) {
201 return parseSubmoduleContext(rootStatement, sourceName);
203 throw new IllegalArgumentException("Root of parsed AST must be either module or submodule");
207 * Extracts {@link YangModelDependencyInfo} from input stream containing
210 * This parsing does not validate full YANG module, only parses header up to
211 * the revisions and imports.
214 * Opened Input stream containing text source of YANG model
215 * @return {@link YangModelDependencyInfo}
216 * @throws IllegalArgumentException
217 * If input stream is not valid YANG stream
219 public static YangModelDependencyInfo fromInputStream(final InputStream yangStream) {
220 final StatementContext yangAST = new YangStatementSourceImpl(yangStream).getYangAST();
221 return parseAST(yangAST, yangStream instanceof NamedInputStream ? yangStream.toString() : null);
224 private static YangModelDependencyInfo parseModuleContext(final StatementContext module, final String sourceName) {
225 final String name = Utils.stringFromStringContext(module.argument(), getReference(sourceName, module));
226 final String latestRevision = getLatestRevision(module, sourceName);
227 final Optional<SemVer> semVer = Optional.fromNullable(getSemanticVersion(module, sourceName));
228 final ImmutableSet<ModuleImport> imports = parseImports(module, sourceName);
229 final ImmutableSet<ModuleImport> includes = parseIncludes(module, sourceName);
231 return new ModuleDependencyInfo(name, latestRevision, imports, includes, semVer);
234 private static ImmutableSet<ModuleImport> parseImports(final StatementContext module, final String sourceName) {
235 final Set<ModuleImport> result = new HashSet<>();
236 final List<StatementContext> subStatements = module.statement();
237 for (final StatementContext subStatementContext : subStatements) {
238 if (subStatementContext
241 .equals(YangStmtMapping.IMPORT.getStatementName()
243 final String revisionDateStr = getRevisionDateString(subStatementContext, sourceName);
244 final String importedModuleName = Utils.stringFromStringContext(subStatementContext.argument(),
245 getReference(sourceName, subStatementContext));
246 final Date revisionDate = (revisionDateStr == null) ? null : QName
247 .parseRevision(revisionDateStr);
248 final Optional<SemVer> importSemVer = Optional.fromNullable(getSemanticVersion(subStatementContext, sourceName));
249 result.add(new ModuleImportImpl(importedModuleName,
250 revisionDate, importSemVer));
253 return ImmutableSet.copyOf(result);
256 private static SemVer getSemanticVersion(final StatementContext statement, final String sourceName) {
257 final List<StatementContext> subStatements = statement.statement();
258 String semVerString = null;
259 final String semVerStmtName = SupportedExtensionsMapping.SEMANTIC_VERSION.getStatementName().getLocalName();
260 for (final StatementContext subStatement : subStatements) {
261 final String subStatementName = Utils.trimPrefix(subStatement.keyword().getText());
262 if (semVerStmtName.equals(subStatementName)) {
263 semVerString = Utils.stringFromStringContext(subStatement.argument(),
264 getReference(sourceName, subStatement));
269 if (Strings.isNullOrEmpty(semVerString)) {
273 return SemVer.valueOf(semVerString);
276 private static ImmutableSet<ModuleImport> parseIncludes(final StatementContext module, final String sourceName) {
277 final Set<ModuleImport> result = new HashSet<>();
278 final List<StatementContext> subStatements = module.statement();
279 for (final StatementContext subStatementContext : subStatements) {
280 if (subStatementContext
283 .equals(YangStmtMapping.INCLUDE.getStatementName()
285 final String revisionDateStr = getRevisionDateString(subStatementContext, sourceName);
286 final String IncludeModuleName = Utils.stringFromStringContext(subStatementContext.argument(),
287 getReference(sourceName, subStatementContext));
288 final Date revisionDate = (revisionDateStr == null) ? null : QName
289 .parseRevision(revisionDateStr);
290 result.add(new ModuleImportImpl(IncludeModuleName, revisionDate));
293 return ImmutableSet.copyOf(result);
296 private static String getRevisionDateString(final StatementContext importStatement, final String sourceName) {
297 final List<StatementContext> importSubStatements = importStatement.statement();
298 String revisionDateStr = null;
299 for (final StatementContext importSubStatement : importSubStatements) {
300 if (importSubStatement
303 .equals(YangStmtMapping.REVISION_DATE.getStatementName()
305 revisionDateStr = Utils.stringFromStringContext(importSubStatement.argument(),
306 getReference(sourceName, importSubStatement));
309 return revisionDateStr;
312 public static String getLatestRevision(final StatementContext module, final String sourceName) {
313 final List<StatementContext> subStatements = module.statement();
314 String latestRevision = null;
315 for (final StatementContext subStatementContext : subStatements) {
316 if (subStatementContext
319 .equals(YangStmtMapping.REVISION.getStatementName()
321 final String currentRevision = Utils.stringFromStringContext(subStatementContext.argument(),
322 getReference(sourceName, subStatementContext));
323 if (latestRevision == null
324 || latestRevision.compareTo(currentRevision) == -1) {
325 latestRevision = currentRevision;
329 return latestRevision;
332 private static YangModelDependencyInfo parseSubmoduleContext(final StatementContext submodule, final String sourceName) {
333 final String name = Utils.stringFromStringContext(submodule.argument(), getReference(sourceName, submodule));
334 final String belongsTo = parseBelongsTo(submodule, sourceName);
336 final String latestRevision = getLatestRevision(submodule, sourceName);
337 final ImmutableSet<ModuleImport> imports = parseImports(submodule, sourceName);
338 final ImmutableSet<ModuleImport> includes = parseIncludes(submodule, sourceName);
340 return new SubmoduleDependencyInfo(name, latestRevision, belongsTo, imports, includes);
343 private static String parseBelongsTo(final StatementContext submodule, final String sourceName) {
344 final List<StatementContext> subStatements = submodule.statement();
345 for (final StatementContext subStatementContext : subStatements) {
346 if (subStatementContext
349 .equals(YangStmtMapping.BELONGS_TO.getStatementName()
351 return Utils.stringFromStringContext(subStatementContext
352 .argument(), getReference(sourceName, subStatementContext));
358 private static StatementSourceReference getReference(final String sourceName,
359 final StatementContext context) {
360 return DeclarationInTextSource.atPosition(sourceName, context.getStart().getLine(), context.getStart()
361 .getCharPositionInLine());
366 * Dependency information for YANG module.
369 public static class ModuleDependencyInfo extends
370 YangModelDependencyInfo {
372 private ModuleDependencyInfo(final String name,
373 final String latestRevision,
374 final ImmutableSet<ModuleImport> imports,
375 final ImmutableSet<ModuleImport> includes) {
376 super(name, latestRevision, imports, includes);
379 private ModuleDependencyInfo(final String name,
380 final String latestRevision,
381 final ImmutableSet<ModuleImport> imports,
382 final ImmutableSet<ModuleImport> includes,
383 final Optional<SemVer> semVer) {
384 super(name, latestRevision, imports, includes, semVer);
388 public String toString() {
389 return "Module [name=" + getName() + ", revision=" + getRevision() + ", semanticVersion="
390 + getSemanticVersion().or(Module.DEFAULT_SEMANTIC_VERSION) + ", dependencies=" + getDependencies()
397 * Dependency information for submodule, also provides name for parent
401 public static final class SubmoduleDependencyInfo extends
402 YangModelDependencyInfo {
404 private final String belongsTo;
406 private SubmoduleDependencyInfo(final String name,
407 final String latestRevision, final String belongsTo,
408 final ImmutableSet<ModuleImport> imports,
409 final ImmutableSet<ModuleImport> includes) {
410 super(name, latestRevision, imports, includes);
411 this.belongsTo = belongsTo;
415 * Returns name of parent module.
418 public String getParentModule() {
423 public String toString() {
424 return "Submodule [name=" + getName() + ", revision="
425 + getRevision() + ", dependencies=" + getDependencies()
431 * Utility implementation of {@link ModuleImport} to be used by
432 * {@link YangModelDependencyInfo}.
435 private static final class ModuleImportImpl implements ModuleImport {
437 private final Date revision;
438 private final SemVer semVer;
439 private final String name;
441 public ModuleImportImpl(final String moduleName, final Date revision) {
442 this(moduleName, revision, Optional.absent());
445 public ModuleImportImpl(final String moduleName, final Date revision, final Optional<SemVer> semVer) {
446 this.name = Preconditions.checkNotNull(moduleName, "Module name must not be null.");
447 this.revision = revision;
448 this.semVer = semVer.or(Module.DEFAULT_SEMANTIC_VERSION);
452 public String getModuleName() {
457 public Date getRevision() {
458 return this.revision;
462 public SemVer getSemanticVersion() {
467 public String getPrefix() {
472 public int hashCode() {
473 final int prime = 31;
475 result = prime * result + Objects.hashCode(name);
476 result = prime * result + Objects.hashCode(revision);
477 result = prime * result + Objects.hashCode(semVer);
482 public boolean equals(final Object obj) {
489 if (getClass() != obj.getClass()) {
492 final ModuleImportImpl other = (ModuleImportImpl) obj;
494 if (other.name != null) {
497 } else if (!name.equals(other.name)) {
500 if (revision == null) {
501 if (other.revision != null) {
504 } else if (!revision.equals(other.revision)) {
508 if (!Objects.equals(getSemanticVersion(), other.getSemanticVersion())) {
515 public String toString() {
516 return "ModuleImportImpl [name=" + name + ", revision="
517 + QName.formattedRevision(revision) + ", semanticVersion=" + getSemanticVersion() + "]";