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 org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils;
14 import com.google.common.base.Optional;
15 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils;
16 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
17 import java.util.HashSet;
19 import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping;
20 import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.StatementContext;
21 import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser;
22 import com.google.common.collect.ImmutableSet;
23 import java.io.InputStream;
24 import java.util.Date;
25 import java.util.List;
26 import java.util.Objects;
27 import org.antlr.v4.runtime.ParserRuleContext;
28 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Belongs_to_stmtContext;
29 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Import_stmtContext;
30 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Include_stmtContext;
31 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Module_stmtContext;
32 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_date_stmtContext;
33 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtContext;
34 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtsContext;
35 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_stmtContext;
36 import org.opendaylight.yangtools.yang.common.QName;
37 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
38 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
41 * Helper transfer object which holds basic and dependency information for YANG
46 * There are two concrete implementations of this interface:
48 * <li>{@link ModuleDependencyInfo} - Dependency information for module</li>
49 * <li>{@link SubmoduleDependencyInfo} - Dependency information for submodule</li>
52 * @see ModuleDependencyInfo
53 * @see SubmoduleDependencyInfo
57 public abstract class YangModelDependencyInfo {
59 private final String name;
60 private final String formattedRevision;
61 private final Date revision;
62 private final ImmutableSet<ModuleImport> submoduleIncludes;
63 private final ImmutableSet<ModuleImport> moduleImports;
64 private final ImmutableSet<ModuleImport> dependencies;
66 YangModelDependencyInfo(final String name, final String formattedRevision,
67 final ImmutableSet<ModuleImport> imports,
68 final ImmutableSet<ModuleImport> includes) {
70 this.formattedRevision = formattedRevision;
71 this.revision = formattedRevision == null ? null : QName
72 .parseRevision(formattedRevision);
73 this.moduleImports = imports;
74 this.submoduleIncludes = includes;
75 this.dependencies = ImmutableSet.<ModuleImport> builder()
76 .addAll(moduleImports).addAll(submoduleIncludes).build();
80 * Returns immutable collection of all module imports.
82 * This collection contains both <code>import</code> statements and
83 * <code>include</code> statements for submodules.
85 * @return Immutable collection of imports.
87 public ImmutableSet<ModuleImport> getDependencies() {
96 public String getName() {
101 * Returns formatted revision string
103 * @return formatted revision string
105 public String getFormattedRevision() {
106 return formattedRevision;
119 public int hashCode() {
120 final int prime = 31;
122 result = prime * result + Objects.hashCode(formattedRevision);
123 result = prime * result + Objects.hashCode(name);
128 public boolean equals(final Object obj) {
135 if (!(obj instanceof YangModelDependencyInfo)) {
138 YangModelDependencyInfo other = (YangModelDependencyInfo) obj;
139 if (formattedRevision == null) {
140 if (other.formattedRevision != null) {
143 } else if (!formattedRevision.equals(other.formattedRevision)) {
147 if (other.name != null) {
150 } else if (!name.equals(other.name)) {
157 * Extracts {@link YangModelDependencyInfo} from an abstract syntax tree of
161 * Abstract syntax tree
162 * @return {@link YangModelDependencyInfo}
163 * @throws YangSyntaxErrorException
164 * If the AST is not a valid YANG module/submodule
166 public static YangModelDependencyInfo fromAST(final String name,
167 final ParserRuleContext tree) throws YangSyntaxErrorException {
169 if (tree instanceof YangStatementParser.StatementContext) {
170 YangStatementParser.StatementContext rootStatement = (YangStatementParser.StatementContext) tree;
171 return parseAST(rootStatement);
174 final Optional<Module_stmtContext> moduleCtx = ParserListenerUtils
175 .getFirstContext(tree, Module_stmtContext.class);
176 if (moduleCtx.isPresent()) {
177 return parseModuleContext(moduleCtx.get());
180 final Optional<Submodule_stmtContext> submoduleCtx = ParserListenerUtils
181 .getFirstContext(tree, Submodule_stmtContext.class);
182 if (submoduleCtx.isPresent()) {
183 return parseSubmoduleContext(submoduleCtx.get());
186 throw new YangSyntaxErrorException(name, 0, 0, "Unknown YANG text type");
189 private static YangModelDependencyInfo parseAST(
190 YangStatementParser.StatementContext rootStatement) {
194 .equals(Rfc6020Mapping.MODULE.getStatementName().getLocalName())) {
195 return parseModuleContext(rootStatement);
196 } else if (rootStatement
199 .equals(Rfc6020Mapping.SUBMODULE.getStatementName()
201 return parseSubmoduleContext(rootStatement);
204 throw new IllegalArgumentException(
205 "Root of parsed AST must be either module or submodule");
209 * Extracts {@link YangModelDependencyInfo} from input stream containing
212 * This parsing does not validate full YANG module, only parses header up to
213 * the revisions and imports.
216 * Opened Input stream containing text source of YANG model
217 * @return {@link YangModelDependencyInfo}
218 * @throws IllegalArgumentException
219 * If input stream is not valid YANG stream
221 public static YangModelDependencyInfo fromInputStream(
222 final InputStream yangStream) {
223 StatementContext yangAST = new YangStatementSourceImpl(yangStream)
225 return parseAST(yangAST);
228 private static YangModelDependencyInfo parseModuleContext(
229 final Module_stmtContext module) {
230 String name = getArgumentString(module);
231 String latestRevision = getLatestRevision(module.revision_stmts());
232 ImmutableSet<ModuleImport> imports = parseImports(module
233 .linkage_stmts().import_stmt());
234 ImmutableSet<ModuleImport> includes = parseIncludes(module
235 .linkage_stmts().include_stmt());
237 return new ModuleDependencyInfo(name, latestRevision, imports, includes);
240 private static YangModelDependencyInfo parseModuleContext(
241 final YangStatementParser.StatementContext module) {
242 String name = Utils.stringFromStringContext(module.argument());
243 String latestRevision = getLatestRevision(module);
244 ImmutableSet<ModuleImport> imports = parseImports(module);
245 ImmutableSet<ModuleImport> includes = parseIncludes(module);
247 return new ModuleDependencyInfo(name, latestRevision, imports, includes);
250 private static ImmutableSet<ModuleImport> parseImports(
251 final YangStatementParser.StatementContext module) {
252 Set<ModuleImport> result = new HashSet<>();
253 List<StatementContext> subStatements = module.statement();
254 for (StatementContext subStatementContext : subStatements) {
255 if (subStatementContext
258 .equals(Rfc6020Mapping.IMPORT.getStatementName()
260 String revisionDateStr = getRevisionDateString(subStatementContext);
261 String importedModuleName = Utils
262 .stringFromStringContext(subStatementContext.argument());
263 Date revisionDate = (revisionDateStr == null) ? null : QName
264 .parseRevision(revisionDateStr);
265 result.add(new ModuleImportImpl(importedModuleName,
269 return ImmutableSet.copyOf(result);
272 private static ImmutableSet<ModuleImport> parseIncludes(
273 final YangStatementParser.StatementContext module) {
274 Set<ModuleImport> result = new HashSet<>();
275 List<StatementContext> subStatements = module.statement();
276 for (StatementContext subStatementContext : subStatements) {
277 if (subStatementContext
280 .equals(Rfc6020Mapping.INCLUDE.getStatementName()
282 String revisionDateStr = getRevisionDateString(subStatementContext);
283 String IncludeModuleName = Utils
284 .stringFromStringContext(subStatementContext.argument());
285 Date revisionDate = (revisionDateStr == null) ? null : QName
286 .parseRevision(revisionDateStr);
287 result.add(new ModuleImportImpl(IncludeModuleName, revisionDate));
290 return ImmutableSet.copyOf(result);
293 private static String getRevisionDateString(StatementContext importStatement) {
294 List<StatementContext> importSubStatements = importStatement
296 String revisionDateStr = null;
297 for (StatementContext importSubStatement : importSubStatements) {
298 if (importSubStatement
301 .equals(Rfc6020Mapping.REVISION_DATE.getStatementName()
303 revisionDateStr = Utils
304 .stringFromStringContext(importSubStatement.argument());
307 return revisionDateStr;
310 private static ImmutableSet<ModuleImport> parseImports(
311 final List<Import_stmtContext> importStatements) {
312 ImmutableSet.Builder<ModuleImport> builder = ImmutableSet.builder();
313 for (Import_stmtContext importStmt : importStatements) {
314 String moduleName = getArgumentString(importStmt);
315 Date revision = getRevision(importStmt.revision_date_stmt());
316 builder.add(new ModuleImportImpl(moduleName, revision));
318 return builder.build();
321 public static String getLatestRevision(
322 final YangStatementParser.StatementContext module) {
323 List<StatementContext> subStatements = module.statement();
324 String latestRevision = null;
325 for (StatementContext subStatementContext : subStatements) {
326 if (subStatementContext
329 .equals(Rfc6020Mapping.REVISION.getStatementName()
331 String currentRevision = Utils
332 .stringFromStringContext(subStatementContext.argument());
333 if (latestRevision == null
334 || latestRevision.compareTo(currentRevision) == -1) {
335 latestRevision = currentRevision;
339 return latestRevision;
342 public static String getLatestRevision(
343 final Revision_stmtsContext revisionStmts) {
344 List<Revision_stmtContext> revisions = revisionStmts
345 .getRuleContexts(Revision_stmtContext.class);
346 String latestRevision = null;
347 for (Revision_stmtContext revisionStmt : revisions) {
348 String currentRevision = getArgumentString(revisionStmt);
349 if (latestRevision == null
350 || latestRevision.compareTo(currentRevision) == -1) {
351 latestRevision = currentRevision;
354 return latestRevision;
357 private static YangModelDependencyInfo parseSubmoduleContext(
358 final YangStatementParser.StatementContext submodule) {
359 String name = Utils.stringFromStringContext(submodule.argument());
360 String belongsTo = parseBelongsTo(submodule);
362 String latestRevision = getLatestRevision(submodule);
363 ImmutableSet<ModuleImport> imports = parseImports(submodule);
364 ImmutableSet<ModuleImport> includes = parseIncludes(submodule);
366 return new SubmoduleDependencyInfo(name, latestRevision, belongsTo,
370 private static String parseBelongsTo(StatementContext submodule) {
371 List<StatementContext> subStatements = submodule.statement();
372 for (StatementContext subStatementContext : subStatements) {
373 if (subStatementContext
376 .equals(Rfc6020Mapping.BELONGS_TO.getStatementName()
378 return Utils.stringFromStringContext(subStatementContext
385 private static YangModelDependencyInfo parseSubmoduleContext(
386 final Submodule_stmtContext submodule) {
387 String name = getArgumentString(submodule);
388 Belongs_to_stmtContext belongsToStmt = submodule
389 .submodule_header_stmts().belongs_to_stmt(0);
390 String belongsTo = getArgumentString(belongsToStmt);
392 String latestRevision = getLatestRevision(submodule.revision_stmts());
393 ImmutableSet<ModuleImport> imports = parseImports(submodule
394 .linkage_stmts().import_stmt());
395 ImmutableSet<ModuleImport> includes = parseIncludes(submodule
396 .linkage_stmts().include_stmt());
398 return new SubmoduleDependencyInfo(name, latestRevision, belongsTo,
402 private static ImmutableSet<ModuleImport> parseIncludes(
403 final List<Include_stmtContext> importStatements) {
404 ImmutableSet.Builder<ModuleImport> builder = ImmutableSet.builder();
405 for (Include_stmtContext importStmt : importStatements) {
406 String moduleName = getArgumentString(importStmt);
407 Date revision = getRevision(importStmt.revision_date_stmt());
408 builder.add(new ModuleImportImpl(moduleName, revision));
410 return builder.build();
413 private static Date getRevision(
414 final Revision_date_stmtContext revisionDateStmt) {
415 if (revisionDateStmt == null) {
418 String formatedDate = getArgumentString(revisionDateStmt);
419 return QName.parseRevision(formatedDate);
424 * Dependency information for YANG module.
427 public static final class ModuleDependencyInfo extends
428 YangModelDependencyInfo {
430 private ModuleDependencyInfo(final String name,
431 final String latestRevision,
432 final ImmutableSet<ModuleImport> imports,
433 final ImmutableSet<ModuleImport> includes) {
434 super(name, latestRevision, imports, includes);
438 public String toString() {
439 return "Module [name=" + getName() + ", revision=" + getRevision()
440 + ", dependencies=" + getDependencies() + "]";
446 * Dependency information for submodule, also provides name for parent
450 public static final class SubmoduleDependencyInfo extends
451 YangModelDependencyInfo {
453 private final String belongsTo;
455 private SubmoduleDependencyInfo(final String name,
456 final String latestRevision, final String belongsTo,
457 final ImmutableSet<ModuleImport> imports,
458 final ImmutableSet<ModuleImport> includes) {
459 super(name, latestRevision, imports, includes);
460 this.belongsTo = belongsTo;
464 * Returns name of parent module.
467 public String getParentModule() {
472 public String toString() {
473 return "Submodule [name=" + getName() + ", revision="
474 + getRevision() + ", dependencies=" + getDependencies()
480 * Utility implementation of {@link ModuleImport} to be used by
481 * {@link YangModelDependencyInfo}.
484 private static final class ModuleImportImpl implements ModuleImport {
486 private final Date revision;
487 private final String name;
489 public ModuleImportImpl(final String moduleName, final Date revision) {
490 this.name = moduleName;
491 this.revision = revision;
495 public String getModuleName() {
500 public Date getRevision() {
501 return this.revision;
505 public String getPrefix() {
510 public int hashCode() {
511 final int prime = 31;
513 result = prime * result + Objects.hashCode(name);
514 result = prime * result + Objects.hashCode(revision);
519 public boolean equals(final Object obj) {
526 if (getClass() != obj.getClass()) {
529 ModuleImportImpl other = (ModuleImportImpl) obj;
531 if (other.name != null) {
534 } else if (!name.equals(other.name)) {
537 if (revision == null) {
538 if (other.revision != null) {
541 } else if (!revision.equals(other.revision)) {
548 public String toString() {
549 return "ModuleImportImpl [name=" + name + ", revision="
550 + QName.formattedRevision(revision) + "]";