Eliminate unneeded import
[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.Rfc6020Mapping;
27 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
28 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.SupportedExtensionsMapping;
29 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils;
30 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
31
32 /**
33  * Helper transfer object which holds basic and dependency information for YANG
34  * model.
35  *
36  *
37  *
38  * There are two concrete implementations of this interface:
39  * <ul>
40  * <li>{@link ModuleDependencyInfo} - Dependency information for module</li>
41  * <li>{@link SubmoduleDependencyInfo} - Dependency information for submodule</li>
42  * </ul>
43  *
44  * @see ModuleDependencyInfo
45  * @see SubmoduleDependencyInfo
46  *
47  */
48
49 public abstract class YangModelDependencyInfo {
50
51     private final String name;
52     private final String formattedRevision;
53     private final Date revision;
54     private final Optional<SemVer> semVer;
55     private final ImmutableSet<ModuleImport> submoduleIncludes;
56     private final ImmutableSet<ModuleImport> moduleImports;
57     private final ImmutableSet<ModuleImport> dependencies;
58
59     YangModelDependencyInfo(final String name, final String formattedRevision,
60             final ImmutableSet<ModuleImport> imports,
61             final ImmutableSet<ModuleImport> includes) {
62         this(name, formattedRevision, imports, includes, Optional.absent());
63     }
64
65     YangModelDependencyInfo(final String name, final String formattedRevision,
66             final ImmutableSet<ModuleImport> imports,
67             final ImmutableSet<ModuleImport> includes,
68             final Optional<SemVer> semVer) {
69         this.name = name;
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();
77         this.semVer = semVer;
78     }
79
80     /**
81      * Returns immutable collection of all module imports.
82      *
83      * This collection contains both <code>import</code> statements and
84      * <code>include</code> statements for submodules.
85      *
86      * @return Immutable collection of imports.
87      */
88     public ImmutableSet<ModuleImport> getDependencies() {
89         return dependencies;
90     }
91
92     /**
93      * Returns model name
94      *
95      * @return model name
96      */
97     public String getName() {
98         return name;
99     }
100
101     /**
102      * Returns formatted revision string
103      *
104      * @return formatted revision string
105      */
106     public String getFormattedRevision() {
107         return formattedRevision;
108     }
109
110     /**
111      * Returns revision
112      *
113      * @return revision
114      */
115     Date getRevision() {
116         return revision;
117     }
118
119     /**
120      * Returns semantic version of module
121      *
122      * @return semantic version
123      */
124     public Optional<SemVer> getSemanticVersion() {
125         return semVer;
126     }
127
128     @Override
129     public int hashCode() {
130         final int prime = 31;
131         int result = 1;
132         result = prime * result + Objects.hashCode(formattedRevision);
133         result = prime * result + Objects.hashCode(name);
134         result = prime * result + Objects.hashCode(semVer);
135         return result;
136     }
137
138     @Override
139     public boolean equals(final Object obj) {
140         if (this == obj) {
141             return true;
142         }
143         if (obj == null) {
144             return false;
145         }
146         if (!(obj instanceof YangModelDependencyInfo)) {
147             return false;
148         }
149         final YangModelDependencyInfo other = (YangModelDependencyInfo) obj;
150         if (formattedRevision == null) {
151             if (other.formattedRevision != null) {
152                 return false;
153             }
154         } else if (!formattedRevision.equals(other.formattedRevision)) {
155             return false;
156         }
157         if (name == null) {
158             if (other.name != null) {
159                 return false;
160             }
161         } else if (!name.equals(other.name)) {
162             return false;
163         }
164         if(!Objects.equals(semVer, other.semVer)) {
165             return false;
166         }
167
168         return true;
169     }
170
171     /**
172      * Extracts {@link YangModelDependencyInfo} from an abstract syntax tree of
173      * a YANG model.
174      *
175      * @param tree
176      *            Abstract syntax tree
177      * @return {@link YangModelDependencyInfo}
178      * @throws YangSyntaxErrorException
179      *             If the AST is not a valid YANG module/submodule
180      */
181     public static YangModelDependencyInfo fromAST(final String name,
182             final ParserRuleContext tree) throws YangSyntaxErrorException {
183
184         if (tree instanceof StatementContext) {
185             final StatementContext rootStatement = (StatementContext) tree;
186             return parseAST(rootStatement);
187         }
188
189         throw new YangSyntaxErrorException(name, 0, 0, "Unknown YANG text type");
190     }
191
192     private static YangModelDependencyInfo parseAST(final StatementContext rootStatement) {
193         if (rootStatement
194                 .keyword()
195                 .getText()
196                 .equals(Rfc6020Mapping.MODULE.getStatementName().getLocalName())) {
197             return parseModuleContext(rootStatement);
198         } else if (rootStatement
199                 .keyword()
200                 .getText()
201                 .equals(Rfc6020Mapping.SUBMODULE.getStatementName()
202                         .getLocalName())) {
203             return parseSubmoduleContext(rootStatement);
204         }
205
206         throw new IllegalArgumentException(
207                 "Root of parsed AST must be either module or submodule");
208     }
209
210     /**
211      * Extracts {@link YangModelDependencyInfo} from input stream containing
212      * YANG model.
213      *
214      * This parsing does not validate full YANG module, only parses header up to
215      * the revisions and imports.
216      *
217      * @param yangStream
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
222      */
223     public static YangModelDependencyInfo fromInputStream(
224             final InputStream yangStream) {
225         final StatementContext yangAST = new YangStatementSourceImpl(yangStream)
226                 .getYangAST();
227         return parseAST(yangAST);
228     }
229
230     private static YangModelDependencyInfo parseModuleContext(final StatementContext module) {
231         final String name = Utils.stringFromStringContext(module.argument());
232         final String latestRevision = getLatestRevision(module);
233         final Optional<SemVer> semVer = Optional.fromNullable(getSemanticVersion(module));
234         final ImmutableSet<ModuleImport> imports = parseImports(module);
235         final ImmutableSet<ModuleImport> includes = parseIncludes(module);
236
237         return new ModuleDependencyInfo(name, latestRevision, imports, includes, semVer);
238     }
239
240     private static ImmutableSet<ModuleImport> parseImports(final StatementContext module) {
241         final Set<ModuleImport> result = new HashSet<>();
242         final List<StatementContext> subStatements = module.statement();
243         for (final StatementContext subStatementContext : subStatements) {
244             if (subStatementContext
245                     .keyword()
246                     .getText()
247                     .equals(Rfc6020Mapping.IMPORT.getStatementName()
248                             .getLocalName())) {
249                 final String revisionDateStr = getRevisionDateString(subStatementContext);
250                 final String importedModuleName = Utils
251                         .stringFromStringContext(subStatementContext.argument());
252                 final Date revisionDate = (revisionDateStr == null) ? null : QName
253                         .parseRevision(revisionDateStr);
254                 final Optional<SemVer> importSemVer = Optional.fromNullable(getSemanticVersion(subStatementContext));
255                 result.add(new ModuleImportImpl(importedModuleName,
256                         revisionDate, importSemVer));
257             }
258         }
259         return ImmutableSet.copyOf(result);
260     }
261
262     private static SemVer getSemanticVersion(final StatementContext statement) {
263         final List<StatementContext> subStatements = statement.statement();
264         String semVerString = null;
265         final String semVerStmtName = SupportedExtensionsMapping.SEMANTIC_VERSION.getStatementName().getLocalName();
266         for (final StatementContext subStatement : subStatements) {
267             final String subStatementName = Utils.trimPrefix(subStatement.keyword().getText());
268             if (semVerStmtName.equals(subStatementName)) {
269                 semVerString = Utils.stringFromStringContext(subStatement.argument());
270                 break;
271             }
272         }
273
274         if (Strings.isNullOrEmpty(semVerString)) {
275             return null;
276         }
277
278         return SemVer.valueOf(semVerString);
279     }
280
281     private static ImmutableSet<ModuleImport> parseIncludes(final StatementContext module) {
282         final Set<ModuleImport> result = new HashSet<>();
283         final List<StatementContext> subStatements = module.statement();
284         for (final StatementContext subStatementContext : subStatements) {
285             if (subStatementContext
286                     .keyword()
287                     .getText()
288                     .equals(Rfc6020Mapping.INCLUDE.getStatementName()
289                             .getLocalName())) {
290                 final String revisionDateStr = getRevisionDateString(subStatementContext);
291                 final String IncludeModuleName = Utils
292                         .stringFromStringContext(subStatementContext.argument());
293                 final Date revisionDate = (revisionDateStr == null) ? null : QName
294                         .parseRevision(revisionDateStr);
295                 result.add(new ModuleImportImpl(IncludeModuleName, revisionDate));
296             }
297         }
298         return ImmutableSet.copyOf(result);
299     }
300
301     private static String getRevisionDateString(final StatementContext importStatement) {
302         final List<StatementContext> importSubStatements = importStatement.statement();
303         String revisionDateStr = null;
304         for (final StatementContext importSubStatement : importSubStatements) {
305             if (importSubStatement
306                     .keyword()
307                     .getText()
308                     .equals(Rfc6020Mapping.REVISION_DATE.getStatementName()
309                             .getLocalName())) {
310                 revisionDateStr = Utils
311                         .stringFromStringContext(importSubStatement.argument());
312             }
313         }
314         return revisionDateStr;
315     }
316
317     public static String getLatestRevision(final StatementContext module) {
318         final List<StatementContext> subStatements = module.statement();
319         String latestRevision = null;
320         for (final StatementContext subStatementContext : subStatements) {
321             if (subStatementContext
322                     .keyword()
323                     .getText()
324                     .equals(Rfc6020Mapping.REVISION.getStatementName()
325                             .getLocalName())) {
326                 final String currentRevision = Utils
327                         .stringFromStringContext(subStatementContext.argument());
328                 if (latestRevision == null
329                         || latestRevision.compareTo(currentRevision) == -1) {
330                     latestRevision = currentRevision;
331                 }
332             }
333         }
334         return latestRevision;
335     }
336
337     private static YangModelDependencyInfo parseSubmoduleContext(final StatementContext submodule) {
338         final String name = Utils.stringFromStringContext(submodule.argument());
339         final String belongsTo = parseBelongsTo(submodule);
340
341         final String latestRevision = getLatestRevision(submodule);
342         final ImmutableSet<ModuleImport> imports = parseImports(submodule);
343         final ImmutableSet<ModuleImport> includes = parseIncludes(submodule);
344
345         return new SubmoduleDependencyInfo(name, latestRevision, belongsTo,
346                 imports, includes);
347     }
348
349     private static String parseBelongsTo(final StatementContext submodule) {
350         final List<StatementContext> subStatements = submodule.statement();
351         for (final StatementContext subStatementContext : subStatements) {
352             if (subStatementContext
353                     .keyword()
354                     .getText()
355                     .equals(Rfc6020Mapping.BELONGS_TO.getStatementName()
356                             .getLocalName())) {
357                 return Utils.stringFromStringContext(subStatementContext
358                         .argument());
359             }
360         }
361         return null;
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 }