/**
* Copyright (C) 2006-2017 INRIA and contributors
* Spoon - http://spoon.gforge.inria.fr/
*
* This software is governed by the CeCILL-C License under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL-C license as
* circulated by CEA, CNRS and INRIA at http://www.cecill.info.
*
* This program 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 CeCILL-C License for more details.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C license and that you accept its terms.
*/
package spoon.reflect.visitor.printer;
import spoon.compiler.Environment;
import spoon.reflect.code.CtBlock;
import spoon.reflect.code.CtComment;
import spoon.reflect.code.CtFor;
import spoon.reflect.code.CtForEach;
import spoon.reflect.code.CtIf;
import spoon.reflect.code.CtStatement;
import spoon.reflect.code.CtSwitch;
import spoon.reflect.code.CtSynchronized;
import spoon.reflect.code.CtTry;
import spoon.reflect.code.CtTypeAccess;
import spoon.reflect.code.CtWhile;
import spoon.reflect.cu.CompilationUnit;
import spoon.reflect.declaration.CtAnnotation;
import spoon.reflect.declaration.CtClass;
import spoon.reflect.declaration.CtConstructor;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtExecutable;
import spoon.reflect.declaration.CtFormalTypeDeclarer;
import spoon.reflect.declaration.CtModifiable;
import spoon.reflect.declaration.CtNamedElement;
import spoon.reflect.declaration.CtParameter;
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.CtTypeMember;
import spoon.reflect.declaration.CtTypeParameter;
import spoon.reflect.declaration.ModifierKind;
import spoon.reflect.factory.Factory;
import spoon.reflect.reference.CtActualTypeContainer;
import spoon.reflect.reference.CtExecutableReference;
import spoon.reflect.reference.CtFieldReference;
import spoon.reflect.reference.CtReference;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.DefaultJavaPrettyPrinter;
import spoon.reflect.visitor.PrintingContext.Writable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class ElementPrinterHelper {
private final DefaultJavaPrettyPrinter prettyPrinter;
private final Environment env;
private PrinterHelper printer;
public ElementPrinterHelper(PrinterHelper printerHelper, DefaultJavaPrettyPrinter prettyPrinter, Environment env) {
this.printer = printerHelper;
this.prettyPrinter = prettyPrinter;
this.env = env;
}
public void setPrinter(PrinterHelper printer) {
this.printer = printer;
}
/**
* Writes the annotations for the given element.
*/
public void writeAnnotations(CtElement element) {
for (CtAnnotation<?> annotation : element.getAnnotations()) {
prettyPrinter.scan(annotation);
}
}
public void writeModifiers(CtModifiable modifiable) {
for (ModifierKind modifierKind : modifiable.getModifiers()) {
printer.write(modifierKind.toString() + " ");
}
}
public void visitCtNamedElement(CtNamedElement namedElement, CompilationUnit sourceCompilationUnit) {
writeAnnotations(namedElement);
if (env.isPreserveLineNumbers()) {
printer.adjustPosition(namedElement, sourceCompilationUnit);
}
}
public void writeExtendsClause(CtType<?> type) {
if (type.getSuperclass() != null) {
printer.write(" extends ");
prettyPrinter.scan(type.getSuperclass());
}
}
public void writeImplementsClause(CtType<?> type) {
if (type.getSuperInterfaces().size() > 0) {
printer.write(" implements ");
for (CtTypeReference<?> ref : type.getSuperInterfaces()) {
prettyPrinter.scan(ref);
printer.write(" , ");
}
printer.removeLastChar();
}
}
public void writeExecutableParameters(CtExecutable<?> executable) {
printer.write("(");
if (executable.getParameters().size() > 0) {
for (CtParameter<?> p : executable.getParameters()) {
prettyPrinter.scan(p);
printer.write(", ");
}
printer.removeLastChar();
}
printer.write(")");
}
public void writeThrowsClause(CtExecutable<?> executable) {
if (executable.getThrownTypes().size() > 0) {
printer.write(" throws ");
for (CtTypeReference<?> ref : executable.getThrownTypes()) {
prettyPrinter.scan(ref);
printer.write(", ");
}
printer.removeLastChar();
}
}
/**
* Writes a statement.
*/
public void writeStatement(CtStatement statement) {
prettyPrinter.scan(statement);
if (!(statement instanceof CtBlock || statement instanceof CtIf || statement instanceof CtFor || statement instanceof CtForEach || statement instanceof CtWhile || statement instanceof CtTry
|| statement instanceof CtSwitch || statement instanceof CtSynchronized || statement instanceof CtClass || statement instanceof CtComment)) {
printer.write(";");
}
writeComment(statement, CommentOffset.AFTER);
}
public void writeElementList(List<CtTypeMember> elements) {
for (CtTypeMember element : elements) {
if (element instanceof CtConstructor && element.isImplicit()) {
continue;
}
printer.writeln().writeTabs();
prettyPrinter.scan(element);
if (!env.isPreserveLineNumbers()) {
printer.writeln();
}
}
}
/**
* Writes an annotation element.
*/
public void writeAnnotationElement(Factory factory, Object value) {
if (value instanceof CtTypeAccess) {
prettyPrinter.scan((CtTypeAccess) value);
printer.write(".class");
} else if (value instanceof CtFieldReference) {
prettyPrinter.scan(((CtFieldReference<?>) value).getDeclaringType());
printer.write("." + ((CtFieldReference<?>) value).getSimpleName());
} else if (value instanceof CtElement) {
prettyPrinter.scan((CtElement) value);
} else if (value instanceof String) {
printer.write("\"" + value.toString() + "\"");
} else if (value instanceof Collection) {
printer.write("{");
if (!((Collection<?>) value).isEmpty()) {
for (Object obj : (Collection<?>) value) {
writeAnnotationElement(factory, obj);
printer.write(" ,");
}
printer.removeLastChar();
}
printer.write("}");
} else if (value instanceof Object[]) {
printer.write("{");
if (((Object[]) value).length > 0) {
for (Object obj : (Object[]) value) {
writeAnnotationElement(factory, obj);
printer.write(" ,");
}
printer.removeLastChar();
}
printer.write("}");
} else if (value instanceof Enum) {
try (Writable c = prettyPrinter.getContext().modify().ignoreGenerics(true)) {
prettyPrinter.scan(factory.Type().createReference(((Enum<?>) value).getDeclaringClass()));
}
printer.write(".");
printer.write(value.toString());
} else {
printer.write(value.toString());
}
}
/**
* Writes formal type parameters given in parameter.
*
* @param ctFormalTypeDeclarer
* Reference with formal type arguments.
*/
public void writeFormalTypeParameters(CtFormalTypeDeclarer ctFormalTypeDeclarer) {
final Collection<CtTypeParameter> parameters = ctFormalTypeDeclarer.getFormalCtTypeParameters();
if (parameters == null) {
return;
}
if (parameters.size() > 0) {
printer.write('<');
for (CtTypeParameter parameter : parameters) {
prettyPrinter.scan(parameter);
printer.write(", ");
}
printer.removeLastChar();
printer.write('>');
}
}
/**
* Writes actual type arguments in a {@link CtActualTypeContainer} element.
*
* @param ctGenericElementReference
* Reference with actual type arguments.
*/
public void writeActualTypeArguments(CtActualTypeContainer ctGenericElementReference) {
final Collection<CtTypeReference<?>> arguments = ctGenericElementReference.getActualTypeArguments();
if (arguments != null && arguments.size() > 0) {
printer.write("<");
boolean isImplicitTypeReference = true;
for (CtTypeReference<?> argument : arguments) {
if (!argument.isImplicit()) {
isImplicitTypeReference = false;
prettyPrinter.scan(argument);
printer.write(", ");
}
}
if (!isImplicitTypeReference) {
printer.removeLastChar();
}
printer.write(">");
}
}
/**
* Write the compilation unit header.
*/
public void writeHeader(List<CtType<?>> types, Collection<CtReference> imports) {
if (!types.isEmpty()) {
for (CtType<?> ctType : types) {
writeComment(ctType, CommentOffset.TOP_FILE);
printer.writeln().writeln().writeTabs();
}
// writing the header package
if (!types.get(0).getPackage().isUnnamedPackage()) {
printer.write("package " + types.get(0).getPackage().getQualifiedName() + ";");
}
printer.writeln().writeln().writeTabs();
for (CtReference ref : imports) {
String importStr = "import";
String importTypeStr = "";
if (ref instanceof CtTypeReference) {
CtTypeReference typeRef = (CtTypeReference) ref;
importTypeStr = typeRef.getQualifiedName();
} else if (ref instanceof CtExecutableReference) {
importStr += " static";
CtExecutableReference execRef = (CtExecutableReference) ref;
if (execRef.getDeclaringType() != null) {
importTypeStr = this.removeInnerTypeSeparator(execRef.getDeclaringType().getQualifiedName()) + "." + execRef.getSimpleName();
}
} else if (ref instanceof CtFieldReference) {
importStr += " static";
CtFieldReference fieldRef = (CtFieldReference) ref;
importTypeStr = this.removeInnerTypeSeparator(fieldRef.getDeclaringType().getQualifiedName()) + "." + fieldRef.getSimpleName();
}
if (!importTypeStr.equals("")) {
printer.write(importStr + " " + importTypeStr + ";").writeln().writeTabs();
}
}
printer.writeln().writeTabs();
}
}
private String removeInnerTypeSeparator(String fqn) {
return fqn.replace(CtType.INNERTTYPE_SEPARATOR, ".");
}
public void writeComment(CtComment comment) {
if (!env.isCommentsEnabled() || comment == null) {
return;
}
prettyPrinter.scan(comment);
printer.writeln().writeTabs();
}
private void writeComment(List<CtComment> comments) {
if (!env.isCommentsEnabled() || comments == null) {
return;
}
for (CtComment comment : comments) {
writeComment(comment);
}
}
public void writeComment(CtElement element) {
if (element == null) {
return;
}
writeComment(element.getComments());
}
public void writeComment(CtElement element, CommentOffset offset) {
writeComment(getComments(element, offset));
}
public List<CtComment> getComments(CtElement element, CommentOffset offset) {
List<CtComment> commentsToPrint = new ArrayList<>();
if (!env.isCommentsEnabled() || element == null) {
return commentsToPrint;
}
for (CtComment comment : element.getComments()) {
if (comment.getCommentType() == CtComment.CommentType.FILE && offset == CommentOffset.TOP_FILE) {
commentsToPrint.add(comment);
continue;
}
if (comment.getCommentType() == CtComment.CommentType.FILE) {
continue;
}
if (comment.getPosition() == null || element.getPosition() == null) {
if (offset == CommentOffset.BEFORE) {
commentsToPrint.add(comment);
}
continue;
}
final int line = element.getPosition().getLine();
final int sourceEnd = element.getPosition().getSourceEnd();
final int sourceStart = element.getPosition().getSourceStart();
if (offset == CommentOffset.BEFORE && (comment.getPosition().getLine() < line || (sourceStart <= comment.getPosition().getSourceStart() && sourceEnd >= comment.getPosition().getSourceEnd()))) {
commentsToPrint.add(comment);
} else if (offset == CommentOffset.AFTER && comment.getPosition().getSourceStart() > sourceEnd) {
commentsToPrint.add(comment);
} else {
final int endLine = element.getPosition().getEndLine();
if (offset == CommentOffset.INSIDE && comment.getPosition().getLine() >= line && comment.getPosition().getEndLine() <= endLine) {
commentsToPrint.add(comment);
}
}
}
return commentsToPrint;
}
public void writeIfOrLoopBlock(CtStatement block) {
if (block != null) {
if (!block.isImplicit() && (block instanceof CtBlock || block instanceof CtIf)) {
printer.write(" ");
}
if (!(block instanceof CtBlock) && !(block instanceof CtIf)) {
printer.incTab();
printer.writeln().writeTabs();
}
writeStatement(block);
if (!(block instanceof CtBlock) && !(block instanceof CtIf)) {
printer.decTab().writeln().writeTabs();
}
if (!block.isImplicit()) {
if (!block.isParentInitialized() || (!(block.getParent() instanceof CtFor) && !(block.getParent() instanceof CtForEach) && !(block.getParent() instanceof CtIf))) {
printer.write(" ");
}
}
} else {
printer.write(";");
}
}
}