Sonar issues clean-up
[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  * This program and the accompanying materials are made available under the
4  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
5  * and is available at http://www.eclipse.org/legal/eplv10.html
6  */
7 package org.opendaylight.yangtools.yang.parser.impl.util;
8
9 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.getArgumentString;
10 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.getFirstContext;
11
12 import com.google.common.base.Optional;
13 import com.google.common.collect.ImmutableSet;
14 import java.io.InputStream;
15 import java.util.Date;
16 import java.util.List;
17 import org.antlr.v4.runtime.ParserRuleContext;
18 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Belongs_to_stmtContext;
19 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Import_stmtContext;
20 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Include_stmtContext;
21 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Module_stmtContext;
22 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_date_stmtContext;
23 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtContext;
24 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtsContext;
25 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_stmtContext;
26 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.YangContext;
27 import org.opendaylight.yangtools.yang.common.QName;
28 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
29 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
30 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
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 ImmutableSet<ModuleImport> submoduleIncludes;
55     private final ImmutableSet<ModuleImport> moduleImports;
56     private final ImmutableSet<ModuleImport> dependencies;
57
58     YangModelDependencyInfo(final String name, final String formattedRevision,
59             final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
60         this.name = name;
61         this.formattedRevision = formattedRevision;
62         this.revision = formattedRevision == null ? null : QName.parseRevision(formattedRevision);
63         this.moduleImports = imports;
64         this.submoduleIncludes = includes;
65         this.dependencies = ImmutableSet.<ModuleImport> builder()
66                 .addAll(moduleImports)
67                 .addAll(submoduleIncludes)
68                 .build();
69     }
70
71     /**
72      * Returns immutable collection of all module imports.
73      *
74      * This collection contains both <code>import</code> statements
75      * and <code>include</code> statements for submodules.
76      *
77      * @return Immutable collection of imports.
78      */
79     public ImmutableSet<ModuleImport> getDependencies() {
80         return dependencies;
81     }
82
83     /**
84      * Returns model name
85      *
86      * @return model name
87      */
88     public String getName() {
89         return name;
90     }
91
92     /**
93      * Returns formatted revision string
94      *
95      * @return formatted revision string
96      */
97     public String getFormattedRevision() {
98         return formattedRevision;
99     }
100
101     /**
102      * Returns revision
103      *
104      * @return revision
105      */
106     Date getRevision() {
107         return revision;
108     }
109
110     @Override
111     public int hashCode() {
112         final int prime = 31;
113         int result = 1;
114         result = prime * result + ((formattedRevision == null) ? 0 : formattedRevision.hashCode());
115         result = prime * result + ((name == null) ? 0 : name.hashCode());
116         return result;
117     }
118
119     @Override
120     public boolean equals(final Object obj) {
121         if (this == obj) {
122             return true;
123         }
124         if (obj == null) {
125             return false;
126         }
127         if (!(obj instanceof YangModelDependencyInfo)) {
128             return false;
129         }
130         YangModelDependencyInfo other = (YangModelDependencyInfo) obj;
131         if (formattedRevision == null) {
132             if (other.formattedRevision != null) {
133                 return false;
134             }
135         } else if (!formattedRevision.equals(other.formattedRevision)) {
136             return false;
137         }
138         if (name == null) {
139             if (other.name != null) {
140                 return false;
141             }
142         } else if (!name.equals(other.name)) {
143             return false;
144         }
145         return true;
146     }
147
148     /**
149      * Extracts {@link YangModelDependencyInfo} from an abstract syntax tree
150      * of a YANG model.
151      *
152      * @param tree Abstract syntax tree
153      * @return {@link YangModelDependencyInfo}
154      * @throws YangSyntaxErrorException
155      *             If the AST is not a valid YANG module/submodule
156      */
157     public static YangModelDependencyInfo fromAST(final String name, final ParserRuleContext tree) throws YangSyntaxErrorException {
158         final Optional<Module_stmtContext> moduleCtx = getFirstContext(tree, Module_stmtContext.class);
159         if (moduleCtx.isPresent()) {
160             return parseModuleContext(moduleCtx.get());
161         }
162
163         final Optional<Submodule_stmtContext> submoduleCtx = getFirstContext(tree, Submodule_stmtContext.class);
164         if (submoduleCtx.isPresent()) {
165             return parseSubmoduleContext(submoduleCtx.get());
166         }
167
168         throw new YangSyntaxErrorException(name, 0, 0, "Unknown YANG text type");
169     }
170
171     /**
172      * Extracts {@link YangModelDependencyInfo} from input stream
173      * containing YANG model.
174      *
175      * This parsing does not validate full YANG module, only
176      * parses header up to the revisions and imports.
177      *
178      * @param yangStream
179      *            Opened Input stream containing text source of YANG model
180      * @return {@link YangModelDependencyInfo}
181      * @throws IllegalArgumentException
182      *             If input stream is not valid YANG stream
183      */
184     public static YangModelDependencyInfo fromInputStream(final InputStream yangStream) {
185         YangContext yangContext = YangParserImpl.parseStreamWithoutErrorListeners(yangStream);
186
187         Optional<Module_stmtContext> moduleCtx = getFirstContext(yangContext, Module_stmtContext.class);
188         if (moduleCtx.isPresent()) {
189             return parseModuleContext(moduleCtx.get());
190         }
191         Optional<Submodule_stmtContext> submoduleCtx = getFirstContext(yangContext, Submodule_stmtContext.class);
192         if (submoduleCtx.isPresent()) {
193             return parseSubmoduleContext(submoduleCtx.get());
194         }
195         throw new IllegalArgumentException("Supplied stream is not valid yang file.");
196     }
197
198     private static YangModelDependencyInfo parseModuleContext(final Module_stmtContext module) {
199         String name = getArgumentString(module);
200         String latestRevision = getLatestRevision(module.revision_stmts());
201         ImmutableSet<ModuleImport> imports = parseImports(module.linkage_stmts().import_stmt());
202         ImmutableSet<ModuleImport> includes = parseIncludes(module.linkage_stmts().include_stmt());
203
204         return new ModuleDependencyInfo(name, latestRevision, imports, includes);
205     }
206
207     private static ImmutableSet<ModuleImport> parseImports(final List<Import_stmtContext> importStatements) {
208         ImmutableSet.Builder<ModuleImport> builder = ImmutableSet.builder();
209         for (Import_stmtContext importStmt : importStatements) {
210             String moduleName = getArgumentString(importStmt);
211             Date revision = getRevision(importStmt.revision_date_stmt());
212             builder.add(new ModuleImportImpl(moduleName, revision));
213         }
214         return builder.build();
215     }
216
217     public static String getLatestRevision(final Revision_stmtsContext revisionStmts) {
218         List<Revision_stmtContext> revisions = revisionStmts.getRuleContexts(Revision_stmtContext.class);
219         String latestRevision = null;
220         for (Revision_stmtContext revisionStmt : revisions) {
221             String currentRevision = getArgumentString(revisionStmt);
222             if (latestRevision == null || latestRevision.compareTo(currentRevision) == -1) {
223                 latestRevision = currentRevision;
224             }
225         }
226         return latestRevision;
227     }
228
229     private static YangModelDependencyInfo parseSubmoduleContext(final Submodule_stmtContext submodule) {
230         String name = getArgumentString(submodule);
231         Belongs_to_stmtContext belongsToStmt = submodule.submodule_header_stmts().belongs_to_stmt(0);
232         String belongsTo = getArgumentString(belongsToStmt);
233
234         String latestRevision = getLatestRevision(submodule.revision_stmts());
235         ImmutableSet<ModuleImport> imports = parseImports(submodule.linkage_stmts().import_stmt());
236         ImmutableSet<ModuleImport> includes = parseIncludes(submodule.linkage_stmts().include_stmt());
237
238         return new SubmoduleDependencyInfo(name, latestRevision, belongsTo, imports, includes);
239     }
240
241     private static ImmutableSet<ModuleImport> parseIncludes(final List<Include_stmtContext> importStatements) {
242         ImmutableSet.Builder<ModuleImport> builder = ImmutableSet.builder();
243         for (Include_stmtContext importStmt : importStatements) {
244             String moduleName = getArgumentString(importStmt);
245             Date revision = getRevision(importStmt.revision_date_stmt());
246             builder.add(new ModuleImportImpl(moduleName, revision));
247         }
248         return builder.build();
249     }
250
251     private static Date getRevision(final Revision_date_stmtContext revisionDateStmt) {
252         if (revisionDateStmt == null) {
253             return null;
254         }
255         String formatedDate = getArgumentString(revisionDateStmt);
256         return QName.parseRevision(formatedDate);
257     }
258
259     /**
260      *
261      * Dependency information for YANG module.
262      *
263      */
264     public static final class ModuleDependencyInfo extends YangModelDependencyInfo {
265
266         private ModuleDependencyInfo(final String name, final String latestRevision,
267                 final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
268             super(name, latestRevision, imports, includes);
269         }
270
271         @Override
272         public String toString() {
273             return "Module [name=" + getName() + ", revision=" + getRevision() + ", dependencies=" + getDependencies()
274                     + "]";
275         }
276     }
277
278     /**
279      *
280      * Dependency information for submodule, also provides name
281      * for parent module.
282      *
283      */
284     public static final class SubmoduleDependencyInfo extends YangModelDependencyInfo {
285
286         private final String belongsTo;
287
288         private SubmoduleDependencyInfo(final String name, final String latestRevision, final String belongsTo,
289                                         final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
290             super(name, latestRevision, imports, includes);
291             this.belongsTo = belongsTo;
292         }
293
294         /**
295          * Returns name of parent module.
296          *
297          */
298         public String getParentModule() {
299             return belongsTo;
300         }
301
302         @Override
303         public String toString() {
304             return "Submodule [name=" + getName() + ", revision=" + getRevision() + ", dependencies="
305                     + getDependencies() + "]";
306         }
307     }
308
309     /**
310      * Utility implementation of {@link ModuleImport} to be used by
311      * {@link YangModelDependencyInfo}.
312      *
313      */
314     private static final class ModuleImportImpl implements ModuleImport {
315
316         private final Date revision;
317         private final String name;
318
319         public ModuleImportImpl(final String moduleName, final Date revision) {
320             this.name = moduleName;
321             this.revision = revision;
322         }
323
324         @Override
325         public String getModuleName() {
326             return this.name;
327         }
328
329         @Override
330         public Date getRevision() {
331             return this.revision;
332         }
333
334         @Override
335         public String getPrefix() {
336             return null;
337         }
338
339         @Override
340         public int hashCode() {
341             final int prime = 31;
342             int result = 1;
343             result = prime * result + ((name == null) ? 0 : name.hashCode());
344             result = prime * result + ((revision == null) ? 0 : revision.hashCode());
345             return result;
346         }
347
348         @Override
349         public boolean equals(final Object obj) {
350             if (this == obj) {
351                 return true;
352             }
353             if (obj == null) {
354                 return false;
355             }
356             if (getClass() != obj.getClass()) {
357                 return false;
358             }
359             ModuleImportImpl other = (ModuleImportImpl) obj;
360             if (name == null) {
361                 if (other.name != null) {
362                     return false;
363                 }
364             } else if (!name.equals(other.name)) {
365                 return false;
366             }
367             if (revision == null) {
368                 if (other.revision != null) {
369                     return false;
370                 }
371             } else if (!revision.equals(other.revision)) {
372                 return false;
373             }
374             return true;
375         }
376
377         @Override
378         public String toString() {
379             return "ModuleImportImpl [name=" + name + ", revision=" + QName.formattedRevision(revision) + "]";
380         }
381     }
382 }