bb7348c2fafd392441be88fd70546283df4a2bf0
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / impl / util / YangModelDependencyInfo.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.yangtools.yang.parser.impl.util;
9
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;
19 import java.util.Set;
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;
32
33 /**
34  * Helper transfer object which holds basic and dependency information for YANG
35  * model.
36  *
37  *
38  *
39  * There are two concrete implementations of this interface:
40  * <ul>
41  * <li>{@link ModuleDependencyInfo} - Dependency information for module</li>
42  * <li>{@link SubmoduleDependencyInfo} - Dependency information for submodule</li>
43  * </ul>
44  *
45  * @see ModuleDependencyInfo
46  * @see SubmoduleDependencyInfo
47  *
48  */
49
50 public abstract class YangModelDependencyInfo {
51
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;
59
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());
64     }
65
66     YangModelDependencyInfo(final String name, final String formattedRevision,
67             final ImmutableSet<ModuleImport> imports,
68             final ImmutableSet<ModuleImport> includes,
69             final Optional<SemVer> semVer) {
70         this.name = name;
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();
78         this.semVer = semVer;
79     }
80
81     /**
82      * Returns immutable collection of all module imports.
83      *
84      * This collection contains both <code>import</code> statements and
85      * <code>include</code> statements for submodules.
86      *
87      * @return Immutable collection of imports.
88      */
89     public ImmutableSet<ModuleImport> getDependencies() {
90         return dependencies;
91     }
92
93     /**
94      * Returns model name
95      *
96      * @return model name
97      */
98     public String getName() {
99         return name;
100     }
101
102     /**
103      * Returns formatted revision string
104      *
105      * @return formatted revision string
106      */
107     public String getFormattedRevision() {
108         return formattedRevision;
109     }
110
111     /**
112      * Returns revision
113      *
114      * @return revision
115      */
116     Date getRevision() {
117         return revision;
118     }
119
120     /**
121      * Returns semantic version of module
122      *
123      * @return semantic version
124      */
125     public Optional<SemVer> getSemanticVersion() {
126         return semVer;
127     }
128
129     @Override
130     public int hashCode() {
131         final int prime = 31;
132         int result = 1;
133         result = prime * result + Objects.hashCode(formattedRevision);
134         result = prime * result + Objects.hashCode(name);
135         result = prime * result + Objects.hashCode(semVer);
136         return result;
137     }
138
139     @Override
140     public boolean equals(final Object obj) {
141         if (this == obj) {
142             return true;
143         }
144         if (obj == null) {
145             return false;
146         }
147         if (!(obj instanceof YangModelDependencyInfo)) {
148             return false;
149         }
150         final YangModelDependencyInfo other = (YangModelDependencyInfo) obj;
151         if (formattedRevision == null) {
152             if (other.formattedRevision != null) {
153                 return false;
154             }
155         } else if (!formattedRevision.equals(other.formattedRevision)) {
156             return false;
157         }
158         if (name == null) {
159             if (other.name != null) {
160                 return false;
161             }
162         } else if (!name.equals(other.name)) {
163             return false;
164         }
165         if(!Objects.equals(semVer, other.semVer)) {
166             return false;
167         }
168
169         return true;
170     }
171
172     /**
173      * Extracts {@link YangModelDependencyInfo} from an abstract syntax tree of
174      * a YANG model.
175      *
176      * @param tree
177      *            Abstract syntax tree
178      * @return {@link YangModelDependencyInfo}
179      * @throws YangSyntaxErrorException
180      *             If the AST is not a valid YANG module/submodule
181      */
182     public static YangModelDependencyInfo fromAST(final String name,
183             final ParserRuleContext tree) throws YangSyntaxErrorException {
184
185         if (tree instanceof YangStatementParser.StatementContext) {
186             final YangStatementParser.StatementContext rootStatement = (YangStatementParser.StatementContext) tree;
187             return parseAST(rootStatement);
188         }
189
190         throw new YangSyntaxErrorException(name, 0, 0, "Unknown YANG text type");
191     }
192
193     private static YangModelDependencyInfo parseAST(
194             final YangStatementParser.StatementContext rootStatement) {
195         if (rootStatement
196                 .keyword()
197                 .getText()
198                 .equals(Rfc6020Mapping.MODULE.getStatementName().getLocalName())) {
199             return parseModuleContext(rootStatement);
200         } else if (rootStatement
201                 .keyword()
202                 .getText()
203                 .equals(Rfc6020Mapping.SUBMODULE.getStatementName()
204                         .getLocalName())) {
205             return parseSubmoduleContext(rootStatement);
206         }
207
208         throw new IllegalArgumentException(
209                 "Root of parsed AST must be either module or submodule");
210     }
211
212     /**
213      * Extracts {@link YangModelDependencyInfo} from input stream containing
214      * YANG model.
215      *
216      * This parsing does not validate full YANG module, only parses header up to
217      * the revisions and imports.
218      *
219      * @param yangStream
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
224      */
225     public static YangModelDependencyInfo fromInputStream(
226             final InputStream yangStream) {
227         final StatementContext yangAST = new YangStatementSourceImpl(yangStream)
228                 .getYangAST();
229         return parseAST(yangAST);
230     }
231
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);
239
240         return new ModuleDependencyInfo(name, latestRevision, imports, includes, semVer);
241     }
242
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
249                     .keyword()
250                     .getText()
251                     .equals(Rfc6020Mapping.IMPORT.getStatementName()
252                             .getLocalName())) {
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));
261             }
262         }
263         return ImmutableSet.copyOf(result);
264     }
265
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());
274                 break;
275             }
276         }
277
278         if (Strings.isNullOrEmpty(semVerString)) {
279             return null;
280         }
281
282         return SemVer.valueOf(semVerString);
283     }
284
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
291                     .keyword()
292                     .getText()
293                     .equals(Rfc6020Mapping.INCLUDE.getStatementName()
294                             .getLocalName())) {
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));
301             }
302         }
303         return ImmutableSet.copyOf(result);
304     }
305
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
311                     .keyword()
312                     .getText()
313                     .equals(Rfc6020Mapping.REVISION_DATE.getStatementName()
314                             .getLocalName())) {
315                 revisionDateStr = Utils
316                         .stringFromStringContext(importSubStatement.argument());
317             }
318         }
319         return revisionDateStr;
320     }
321
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
328                     .keyword()
329                     .getText()
330                     .equals(Rfc6020Mapping.REVISION.getStatementName()
331                             .getLocalName())) {
332                 final String currentRevision = Utils
333                         .stringFromStringContext(subStatementContext.argument());
334                 if (latestRevision == null
335                         || latestRevision.compareTo(currentRevision) == -1) {
336                     latestRevision = currentRevision;
337                 }
338             }
339         }
340         return latestRevision;
341     }
342
343     private static YangModelDependencyInfo parseSubmoduleContext(
344             final YangStatementParser.StatementContext submodule) {
345         final String name = Utils.stringFromStringContext(submodule.argument());
346         final String belongsTo = parseBelongsTo(submodule);
347
348         final String latestRevision = getLatestRevision(submodule);
349         final ImmutableSet<ModuleImport> imports = parseImports(submodule);
350         final ImmutableSet<ModuleImport> includes = parseIncludes(submodule);
351
352         return new SubmoduleDependencyInfo(name, latestRevision, belongsTo,
353                 imports, includes);
354     }
355
356     private static String parseBelongsTo(final StatementContext submodule) {
357         final List<StatementContext> subStatements = submodule.statement();
358         for (final StatementContext subStatementContext : subStatements) {
359             if (subStatementContext
360                     .keyword()
361                     .getText()
362                     .equals(Rfc6020Mapping.BELONGS_TO.getStatementName()
363                             .getLocalName())) {
364                 return Utils.stringFromStringContext(subStatementContext
365                         .argument());
366             }
367         }
368         return null;
369     }
370
371     /**
372      *
373      * Dependency information for YANG module.
374      *
375      */
376     public static class ModuleDependencyInfo extends
377             YangModelDependencyInfo {
378
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);
384         }
385
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);
392         }
393
394         @Override
395         public String toString() {
396             return "Module [name=" + getName() + ", revision=" + getRevision() + ", semanticVersion="
397                     + getSemanticVersion().or(Module.DEFAULT_SEMANTIC_VERSION) + ", dependencies=" + getDependencies()
398                     + "]";
399         }
400     }
401
402     /**
403      *
404      * Dependency information for submodule, also provides name for parent
405      * module.
406      *
407      */
408     public static final class SubmoduleDependencyInfo extends
409             YangModelDependencyInfo {
410
411         private final String belongsTo;
412
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;
419         }
420
421         /**
422          * Returns name of parent module.
423          *
424          */
425         public String getParentModule() {
426             return belongsTo;
427         }
428
429         @Override
430         public String toString() {
431             return "Submodule [name=" + getName() + ", revision="
432                     + getRevision() + ", dependencies=" + getDependencies()
433                     + "]";
434         }
435     }
436
437     /**
438      * Utility implementation of {@link ModuleImport} to be used by
439      * {@link YangModelDependencyInfo}.
440      *
441      */
442     private static final class ModuleImportImpl implements ModuleImport {
443
444         private final Date revision;
445         private final SemVer semVer;
446         private final String name;
447
448         public ModuleImportImpl(final String moduleName, final Date revision) {
449             this(moduleName, revision, Optional.absent());
450         }
451
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);
456         }
457
458         @Override
459         public String getModuleName() {
460             return this.name;
461         }
462
463         @Override
464         public Date getRevision() {
465             return this.revision;
466         }
467
468         @Override
469         public SemVer getSemanticVersion() {
470             return this.semVer;
471         }
472
473         @Override
474         public String getPrefix() {
475             return null;
476         }
477
478         @Override
479         public int hashCode() {
480             final int prime = 31;
481             int result = 1;
482             result = prime * result + Objects.hashCode(name);
483             result = prime * result + Objects.hashCode(revision);
484             result = prime * result + Objects.hashCode(semVer);
485             return result;
486         }
487
488         @Override
489         public boolean equals(final Object obj) {
490             if (this == obj) {
491                 return true;
492             }
493             if (obj == null) {
494                 return false;
495             }
496             if (getClass() != obj.getClass()) {
497                 return false;
498             }
499             final ModuleImportImpl other = (ModuleImportImpl) obj;
500             if (name == null) {
501                 if (other.name != null) {
502                     return false;
503                 }
504             } else if (!name.equals(other.name)) {
505                 return false;
506             }
507             if (revision == null) {
508                 if (other.revision != null) {
509                     return false;
510                 }
511             } else if (!revision.equals(other.revision)) {
512                 return false;
513             }
514
515             if (!Objects.equals(getSemanticVersion(), other.getSemanticVersion())) {
516                 return false;
517             }
518             return true;
519         }
520
521         @Override
522         public String toString() {
523             return "ModuleImportImpl [name=" + name + ", revision="
524                     + QName.formattedRevision(revision) + ", semanticVersion=" + getSemanticVersion() + "]";
525         }
526     }
527 }