2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.parser.impl.util;
10 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.getArgumentString;
11 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.getFirstContext;
13 import com.google.common.base.Optional;
14 import com.google.common.collect.ImmutableSet;
15 import java.io.InputStream;
16 import java.util.Date;
17 import java.util.List;
18 import org.antlr.v4.runtime.ParserRuleContext;
19 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Belongs_to_stmtContext;
20 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Import_stmtContext;
21 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Include_stmtContext;
22 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Module_stmtContext;
23 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_date_stmtContext;
24 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtContext;
25 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtsContext;
26 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_stmtContext;
27 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.YangContext;
28 import org.opendaylight.yangtools.yang.common.QName;
29 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
30 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
31 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
34 * Helper transfer object which holds basic and dependency information for YANG
39 * There are two concrete implementations of this interface:
41 * <li>{@link ModuleDependencyInfo} - Dependency information for module</li>
42 * <li>{@link SubmoduleDependencyInfo} - Dependency information for submodule</li>
45 * @see ModuleDependencyInfo
46 * @see SubmoduleDependencyInfo
50 public abstract class YangModelDependencyInfo {
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;
59 YangModelDependencyInfo(final String name, final String formattedRevision,
60 final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
62 this.formattedRevision = formattedRevision;
63 this.revision = formattedRevision == null ? null : QName.parseRevision(formattedRevision);
64 this.moduleImports = imports;
65 this.submoduleIncludes = includes;
66 this.dependencies = ImmutableSet.<ModuleImport> builder()
67 .addAll(moduleImports)
68 .addAll(submoduleIncludes)
73 * Returns immutable collection of all module imports.
75 * This collection contains both <code>import</code> statements
76 * and <code>include</code> statements for submodules.
78 * @return Immutable collection of imports.
80 public ImmutableSet<ModuleImport> getDependencies() {
89 public String getName() {
94 * Returns formatted revision string
96 * @return formatted revision string
98 public String getFormattedRevision() {
99 return formattedRevision;
112 public int hashCode() {
113 final int prime = 31;
115 result = prime * result + ((formattedRevision == null) ? 0 : formattedRevision.hashCode());
116 result = prime * result + ((name == null) ? 0 : name.hashCode());
121 public boolean equals(final Object obj) {
128 if (!(obj instanceof YangModelDependencyInfo)) {
131 YangModelDependencyInfo other = (YangModelDependencyInfo) obj;
132 if (formattedRevision == null) {
133 if (other.formattedRevision != null) {
136 } else if (!formattedRevision.equals(other.formattedRevision)) {
140 if (other.name != null) {
143 } else if (!name.equals(other.name)) {
150 * Extracts {@link YangModelDependencyInfo} from an abstract syntax tree
153 * @param tree Abstract syntax tree
154 * @return {@link YangModelDependencyInfo}
155 * @throws YangSyntaxErrorException
156 * If the AST is not a valid YANG module/submodule
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());
164 final Optional<Submodule_stmtContext> submoduleCtx = getFirstContext(tree, Submodule_stmtContext.class);
165 if (submoduleCtx.isPresent()) {
166 return parseSubmoduleContext(submoduleCtx.get());
169 throw new YangSyntaxErrorException(name, 0, 0, "Unknown YANG text type");
173 * Extracts {@link YangModelDependencyInfo} from input stream
174 * containing YANG model.
176 * This parsing does not validate full YANG module, only
177 * parses header up to the revisions and imports.
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
185 public static YangModelDependencyInfo fromInputStream(final InputStream yangStream) {
186 YangContext yangContext = YangParserImpl.parseStreamWithoutErrorListeners(yangStream);
188 Optional<Module_stmtContext> moduleCtx = getFirstContext(yangContext, Module_stmtContext.class);
189 if (moduleCtx.isPresent()) {
190 return parseModuleContext(moduleCtx.get());
192 Optional<Submodule_stmtContext> submoduleCtx = getFirstContext(yangContext, Submodule_stmtContext.class);
193 if (submoduleCtx.isPresent()) {
194 return parseSubmoduleContext(submoduleCtx.get());
196 throw new IllegalArgumentException("Supplied stream is not valid yang file.");
199 private static YangModelDependencyInfo parseModuleContext(final Module_stmtContext module) {
200 String name = getArgumentString(module);
201 String latestRevision = getLatestRevision(module.revision_stmts());
202 ImmutableSet<ModuleImport> imports = parseImports(module.linkage_stmts().import_stmt());
203 ImmutableSet<ModuleImport> includes = parseIncludes(module.linkage_stmts().include_stmt());
205 return new ModuleDependencyInfo(name, latestRevision, imports, includes);
208 private static ImmutableSet<ModuleImport> parseImports(final List<Import_stmtContext> importStatements) {
209 ImmutableSet.Builder<ModuleImport> builder = ImmutableSet.builder();
210 for (Import_stmtContext importStmt : importStatements) {
211 String moduleName = getArgumentString(importStmt);
212 Date revision = getRevision(importStmt.revision_date_stmt());
213 builder.add(new ModuleImportImpl(moduleName, revision));
215 return builder.build();
218 public static String getLatestRevision(final Revision_stmtsContext revisionStmts) {
219 List<Revision_stmtContext> revisions = revisionStmts.getRuleContexts(Revision_stmtContext.class);
220 String latestRevision = null;
221 for (Revision_stmtContext revisionStmt : revisions) {
222 String currentRevision = getArgumentString(revisionStmt);
223 if (latestRevision == null || latestRevision.compareTo(currentRevision) == -1) {
224 latestRevision = currentRevision;
227 return latestRevision;
230 private static YangModelDependencyInfo parseSubmoduleContext(final Submodule_stmtContext submodule) {
231 String name = getArgumentString(submodule);
232 Belongs_to_stmtContext belongsToStmt = submodule.submodule_header_stmts().belongs_to_stmt(0);
233 String belongsTo = getArgumentString(belongsToStmt);
235 String latestRevision = getLatestRevision(submodule.revision_stmts());
236 ImmutableSet<ModuleImport> imports = parseImports(submodule.linkage_stmts().import_stmt());
237 ImmutableSet<ModuleImport> includes = parseIncludes(submodule.linkage_stmts().include_stmt());
239 return new SubmoduleDependencyInfo(name, latestRevision, belongsTo, imports, includes);
242 private static ImmutableSet<ModuleImport> parseIncludes(final List<Include_stmtContext> importStatements) {
243 ImmutableSet.Builder<ModuleImport> builder = ImmutableSet.builder();
244 for (Include_stmtContext importStmt : importStatements) {
245 String moduleName = getArgumentString(importStmt);
246 Date revision = getRevision(importStmt.revision_date_stmt());
247 builder.add(new ModuleImportImpl(moduleName, revision));
249 return builder.build();
252 private static Date getRevision(final Revision_date_stmtContext revisionDateStmt) {
253 if (revisionDateStmt == null) {
256 String formatedDate = getArgumentString(revisionDateStmt);
257 return QName.parseRevision(formatedDate);
262 * Dependency information for YANG module.
265 public static final class ModuleDependencyInfo extends YangModelDependencyInfo {
267 private ModuleDependencyInfo(final String name, final String latestRevision,
268 final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
269 super(name, latestRevision, imports, includes);
273 public String toString() {
274 return "Module [name=" + getName() + ", revision=" + getRevision() + ", dependencies=" + getDependencies()
281 * Dependency information for submodule, also provides name
285 public static final class SubmoduleDependencyInfo extends YangModelDependencyInfo {
287 private final String belongsTo;
289 private SubmoduleDependencyInfo(final String name, final String latestRevision, final String belongsTo,
290 final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
291 super(name, latestRevision, imports, includes);
292 this.belongsTo = belongsTo;
296 * Returns name of parent module.
299 public String getParentModule() {
304 public String toString() {
305 return "Submodule [name=" + getName() + ", revision=" + getRevision() + ", dependencies="
306 + getDependencies() + "]";
311 * Utility implementation of {@link ModuleImport} to be used by
312 * {@link YangModelDependencyInfo}.
315 private static final class ModuleImportImpl implements ModuleImport {
317 private final Date revision;
318 private final String name;
320 public ModuleImportImpl(final String moduleName, final Date revision) {
321 this.name = moduleName;
322 this.revision = revision;
326 public String getModuleName() {
331 public Date getRevision() {
332 return this.revision;
336 public String getPrefix() {
341 public int hashCode() {
342 final int prime = 31;
344 result = prime * result + ((name == null) ? 0 : name.hashCode());
345 result = prime * result + ((revision == null) ? 0 : revision.hashCode());
350 public boolean equals(final Object obj) {
357 if (getClass() != obj.getClass()) {
360 ModuleImportImpl other = (ModuleImportImpl) obj;
362 if (other.name != null) {
365 } else if (!name.equals(other.name)) {
368 if (revision == null) {
369 if (other.revision != null) {
372 } else if (!revision.equals(other.revision)) {
379 public String toString() {
380 return "ModuleImportImpl [name=" + name + ", revision=" + QName.formattedRevision(revision) + "]";