Bug 3670: Replaced YANG parser for new statement parser
[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 static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.getArgumentString;
11
12 import org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils;
13
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;
18 import java.util.Set;
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;
39
40 /**
41  * Helper transfer object which holds basic and dependency information for YANG
42  * model.
43  *
44  *
45  *
46  * There are two concrete implementations of this interface:
47  * <ul>
48  * <li>{@link ModuleDependencyInfo} - Dependency information for module</li>
49  * <li>{@link SubmoduleDependencyInfo} - Dependency information for submodule</li>
50  * </ul>
51  *
52  * @see ModuleDependencyInfo
53  * @see SubmoduleDependencyInfo
54  *
55  */
56
57 public abstract class YangModelDependencyInfo {
58
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;
65
66     YangModelDependencyInfo(final String name, final String formattedRevision,
67             final ImmutableSet<ModuleImport> imports,
68             final ImmutableSet<ModuleImport> includes) {
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     }
78
79     /**
80      * Returns immutable collection of all module imports.
81      *
82      * This collection contains both <code>import</code> statements and
83      * <code>include</code> statements for submodules.
84      *
85      * @return Immutable collection of imports.
86      */
87     public ImmutableSet<ModuleImport> getDependencies() {
88         return dependencies;
89     }
90
91     /**
92      * Returns model name
93      *
94      * @return model name
95      */
96     public String getName() {
97         return name;
98     }
99
100     /**
101      * Returns formatted revision string
102      *
103      * @return formatted revision string
104      */
105     public String getFormattedRevision() {
106         return formattedRevision;
107     }
108
109     /**
110      * Returns revision
111      *
112      * @return revision
113      */
114     Date getRevision() {
115         return revision;
116     }
117
118     @Override
119     public int hashCode() {
120         final int prime = 31;
121         int result = 1;
122         result = prime * result + Objects.hashCode(formattedRevision);
123         result = prime * result + Objects.hashCode(name);
124         return result;
125     }
126
127     @Override
128     public boolean equals(final Object obj) {
129         if (this == obj) {
130             return true;
131         }
132         if (obj == null) {
133             return false;
134         }
135         if (!(obj instanceof YangModelDependencyInfo)) {
136             return false;
137         }
138         YangModelDependencyInfo other = (YangModelDependencyInfo) obj;
139         if (formattedRevision == null) {
140             if (other.formattedRevision != null) {
141                 return false;
142             }
143         } else if (!formattedRevision.equals(other.formattedRevision)) {
144             return false;
145         }
146         if (name == null) {
147             if (other.name != null) {
148                 return false;
149             }
150         } else if (!name.equals(other.name)) {
151             return false;
152         }
153         return true;
154     }
155
156     /**
157      * Extracts {@link YangModelDependencyInfo} from an abstract syntax tree of
158      * a YANG model.
159      *
160      * @param tree
161      *            Abstract syntax tree
162      * @return {@link YangModelDependencyInfo}
163      * @throws YangSyntaxErrorException
164      *             If the AST is not a valid YANG module/submodule
165      */
166     public static YangModelDependencyInfo fromAST(final String name,
167             final ParserRuleContext tree) throws YangSyntaxErrorException {
168
169         if (tree instanceof YangStatementParser.StatementContext) {
170             YangStatementParser.StatementContext rootStatement = (YangStatementParser.StatementContext) tree;
171             return parseAST(rootStatement);
172         }
173
174         final Optional<Module_stmtContext> moduleCtx = ParserListenerUtils
175                 .getFirstContext(tree, Module_stmtContext.class);
176         if (moduleCtx.isPresent()) {
177             return parseModuleContext(moduleCtx.get());
178         }
179
180         final Optional<Submodule_stmtContext> submoduleCtx = ParserListenerUtils
181                 .getFirstContext(tree, Submodule_stmtContext.class);
182         if (submoduleCtx.isPresent()) {
183             return parseSubmoduleContext(submoduleCtx.get());
184         }
185
186         throw new YangSyntaxErrorException(name, 0, 0, "Unknown YANG text type");
187     }
188
189     private static YangModelDependencyInfo parseAST(
190             YangStatementParser.StatementContext rootStatement) {
191         if (rootStatement
192                 .keyword()
193                 .getText()
194                 .equals(Rfc6020Mapping.MODULE.getStatementName().getLocalName())) {
195             return parseModuleContext(rootStatement);
196         } else if (rootStatement
197                 .keyword()
198                 .getText()
199                 .equals(Rfc6020Mapping.SUBMODULE.getStatementName()
200                         .getLocalName())) {
201             return parseSubmoduleContext(rootStatement);
202         }
203
204         throw new IllegalArgumentException(
205                 "Root of parsed AST must be either module or submodule");
206     }
207
208     /**
209      * Extracts {@link YangModelDependencyInfo} from input stream containing
210      * YANG model.
211      *
212      * This parsing does not validate full YANG module, only parses header up to
213      * the revisions and imports.
214      *
215      * @param yangStream
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
220      */
221     public static YangModelDependencyInfo fromInputStream(
222             final InputStream yangStream) {
223         StatementContext yangAST = new YangStatementSourceImpl(yangStream)
224                 .getYangAST();
225         return parseAST(yangAST);
226     }
227
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());
236
237         return new ModuleDependencyInfo(name, latestRevision, imports, includes);
238     }
239
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);
246
247         return new ModuleDependencyInfo(name, latestRevision, imports, includes);
248     }
249
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
256                     .keyword()
257                     .getText()
258                     .equals(Rfc6020Mapping.IMPORT.getStatementName()
259                             .getLocalName())) {
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,
266                         revisionDate));
267             }
268         }
269         return ImmutableSet.copyOf(result);
270     }
271
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
278                     .keyword()
279                     .getText()
280                     .equals(Rfc6020Mapping.INCLUDE.getStatementName()
281                             .getLocalName())) {
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));
288             }
289         }
290         return ImmutableSet.copyOf(result);
291     }
292
293     private static String getRevisionDateString(StatementContext importStatement) {
294         List<StatementContext> importSubStatements = importStatement
295                 .statement();
296         String revisionDateStr = null;
297         for (StatementContext importSubStatement : importSubStatements) {
298             if (importSubStatement
299                     .keyword()
300                     .getText()
301                     .equals(Rfc6020Mapping.REVISION_DATE.getStatementName()
302                             .getLocalName())) {
303                 revisionDateStr = Utils
304                         .stringFromStringContext(importSubStatement.argument());
305             }
306         }
307         return revisionDateStr;
308     }
309
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));
317         }
318         return builder.build();
319     }
320
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
327                     .keyword()
328                     .getText()
329                     .equals(Rfc6020Mapping.REVISION.getStatementName()
330                             .getLocalName())) {
331                 String currentRevision = Utils
332                         .stringFromStringContext(subStatementContext.argument());
333                 if (latestRevision == null
334                         || latestRevision.compareTo(currentRevision) == -1) {
335                     latestRevision = currentRevision;
336                 }
337             }
338         }
339         return latestRevision;
340     }
341
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;
352             }
353         }
354         return latestRevision;
355     }
356
357     private static YangModelDependencyInfo parseSubmoduleContext(
358             final YangStatementParser.StatementContext submodule) {
359         String name = Utils.stringFromStringContext(submodule.argument());
360         String belongsTo = parseBelongsTo(submodule);
361
362         String latestRevision = getLatestRevision(submodule);
363         ImmutableSet<ModuleImport> imports = parseImports(submodule);
364         ImmutableSet<ModuleImport> includes = parseIncludes(submodule);
365
366         return new SubmoduleDependencyInfo(name, latestRevision, belongsTo,
367                 imports, includes);
368     }
369
370     private static String parseBelongsTo(StatementContext submodule) {
371         List<StatementContext> subStatements = submodule.statement();
372         for (StatementContext subStatementContext : subStatements) {
373             if (subStatementContext
374                     .keyword()
375                     .getText()
376                     .equals(Rfc6020Mapping.BELONGS_TO.getStatementName()
377                             .getLocalName())) {
378                 return Utils.stringFromStringContext(subStatementContext
379                         .argument());
380             }
381         }
382         return null;
383     }
384
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);
391
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());
397
398         return new SubmoduleDependencyInfo(name, latestRevision, belongsTo,
399                 imports, includes);
400     }
401
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));
409         }
410         return builder.build();
411     }
412
413     private static Date getRevision(
414             final Revision_date_stmtContext revisionDateStmt) {
415         if (revisionDateStmt == null) {
416             return null;
417         }
418         String formatedDate = getArgumentString(revisionDateStmt);
419         return QName.parseRevision(formatedDate);
420     }
421
422     /**
423      *
424      * Dependency information for YANG module.
425      *
426      */
427     public static final class ModuleDependencyInfo extends
428             YangModelDependencyInfo {
429
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);
435         }
436
437         @Override
438         public String toString() {
439             return "Module [name=" + getName() + ", revision=" + getRevision()
440                     + ", dependencies=" + getDependencies() + "]";
441         }
442     }
443
444     /**
445      *
446      * Dependency information for submodule, also provides name for parent
447      * module.
448      *
449      */
450     public static final class SubmoduleDependencyInfo extends
451             YangModelDependencyInfo {
452
453         private final String belongsTo;
454
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;
461         }
462
463         /**
464          * Returns name of parent module.
465          *
466          */
467         public String getParentModule() {
468             return belongsTo;
469         }
470
471         @Override
472         public String toString() {
473             return "Submodule [name=" + getName() + ", revision="
474                     + getRevision() + ", dependencies=" + getDependencies()
475                     + "]";
476         }
477     }
478
479     /**
480      * Utility implementation of {@link ModuleImport} to be used by
481      * {@link YangModelDependencyInfo}.
482      *
483      */
484     private static final class ModuleImportImpl implements ModuleImport {
485
486         private final Date revision;
487         private final String name;
488
489         public ModuleImportImpl(final String moduleName, final Date revision) {
490             this.name = moduleName;
491             this.revision = revision;
492         }
493
494         @Override
495         public String getModuleName() {
496             return this.name;
497         }
498
499         @Override
500         public Date getRevision() {
501             return this.revision;
502         }
503
504         @Override
505         public String getPrefix() {
506             return null;
507         }
508
509         @Override
510         public int hashCode() {
511             final int prime = 31;
512             int result = 1;
513             result = prime * result + Objects.hashCode(name);
514             result = prime * result + Objects.hashCode(revision);
515             return result;
516         }
517
518         @Override
519         public boolean equals(final Object obj) {
520             if (this == obj) {
521                 return true;
522             }
523             if (obj == null) {
524                 return false;
525             }
526             if (getClass() != obj.getClass()) {
527                 return false;
528             }
529             ModuleImportImpl other = (ModuleImportImpl) obj;
530             if (name == null) {
531                 if (other.name != null) {
532                     return false;
533                 }
534             } else if (!name.equals(other.name)) {
535                 return false;
536             }
537             if (revision == null) {
538                 if (other.revision != null) {
539                     return false;
540                 }
541             } else if (!revision.equals(other.revision)) {
542                 return false;
543             }
544             return true;
545         }
546
547         @Override
548         public String toString() {
549             return "ModuleImportImpl [name=" + name + ", revision="
550                     + QName.formattedRevision(revision) + "]";
551         }
552     }
553 }