/**
* 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;
import spoon.compiler.Environment;
import spoon.reflect.code.CtAnnotationFieldAccess;
import spoon.reflect.code.CtArrayAccess;
import spoon.reflect.code.CtArrayRead;
import spoon.reflect.code.CtArrayWrite;
import spoon.reflect.code.CtAssert;
import spoon.reflect.code.CtAssignment;
import spoon.reflect.code.CtBinaryOperator;
import spoon.reflect.code.CtBlock;
import spoon.reflect.code.CtBreak;
import spoon.reflect.code.CtCase;
import spoon.reflect.code.CtCatch;
import spoon.reflect.code.CtCatchVariable;
import spoon.reflect.code.CtCodeElement;
import spoon.reflect.code.CtCodeSnippetExpression;
import spoon.reflect.code.CtCodeSnippetStatement;
import spoon.reflect.code.CtComment;
import spoon.reflect.code.CtConditional;
import spoon.reflect.code.CtConstructorCall;
import spoon.reflect.code.CtContinue;
import spoon.reflect.code.CtDo;
import spoon.reflect.code.CtExecutableReferenceExpression;
import spoon.reflect.code.CtExpression;
import spoon.reflect.code.CtFieldAccess;
import spoon.reflect.code.CtFieldRead;
import spoon.reflect.code.CtFieldWrite;
import spoon.reflect.code.CtFor;
import spoon.reflect.code.CtForEach;
import spoon.reflect.code.CtIf;
import spoon.reflect.code.CtInvocation;
import spoon.reflect.code.CtLambda;
import spoon.reflect.code.CtLiteral;
import spoon.reflect.code.CtLocalVariable;
import spoon.reflect.code.CtNewArray;
import spoon.reflect.code.CtNewClass;
import spoon.reflect.code.CtOperatorAssignment;
import spoon.reflect.code.CtReturn;
import spoon.reflect.code.CtStatement;
import spoon.reflect.code.CtStatementList;
import spoon.reflect.code.CtSuperAccess;
import spoon.reflect.code.CtSwitch;
import spoon.reflect.code.CtSynchronized;
import spoon.reflect.code.CtTargetedExpression;
import spoon.reflect.code.CtThisAccess;
import spoon.reflect.code.CtThrow;
import spoon.reflect.code.CtTry;
import spoon.reflect.code.CtTryWithResource;
import spoon.reflect.code.CtTypeAccess;
import spoon.reflect.code.CtUnaryOperator;
import spoon.reflect.code.CtVariableRead;
import spoon.reflect.code.CtVariableWrite;
import spoon.reflect.code.CtWhile;
import spoon.reflect.cu.CompilationUnit;
import spoon.reflect.cu.SourcePosition;
import spoon.reflect.declaration.CtAnnotation;
import spoon.reflect.declaration.CtAnnotationMethod;
import spoon.reflect.declaration.CtAnnotationType;
import spoon.reflect.declaration.CtAnonymousExecutable;
import spoon.reflect.declaration.CtClass;
import spoon.reflect.declaration.CtConstructor;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtEnum;
import spoon.reflect.declaration.CtEnumValue;
import spoon.reflect.declaration.CtField;
import spoon.reflect.declaration.CtInterface;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtNamedElement;
import spoon.reflect.declaration.CtPackage;
import spoon.reflect.declaration.CtParameter;
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.CtTypeParameter;
import spoon.reflect.declaration.CtVariable;
import spoon.reflect.declaration.ModifierKind;
import spoon.reflect.declaration.ParentNotInitializedException;
import spoon.reflect.reference.CtArrayTypeReference;
import spoon.reflect.reference.CtCatchVariableReference;
import spoon.reflect.reference.CtExecutableReference;
import spoon.reflect.reference.CtFieldReference;
import spoon.reflect.reference.CtIntersectionTypeReference;
import spoon.reflect.reference.CtLocalVariableReference;
import spoon.reflect.reference.CtPackageReference;
import spoon.reflect.reference.CtParameterReference;
import spoon.reflect.reference.CtReference;
import spoon.reflect.reference.CtTypeParameterReference;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.reference.CtUnboundVariableReference;
import spoon.reflect.reference.CtWildcardReference;
import spoon.reflect.visitor.PrintingContext.Writable;
import spoon.reflect.visitor.filter.PotentialVariableDeclarationFunction;
import spoon.reflect.visitor.printer.CommentOffset;
import spoon.reflect.visitor.printer.ElementPrinterHelper;
import spoon.reflect.visitor.printer.PrinterHelper;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
/**
* A visitor for generating Java code from the program compile-time model.
*/
public class DefaultJavaPrettyPrinter implements CtVisitor, PrettyPrinter {
/**
* Java file extension (.java).
*/
public static final String JAVA_FILE_EXTENSION = ".java";
/**
* Package declaration file name.
*/
public static final String JAVA_PACKAGE_DECLARATION = "package-info" + JAVA_FILE_EXTENSION;
/**
* Line separator which is used by the system
*/
public static final String LINE_SEPARATOR = System.getProperty("line.separator");
/**
* The printing context.
*/
public PrintingContext context = new PrintingContext();
/**
* Handle imports of classes.
*/
private ImportScanner importsContext;
/**
* Environment which Spoon is executed.
*/
private Environment env;
/**
* Printer helper.
*/
private PrinterHelper printer;
/**
* Element printer helper.
*/
private ElementPrinterHelper elementPrinterHelper;
/**
* Compilation unit we are printing.
*/
private CompilationUnit sourceCompilationUnit;
/**
* Creates a new code generator visitor.
*/
public DefaultJavaPrettyPrinter(Environment env) {
this.env = env;
printer = new PrinterHelper(env);
elementPrinterHelper = new ElementPrinterHelper(printer, this, env);
if (env.isAutoImports()) {
this.importsContext = new ImportScannerImpl();
} else {
this.importsContext = new MinimalImportScanner();
}
}
/**
* Enters an expression.
*/
protected void enterCtExpression(CtExpression<?> e) {
if (!(e instanceof CtStatement)) {
elementPrinterHelper.writeComment(e, CommentOffset.BEFORE);
}
printer.mapLine(e, sourceCompilationUnit);
if (shouldSetBracket(e)) {
context.parenthesedExpression.push(e);
printer.write("(");
}
if (!e.getTypeCasts().isEmpty()) {
for (CtTypeReference<?> r : e.getTypeCasts()) {
printer.write("(");
DefaultJavaPrettyPrinter.this.scan(r);
printer.write(") ");
printer.write("(");
context.parenthesedExpression.push(e);
}
}
}
/**
* Enters a statement.
*/
protected void enterCtStatement(CtStatement s) {
elementPrinterHelper.writeComment(s, CommentOffset.BEFORE);
printer.mapLine(s, sourceCompilationUnit);
elementPrinterHelper.writeAnnotations(s);
if (s.getLabel() != null) {
printer.write(s.getLabel()).write(" : ");
}
}
/**
* Exits an expression.
*/
protected void exitCtExpression(CtExpression<?> e) {
while ((context.parenthesedExpression.size() > 0) && e == context.parenthesedExpression.peek()) {
context.parenthesedExpression.pop();
printer.write(")");
}
if (!(e instanceof CtStatement)) {
elementPrinterHelper.writeComment(e, CommentOffset.AFTER);
}
}
/**
* Make the imports for a given type.
*/
public Collection<CtReference> computeImports(CtType<?> type) {
context.currentTopLevel = type;
return importsContext.computeAllImports(context.currentTopLevel);
}
/**
* Make the imports for all elements.
*/
public void computeImports(CtElement element) {
if (env.isAutoImports()) {
importsContext.computeImports(element);
}
}
/**
* The generic scan method for an element.
*/
public DefaultJavaPrettyPrinter scan(CtElement e) {
if (e != null) {
context.elementStack.push(e);
if (env.isPreserveLineNumbers()) {
if (!(e instanceof CtNamedElement)) {
printer.adjustPosition(e, sourceCompilationUnit);
}
}
e.accept(this);
context.elementStack.pop();
}
return this;
}
/**
* The generic scan method for a reference.
*/
public DefaultJavaPrettyPrinter scan(CtReference ref) {
if (ref != null) {
ref.accept(this);
}
return this;
}
private boolean shouldSetBracket(CtExpression<?> e) {
if (e.getTypeCasts().size() != 0) {
return true;
}
try {
if ((e.getParent() instanceof CtBinaryOperator) || (e.getParent() instanceof CtUnaryOperator)) {
return (e instanceof CtTargetedExpression) || (e instanceof CtAssignment) || (e instanceof CtConditional) || (e instanceof CtUnaryOperator) || e instanceof CtBinaryOperator;
}
if (e.getParent() instanceof CtTargetedExpression) {
return (e instanceof CtBinaryOperator) || (e instanceof CtAssignment) || (e instanceof CtConditional) || (e instanceof CtUnaryOperator);
}
} catch (ParentNotInitializedException ex) {
// nothing we accept not to have a parent
}
return false;
}
/**
* Gets the currently pretty-printed string.
*/
@Override
public String toString() {
return printer.toString();
}
@Override
public <A extends Annotation> void visitCtAnnotation(CtAnnotation<A> annotation) {
elementPrinterHelper.writeAnnotations(annotation);
printer.write("@");
scan(annotation.getAnnotationType());
if (annotation.getValues().size() > 0) {
printer.write("(");
for (Entry<String, CtExpression> e : annotation.getValues().entrySet()) {
printer.write(e.getKey() + " = ");
elementPrinterHelper.writeAnnotationElement(annotation.getFactory(), e.getValue());
printer.write(", ");
}
printer.removeLastChar();
printer.write(")");
}
printer.writeln().writeTabs();
}
@Override
public <A extends Annotation> void visitCtAnnotationType(CtAnnotationType<A> annotationType) {
visitCtType(annotationType);
printer.write("@interface " + annotationType.getSimpleName() + " {").incTab();
elementPrinterHelper.writeElementList(annotationType.getTypeMembers());
printer.decTab().writeTabs().write("}");
}
@Override
public void visitCtAnonymousExecutable(CtAnonymousExecutable impl) {
elementPrinterHelper.writeComment(impl);
elementPrinterHelper.writeAnnotations(impl);
elementPrinterHelper.writeModifiers(impl);
scan(impl.getBody());
}
@Override
public <T> void visitCtArrayRead(CtArrayRead<T> arrayRead) {
printCtArrayAccess(arrayRead);
}
@Override
public <T> void visitCtArrayWrite(CtArrayWrite<T> arrayWrite) {
printCtArrayAccess(arrayWrite);
}
private <T, E extends CtExpression<?>> void printCtArrayAccess(CtArrayAccess<T, E> arrayAccess) {
enterCtExpression(arrayAccess);
scan(arrayAccess.getTarget());
printer.write("[");
scan(arrayAccess.getIndexExpression());
printer.write("]");
exitCtExpression(arrayAccess);
}
@Override
public <T> void visitCtArrayTypeReference(CtArrayTypeReference<T> reference) {
if (reference.isImplicit()) {
return;
}
scan(reference.getComponentType());
if (!context.skipArray()) {
printer.write("[]");
}
}
@Override
public <T> void visitCtAssert(CtAssert<T> asserted) {
enterCtStatement(asserted);
printer.write("assert ");
scan(asserted.getAssertExpression());
if (asserted.getExpression() != null) {
printer.write(" : ");
scan(asserted.getExpression());
}
}
@Override
public <T, A extends T> void visitCtAssignment(CtAssignment<T, A> assignement) {
enterCtStatement(assignement);
enterCtExpression(assignement);
scan(assignement.getAssigned());
printer.write(" = ");
scan(assignement.getAssignment());
exitCtExpression(assignement);
}
@Override
public <T> void visitCtBinaryOperator(CtBinaryOperator<T> operator) {
enterCtExpression(operator);
scan(operator.getLeftHandOperand());
printer.write(" ").writeOperator(operator.getKind()).write(" ");
scan(operator.getRightHandOperand());
exitCtExpression(operator);
}
@Override
public <R> void visitCtBlock(CtBlock<R> block) {
enterCtStatement(block);
if (!block.isImplicit()) {
printer.write("{");
}
printer.incTab();
for (CtStatement statement : block.getStatements()) {
if (!statement.isImplicit()) {
printer.writeln().writeTabs();
elementPrinterHelper.writeStatement(statement);
}
}
printer.decTab();
if (env.isPreserveLineNumbers()) {
if (!block.isImplicit()) {
printer.write("}");
}
} else {
printer.writeln().writeTabs();
if (!block.isImplicit()) {
printer.write("}");
}
}
}
@Override
public void visitCtBreak(CtBreak breakStatement) {
enterCtStatement(breakStatement);
printer.write("break");
if (breakStatement.getTargetLabel() != null) {
printer.write(" " + breakStatement.getTargetLabel());
}
}
@Override
@SuppressWarnings("rawtypes")
public <E> void visitCtCase(CtCase<E> caseStatement) {
enterCtStatement(caseStatement);
if (caseStatement.getCaseExpression() != null) {
printer.write("case ");
// writing enum case expression
if (caseStatement.getCaseExpression() instanceof CtFieldAccess) {
final CtFieldReference variable = ((CtFieldAccess) caseStatement.getCaseExpression()).getVariable();
// In noclasspath mode, we don't have always the type of the declaring type.
if (variable.getType() != null
&& variable.getDeclaringType() != null
&& variable.getType().getQualifiedName().equals(variable.getDeclaringType().getQualifiedName())) {
printer.write(variable.getSimpleName());
} else {
scan(caseStatement.getCaseExpression());
}
} else {
scan(caseStatement.getCaseExpression());
}
} else {
printer.write("default");
}
printer.write(" :").incTab();
for (CtStatement statement : caseStatement.getStatements()) {
printer.writeln().writeTabs();
elementPrinterHelper.writeStatement(statement);
}
printer.decTab();
}
@Override
public void visitCtCatch(CtCatch catchBlock) {
elementPrinterHelper.writeComment(catchBlock, CommentOffset.BEFORE);
printer.write(" catch (");
CtCatchVariable<? extends Throwable> parameter = catchBlock.getParameter();
if (parameter.getMultiTypes().size() > 0) {
for (int i = 0; i < parameter.getMultiTypes().size(); i++) {
CtTypeReference<?> type = parameter.getMultiTypes().get(i);
scan(type);
if (i < parameter.getMultiTypes().size() - 1) {
printer.write(" | ");
}
}
printer.write(" " + parameter.getSimpleName());
} else {
scan(parameter);
}
printer.write(") ");
scan(catchBlock.getBody());
}
@Override
public <T> void visitCtClass(CtClass<T> ctClass) {
context.pushCurrentThis(ctClass);
if (ctClass.getSimpleName() != null && !CtType.NAME_UNKNOWN.equals(ctClass.getSimpleName()) && !ctClass.isAnonymous()) {
visitCtType(ctClass);
if (ctClass.isLocalType()) {
printer.write("class " + ctClass.getSimpleName().replaceAll("^[0-9]*", ""));
} else {
printer.write("class " + ctClass.getSimpleName());
}
elementPrinterHelper.writeFormalTypeParameters(ctClass);
elementPrinterHelper.writeExtendsClause(ctClass);
elementPrinterHelper.writeImplementsClause(ctClass);
}
// lst.addAll(elementPrinterHelper.getComments(ctClass, CommentOffset.INSIDE));
printer.write(" {").incTab();
elementPrinterHelper.writeElementList(ctClass.getTypeMembers());
printer.decTab().writeTabs().write("}");
context.popCurrentThis();
}
@Override
public void visitCtTypeParameter(CtTypeParameter typeParameter) {
visitCtTypeParameterReference(typeParameter.getReference());
}
@Override
public <T> void visitCtConditional(CtConditional<T> conditional) {
enterCtExpression(conditional);
CtExpression<Boolean> condition = conditional.getCondition();
boolean parent;
try {
parent = conditional.getParent() instanceof CtAssignment || conditional.getParent() instanceof CtVariable;
} catch (ParentNotInitializedException ex) {
// nothing if we have no parent
parent = false;
}
if (parent) {
printer.write("(");
}
scan(condition);
if (parent) {
printer.write(")");
}
printer.write(" ? ");
CtExpression<T> thenExpression = conditional.getThenExpression();
scan(thenExpression);
printer.write(" : ");
CtExpression<T> elseExpression = conditional.getElseExpression();
boolean isAssign = false;
if ((isAssign = elseExpression instanceof CtAssignment)) {
printer.write("(");
}
scan(elseExpression);
if (isAssign) {
printer.write(")");
}
exitCtExpression(conditional);
}
@Override
public <T> void visitCtConstructor(CtConstructor<T> constructor) {
elementPrinterHelper.writeComment(constructor);
elementPrinterHelper.visitCtNamedElement(constructor, sourceCompilationUnit);
elementPrinterHelper.writeModifiers(constructor);
elementPrinterHelper.writeFormalTypeParameters(constructor);
if (constructor.getFormalCtTypeParameters().size() > 0) {
printer.write(' ');
}
if (constructor.getDeclaringType().isLocalType()) {
printer.write(constructor.getDeclaringType().getSimpleName().replaceAll("^[0-9]*", ""));
} else {
printer.write(constructor.getDeclaringType().getSimpleName());
}
elementPrinterHelper.writeExecutableParameters(constructor);
elementPrinterHelper.writeThrowsClause(constructor);
printer.write(" ");
scan(constructor.getBody());
}
@Override
public void visitCtContinue(CtContinue continueStatement) {
enterCtStatement(continueStatement);
printer.write("continue");
if (continueStatement.getTargetLabel() != null) {
printer.write(" " + continueStatement.getTargetLabel());
}
}
@Override
public void visitCtDo(CtDo doLoop) {
enterCtStatement(doLoop);
printer.write("do");
elementPrinterHelper.writeIfOrLoopBlock(doLoop.getBody());
printer.write("while (");
scan(doLoop.getLoopingExpression());
printer.write(" )");
}
@Override
public <T extends Enum<?>> void visitCtEnum(CtEnum<T> ctEnum) {
visitCtType(ctEnum);
printer.write("enum " + ctEnum.getSimpleName());
elementPrinterHelper.writeImplementsClause(ctEnum);
context.pushCurrentThis(ctEnum);
printer.write(" {").incTab().writeln();
if (ctEnum.getEnumValues().size() == 0) {
printer.writeTabs().write(";").writeln();
} else {
for (CtEnumValue<?> enumValue : ctEnum.getEnumValues()) {
scan(enumValue);
printer.write(", ");
}
printer.removeLastChar();
printer.write(";");
}
elementPrinterHelper.writeElementList(ctEnum.getTypeMembers());
printer.decTab().writeTabs().write("}");
context.popCurrentThis();
}
@Override
public <T> void visitCtExecutableReference(CtExecutableReference<T> reference) {
printer.write(reference.getSignature());
}
@Override
public <T> void visitCtField(CtField<T> f) {
elementPrinterHelper.writeComment(f);
elementPrinterHelper.visitCtNamedElement(f, sourceCompilationUnit);
elementPrinterHelper.writeModifiers(f);
scan(f.getType());
printer.write(" ");
printer.write(f.getSimpleName());
if (f.getDefaultExpression() != null) {
printer.write(" = ");
scan(f.getDefaultExpression());
}
printer.write(";");
}
@Override
public <T> void visitCtEnumValue(CtEnumValue<T> enumValue) {
elementPrinterHelper.visitCtNamedElement(enumValue, sourceCompilationUnit);
printer.write(enumValue.getSimpleName());
if (enumValue.getDefaultExpression() != null) {
CtConstructorCall<?> constructorCall = (CtConstructorCall<?>) enumValue.getDefaultExpression();
if (constructorCall.getArguments().size() > 0) {
printer.write("(");
boolean first = true;
for (CtExpression<?> ctexpr : constructorCall.getArguments()) {
if (first) {
first = false;
} else {
printer.write(",");
}
scan(ctexpr);
}
printer.write(")");
}
if (constructorCall instanceof CtNewClass) {
scan(((CtNewClass<?>) constructorCall).getAnonymousClass());
}
}
}
@Override
public <T> void visitCtFieldRead(CtFieldRead<T> fieldRead) {
printCtFieldAccess(fieldRead);
}
@Override
public <T> void visitCtFieldWrite(CtFieldWrite<T> fieldWrite) {
printCtFieldAccess(fieldWrite);
}
private <T> void printCtFieldAccess(CtFieldAccess<T> f) {
enterCtExpression(f);
try (Writable _context = context.modify()) {
if (f.getVariable().isStatic() && f.getTarget() instanceof CtTypeAccess) {
_context.ignoreGenerics(true);
}
CtExpression<?> target = f.getTarget();
if (target != null) {
boolean isInitializeStaticFinalField = isInitializeStaticFinalField(f.getTarget());
boolean isStaticField = f.getVariable().isStatic();
boolean isImportedField = importsContext.isImported(f.getVariable());
if (!isInitializeStaticFinalField && !(isStaticField && isImportedField)) {
if (target.isImplicit()) {
/*
* target is implicit, check whether there is no conflict with an local variable, catch variable or parameter
* in case of conflict make it explicit, otherwise the field access is shadowed by that variable.
* Search for potential variable declaration until we found a class which declares or inherits this field
*/
final CtField<?> field = f.getVariable().getFieldDeclaration();
final String fieldName = field.getSimpleName();
CtVariable<?> var = f.getVariable().map(new PotentialVariableDeclarationFunction(fieldName)).first();
if (var != field) {
//another variable declaration was found which is hiding the field declaration for this field access. Make the field access expicit
target.setImplicit(false);
}
}
printer.snapshotLength();
scan(target);
if (printer.hasNewContent()) {
printer.write(".");
}
}
_context.ignoreStaticAccess(true);
}
scan(f.getVariable());
}
exitCtExpression(f);
}
/**
* Check if the target expression is a static final field initialized in a static anonymous block.
*/
private <T> boolean isInitializeStaticFinalField(CtExpression<T> targetExp) {
final CtElement parent;
final CtAnonymousExecutable anonymousParent;
try {
parent = targetExp.getParent();
anonymousParent = targetExp.getParent(CtAnonymousExecutable.class);
} catch (ParentNotInitializedException e) {
return false;
}
if (parent instanceof CtFieldWrite
&& targetExp.equals(((CtFieldWrite) parent).getTarget())
&& anonymousParent != null
&& ((CtFieldWrite) parent).getVariable() != null
&& ((CtFieldWrite) parent).getVariable().getModifiers().contains(ModifierKind.STATIC)
&& ((CtFieldWrite) parent).getVariable().getModifiers().contains(ModifierKind.FINAL)) {
return true;
}
return false;
}
@Override
public <T> void visitCtThisAccess(CtThisAccess<T> thisAccess) {
try {
enterCtExpression(thisAccess);
// we only write qualified this when this is required
// this is good both in fully-qualified mode and in readable (with-imports) mode
// the implicit information is used for analysis (eg are visibility caused by implicit bugs?) but
// not for pretty-printing
CtTypeAccess target = (CtTypeAccess) thisAccess.getTarget();
CtTypeReference targetType = target.getAccessedType();
// readable mode as close as possible to the original code
if (thisAccess.isImplicit()) {
// write nothing, "this" is implicit and we unfortunately cannot always know
// what the good target is in JDTTreeBuilder
return;
}
// the simplest case: we always print "this" if we're in the top-level class,
// this is shorter (no qualified this), explicit, and less fragile wrt transformation
if (targetType == null || (thisAccess.getParent(CtType.class) != null && thisAccess.getParent(CtType.class).isTopLevel())) {
printer.write("this");
return; // still go through finally block below
}
// we cannot have fully-qualified this in anonymous classes
// we simply print "this" and it always works
// this has to come after the implicit test just before
if (targetType.isAnonymous()) {
printer.write("this");
return;
}
// complex case of qualifed this
if (!context.currentThis.isEmpty()) {
CtType lastType = context.currentThis.peekFirst().type;
String lastTypeQualifiedName = lastType.getQualifiedName();
String targetTypeQualifiedName = targetType.getQualifiedName();
if (!lastTypeQualifiedName.equals(targetTypeQualifiedName)) {
printer.snapshotLength();
visitCtTypeReferenceWithoutGenerics(targetType);
if (printer.hasNewContent()) {
printer.write(".");
}
printer.write("this");
return;
}
}
// the default super simple case only comes at the end
printer.write("this");
} finally {
exitCtExpression(thisAccess);
}
}
@Override
public <T> void visitCtSuperAccess(CtSuperAccess<T> f) {
enterCtExpression(f);
if (f.getTarget() != null) {
scan(f.getTarget());
printer.write(".");
}
printer.write("super");
exitCtExpression(f);
}
@Override
public void visitCtComment(CtComment comment) {
if (!env.isCommentsEnabled() && context.elementStack.size() > 1) {
return;
}
switch (comment.getCommentType()) {
case FILE:
printer.write("/**").writeln();
break;
case JAVADOC:
printer.write("/**").writeln().writeTabs();
break;
case INLINE:
printer.write("// ");
break;
case BLOCK:
printer.write("/* ");
break;
}
String content = comment.getContent();
switch (comment.getCommentType()) {
case INLINE:
printer.write(content);
break;
default:
String[] lines = content.split("\n");
for (int i = 0; i < lines.length; i++) {
String com = lines[i];
if (comment.getCommentType() == CtComment.CommentType.BLOCK) {
printer.write(com);
if (lines.length > 1) {
printer.writeln().writeTabs();
}
} else {
if (com.length() > 0) {
printer.write(" * " + com).writeln().writeTabs();
} else {
printer.write(" *" /* no trailing space */ + com).writeln().writeTabs();
}
}
}
break;
}
switch (comment.getCommentType()) {
case BLOCK:
printer.write(" */");
break;
case FILE:
printer.write(" */");
break;
case JAVADOC:
printer.write(" */");
break;
}
}
@Override
public <T> void visitCtAnnotationFieldAccess(CtAnnotationFieldAccess<T> annotationFieldAccess) {
enterCtExpression(annotationFieldAccess);
try (Writable _context = context.modify()) {
if (annotationFieldAccess.getTarget() != null) {
scan(annotationFieldAccess.getTarget());
printer.write(".");
_context.ignoreStaticAccess(true);
}
_context.ignoreGenerics(true);
scan(annotationFieldAccess.getVariable());
printer.write("()");
}
exitCtExpression(annotationFieldAccess);
}
@Override
public <T> void visitCtFieldReference(CtFieldReference<T> reference) {
boolean isStatic = reference.getSimpleName().equals("class") || !reference.getSimpleName().equals("super") && reference.isStatic();
boolean printType = true;
if (reference.isFinal() && reference.isStatic()) {
CtTypeReference<?> declTypeRef = reference.getDeclaringType();
if (declTypeRef.isAnonymous()) {
//never print anonymous class ref
printType = false;
} else {
if (context.isInCurrentScope(declTypeRef)) {
//do not printType if we are in scope of that type
printType = false;
}
}
}
if (isStatic && printType && !context.ignoreStaticAccess()) {
try (Writable _context = context.modify().ignoreGenerics(true)) {
scan(reference.getDeclaringType());
}
printer.write(".");
}
printer.write(reference.getSimpleName());
}
@Override
public void visitCtFor(CtFor forLoop) {
enterCtStatement(forLoop);
printer.write("for (");
List<CtStatement> st = forLoop.getForInit();
if (st.size() > 0) {
scan(st.get(0));
}
if (st.size() > 1) {
try (Writable _context = context.modify().noTypeDecl(true)) {
for (int i = 1; i < st.size(); i++) {
printer.write(", ");
scan(st.get(i));
}
}
}
printer.write("; ");
scan(forLoop.getExpression());
printer.write(";");
if (!forLoop.getForUpdate().isEmpty()) {
printer.write(" ");
}
for (CtStatement s : forLoop.getForUpdate()) {
scan(s);
printer.write(" , ");
}
if (forLoop.getForUpdate().size() > 0) {
printer.removeLastChar();
}
printer.write(")");
elementPrinterHelper.writeIfOrLoopBlock(forLoop.getBody());
}
@Override
public void visitCtForEach(CtForEach foreach) {
enterCtStatement(foreach);
printer.write("for (");
scan(foreach.getVariable());
printer.write(" : ");
scan(foreach.getExpression());
printer.write(")");
elementPrinterHelper.writeIfOrLoopBlock(foreach.getBody());
}
@Override
public void visitCtIf(CtIf ifElement) {
enterCtStatement(ifElement);
printer.write("if (");
scan(ifElement.getCondition());
printer.write(")");
elementPrinterHelper.writeIfOrLoopBlock(ifElement.getThenStatement());
if (ifElement.getElseStatement() != null) {
List<CtComment> comments = elementPrinterHelper.getComments(ifElement, CommentOffset.INSIDE);
for (CtComment comment : comments) {
SourcePosition thenPosition =
ifElement.getThenStatement().getPosition() == null ? ((CtBlock) ifElement.getThenStatement()).getStatement(0).getPosition() : ifElement.getThenStatement().getPosition();
if (comment.getPosition().getSourceStart() > thenPosition.getSourceEnd()) {
elementPrinterHelper.writeComment(comment);
}
}
printer.write("else");
elementPrinterHelper.writeIfOrLoopBlock(ifElement.getElseStatement());
}
}
@Override
public <T> void visitCtInterface(CtInterface<T> intrface) {
visitCtType(intrface);
printer.write("interface " + intrface.getSimpleName());
if (intrface.getFormalCtTypeParameters() != null) {
elementPrinterHelper.writeFormalTypeParameters(intrface);
}
if (intrface.getSuperInterfaces().size() > 0) {
printer.write(" extends ");
for (CtTypeReference<?> ref : intrface.getSuperInterfaces()) {
scan(ref);
printer.write(" , ");
}
printer.removeLastChar();
}
context.pushCurrentThis(intrface);
printer.write(" {").incTab();
// Content
elementPrinterHelper.writeElementList(intrface.getTypeMembers());
printer.decTab().writeTabs().write("}");
context.popCurrentThis();
}
@Override
public <T> void visitCtInvocation(CtInvocation<T> invocation) {
enterCtStatement(invocation);
enterCtExpression(invocation);
if (invocation.getExecutable().isConstructor()) {
// It's a constructor (super or this)
elementPrinterHelper.writeActualTypeArguments(invocation.getExecutable());
CtType<?> parentType;
try {
parentType = invocation.getParent(CtType.class);
} catch (ParentNotInitializedException e) {
parentType = null;
}
if (parentType != null && parentType.getQualifiedName() != null && parentType.getQualifiedName().equals(invocation.getExecutable().getDeclaringType().getQualifiedName())) {
printer.write("this");
} else {
printer.snapshotLength();
scan(invocation.getTarget());
if (printer.hasNewContent()) {
printer.write(".");
}
printer.write("super");
}
} else {
// It's a method invocation
printer.snapshotLength();
try (Writable _context = context.modify()) {
if (invocation.getTarget() instanceof CtTypeAccess) {
_context.ignoreGenerics(true);
}
scan(invocation.getTarget());
}
if (printer.hasNewContent()) {
printer.write(".");
}
elementPrinterHelper.writeActualTypeArguments(invocation);
if (env.isPreserveLineNumbers()) {
printer.adjustPosition(invocation, sourceCompilationUnit);
}
printer.write(invocation.getExecutable().getSimpleName());
}
printer.write("(");
boolean remove = false;
for (CtExpression<?> e : invocation.getArguments()) {
scan(e);
printer.write(", ");
remove = true;
}
if (remove) {
printer.removeLastChar();
}
printer.write(")");
exitCtExpression(invocation);
}
@Override
public <T> void visitCtLiteral(CtLiteral<T> literal) {
enterCtExpression(literal);
if (literal.getValue() == null) {
printer.write("null");
} else if (literal.getValue() instanceof Long) {
printer.write(literal.getValue() + "L");
} else if (literal.getValue() instanceof Float) {
printer.write(literal.getValue() + "F");
} else if (literal.getValue() instanceof Character) {
printer.write("'");
boolean mayContainsSpecialCharacter = true;
SourcePosition position = literal.getPosition();
if (position != null) {
// the size of the string in the source code, the -1 is the size of the ' or " in the source code
int stringLength = position.getSourceEnd() - position.getSourceStart() - 1;
// if the string in the source is not the same as the string in the literal, the string may contains special characters
mayContainsSpecialCharacter = stringLength != 1;
}
printer.writeStringLiteral(new String(new char[] { (Character) literal.getValue() }), mayContainsSpecialCharacter);
printer.write("'");
} else if (literal.getValue() instanceof String) {
printer.write('\"');
boolean mayContainsSpecialCharacters = true;
SourcePosition position = literal.getPosition();
if (position != null) {
// the size of the string in the source code, the -1 is the size of the ' or " in the source code
int stringLength = position.getSourceEnd() - position.getSourceStart() - 1;
// if the string in the source is not the same as the string in the literal, the string may contains special characters
mayContainsSpecialCharacters = ((String) literal.getValue()).length() != stringLength;
}
printer.writeStringLiteral((String) literal.getValue(), mayContainsSpecialCharacters);
printer.write('\"');
} else if (literal.getValue() instanceof Class) {
printer.write(((Class<?>) literal.getValue()).getName());
} else {
printer.write(literal.getValue().toString());
}
exitCtExpression(literal);
}
@Override
public <T> void visitCtLocalVariable(CtLocalVariable<T> localVariable) {
if (!context.noTypeDecl()) {
enterCtStatement(localVariable);
}
if (env.isPreserveLineNumbers()) {
printer.adjustPosition(localVariable, sourceCompilationUnit);
}
if (!context.noTypeDecl()) {
elementPrinterHelper.writeModifiers(localVariable);
scan(localVariable.getType());
printer.write(" ");
}
printer.write(localVariable.getSimpleName());
if (localVariable.getDefaultExpression() != null) {
printer.write(" = ");
scan(localVariable.getDefaultExpression());
}
}
@Override
public <T> void visitCtLocalVariableReference(CtLocalVariableReference<T> reference) {
printer.write(reference.getSimpleName());
}
@Override
public <T> void visitCtCatchVariable(CtCatchVariable<T> catchVariable) {
if (env.isPreserveLineNumbers()) {
printer.adjustPosition(catchVariable, sourceCompilationUnit);
}
elementPrinterHelper.writeModifiers(catchVariable);
scan(catchVariable.getType());
printer.write(" ");
printer.write(catchVariable.getSimpleName());
}
@Override
public <T> void visitCtCatchVariableReference(CtCatchVariableReference<T> reference) {
printer.write(reference.getSimpleName());
}
@Override
public <T> void visitCtMethod(CtMethod<T> m) {
elementPrinterHelper.writeComment(m);
elementPrinterHelper.visitCtNamedElement(m, sourceCompilationUnit);
elementPrinterHelper.writeModifiers(m);
if (m.isDefaultMethod()) {
printer.write("default ");
}
elementPrinterHelper.writeFormalTypeParameters(m);
if (m.getFormalCtTypeParameters().size() > 0) {
printer.write(' ');
}
try (Writable _context = context.modify().ignoreGenerics(false)) {
scan(m.getType());
}
printer.write(" ");
printer.write(m.getSimpleName());
elementPrinterHelper.writeExecutableParameters(m);
elementPrinterHelper.writeThrowsClause(m);
if (m.getBody() != null) {
printer.write(" ");
scan(m.getBody());
if (m.getBody().getPosition() != null) {
if (m.getBody().getPosition().getCompilationUnit() == sourceCompilationUnit) {
if (m.getBody().getStatements().isEmpty() || !(m.getBody().getStatements().get(m.getBody().getStatements().size() - 1) instanceof CtReturn)) {
printer.putLineNumberMapping(m.getBody().getPosition().getEndLine());
}
} else {
printer.undefineLine();
}
} else {
printer.undefineLine();
}
} else {
printer.write(";");
}
}
@Override
public <T> void visitCtAnnotationMethod(CtAnnotationMethod<T> annotationMethod) {
elementPrinterHelper.writeComment(annotationMethod);
elementPrinterHelper.visitCtNamedElement(annotationMethod, sourceCompilationUnit);
elementPrinterHelper.writeModifiers(annotationMethod);
scan(annotationMethod.getType());
printer.write(" ");
printer.write(annotationMethod.getSimpleName());
printer.write("()");
if (annotationMethod.getDefaultExpression() != null) {
printer.write(" default ");
scan(annotationMethod.getDefaultExpression());
}
printer.write(";");
}
@Override
@SuppressWarnings("rawtypes")
public <T> void visitCtNewArray(CtNewArray<T> newArray) {
enterCtExpression(newArray);
boolean isNotInAnnotation;
try {
isNotInAnnotation = (newArray.getParent(CtAnnotationType.class) == null) && (newArray.getParent(CtAnnotation.class) == null);
} catch (ParentNotInitializedException e) {
isNotInAnnotation = true;
}
if (isNotInAnnotation) {
CtTypeReference<?> ref = newArray.getType();
if (ref != null) {
printer.write("new ");
}
try (Writable _context = context.modify().skipArray(true)) {
scan(ref);
}
for (int i = 0; ref instanceof CtArrayTypeReference; i++) {
printer.write("[");
if (newArray.getDimensionExpressions().size() > i) {
CtExpression<Integer> e = newArray.getDimensionExpressions().get(i);
scan(e);
}
printer.write("]");
ref = ((CtArrayTypeReference) ref).getComponentType();
}
}
if (newArray.getDimensionExpressions().size() == 0) {
printer.write("{ ");
List<CtExpression<?>> l_elements = newArray.getElements();
for (int i = 0; i < l_elements.size(); i++) {
CtExpression e = l_elements.get(i);
scan(e);
printer.write(" , ");
if (i + 1 == l_elements.size()) {
printer.removeLastChar();
// if the last element c
List<CtComment> comments = elementPrinterHelper.getComments(e, CommentOffset.AFTER);
// if the last element contains an inline comment, print a new line before closing the array
if (!comments.isEmpty() && comments.get(comments.size() - 1).getCommentType() == CtComment.CommentType.INLINE) {
printer.insertLine();
}
}
}
elementPrinterHelper.writeComment(newArray, CommentOffset.INSIDE);
printer.write(" }");
}
elementPrinterHelper.writeComment(newArray, CommentOffset.AFTER);
exitCtExpression(newArray);
}
@Override
public <T> void visitCtConstructorCall(CtConstructorCall<T> ctConstructorCall) {
enterCtStatement(ctConstructorCall);
enterCtExpression(ctConstructorCall);
printConstructorCall(ctConstructorCall);
exitCtExpression(ctConstructorCall);
}
@Override
public <T> void visitCtNewClass(CtNewClass<T> newClass) {
enterCtStatement(newClass);
enterCtExpression(newClass);
printConstructorCall(newClass);
scan(newClass.getAnonymousClass());
exitCtExpression(newClass);
}
private <T> void printConstructorCall(CtConstructorCall<T> ctConstructorCall) {
try (Writable _context = context.modify()) {
if (ctConstructorCall.getTarget() != null) {
scan(ctConstructorCall.getTarget());
printer.write(".");
_context.ignoreEnclosingClass(true);
}
if (hasDeclaringTypeWithGenerics(ctConstructorCall.getType())) {
_context.ignoreEnclosingClass(true);
}
printer.write("new ");
if (ctConstructorCall.getActualTypeArguments().size() > 0) {
elementPrinterHelper.writeActualTypeArguments(ctConstructorCall);
}
scan(ctConstructorCall.getType());
}
printer.write("(");
for (CtCodeElement exp : ctConstructorCall.getArguments()) {
scan(exp);
printer.write(", ");
}
if (ctConstructorCall.getArguments().size() > 0) {
printer.removeLastChar();
}
printer.write(")");
}
/**
* JDT doesn't support <code>new Foo<K>.Bar()</code>. To avoid reprint this kind of type reference,
* we check that the reference has a declaring type with generics.
* See https://bugs.eclipse.org/bugs/show_bug.cgi?id=474593
*
* @param reference
* Type reference concerned by the bug.
* @return true if a declaring type have generic types.
*/
private <T> boolean hasDeclaringTypeWithGenerics(CtTypeReference<T> reference) {
// We don't have a declaring type, it can't have generics.
if (reference == null) {
return false;
}
// If the declaring type isn't a type, we don't need this hack.
if (reference.getDeclaringType() == null) {
return false;
}
// If current reference is a class declared in a method, we don't need this hack.
if (reference.isLocalType()) {
return false;
}
// If declaring type have generics, we return true.
if (reference.getDeclaringType().getActualTypeArguments().size() != 0) {
return true;
}
// Checks if the declaring type has generic types.
return hasDeclaringTypeWithGenerics(reference.getDeclaringType());
}
@Override
public <T> void visitCtLambda(CtLambda<T> lambda) {
enterCtExpression(lambda);
printer.write("(");
if (lambda.getParameters().size() > 0) {
for (CtParameter<?> parameter : lambda.getParameters()) {
scan(parameter);
printer.write(",");
}
printer.removeLastChar();
}
printer.write(") -> ");
if (lambda.getBody() != null) {
scan(lambda.getBody());
} else {
scan(lambda.getExpression());
}
exitCtExpression(lambda);
}
@Override
public <T, E extends CtExpression<?>> void visitCtExecutableReferenceExpression(CtExecutableReferenceExpression<T, E> expression) {
enterCtExpression(expression);
scan(expression.getTarget());
printer.write("::");
if (expression.getExecutable().isConstructor()) {
printer.write("new");
} else {
printer.write(expression.getExecutable().getSimpleName());
}
exitCtExpression(expression);
}
@Override
public <T, A extends T> void visitCtOperatorAssignment(CtOperatorAssignment<T, A> assignment) {
enterCtStatement(assignment);
enterCtExpression(assignment);
scan(assignment.getAssigned());
printer.write(" ");
printer.writeOperator(assignment.getKind());
printer.write("= ");
scan(assignment.getAssignment());
exitCtExpression(assignment);
}
@Override
public void visitCtPackage(CtPackage ctPackage) {
if (!ctPackage.isUnnamedPackage()) {
printer.write("package " + ctPackage.getQualifiedName() + ";");
} else {
printer.write("// default package (CtPackage.TOP_LEVEL_PACKAGE_NAME in Spoon= unnamed package)\n");
}
}
@Override
public void visitCtPackageReference(CtPackageReference reference) {
printer.write(reference.getSimpleName());
}
@Override
public <T> void visitCtParameter(CtParameter<T> parameter) {
elementPrinterHelper.writeComment(parameter);
elementPrinterHelper.writeAnnotations(parameter);
elementPrinterHelper.writeModifiers(parameter);
if (parameter.isVarArgs()) {
scan(((CtArrayTypeReference<T>) parameter.getType()).getComponentType());
printer.write("...");
} else {
scan(parameter.getType());
}
printer.write(" ");
printer.write(parameter.getSimpleName());
}
@Override
public <T> void visitCtParameterReference(CtParameterReference<T> reference) {
printer.write(reference.getSimpleName());
}
@Override
public <R> void visitCtReturn(CtReturn<R> returnStatement) {
enterCtStatement(returnStatement);
printer.write("return ");
scan(returnStatement.getReturnedExpression());
}
private <T> void visitCtType(CtType<T> type) {
elementPrinterHelper.writeComment(type, CommentOffset.BEFORE);
printer.mapLine(type, sourceCompilationUnit);
if (type.isTopLevel()) {
context.currentTopLevel = type;
}
elementPrinterHelper.visitCtNamedElement(type, sourceCompilationUnit);
elementPrinterHelper.writeModifiers(type);
}
@Override
public void visitCtStatementList(CtStatementList statements) {
for (CtStatement s : statements.getStatements()) {
scan(s);
}
}
@Override
public <E> void visitCtSwitch(CtSwitch<E> switchStatement) {
enterCtStatement(switchStatement);
printer.write("switch (");
scan(switchStatement.getSelector());
printer.write(") {").incTab();
for (CtCase<?> c : switchStatement.getCases()) {
printer.writeln().writeTabs();
scan(c);
}
if (env.isPreserveLineNumbers()) {
printer.decTab().write("}");
} else {
printer.decTab().writeln().writeTabs().write("}");
}
}
@Override
public void visitCtSynchronized(CtSynchronized synchro) {
enterCtStatement(synchro);
printer.write("synchronized");
if (synchro.getExpression() != null) {
printer.write("(");
scan(synchro.getExpression());
printer.write(") ");
}
scan(synchro.getBlock());
}
@Override
public void visitCtThrow(CtThrow throwStatement) {
enterCtStatement(throwStatement);
printer.write("throw ");
scan(throwStatement.getThrownExpression());
}
@Override
public void visitCtTry(CtTry tryBlock) {
enterCtStatement(tryBlock);
printer.write("try ");
scan(tryBlock.getBody());
for (CtCatch c : tryBlock.getCatchers()) {
scan(c);
}
if (tryBlock.getFinalizer() != null) {
printer.write(" finally ");
scan(tryBlock.getFinalizer());
}
}
@Override
public void visitCtTryWithResource(CtTryWithResource tryWithResource) {
enterCtStatement(tryWithResource);
printer.write("try ");
if (tryWithResource.getResources() != null && !tryWithResource.getResources().isEmpty()) {
printer.write("(");
for (CtLocalVariable<?> r : tryWithResource.getResources()) {
scan(r);
printer.write(";");
}
printer.removeLastChar();
printer.write(") ");
}
scan(tryWithResource.getBody());
for (CtCatch c : tryWithResource.getCatchers()) {
scan(c);
}
if (tryWithResource.getFinalizer() != null) {
printer.write(" finally ");
scan(tryWithResource.getFinalizer());
}
}
@Override
public void visitCtTypeParameterReference(CtTypeParameterReference ref) {
if (ref.isImplicit()) {
return;
}
elementPrinterHelper.writeAnnotations(ref);
if (printQualified(ref)) {
printer.write(ref.getQualifiedName());
} else {
printer.write(ref.getSimpleName());
}
if (ref.getBoundingType() != null) {
if (ref.isUpper()) {
printer.write(" extends ");
} else {
printer.write(" super ");
}
scan(ref.getBoundingType());
}
}
@Override
public void visitCtWildcardReference(CtWildcardReference wildcardReference) {
visitCtTypeParameterReference(wildcardReference);
}
private boolean printQualified(CtTypeReference<?> ref) {
if (importsContext.isImported(ref) || (this.env.isAutoImports() && ref.getPackage() != null && ref.getPackage().getSimpleName().equals("java.lang"))) {
// If my.pkg.Something is imported, but
//A) we are in the context of a class which is also called "Something",
//B) we are in the context of a class which defines field which is also called "Something",
// we should still use qualified version my.pkg.Something
for (TypeContext typeContext : context.currentThis) {
if (typeContext.getSimpleName().equals(ref.getSimpleName())
&& !Objects.equals(typeContext.getPackage(), ref.getPackage())) {
return true;
}
if (typeContext.isNameConflict(ref.getSimpleName())) {
return true;
}
}
return false;
} else {
return true;
}
}
@Override
public <T> void visitCtIntersectionTypeReference(CtIntersectionTypeReference<T> reference) {
for (CtTypeReference<?> bound : reference.getBounds()) {
scan(bound);
printer.write(" & ");
}
printer.removeLastChar();
}
@Override
public <T> void visitCtTypeReference(CtTypeReference<T> ref) {
visitCtTypeReference(ref, true);
}
@Override
public <T> void visitCtTypeAccess(CtTypeAccess<T> typeAccess) {
if (typeAccess.isImplicit()) {
return;
}
enterCtExpression(typeAccess);
scan(typeAccess.getAccessedType());
exitCtExpression(typeAccess);
}
private void visitCtTypeReferenceWithoutGenerics(CtTypeReference<?> ref) {
visitCtTypeReference(ref, false);
}
private void visitCtTypeReference(CtTypeReference<?> ref, boolean withGenerics) {
if (ref.isImplicit()) {
return;
}
if (ref.isPrimitive()) {
elementPrinterHelper.writeAnnotations(ref);
printer.write(ref.getSimpleName());
return;
}
boolean isInner = ref.getDeclaringType() != null;
if (isInner) {
if (!context.ignoreEnclosingClass() && !ref.isLocalType()) {
//compute visible type which can be used to print access path to ref
CtTypeReference<?> accessType = ref.getAccessType();
if (!accessType.isAnonymous()) {
try (Writable _context = context.modify()) {
if (!withGenerics) {
_context.ignoreGenerics(true);
}
scan(accessType);
}
printer.write(".");
}
}
//?? are these annotations on correct place ??
elementPrinterHelper.writeAnnotations(ref);
if (ref.isLocalType()) {
printer.write(ref.getSimpleName().replaceAll("^[0-9]*", ""));
} else {
printer.write(ref.getSimpleName());
}
} else {
if (ref.getPackage() != null && printQualified(ref)) {
if (!ref.getPackage().isUnnamedPackage()) {
scan(ref.getPackage());
printer.write(CtPackage.PACKAGE_SEPARATOR);
}
}
elementPrinterHelper.writeAnnotations(ref);
printer.write(ref.getSimpleName());
}
if (withGenerics && !context.ignoreGenerics()) {
try (Writable _context = context.modify().ignoreEnclosingClass(false)) {
elementPrinterHelper.writeActualTypeArguments(ref);
}
}
}
@Override
public <T> void visitCtUnaryOperator(CtUnaryOperator<T> operator) {
enterCtStatement(operator);
enterCtExpression(operator);
printer.preWriteUnaryOperator(operator.getKind());
scan(operator.getOperand());
printer.postWriteUnaryOperator(operator.getKind());
exitCtExpression(operator);
}
@Override
public <T> void visitCtVariableRead(CtVariableRead<T> variableRead) {
enterCtExpression(variableRead);
printer.write(variableRead.getVariable().getSimpleName());
exitCtExpression(variableRead);
}
@Override
public <T> void visitCtVariableWrite(CtVariableWrite<T> variableWrite) {
enterCtExpression(variableWrite);
printer.write(variableWrite.getVariable().getSimpleName());
exitCtExpression(variableWrite);
}
public void visitCtWhile(CtWhile whileLoop) {
enterCtStatement(whileLoop);
printer.write("while (");
scan(whileLoop.getLoopingExpression());
printer.write(")");
elementPrinterHelper.writeIfOrLoopBlock(whileLoop.getBody());
}
@Override
public <T> void visitCtCodeSnippetExpression(CtCodeSnippetExpression<T> expression) {
elementPrinterHelper.writeComment(expression);
printer.write(expression.getValue());
}
@Override
public void visitCtCodeSnippetStatement(CtCodeSnippetStatement statement) {
elementPrinterHelper.writeComment(statement);
printer.write(statement.getValue());
}
public ElementPrinterHelper getElementPrinterHelper() {
return elementPrinterHelper;
}
public PrintingContext getContext() {
return context;
}
@Override
public <T> void visitCtUnboundVariableReference(CtUnboundVariableReference<T> reference) {
printer.write(reference.getSimpleName());
}
@Override
public String getPackageDeclaration() {
return printPackageInfo(context.currentTopLevel.getPackage());
}
@Override
public String printPackageInfo(CtPackage pack) {
PrinterHelper bck = printer;
ElementPrinterHelper bck2 = elementPrinterHelper;
printer = new PrinterHelper(env);
elementPrinterHelper = new ElementPrinterHelper(printer, this, env);
elementPrinterHelper.writeComment(pack);
for (CtAnnotation<?> a : pack.getAnnotations()) {
a.accept(this);
}
if (!pack.isUnnamedPackage()) {
printer.write("package " + pack.getQualifiedName() + ";");
}
String ret = printer.toString();
elementPrinterHelper = bck2;
printer = bck;
return ret;
}
@Override
public String getResult() {
return printer.toString();
}
@Override
public void reset() {
printer = new PrinterHelper(env);
elementPrinterHelper.setPrinter(printer);
context = new PrintingContext();
}
@Override
public void calculate(CompilationUnit sourceCompilationUnit, List<CtType<?>> types) {
this.sourceCompilationUnit = sourceCompilationUnit;
// reset the import scanner between each compilationunit
if (env.isAutoImports()) {
this.importsContext = new ImportScannerImpl();
} else {
this.importsContext = new MinimalImportScanner();
}
Set<CtReference> imports = new HashSet<>();
for (CtType<?> t : types) {
imports.addAll(computeImports(t));
}
elementPrinterHelper.writeHeader(types, imports);
for (CtType<?> t : types) {
scan(t);
printer.writeln().writeln().writeTabs();
}
}
@Override
public Map<Integer, Integer> getLineNumberMapping() {
return printer.getLineNumberMapping();
}
}