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 static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.getArgumentString;
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import com.google.common.collect.ImmutableSet;
15 import java.io.InputStream;
16 import java.util.Date;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.Objects;
21 import org.antlr.v4.runtime.ParserRuleContext;
22 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Belongs_to_stmtContext;
23 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Import_stmtContext;
24 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Include_stmtContext;
25 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Module_stmtContext;
26 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_date_stmtContext;
27 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtContext;
28 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtsContext;
29 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_stmtContext;
30 import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser;
31 import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.StatementContext;
32 import org.opendaylight.yangtools.concepts.SemVer;
33 import org.opendaylight.yangtools.yang.common.QName;
34 import org.opendaylight.yangtools.yang.model.api.Module;
35 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
36 import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping;
37 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
38 import org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils;
39 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils;
40 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
43 * Helper transfer object which holds basic and dependency information for YANG
48 * There are two concrete implementations of this interface:
50 * <li>{@link ModuleDependencyInfo} - Dependency information for module</li>
51 * <li>{@link SubmoduleDependencyInfo} - Dependency information for submodule</li>
54 * @see ModuleDependencyInfo
55 * @see SubmoduleDependencyInfo
59 public abstract class YangModelDependencyInfo {
61 private final String name;
62 private final String formattedRevision;
63 private final Date revision;
64 private final ImmutableSet<ModuleImport> submoduleIncludes;
65 private final ImmutableSet<ModuleImport> moduleImports;
66 private final ImmutableSet<ModuleImport> dependencies;
68 YangModelDependencyInfo(final String name, final String formattedRevision,
69 final ImmutableSet<ModuleImport> imports,
70 final ImmutableSet<ModuleImport> includes) {
72 this.formattedRevision = formattedRevision;
73 this.revision = formattedRevision == null ? null : QName
74 .parseRevision(formattedRevision);
75 this.moduleImports = imports;
76 this.submoduleIncludes = includes;
77 this.dependencies = ImmutableSet.<ModuleImport> builder()
78 .addAll(moduleImports).addAll(submoduleIncludes).build();
82 * Returns immutable collection of all module imports.
84 * This collection contains both <code>import</code> statements and
85 * <code>include</code> statements for submodules.
87 * @return Immutable collection of imports.
89 public ImmutableSet<ModuleImport> getDependencies() {
98 public String getName() {
103 * Returns formatted revision string
105 * @return formatted revision string
107 public String getFormattedRevision() {
108 return formattedRevision;
121 public int hashCode() {
122 final int prime = 31;
124 result = prime * result + Objects.hashCode(formattedRevision);
125 result = prime * result + Objects.hashCode(name);
130 public boolean equals(final Object obj) {
137 if (!(obj instanceof YangModelDependencyInfo)) {
140 YangModelDependencyInfo other = (YangModelDependencyInfo) obj;
141 if (formattedRevision == null) {
142 if (other.formattedRevision != null) {
145 } else if (!formattedRevision.equals(other.formattedRevision)) {
149 if (other.name != null) {
152 } else if (!name.equals(other.name)) {
159 * Extracts {@link YangModelDependencyInfo} from an abstract syntax tree of
163 * Abstract syntax tree
164 * @return {@link YangModelDependencyInfo}
165 * @throws YangSyntaxErrorException
166 * If the AST is not a valid YANG module/submodule
168 public static YangModelDependencyInfo fromAST(final String name,
169 final ParserRuleContext tree) throws YangSyntaxErrorException {
171 if (tree instanceof YangStatementParser.StatementContext) {
172 YangStatementParser.StatementContext rootStatement = (YangStatementParser.StatementContext) tree;
173 return parseAST(rootStatement);
176 final Optional<Module_stmtContext> moduleCtx = ParserListenerUtils
177 .getFirstContext(tree, Module_stmtContext.class);
178 if (moduleCtx.isPresent()) {
179 return parseModuleContext(moduleCtx.get());
182 final Optional<Submodule_stmtContext> submoduleCtx = ParserListenerUtils
183 .getFirstContext(tree, Submodule_stmtContext.class);
184 if (submoduleCtx.isPresent()) {
185 return parseSubmoduleContext(submoduleCtx.get());
188 throw new YangSyntaxErrorException(name, 0, 0, "Unknown YANG text type");
191 private static YangModelDependencyInfo parseAST(
192 YangStatementParser.StatementContext rootStatement) {
196 .equals(Rfc6020Mapping.MODULE.getStatementName().getLocalName())) {
197 return parseModuleContext(rootStatement);
198 } else if (rootStatement
201 .equals(Rfc6020Mapping.SUBMODULE.getStatementName()
203 return parseSubmoduleContext(rootStatement);
206 throw new IllegalArgumentException(
207 "Root of parsed AST must be either module or submodule");
211 * Extracts {@link YangModelDependencyInfo} from input stream containing
214 * This parsing does not validate full YANG module, only parses header up to
215 * the revisions and imports.
218 * Opened Input stream containing text source of YANG model
219 * @return {@link YangModelDependencyInfo}
220 * @throws IllegalArgumentException
221 * If input stream is not valid YANG stream
223 public static YangModelDependencyInfo fromInputStream(
224 final InputStream yangStream) {
225 StatementContext yangAST = new YangStatementSourceImpl(yangStream)
227 return parseAST(yangAST);
230 private static YangModelDependencyInfo parseModuleContext(
231 final Module_stmtContext module) {
232 String name = getArgumentString(module);
233 String latestRevision = getLatestRevision(module.revision_stmts());
234 ImmutableSet<ModuleImport> imports = parseImports(module
235 .linkage_stmts().import_stmt());
236 ImmutableSet<ModuleImport> includes = parseIncludes(module
237 .linkage_stmts().include_stmt());
239 return new ModuleDependencyInfo(name, latestRevision, imports, includes);
242 private static YangModelDependencyInfo parseModuleContext(
243 final YangStatementParser.StatementContext module) {
244 String name = Utils.stringFromStringContext(module.argument());
245 String latestRevision = getLatestRevision(module);
246 ImmutableSet<ModuleImport> imports = parseImports(module);
247 ImmutableSet<ModuleImport> includes = parseIncludes(module);
249 return new ModuleDependencyInfo(name, latestRevision, imports, includes);
252 private static ImmutableSet<ModuleImport> parseImports(
253 final YangStatementParser.StatementContext module) {
254 Set<ModuleImport> result = new HashSet<>();
255 List<StatementContext> subStatements = module.statement();
256 for (StatementContext subStatementContext : subStatements) {
257 if (subStatementContext
260 .equals(Rfc6020Mapping.IMPORT.getStatementName()
262 String revisionDateStr = getRevisionDateString(subStatementContext);
263 String importedModuleName = Utils
264 .stringFromStringContext(subStatementContext.argument());
265 Date revisionDate = (revisionDateStr == null) ? null : QName
266 .parseRevision(revisionDateStr);
267 result.add(new ModuleImportImpl(importedModuleName,
271 return ImmutableSet.copyOf(result);
274 private static ImmutableSet<ModuleImport> parseIncludes(
275 final YangStatementParser.StatementContext module) {
276 Set<ModuleImport> result = new HashSet<>();
277 List<StatementContext> subStatements = module.statement();
278 for (StatementContext subStatementContext : subStatements) {
279 if (subStatementContext
282 .equals(Rfc6020Mapping.INCLUDE.getStatementName()
284 String revisionDateStr = getRevisionDateString(subStatementContext);
285 String IncludeModuleName = Utils
286 .stringFromStringContext(subStatementContext.argument());
287 Date revisionDate = (revisionDateStr == null) ? null : QName
288 .parseRevision(revisionDateStr);
289 result.add(new ModuleImportImpl(IncludeModuleName, revisionDate));
292 return ImmutableSet.copyOf(result);
295 private static String getRevisionDateString(StatementContext importStatement) {
296 List<StatementContext> importSubStatements = importStatement
298 String revisionDateStr = null;
299 for (StatementContext importSubStatement : importSubStatements) {
300 if (importSubStatement
303 .equals(Rfc6020Mapping.REVISION_DATE.getStatementName()
305 revisionDateStr = Utils
306 .stringFromStringContext(importSubStatement.argument());
309 return revisionDateStr;
312 private static ImmutableSet<ModuleImport> parseImports(
313 final List<Import_stmtContext> importStatements) {
314 ImmutableSet.Builder<ModuleImport> builder = ImmutableSet.builder();
315 for (Import_stmtContext importStmt : importStatements) {
316 String moduleName = getArgumentString(importStmt);
317 Date revision = getRevision(importStmt.revision_date_stmt());
318 builder.add(new ModuleImportImpl(moduleName, revision));
320 return builder.build();
323 public static String getLatestRevision(
324 final YangStatementParser.StatementContext module) {
325 List<StatementContext> subStatements = module.statement();
326 String latestRevision = null;
327 for (StatementContext subStatementContext : subStatements) {
328 if (subStatementContext
331 .equals(Rfc6020Mapping.REVISION.getStatementName()
333 String currentRevision = Utils
334 .stringFromStringContext(subStatementContext.argument());
335 if (latestRevision == null
336 || latestRevision.compareTo(currentRevision) == -1) {
337 latestRevision = currentRevision;
341 return latestRevision;
344 public static String getLatestRevision(
345 final Revision_stmtsContext revisionStmts) {
346 List<Revision_stmtContext> revisions = revisionStmts
347 .getRuleContexts(Revision_stmtContext.class);
348 String latestRevision = null;
349 for (Revision_stmtContext revisionStmt : revisions) {
350 String currentRevision = getArgumentString(revisionStmt);
351 if (latestRevision == null
352 || latestRevision.compareTo(currentRevision) == -1) {
353 latestRevision = currentRevision;
356 return latestRevision;
359 private static YangModelDependencyInfo parseSubmoduleContext(
360 final YangStatementParser.StatementContext submodule) {
361 String name = Utils.stringFromStringContext(submodule.argument());
362 String belongsTo = parseBelongsTo(submodule);
364 String latestRevision = getLatestRevision(submodule);
365 ImmutableSet<ModuleImport> imports = parseImports(submodule);
366 ImmutableSet<ModuleImport> includes = parseIncludes(submodule);
368 return new SubmoduleDependencyInfo(name, latestRevision, belongsTo,
372 private static String parseBelongsTo(StatementContext submodule) {
373 List<StatementContext> subStatements = submodule.statement();
374 for (StatementContext subStatementContext : subStatements) {
375 if (subStatementContext
378 .equals(Rfc6020Mapping.BELONGS_TO.getStatementName()
380 return Utils.stringFromStringContext(subStatementContext
387 private static YangModelDependencyInfo parseSubmoduleContext(
388 final Submodule_stmtContext submodule) {
389 String name = getArgumentString(submodule);
390 Belongs_to_stmtContext belongsToStmt = submodule
391 .submodule_header_stmts().belongs_to_stmt(0);
392 String belongsTo = getArgumentString(belongsToStmt);
394 String latestRevision = getLatestRevision(submodule.revision_stmts());
395 ImmutableSet<ModuleImport> imports = parseImports(submodule
396 .linkage_stmts().import_stmt());
397 ImmutableSet<ModuleImport> includes = parseIncludes(submodule
398 .linkage_stmts().include_stmt());
400 return new SubmoduleDependencyInfo(name, latestRevision, belongsTo,
404 private static ImmutableSet<ModuleImport> parseIncludes(
405 final List<Include_stmtContext> importStatements) {
406 ImmutableSet.Builder<ModuleImport> builder = ImmutableSet.builder();
407 for (Include_stmtContext importStmt : importStatements) {
408 String moduleName = getArgumentString(importStmt);
409 Date revision = getRevision(importStmt.revision_date_stmt());
410 builder.add(new ModuleImportImpl(moduleName, revision));
412 return builder.build();
415 private static Date getRevision(
416 final Revision_date_stmtContext revisionDateStmt) {
417 if (revisionDateStmt == null) {
420 String formatedDate = getArgumentString(revisionDateStmt);
421 return QName.parseRevision(formatedDate);
426 * Dependency information for YANG module.
429 public static final class ModuleDependencyInfo extends
430 YangModelDependencyInfo {
432 private ModuleDependencyInfo(final String name,
433 final String latestRevision,
434 final ImmutableSet<ModuleImport> imports,
435 final ImmutableSet<ModuleImport> includes) {
436 super(name, latestRevision, imports, includes);
440 public String toString() {
441 return "Module [name=" + getName() + ", revision=" + getRevision()
442 + ", dependencies=" + getDependencies() + "]";
448 * Dependency information for submodule, also provides name for parent
452 public static final class SubmoduleDependencyInfo extends
453 YangModelDependencyInfo {
455 private final String belongsTo;
457 private SubmoduleDependencyInfo(final String name,
458 final String latestRevision, final String belongsTo,
459 final ImmutableSet<ModuleImport> imports,
460 final ImmutableSet<ModuleImport> includes) {
461 super(name, latestRevision, imports, includes);
462 this.belongsTo = belongsTo;
466 * Returns name of parent module.
469 public String getParentModule() {
474 public String toString() {
475 return "Submodule [name=" + getName() + ", revision="
476 + getRevision() + ", dependencies=" + getDependencies()
482 * Utility implementation of {@link ModuleImport} to be used by
483 * {@link YangModelDependencyInfo}.
486 private static final class ModuleImportImpl implements ModuleImport {
488 private final Date revision;
489 private final SemVer semVer;
490 private final String name;
492 public ModuleImportImpl(final String moduleName, final Date revision) {
493 this(moduleName, revision, Module.DEFAULT_SEMANTIC_VERSION);
496 public ModuleImportImpl(final String moduleName, final Date revision, final SemVer semVer) {
497 this.name = Preconditions.checkNotNull(moduleName, "Module name must not be null.");
498 this.revision = revision;
499 this.semVer = Preconditions.checkNotNull(semVer, "Semantic version of module must not be null.");
503 public String getModuleName() {
508 public Date getRevision() {
509 return this.revision;
513 public SemVer getSemanticVersion() {
518 public String getPrefix() {
523 public int hashCode() {
524 final int prime = 31;
526 result = prime * result + Objects.hashCode(name);
527 result = prime * result + Objects.hashCode(revision);
528 result = prime * result + Objects.hashCode(semVer);
533 public boolean equals(final Object obj) {
540 if (getClass() != obj.getClass()) {
543 ModuleImportImpl other = (ModuleImportImpl) obj;
545 if (other.name != null) {
548 } else if (!name.equals(other.name)) {
551 if (revision == null) {
552 if (other.revision != null) {
555 } else if (!revision.equals(other.revision)) {
559 if (!Objects.equals(getSemanticVersion(), other.getSemanticVersion())) {
566 public String toString() {
567 return "ModuleImportImpl [name=" + name + ", revision="
568 + QName.formattedRevision(revision) + "]";