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