Revert "Bug 4640: Change semantic-version to openconfig-version"
[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.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;
34
35 /**
36  * Helper transfer object which holds basic and dependency information for YANG
37  * model.
38  *
39  *
40  *
41  * There are two concrete implementations of this interface:
42  * <ul>
43  * <li>{@link ModuleDependencyInfo} - Dependency information for module</li>
44  * <li>{@link SubmoduleDependencyInfo} - Dependency information for submodule</li>
45  * </ul>
46  *
47  * @see ModuleDependencyInfo
48  * @see SubmoduleDependencyInfo
49  *
50  */
51
52 public abstract class YangModelDependencyInfo {
53
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;
61
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());
66     }
67
68     YangModelDependencyInfo(final String name, final String formattedRevision,
69             final ImmutableSet<ModuleImport> imports,
70             final ImmutableSet<ModuleImport> includes,
71             final Optional<SemVer> semVer) {
72         this.name = name;
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();
80         this.semVer = semVer;
81     }
82
83     /**
84      * Returns immutable collection of all module imports.
85      *
86      * This collection contains both <code>import</code> statements and
87      * <code>include</code> statements for submodules.
88      *
89      * @return Immutable collection of imports.
90      */
91     public ImmutableSet<ModuleImport> getDependencies() {
92         return dependencies;
93     }
94
95     /**
96      * Returns model name
97      *
98      * @return model name
99      */
100     public String getName() {
101         return name;
102     }
103
104     /**
105      * Returns formatted revision string
106      *
107      * @return formatted revision string
108      */
109     public String getFormattedRevision() {
110         return formattedRevision;
111     }
112
113     /**
114      * Returns revision
115      *
116      * @return revision
117      */
118     Date getRevision() {
119         return revision;
120     }
121
122     /**
123      * Returns semantic version of module
124      *
125      * @return semantic version
126      */
127     public Optional<SemVer> getSemanticVersion() {
128         return semVer;
129     }
130
131     @Override
132     public int hashCode() {
133         final int prime = 31;
134         int result = 1;
135         result = prime * result + Objects.hashCode(formattedRevision);
136         result = prime * result + Objects.hashCode(name);
137         result = prime * result + Objects.hashCode(semVer);
138         return result;
139     }
140
141     @Override
142     public boolean equals(final Object obj) {
143         if (this == obj) {
144             return true;
145         }
146         if (obj == null) {
147             return false;
148         }
149         if (!(obj instanceof YangModelDependencyInfo)) {
150             return false;
151         }
152         final YangModelDependencyInfo other = (YangModelDependencyInfo) obj;
153         if (formattedRevision == null) {
154             if (other.formattedRevision != null) {
155                 return false;
156             }
157         } else if (!formattedRevision.equals(other.formattedRevision)) {
158             return false;
159         }
160         if (name == null) {
161             if (other.name != null) {
162                 return false;
163             }
164         } else if (!name.equals(other.name)) {
165             return false;
166         }
167         if(!Objects.equals(semVer, other.semVer)) {
168             return false;
169         }
170
171         return true;
172     }
173
174     /**
175      * Extracts {@link YangModelDependencyInfo} from an abstract syntax tree of
176      * a YANG model.
177      *
178      * @param tree
179      *            Abstract syntax tree
180      * @return {@link YangModelDependencyInfo}
181      * @throws YangSyntaxErrorException
182      *             If the AST is not a valid YANG module/submodule
183      */
184     public static YangModelDependencyInfo fromAST(final String name,
185             final ParserRuleContext tree) throws YangSyntaxErrorException {
186
187         if (tree instanceof StatementContext) {
188             final StatementContext rootStatement = (StatementContext) tree;
189             return parseAST(rootStatement, name);
190         }
191
192         throw new YangSyntaxErrorException(name, 0, 0, "Unknown YANG text type");
193     }
194
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);
199         }
200         if (YangStmtMapping.SUBMODULE.getStatementName().getLocalName().equals(keyWordText)) {
201             return parseSubmoduleContext(rootStatement, sourceName);
202         }
203         throw new IllegalArgumentException("Root of parsed AST must be either module or submodule");
204     }
205
206     /**
207      * Extracts {@link YangModelDependencyInfo} from input stream containing
208      * YANG model.
209      *
210      * This parsing does not validate full YANG module, only parses header up to
211      * the revisions and imports.
212      *
213      * @param yangStream
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
218      */
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);
222     }
223
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);
230
231         return new ModuleDependencyInfo(name, latestRevision, imports, includes, semVer);
232     }
233
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
239                     .keyword()
240                     .getText()
241                     .equals(YangStmtMapping.IMPORT.getStatementName()
242                             .getLocalName())) {
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));
251             }
252         }
253         return ImmutableSet.copyOf(result);
254     }
255
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));
265                 break;
266             }
267         }
268
269         if (Strings.isNullOrEmpty(semVerString)) {
270             return null;
271         }
272
273         return SemVer.valueOf(semVerString);
274     }
275
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
281                     .keyword()
282                     .getText()
283                     .equals(YangStmtMapping.INCLUDE.getStatementName()
284                             .getLocalName())) {
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));
291             }
292         }
293         return ImmutableSet.copyOf(result);
294     }
295
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
301                     .keyword()
302                     .getText()
303                     .equals(YangStmtMapping.REVISION_DATE.getStatementName()
304                             .getLocalName())) {
305                 revisionDateStr = Utils.stringFromStringContext(importSubStatement.argument(),
306                         getReference(sourceName, importSubStatement));
307             }
308         }
309         return revisionDateStr;
310     }
311
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
317                     .keyword()
318                     .getText()
319                     .equals(YangStmtMapping.REVISION.getStatementName()
320                             .getLocalName())) {
321                 final String currentRevision = Utils.stringFromStringContext(subStatementContext.argument(),
322                         getReference(sourceName, subStatementContext));
323                 if (latestRevision == null
324                         || latestRevision.compareTo(currentRevision) == -1) {
325                     latestRevision = currentRevision;
326                 }
327             }
328         }
329         return latestRevision;
330     }
331
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);
335
336         final String latestRevision = getLatestRevision(submodule, sourceName);
337         final ImmutableSet<ModuleImport> imports = parseImports(submodule, sourceName);
338         final ImmutableSet<ModuleImport> includes = parseIncludes(submodule, sourceName);
339
340         return new SubmoduleDependencyInfo(name, latestRevision, belongsTo, imports, includes);
341     }
342
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
347                     .keyword()
348                     .getText()
349                     .equals(YangStmtMapping.BELONGS_TO.getStatementName()
350                             .getLocalName())) {
351                 return Utils.stringFromStringContext(subStatementContext
352                         .argument(), getReference(sourceName, subStatementContext));
353             }
354         }
355         return null;
356     }
357
358     private static StatementSourceReference getReference(final String sourceName,
359             final StatementContext context) {
360         return DeclarationInTextSource.atPosition(sourceName, context.getStart().getLine(), context.getStart()
361                 .getCharPositionInLine());
362     }
363
364     /**
365      *
366      * Dependency information for YANG module.
367      *
368      */
369     public static class ModuleDependencyInfo extends
370             YangModelDependencyInfo {
371
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);
377         }
378
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);
385         }
386
387         @Override
388         public String toString() {
389             return "Module [name=" + getName() + ", revision=" + getRevision() + ", semanticVersion="
390                     + getSemanticVersion().or(Module.DEFAULT_SEMANTIC_VERSION) + ", dependencies=" + getDependencies()
391                     + "]";
392         }
393     }
394
395     /**
396      *
397      * Dependency information for submodule, also provides name for parent
398      * module.
399      *
400      */
401     public static final class SubmoduleDependencyInfo extends
402             YangModelDependencyInfo {
403
404         private final String belongsTo;
405
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;
412         }
413
414         /**
415          * Returns name of parent module.
416          *
417          */
418         public String getParentModule() {
419             return belongsTo;
420         }
421
422         @Override
423         public String toString() {
424             return "Submodule [name=" + getName() + ", revision="
425                     + getRevision() + ", dependencies=" + getDependencies()
426                     + "]";
427         }
428     }
429
430     /**
431      * Utility implementation of {@link ModuleImport} to be used by
432      * {@link YangModelDependencyInfo}.
433      *
434      */
435     private static final class ModuleImportImpl implements ModuleImport {
436
437         private final Date revision;
438         private final SemVer semVer;
439         private final String name;
440
441         public ModuleImportImpl(final String moduleName, final Date revision) {
442             this(moduleName, revision, Optional.absent());
443         }
444
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);
449         }
450
451         @Override
452         public String getModuleName() {
453             return this.name;
454         }
455
456         @Override
457         public Date getRevision() {
458             return this.revision;
459         }
460
461         @Override
462         public SemVer getSemanticVersion() {
463             return this.semVer;
464         }
465
466         @Override
467         public String getPrefix() {
468             return null;
469         }
470
471         @Override
472         public int hashCode() {
473             final int prime = 31;
474             int result = 1;
475             result = prime * result + Objects.hashCode(name);
476             result = prime * result + Objects.hashCode(revision);
477             result = prime * result + Objects.hashCode(semVer);
478             return result;
479         }
480
481         @Override
482         public boolean equals(final Object obj) {
483             if (this == obj) {
484                 return true;
485             }
486             if (obj == null) {
487                 return false;
488             }
489             if (getClass() != obj.getClass()) {
490                 return false;
491             }
492             final ModuleImportImpl other = (ModuleImportImpl) obj;
493             if (name == null) {
494                 if (other.name != null) {
495                     return false;
496                 }
497             } else if (!name.equals(other.name)) {
498                 return false;
499             }
500             if (revision == null) {
501                 if (other.revision != null) {
502                     return false;
503                 }
504             } else if (!revision.equals(other.revision)) {
505                 return false;
506             }
507
508             if (!Objects.equals(getSemanticVersion(), other.getSemanticVersion())) {
509                 return false;
510             }
511             return true;
512         }
513
514         @Override
515         public String toString() {
516             return "ModuleImportImpl [name=" + name + ", revision="
517                     + QName.formattedRevision(revision) + ", semanticVersion=" + getSemanticVersion() + "]";
518         }
519     }
520 }