/*
* Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.yangtools.yang.parser.impl.util;
import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.getArgumentString;
import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.getFirstContext;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import org.antlr.v4.runtime.ParserRuleContext;
import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Belongs_to_stmtContext;
import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Import_stmtContext;
import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Include_stmtContext;
import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Module_stmtContext;
import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_date_stmtContext;
import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtContext;
import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtsContext;
import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_stmtContext;
import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.YangContext;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.model.api.ModuleImport;
import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
/**
* Helper transfer object which holds basic and dependency information for YANG
* model.
*
*
*
* There are two concrete implementations of this interface:
*
* - {@link ModuleDependencyInfo} - Dependency information for module
* - {@link SubmoduleDependencyInfo} - Dependency information for submodule
*
*
* @see ModuleDependencyInfo
* @see SubmoduleDependencyInfo
*
*/
public abstract class YangModelDependencyInfo {
private final String name;
private final String formattedRevision;
private final Date revision;
private final ImmutableSet submoduleIncludes;
private final ImmutableSet moduleImports;
private final ImmutableSet dependencies;
YangModelDependencyInfo(final String name, final String formattedRevision,
final ImmutableSet imports, final ImmutableSet includes) {
this.name = name;
this.formattedRevision = formattedRevision;
this.revision = formattedRevision == null ? null : QName.parseRevision(formattedRevision);
this.moduleImports = imports;
this.submoduleIncludes = includes;
this.dependencies = ImmutableSet. builder()
.addAll(moduleImports)
.addAll(submoduleIncludes)
.build();
}
/**
* Returns immutable collection of all module imports.
*
* This collection contains both import
statements
* and include
statements for submodules.
*
* @return Immutable collection of imports.
*/
public ImmutableSet getDependencies() {
return dependencies;
}
/**
* Returns model name
*
* @return model name
*/
public String getName() {
return name;
}
/**
* Returns formatted revision string
*
* @return formatted revision string
*/
public String getFormattedRevision() {
return formattedRevision;
}
/**
* Returns revision
*
* @return revision
*/
Date getRevision() {
return revision;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Objects.hashCode(formattedRevision);
result = prime * result + Objects.hashCode(name);
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof YangModelDependencyInfo)) {
return false;
}
YangModelDependencyInfo other = (YangModelDependencyInfo) obj;
if (formattedRevision == null) {
if (other.formattedRevision != null) {
return false;
}
} else if (!formattedRevision.equals(other.formattedRevision)) {
return false;
}
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
}
/**
* Extracts {@link YangModelDependencyInfo} from an abstract syntax tree
* of a YANG model.
*
* @param tree Abstract syntax tree
* @return {@link YangModelDependencyInfo}
* @throws YangSyntaxErrorException
* If the AST is not a valid YANG module/submodule
*/
public static YangModelDependencyInfo fromAST(final String name, final ParserRuleContext tree) throws YangSyntaxErrorException {
final Optional moduleCtx = getFirstContext(tree, Module_stmtContext.class);
if (moduleCtx.isPresent()) {
return parseModuleContext(moduleCtx.get());
}
final Optional submoduleCtx = getFirstContext(tree, Submodule_stmtContext.class);
if (submoduleCtx.isPresent()) {
return parseSubmoduleContext(submoduleCtx.get());
}
throw new YangSyntaxErrorException(name, 0, 0, "Unknown YANG text type");
}
/**
* Extracts {@link YangModelDependencyInfo} from input stream
* containing YANG model.
*
* This parsing does not validate full YANG module, only
* parses header up to the revisions and imports.
*
* @param yangStream
* Opened Input stream containing text source of YANG model
* @return {@link YangModelDependencyInfo}
* @throws IllegalArgumentException
* If input stream is not valid YANG stream
*/
public static YangModelDependencyInfo fromInputStream(final InputStream yangStream) {
YangContext yangContext = YangParserImpl.parseStreamWithoutErrorListeners(yangStream);
Optional moduleCtx = getFirstContext(yangContext, Module_stmtContext.class);
if (moduleCtx.isPresent()) {
return parseModuleContext(moduleCtx.get());
}
Optional submoduleCtx = getFirstContext(yangContext, Submodule_stmtContext.class);
if (submoduleCtx.isPresent()) {
return parseSubmoduleContext(submoduleCtx.get());
}
throw new IllegalArgumentException("Supplied stream is not valid yang file.");
}
private static YangModelDependencyInfo parseModuleContext(final Module_stmtContext module) {
String name = getArgumentString(module);
String latestRevision = getLatestRevision(module.revision_stmts());
ImmutableSet imports = parseImports(module.linkage_stmts().import_stmt());
ImmutableSet includes = parseIncludes(module.linkage_stmts().include_stmt());
return new ModuleDependencyInfo(name, latestRevision, imports, includes);
}
private static ImmutableSet parseImports(final List importStatements) {
ImmutableSet.Builder builder = ImmutableSet.builder();
for (Import_stmtContext importStmt : importStatements) {
String moduleName = getArgumentString(importStmt);
Date revision = getRevision(importStmt.revision_date_stmt());
builder.add(new ModuleImportImpl(moduleName, revision));
}
return builder.build();
}
public static String getLatestRevision(final Revision_stmtsContext revisionStmts) {
List revisions = revisionStmts.getRuleContexts(Revision_stmtContext.class);
String latestRevision = null;
for (Revision_stmtContext revisionStmt : revisions) {
String currentRevision = getArgumentString(revisionStmt);
if (latestRevision == null || latestRevision.compareTo(currentRevision) == -1) {
latestRevision = currentRevision;
}
}
return latestRevision;
}
private static YangModelDependencyInfo parseSubmoduleContext(final Submodule_stmtContext submodule) {
String name = getArgumentString(submodule);
Belongs_to_stmtContext belongsToStmt = submodule.submodule_header_stmts().belongs_to_stmt(0);
String belongsTo = getArgumentString(belongsToStmt);
String latestRevision = getLatestRevision(submodule.revision_stmts());
ImmutableSet imports = parseImports(submodule.linkage_stmts().import_stmt());
ImmutableSet includes = parseIncludes(submodule.linkage_stmts().include_stmt());
return new SubmoduleDependencyInfo(name, latestRevision, belongsTo, imports, includes);
}
private static ImmutableSet parseIncludes(final List importStatements) {
ImmutableSet.Builder builder = ImmutableSet.builder();
for (Include_stmtContext importStmt : importStatements) {
String moduleName = getArgumentString(importStmt);
Date revision = getRevision(importStmt.revision_date_stmt());
builder.add(new ModuleImportImpl(moduleName, revision));
}
return builder.build();
}
private static Date getRevision(final Revision_date_stmtContext revisionDateStmt) {
if (revisionDateStmt == null) {
return null;
}
String formatedDate = getArgumentString(revisionDateStmt);
return QName.parseRevision(formatedDate);
}
/**
*
* Dependency information for YANG module.
*
*/
public static final class ModuleDependencyInfo extends YangModelDependencyInfo {
private ModuleDependencyInfo(final String name, final String latestRevision,
final ImmutableSet imports, final ImmutableSet includes) {
super(name, latestRevision, imports, includes);
}
@Override
public String toString() {
return "Module [name=" + getName() + ", revision=" + getRevision() + ", dependencies=" + getDependencies()
+ "]";
}
}
/**
*
* Dependency information for submodule, also provides name
* for parent module.
*
*/
public static final class SubmoduleDependencyInfo extends YangModelDependencyInfo {
private final String belongsTo;
private SubmoduleDependencyInfo(final String name, final String latestRevision, final String belongsTo,
final ImmutableSet imports, final ImmutableSet includes) {
super(name, latestRevision, imports, includes);
this.belongsTo = belongsTo;
}
/**
* Returns name of parent module.
*
*/
public String getParentModule() {
return belongsTo;
}
@Override
public String toString() {
return "Submodule [name=" + getName() + ", revision=" + getRevision() + ", dependencies="
+ getDependencies() + "]";
}
}
/**
* Utility implementation of {@link ModuleImport} to be used by
* {@link YangModelDependencyInfo}.
*
*/
private static final class ModuleImportImpl implements ModuleImport {
private final Date revision;
private final String name;
public ModuleImportImpl(final String moduleName, final Date revision) {
this.name = moduleName;
this.revision = revision;
}
@Override
public String getModuleName() {
return this.name;
}
@Override
public Date getRevision() {
return this.revision;
}
@Override
public String getPrefix() {
return null;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Objects.hashCode(name);
result = prime * result + Objects.hashCode(revision);
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ModuleImportImpl other = (ModuleImportImpl) obj;
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
if (revision == null) {
if (other.revision != null) {
return false;
}
} else if (!revision.equals(other.revision)) {
return false;
}
return true;
}
@Override
public String toString() {
return "ModuleImportImpl [name=" + name + ", revision=" + QName.formattedRevision(revision) + "]";
}
}
}