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;
22 import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.StatementContext;
23 import org.opendaylight.yangtools.concepts.SemVer;
24 import org.opendaylight.yangtools.yang.common.QName;
25 import org.opendaylight.yangtools.yang.model.api.Module;
26 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
27 import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping;
28 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
29 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.SupportedExtensionsMapping;
30 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils;
31 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
34 * Helper transfer object which holds basic and dependency information for YANG
39 * There are two concrete implementations of this interface:
41 * <li>{@link ModuleDependencyInfo} - Dependency information for module</li>
42 * <li>{@link SubmoduleDependencyInfo} - Dependency information for submodule</li>
45 * @see ModuleDependencyInfo
46 * @see SubmoduleDependencyInfo
50 public abstract class YangModelDependencyInfo {
52 private final String name;
53 private final String formattedRevision;
54 private final Date revision;
55 private final Optional<SemVer> semVer;
56 private final ImmutableSet<ModuleImport> submoduleIncludes;
57 private final ImmutableSet<ModuleImport> moduleImports;
58 private final ImmutableSet<ModuleImport> dependencies;
60 YangModelDependencyInfo(final String name, final String formattedRevision,
61 final ImmutableSet<ModuleImport> imports,
62 final ImmutableSet<ModuleImport> includes) {
63 this(name, formattedRevision, imports, includes, Optional.absent());
66 YangModelDependencyInfo(final String name, final String formattedRevision,
67 final ImmutableSet<ModuleImport> imports,
68 final ImmutableSet<ModuleImport> includes,
69 final Optional<SemVer> semVer) {
71 this.formattedRevision = formattedRevision;
72 this.revision = formattedRevision == null ? null : QName
73 .parseRevision(formattedRevision);
74 this.moduleImports = imports;
75 this.submoduleIncludes = includes;
76 this.dependencies = ImmutableSet.<ModuleImport> builder()
77 .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 * Returns semantic version of module
123 * @return semantic version
125 public Optional<SemVer> getSemanticVersion() {
130 public int hashCode() {
131 final int prime = 31;
133 result = prime * result + Objects.hashCode(formattedRevision);
134 result = prime * result + Objects.hashCode(name);
135 result = prime * result + Objects.hashCode(semVer);
140 public boolean equals(final Object obj) {
147 if (!(obj instanceof YangModelDependencyInfo)) {
150 final YangModelDependencyInfo other = (YangModelDependencyInfo) obj;
151 if (formattedRevision == null) {
152 if (other.formattedRevision != null) {
155 } else if (!formattedRevision.equals(other.formattedRevision)) {
159 if (other.name != null) {
162 } else if (!name.equals(other.name)) {
165 if(!Objects.equals(semVer, other.semVer)) {
173 * Extracts {@link YangModelDependencyInfo} from an abstract syntax tree of
177 * Abstract syntax tree
178 * @return {@link YangModelDependencyInfo}
179 * @throws YangSyntaxErrorException
180 * If the AST is not a valid YANG module/submodule
182 public static YangModelDependencyInfo fromAST(final String name,
183 final ParserRuleContext tree) throws YangSyntaxErrorException {
185 if (tree instanceof YangStatementParser.StatementContext) {
186 final YangStatementParser.StatementContext rootStatement = (YangStatementParser.StatementContext) tree;
187 return parseAST(rootStatement);
190 throw new YangSyntaxErrorException(name, 0, 0, "Unknown YANG text type");
193 private static YangModelDependencyInfo parseAST(
194 final YangStatementParser.StatementContext rootStatement) {
198 .equals(Rfc6020Mapping.MODULE.getStatementName().getLocalName())) {
199 return parseModuleContext(rootStatement);
200 } else if (rootStatement
203 .equals(Rfc6020Mapping.SUBMODULE.getStatementName()
205 return parseSubmoduleContext(rootStatement);
208 throw new IllegalArgumentException(
209 "Root of parsed AST must be either module or submodule");
213 * Extracts {@link YangModelDependencyInfo} from input stream containing
216 * This parsing does not validate full YANG module, only parses header up to
217 * the revisions and imports.
220 * Opened Input stream containing text source of YANG model
221 * @return {@link YangModelDependencyInfo}
222 * @throws IllegalArgumentException
223 * If input stream is not valid YANG stream
225 public static YangModelDependencyInfo fromInputStream(
226 final InputStream yangStream) {
227 final StatementContext yangAST = new YangStatementSourceImpl(yangStream)
229 return parseAST(yangAST);
232 private static YangModelDependencyInfo parseModuleContext(
233 final YangStatementParser.StatementContext module) {
234 final String name = Utils.stringFromStringContext(module.argument());
235 final String latestRevision = getLatestRevision(module);
236 final Optional<SemVer> semVer = Optional.fromNullable(getSemanticVersion(module));
237 final ImmutableSet<ModuleImport> imports = parseImports(module);
238 final ImmutableSet<ModuleImport> includes = parseIncludes(module);
240 return new ModuleDependencyInfo(name, latestRevision, imports, includes, semVer);
243 private static ImmutableSet<ModuleImport> parseImports(
244 final YangStatementParser.StatementContext module) {
245 final Set<ModuleImport> result = new HashSet<>();
246 final List<StatementContext> subStatements = module.statement();
247 for (final StatementContext subStatementContext : subStatements) {
248 if (subStatementContext
251 .equals(Rfc6020Mapping.IMPORT.getStatementName()
253 final String revisionDateStr = getRevisionDateString(subStatementContext);
254 final String importedModuleName = Utils
255 .stringFromStringContext(subStatementContext.argument());
256 final Date revisionDate = (revisionDateStr == null) ? null : QName
257 .parseRevision(revisionDateStr);
258 final Optional<SemVer> importSemVer = Optional.fromNullable(getSemanticVersion(subStatementContext));
259 result.add(new ModuleImportImpl(importedModuleName,
260 revisionDate, importSemVer));
263 return ImmutableSet.copyOf(result);
266 private static SemVer getSemanticVersion(final StatementContext statement) {
267 final List<StatementContext> subStatements = statement.statement();
268 String semVerString = null;
269 final String semVerStmtName = SupportedExtensionsMapping.SEMANTIC_VERSION.getStatementName().getLocalName();
270 for (final StatementContext subStatement : subStatements) {
271 final String subStatementName = Utils.trimPrefix(subStatement.keyword().getText());
272 if (semVerStmtName.equals(subStatementName)) {
273 semVerString = Utils.stringFromStringContext(subStatement.argument());
278 if (Strings.isNullOrEmpty(semVerString)) {
282 return SemVer.valueOf(semVerString);
285 private static ImmutableSet<ModuleImport> parseIncludes(
286 final YangStatementParser.StatementContext module) {
287 final Set<ModuleImport> result = new HashSet<>();
288 final List<StatementContext> subStatements = module.statement();
289 for (final StatementContext subStatementContext : subStatements) {
290 if (subStatementContext
293 .equals(Rfc6020Mapping.INCLUDE.getStatementName()
295 final String revisionDateStr = getRevisionDateString(subStatementContext);
296 final String IncludeModuleName = Utils
297 .stringFromStringContext(subStatementContext.argument());
298 final Date revisionDate = (revisionDateStr == null) ? null : QName
299 .parseRevision(revisionDateStr);
300 result.add(new ModuleImportImpl(IncludeModuleName, revisionDate));
303 return ImmutableSet.copyOf(result);
306 private static String getRevisionDateString(final StatementContext importStatement) {
307 final List<StatementContext> importSubStatements = importStatement.statement();
308 String revisionDateStr = null;
309 for (final StatementContext importSubStatement : importSubStatements) {
310 if (importSubStatement
313 .equals(Rfc6020Mapping.REVISION_DATE.getStatementName()
315 revisionDateStr = Utils
316 .stringFromStringContext(importSubStatement.argument());
319 return revisionDateStr;
322 public static String getLatestRevision(
323 final YangStatementParser.StatementContext module) {
324 final List<StatementContext> subStatements = module.statement();
325 String latestRevision = null;
326 for (final StatementContext subStatementContext : subStatements) {
327 if (subStatementContext
330 .equals(Rfc6020Mapping.REVISION.getStatementName()
332 final String currentRevision = Utils
333 .stringFromStringContext(subStatementContext.argument());
334 if (latestRevision == null
335 || latestRevision.compareTo(currentRevision) == -1) {
336 latestRevision = currentRevision;
340 return latestRevision;
343 private static YangModelDependencyInfo parseSubmoduleContext(
344 final YangStatementParser.StatementContext submodule) {
345 final String name = Utils.stringFromStringContext(submodule.argument());
346 final String belongsTo = parseBelongsTo(submodule);
348 final String latestRevision = getLatestRevision(submodule);
349 final ImmutableSet<ModuleImport> imports = parseImports(submodule);
350 final ImmutableSet<ModuleImport> includes = parseIncludes(submodule);
352 return new SubmoduleDependencyInfo(name, latestRevision, belongsTo,
356 private static String parseBelongsTo(final StatementContext submodule) {
357 final List<StatementContext> subStatements = submodule.statement();
358 for (final StatementContext subStatementContext : subStatements) {
359 if (subStatementContext
362 .equals(Rfc6020Mapping.BELONGS_TO.getStatementName()
364 return Utils.stringFromStringContext(subStatementContext
373 * Dependency information for YANG module.
376 public static class ModuleDependencyInfo extends
377 YangModelDependencyInfo {
379 private ModuleDependencyInfo(final String name,
380 final String latestRevision,
381 final ImmutableSet<ModuleImport> imports,
382 final ImmutableSet<ModuleImport> includes) {
383 super(name, latestRevision, imports, includes);
386 private ModuleDependencyInfo(final String name,
387 final String latestRevision,
388 final ImmutableSet<ModuleImport> imports,
389 final ImmutableSet<ModuleImport> includes,
390 final Optional<SemVer> semVer) {
391 super(name, latestRevision, imports, includes, semVer);
395 public String toString() {
396 return "Module [name=" + getName() + ", revision=" + getRevision() + ", semanticVersion="
397 + getSemanticVersion().or(Module.DEFAULT_SEMANTIC_VERSION) + ", dependencies=" + getDependencies()
404 * Dependency information for submodule, also provides name for parent
408 public static final class SubmoduleDependencyInfo extends
409 YangModelDependencyInfo {
411 private final String belongsTo;
413 private SubmoduleDependencyInfo(final String name,
414 final String latestRevision, final String belongsTo,
415 final ImmutableSet<ModuleImport> imports,
416 final ImmutableSet<ModuleImport> includes) {
417 super(name, latestRevision, imports, includes);
418 this.belongsTo = belongsTo;
422 * Returns name of parent module.
425 public String getParentModule() {
430 public String toString() {
431 return "Submodule [name=" + getName() + ", revision="
432 + getRevision() + ", dependencies=" + getDependencies()
438 * Utility implementation of {@link ModuleImport} to be used by
439 * {@link YangModelDependencyInfo}.
442 private static final class ModuleImportImpl implements ModuleImport {
444 private final Date revision;
445 private final SemVer semVer;
446 private final String name;
448 public ModuleImportImpl(final String moduleName, final Date revision) {
449 this(moduleName, revision, Optional.absent());
452 public ModuleImportImpl(final String moduleName, final Date revision, final Optional<SemVer> semVer) {
453 this.name = Preconditions.checkNotNull(moduleName, "Module name must not be null.");
454 this.revision = revision;
455 this.semVer = semVer.or(Module.DEFAULT_SEMANTIC_VERSION);
459 public String getModuleName() {
464 public Date getRevision() {
465 return this.revision;
469 public SemVer getSemanticVersion() {
474 public String getPrefix() {
479 public int hashCode() {
480 final int prime = 31;
482 result = prime * result + Objects.hashCode(name);
483 result = prime * result + Objects.hashCode(revision);
484 result = prime * result + Objects.hashCode(semVer);
489 public boolean equals(final Object obj) {
496 if (getClass() != obj.getClass()) {
499 final ModuleImportImpl other = (ModuleImportImpl) obj;
501 if (other.name != null) {
504 } else if (!name.equals(other.name)) {
507 if (revision == null) {
508 if (other.revision != null) {
511 } else if (!revision.equals(other.revision)) {
515 if (!Objects.equals(getSemanticVersion(), other.getSemanticVersion())) {
522 public String toString() {
523 return "ModuleImportImpl [name=" + name + ", revision="
524 + QName.formattedRevision(revision) + ", semanticVersion=" + getSemanticVersion() + "]";