/*
Copyright (C) 2013 Raquel Pau and Albert Coroleu.
Walkmod is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Walkmod is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Walkmod. If not, see <http://www.gnu.org/licenses/>.*/
package org.walkmod.javalang.ast;
import java.io.File;
import java.net.URI;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import org.walkmod.javalang.ast.body.AnnotationDeclaration;
import org.walkmod.javalang.ast.body.ClassOrInterfaceDeclaration;
import org.walkmod.javalang.ast.body.EmptyTypeDeclaration;
import org.walkmod.javalang.ast.body.EnumDeclaration;
import org.walkmod.javalang.ast.body.JavadocComment;
import org.walkmod.javalang.ast.body.ModifierSet;
import org.walkmod.javalang.ast.body.TypeDeclaration;
import org.walkmod.javalang.ast.expr.NameExpr;
import org.walkmod.javalang.comparators.CompilationUnitComparator;
import org.walkmod.javalang.visitors.GenericVisitor;
import org.walkmod.javalang.visitors.VoidVisitor;
import org.walkmod.merger.MergeEngine;
import org.walkmod.merger.Mergeable;
/**
* <p>
* This class represents the entire compilation unit. Each java file denotes a compilation unit.
* </p>
* The CompilationUnit is constructed following the syntax:<br>
* <code>
* <table>
* <tr valign=baseline>
* <td align=right>CompilationUnit</td>
* <td align=center>::=</td>
* <td align=left>
* ( {@link PackageDeclaration} )?<br>
* ( {@link ImportDeclaration} )*<br>
* ( {@link TypeDeclaration} )*<br>
* </td>
* </tr>
* </table>
* </code>
*
* @author Julio Vilmar Gesser
*/
public final class CompilationUnit extends Node implements Mergeable<CompilationUnit> {
private PackageDeclaration pakage;
private List<ImportDeclaration> imports;
private List<TypeDeclaration> types;
private List<Comment> comments;
private URI uri;
public CompilationUnit() {
}
public CompilationUnit(PackageDeclaration pakage, List<ImportDeclaration> imports, List<TypeDeclaration> types,
List<Comment> comments) {
setPackage(pakage);
setImports(imports);
setTypes(types);
setComments(comments);
}
public CompilationUnit(int beginLine, int beginColumn, int endLine, int endColumn, PackageDeclaration pakage,
List<ImportDeclaration> imports, List<TypeDeclaration> types, List<Comment> comments) {
super(beginLine, beginColumn, endLine, endColumn);
setPackage(pakage);
setImports(imports);
setTypes(types);
setComments(comments);
}
public void setURI(URI uri) {
this.uri = uri;
}
public URI getURI() {
return uri;
}
@Override
public List<Node> getChildren() {
List<Node> aux = new LinkedList<Node>();
if (pakage != null) {
aux.add(pakage);
}
if (imports != null) {
aux.addAll(imports);
}
if (types != null) {
aux.addAll(types);
}
if (comments != null) {
aux.addAll(comments);
}
return aux;
}
@Override
public boolean removeChild(Node child) {
boolean result = false;
if (child != null) {
if (child instanceof ImportDeclaration) {
if (imports != null) {
List<ImportDeclaration> auxImports = new LinkedList<ImportDeclaration>(imports);
result = auxImports.remove(child);
this.imports = auxImports;
}
} else if (child instanceof TypeDeclaration) {
if (types != null) {
List<TypeDeclaration> typesAux = new LinkedList<TypeDeclaration>(types);
result = typesAux.remove(child);
types = typesAux;
}
} else if (child instanceof Comment) {
if (comments != null) {
List<Comment> commentAux = new LinkedList<Comment>(comments);
result = commentAux.remove(child);
comments = commentAux;
}
} else if (child == pakage) {
pakage = null;
result = true;
}
}
if (result) {
updateReferences(result);
}
return result;
}
@Override
public <R, A> R accept(GenericVisitor<R, A> v, A arg) {
if (!check()) {
return null;
}
return v.visit(this, arg);
}
@Override
public <A> void accept(VoidVisitor<A> v, A arg) {
if (check()) {
v.visit(this, arg);
}
}
/**
* Return a list containing all comments declared in this compilation unit. Including javadocs,
* line comments and block comments of all types, inner-classes and other members.<br>
* If there is no comment, <code>null</code> is returned.
*
* @return list with all comments of this compilation unit or <code>null</code>
* @see JavadocComment
* @see LineComment
* @see BlockComment
*/
public List<Comment> getComments() {
return comments;
}
/**
* Retrieves the list of imports declared in this compilation unit or <code>null</code> if there
* is no import.
*
* @return the list of imports or <code>null</code> if there is no import
*/
public List<ImportDeclaration> getImports() {
return imports;
}
/**
* Retrieves the package declaration of this compilation unit.<br>
* If this compilation unit has no package declaration (default package), <code>null</code> is
* returned.
*
* @return the package declaration or <code>null</code>
*/
public PackageDeclaration getPackage() {
return pakage;
}
/**
* Return the list of types declared in this compilation unit.<br>
* If there is no types declared, <code>null</code> is returned.
*
* @return the list of types or <code>null</code> null if there is no type
* @see AnnotationDeclaration
* @see ClassOrInterfaceDeclaration
* @see EmptyTypeDeclaration
* @see EnumDeclaration
*/
public List<TypeDeclaration> getTypes() {
return types;
}
/**
* Sets the list of comments of this compilation unit.
*
* @param comments
* the list of comments
*/
public void setComments(List<Comment> comments) {
this.comments = comments;
}
/**
* Sets the list of imports of this compilation unit. The list is initially <code>null</code>.
*
* @param imports
* the list of imports
*/
public void setImports(List<ImportDeclaration> imports) {
this.imports = imports;
setAsParentNodeOf(imports);
}
/**
* Sets or clear the package declarations of this compilation unit.
*
* @param pakage
* the pakage declaration to set or <code>null</code> to default package
*/
public void setPackage(PackageDeclaration pakage) {
if (this.pakage != null) {
updateReferences(this.pakage);
}
this.pakage = pakage;
setAsParentNodeOf(pakage);
}
/**
* Sets the list of types declared in this compilation unit.
*
* @param types
* the lis of types
*/
public void setTypes(List<TypeDeclaration> types) {
this.types = types;
setAsParentNodeOf(types);
}
@Override
public Comparator<?> getIdentityComparator() {
return new CompilationUnitComparator();
}
@Override
public void merge(CompilationUnit remoteCU, MergeEngine configuration) {
List<ImportDeclaration> resultImports = new LinkedList<ImportDeclaration>();
configuration.apply(getImports(), remoteCU.getImports(), resultImports, ImportDeclaration.class);
setImports(resultImports);
List<TypeDeclaration> resultTypes = new LinkedList<TypeDeclaration>();
configuration.apply(getTypes(), remoteCU.getTypes(), resultTypes, TypeDeclaration.class);
setTypes(resultTypes);
List<Comment> resultComments = new LinkedList<Comment>();
configuration.apply(getComments(), remoteCU.getComments(), resultComments, Comment.class);
setComments(resultComments);
}
public boolean replaceChildNode(Node oldChild, Node newChild) {
boolean updated = false;
if (pakage == oldChild) {
setPackage((PackageDeclaration) newChild);
updated = true;
}
updated = replaceChildNodeInList(oldChild, newChild, imports);
if (!updated) {
updated = replaceChildNodeInList(oldChild, newChild, types);
if (!updated) {
updated = replaceChildNodeInList(oldChild, newChild, comments);
}
}
return updated;
}
public boolean hasEqualFileName(CompilationUnit other) {
boolean samePackage = getPackage() == null && other.getPackage() == null;
samePackage = samePackage
|| (getPackage() != null && other.getPackage() != null && getPackage().equals(other.getPackage()));
boolean sameType = getTypes() == null && other.getTypes() == null;
sameType = sameType || (getTypes() != null && other.getTypes() != null
&& getTypes().get(0).getName().equals(other.getTypes().get(0).getName()));
return (samePackage && sameType);
}
public String getQualifiedName() {
String name = "";
if (getPackage() != null) {
name = getPackage().getName().toString();
}
if (getTypes() != null && !getTypes().isEmpty()) {
if (getPackage() != null) {
name = name + ".";
}
name = name + getTypes().get(0).getName();
}
return name;
}
public String getFileName() {
String path = "";
if (getPackage() != null) {
NameExpr packageName = getPackage().getName();
String packPath = packageName.toString().replace('.', File.separatorChar);
path = path + packPath + File.separator + getSimpleName() + ".java";
} else {
path = path + getSimpleName() + ".java";
}
return path;
}
public String getSimpleName() {
String name = "";
if (getTypes() != null) {
name = getTypes().get(0).getName();
}
return name;
}
@Override
public CompilationUnit clone() throws CloneNotSupportedException {
return new CompilationUnit(clone(pakage), clone(imports), clone(types), clone(comments));
}
}