/**
* Copyright 2011-2017 Asakusa Framework Team.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.asakusafw.utils.java.internal.model.util;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.asakusafw.utils.java.model.syntax.AlternateConstructorInvocation;
import com.asakusafw.utils.java.model.syntax.AnnotationDeclaration;
import com.asakusafw.utils.java.model.syntax.AnnotationElement;
import com.asakusafw.utils.java.model.syntax.AnnotationElementDeclaration;
import com.asakusafw.utils.java.model.syntax.ArrayAccessExpression;
import com.asakusafw.utils.java.model.syntax.ArrayCreationExpression;
import com.asakusafw.utils.java.model.syntax.ArrayInitializer;
import com.asakusafw.utils.java.model.syntax.ArrayType;
import com.asakusafw.utils.java.model.syntax.AssertStatement;
import com.asakusafw.utils.java.model.syntax.AssignmentExpression;
import com.asakusafw.utils.java.model.syntax.Attribute;
import com.asakusafw.utils.java.model.syntax.BasicType;
import com.asakusafw.utils.java.model.syntax.Block;
import com.asakusafw.utils.java.model.syntax.BlockComment;
import com.asakusafw.utils.java.model.syntax.BreakStatement;
import com.asakusafw.utils.java.model.syntax.CastExpression;
import com.asakusafw.utils.java.model.syntax.CatchClause;
import com.asakusafw.utils.java.model.syntax.ClassBody;
import com.asakusafw.utils.java.model.syntax.ClassDeclaration;
import com.asakusafw.utils.java.model.syntax.ClassInstanceCreationExpression;
import com.asakusafw.utils.java.model.syntax.ClassLiteral;
import com.asakusafw.utils.java.model.syntax.CompilationUnit;
import com.asakusafw.utils.java.model.syntax.ConditionalExpression;
import com.asakusafw.utils.java.model.syntax.ConstructorDeclaration;
import com.asakusafw.utils.java.model.syntax.ConstructorReferenceExpression;
import com.asakusafw.utils.java.model.syntax.ContinueStatement;
import com.asakusafw.utils.java.model.syntax.DoStatement;
import com.asakusafw.utils.java.model.syntax.DocBlock;
import com.asakusafw.utils.java.model.syntax.DocElement;
import com.asakusafw.utils.java.model.syntax.DocField;
import com.asakusafw.utils.java.model.syntax.DocMethod;
import com.asakusafw.utils.java.model.syntax.DocMethodParameter;
import com.asakusafw.utils.java.model.syntax.DocText;
import com.asakusafw.utils.java.model.syntax.EmptyStatement;
import com.asakusafw.utils.java.model.syntax.EnhancedForStatement;
import com.asakusafw.utils.java.model.syntax.EnumConstantDeclaration;
import com.asakusafw.utils.java.model.syntax.EnumDeclaration;
import com.asakusafw.utils.java.model.syntax.Expression;
import com.asakusafw.utils.java.model.syntax.ExpressionStatement;
import com.asakusafw.utils.java.model.syntax.FieldAccessExpression;
import com.asakusafw.utils.java.model.syntax.FieldDeclaration;
import com.asakusafw.utils.java.model.syntax.ForStatement;
import com.asakusafw.utils.java.model.syntax.FormalParameterDeclaration;
import com.asakusafw.utils.java.model.syntax.IfStatement;
import com.asakusafw.utils.java.model.syntax.ImportDeclaration;
import com.asakusafw.utils.java.model.syntax.ImportKind.Range;
import com.asakusafw.utils.java.model.syntax.ImportKind.Target;
import com.asakusafw.utils.java.model.syntax.InfixExpression;
import com.asakusafw.utils.java.model.syntax.InitializerDeclaration;
import com.asakusafw.utils.java.model.syntax.InstanceofExpression;
import com.asakusafw.utils.java.model.syntax.InterfaceDeclaration;
import com.asakusafw.utils.java.model.syntax.Javadoc;
import com.asakusafw.utils.java.model.syntax.LabeledStatement;
import com.asakusafw.utils.java.model.syntax.LambdaExpression;
import com.asakusafw.utils.java.model.syntax.LambdaParameter;
import com.asakusafw.utils.java.model.syntax.LineComment;
import com.asakusafw.utils.java.model.syntax.Literal;
import com.asakusafw.utils.java.model.syntax.LocalClassDeclaration;
import com.asakusafw.utils.java.model.syntax.LocalVariableDeclaration;
import com.asakusafw.utils.java.model.syntax.MarkerAnnotation;
import com.asakusafw.utils.java.model.syntax.MethodDeclaration;
import com.asakusafw.utils.java.model.syntax.MethodInvocationExpression;
import com.asakusafw.utils.java.model.syntax.MethodReferenceExpression;
import com.asakusafw.utils.java.model.syntax.Model;
import com.asakusafw.utils.java.model.syntax.ModelKind;
import com.asakusafw.utils.java.model.syntax.Modifier;
import com.asakusafw.utils.java.model.syntax.NamedType;
import com.asakusafw.utils.java.model.syntax.NormalAnnotation;
import com.asakusafw.utils.java.model.syntax.PackageDeclaration;
import com.asakusafw.utils.java.model.syntax.ParameterizedType;
import com.asakusafw.utils.java.model.syntax.ParenthesizedExpression;
import com.asakusafw.utils.java.model.syntax.PostfixExpression;
import com.asakusafw.utils.java.model.syntax.QualifiedName;
import com.asakusafw.utils.java.model.syntax.QualifiedType;
import com.asakusafw.utils.java.model.syntax.ReturnStatement;
import com.asakusafw.utils.java.model.syntax.SimpleName;
import com.asakusafw.utils.java.model.syntax.SingleElementAnnotation;
import com.asakusafw.utils.java.model.syntax.Statement;
import com.asakusafw.utils.java.model.syntax.StatementExpressionList;
import com.asakusafw.utils.java.model.syntax.StrictVisitor;
import com.asakusafw.utils.java.model.syntax.Super;
import com.asakusafw.utils.java.model.syntax.SuperConstructorInvocation;
import com.asakusafw.utils.java.model.syntax.SwitchCaseLabel;
import com.asakusafw.utils.java.model.syntax.SwitchDefaultLabel;
import com.asakusafw.utils.java.model.syntax.SwitchLabel;
import com.asakusafw.utils.java.model.syntax.SwitchStatement;
import com.asakusafw.utils.java.model.syntax.SynchronizedStatement;
import com.asakusafw.utils.java.model.syntax.This;
import com.asakusafw.utils.java.model.syntax.ThrowStatement;
import com.asakusafw.utils.java.model.syntax.TryResource;
import com.asakusafw.utils.java.model.syntax.TryStatement;
import com.asakusafw.utils.java.model.syntax.Type;
import com.asakusafw.utils.java.model.syntax.TypeParameterDeclaration;
import com.asakusafw.utils.java.model.syntax.UnaryExpression;
import com.asakusafw.utils.java.model.syntax.UnionType;
import com.asakusafw.utils.java.model.syntax.VariableDeclarator;
import com.asakusafw.utils.java.model.syntax.WhileStatement;
import com.asakusafw.utils.java.model.syntax.Wildcard;
import com.asakusafw.utils.java.model.syntax.WildcardBoundKind;
import com.asakusafw.utils.java.model.util.CommentEmitTrait;
import com.asakusafw.utils.java.model.util.NoThrow;
/**
* Emits Java DOM objects.
*/
public class ModelEmitter {
private static final EmitEngine ENGINE = new EmitEngine();
private final PrintWriter writer;
/**
* Creates a new instance.
* @param writer the target writer
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public ModelEmitter(PrintWriter writer) {
if (writer == null) {
throw new IllegalArgumentException("writer must not be null"); //$NON-NLS-1$
}
this.writer = writer;
}
/**
* Emits a Java DOM object into the target writer.
* @param element the target object
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public void emit(Model element) {
if (element == null) {
throw new IllegalArgumentException("element must not be null"); //$NON-NLS-1$
}
PrintEmitContext context = new PrintEmitContext(writer);
emit(element, context);
context.flushComments();
}
/**
* Emits a Java DOM object into the target context.
* @param element the target object
* @param context the current context
* @throws IllegalArgumentException if the parameters are {@code null}
*/
public static void emit(Model element, EmitContext context) {
if (element == null) {
throw new IllegalArgumentException("element must not be null"); //$NON-NLS-1$
}
if (context == null) {
throw new IllegalArgumentException("context must not be null"); //$NON-NLS-1$
}
element.accept(ENGINE, context);
}
}
/**
* An engine for {@link ModelEmitter}.
*/
class EmitEngine extends StrictVisitor<Void, EmitContext, NoThrow> {
@Override
public Void visitAlternateConstructorInvocation(AlternateConstructorInvocation elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
processTypeParameters(elem.getTypeArguments(), context);
context.keyword("this"); //$NON-NLS-1$
processParameters(elem.getArguments(), context);
context.separator(";"); //$NON-NLS-1$
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitAnnotationDeclaration(AnnotationDeclaration elem, EmitContext context) {
begin(elem, context);
context.declaration(EmitDirection.BEGIN);
process(elem.getJavadoc(), context);
processBlockComment(elem, context);
processAttributes(elem.getModifiers(), context);
context.symbol("@"); //$NON-NLS-1$
context.keyword("interface"); //$NON-NLS-1$
process(elem.getName(), context);
context.classBlock(EmitDirection.BEGIN);
process(elem.getBodyDeclarations(), context);
context.classBlock(EmitDirection.END);
context.declaration(EmitDirection.END);
return null;
}
@Override
public Void visitAnnotationElement(AnnotationElement elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getName(), context);
context.operator("="); //$NON-NLS-1$
process(elem.getExpression(), context);
return null;
}
@Override
public Void visitAnnotationElementDeclaration(AnnotationElementDeclaration elem, EmitContext context) {
begin(elem, context);
context.declaration(EmitDirection.END);
process(elem.getJavadoc(), context);
processBlockComment(elem, context);
processAttributes(elem.getModifiers(), context);
process(elem.getType(), context);
process(elem.getName(), context);
processParameters(Collections.emptyList(), context);
if (appears(elem.getDefaultExpression())) {
context.keyword("default"); //$NON-NLS-1$
process(elem.getDefaultExpression(), context);
}
context.separator(";"); //$NON-NLS-1$
context.declaration(EmitDirection.END);
return null;
}
@Override
public Void visitArrayAccessExpression(ArrayAccessExpression elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getArray(), context);
context.symbol("["); //$NON-NLS-1$
process(elem.getIndex(), context);
context.separator("]"); //$NON-NLS-1$
return null;
}
@Override
public Void visitArrayCreationExpression(ArrayCreationExpression elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
Type scalar;
int dim = 0;
{
Type current = elem.getType();
while (current instanceof ArrayType) {
dim++;
current = ((ArrayType) current).getComponentType();
}
scalar = current;
}
context.keyword("new"); //$NON-NLS-1$
process(scalar, context);
for (Expression expr : elem.getDimensionExpressions()) {
context.symbol("["); //$NON-NLS-1$
process(expr, context);
context.separator("]"); //$NON-NLS-1$
dim--;
}
// can be dim < 0 (invalid array type)
for (int i = 0; i < dim; i++) {
context.symbol("["); //$NON-NLS-1$
context.separator("]"); //$NON-NLS-1$
}
process(elem.getArrayInitializer(), context);
return null;
}
@Override
public Void visitArrayInitializer(ArrayInitializer elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
context.arrayInitializerBlock(EmitDirection.BEGIN);
processJoinWithComma(elem.getElements(), context);
context.arrayInitializerBlock(EmitDirection.END);
return null;
}
@Override
public Void visitArrayType(ArrayType elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getComponentType(), context);
context.symbol("["); //$NON-NLS-1$
context.separator("]"); //$NON-NLS-1$
return null;
}
@Override
public Void visitAssertStatement(AssertStatement elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
context.keyword("assert"); //$NON-NLS-1$
process(elem.getExpression(), context);
if (appears(elem.getMessage())) {
context.separator(":"); //$NON-NLS-1$
process(elem.getMessage(), context);
}
context.separator(";"); //$NON-NLS-1$
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitAssignmentExpression(AssignmentExpression elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getLeftHandSide(), context);
context.operator(elem.getOperator().getAssignmentSymbol());
process(elem.getRightHandSide(), context);
return null;
}
@Override
public Void visitBasicType(BasicType elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
context.keyword(elem.getTypeKind().getKeyword());
return null;
}
@Override
public Void visitBlock(Block elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statementBlock(EmitDirection.BEGIN);
process(elem.getStatements(), context);
context.statementBlock(EmitDirection.END);
return null;
}
private static final Pattern HEAD_ASTER = Pattern.compile("^ ?\\* ?"); //$NON-NLS-1$
@Override
public Void visitBlockComment(BlockComment elem, EmitContext context) {
String content = elem.getString();
if (content.startsWith("/*")) { //$NON-NLS-1$
content = content.substring(2);
}
if (content.endsWith("*/")) { //$NON-NLS-1$
content = content.substring(0, content.length() - 2);
}
List<String> results = new ArrayList<>();
String[] lines = content.split("\\n|\\r|\\r\\n"); //$NON-NLS-1$
for (String line : lines) {
if (line.startsWith(" * ")) { //$NON-NLS-1$
Matcher m = HEAD_ASTER.matcher(line);
if (m.find()) {
results.add(line.substring(m.end()));
} else {
results.add(line);
}
}
}
context.putBlockComment(results);
return null;
}
@Override
public Void visitBreakStatement(BreakStatement elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
context.keyword("break"); //$NON-NLS-1$
process(elem.getTarget(), context);
context.separator(";"); //$NON-NLS-1$
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitCastExpression(CastExpression elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
context.symbol("("); //$NON-NLS-1$
process(elem.getType(), context);
context.separator(")"); //$NON-NLS-1$
process(elem.getExpression(), context);
return null;
}
@Override
public Void visitCatchClause(CatchClause elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.keyword("catch"); //$NON-NLS-1$
context.symbol("("); //$NON-NLS-1$
process(elem.getParameter(), context);
context.separator(")"); //$NON-NLS-1$
process(elem.getBody(), context);
return null;
}
@Override
public Void visitClassBody(ClassBody elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
context.classBlock(EmitDirection.BEGIN);
process(elem.getBodyDeclarations(), context);
context.classBlock(EmitDirection.END);
return null;
}
@Override
public Void visitClassDeclaration(ClassDeclaration elem, EmitContext context) {
begin(elem, context);
context.declaration(EmitDirection.BEGIN);
process(elem.getJavadoc(), context);
processBlockComment(elem, context);
processAttributes(elem.getModifiers(), context);
context.keyword("class"); //$NON-NLS-1$
process(elem.getName(), context);
processTypeParameters(elem.getTypeParameters(), context);
if (appears(elem.getSuperClass())) {
context.keyword("extends"); //$NON-NLS-1$
process(elem.getSuperClass(), context);
}
if (appears(elem.getSuperInterfaceTypes())) {
context.keyword("implements"); //$NON-NLS-1$
processJoinWithComma(elem.getSuperInterfaceTypes(), context);
}
context.classBlock(EmitDirection.BEGIN);
process(elem.getBodyDeclarations(), context);
context.classBlock(EmitDirection.END);
context.declaration(EmitDirection.END);
return null;
}
@Override
public Void visitClassInstanceCreationExpression(ClassInstanceCreationExpression elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
if (process(elem.getQualifier(), context)) {
context.symbol("."); //$NON-NLS-1$
}
context.keyword("new"); //$NON-NLS-1$
processTypeParameters(elem.getTypeArguments(), context);
process(elem.getType(), context);
processParameters(elem.getArguments(), context);
process(elem.getBody(), context);
return null;
}
@Override
public Void visitClassLiteral(ClassLiteral elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getType(), context);
context.symbol("."); //$NON-NLS-1$
context.keyword("class"); //$NON-NLS-1$
return null;
}
@Override
public Void visitCompilationUnit(CompilationUnit elem, EmitContext context) {
begin(elem, context);
processCompilationUnitComment(elem, context);
process(elem.getPackageDeclaration(), context);
process(elem.getImportDeclarations(), context);
process(elem.getTypeDeclarations(), context);
return null;
}
@Override
public Void visitConditionalExpression(ConditionalExpression elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getCondition(), context);
context.operator("?"); //$NON-NLS-1$
process(elem.getThenExpression(), context);
context.operator(":"); //$NON-NLS-1$
process(elem.getElseExpression(), context);
return null;
}
@Override
public Void visitConstructorDeclaration(ConstructorDeclaration elem, EmitContext context) {
begin(elem, context);
context.declaration(EmitDirection.BEGIN);
process(elem.getJavadoc(), context);
processBlockComment(elem, context);
processAttributes(elem.getModifiers(), context);
processTypeParameters(elem.getTypeParameters(), context);
process(elem.getName(), context);
processParameters(elem.getFormalParameters(), context);
if (appears(elem.getExceptionTypes())) {
context.keyword("throws"); //$NON-NLS-1$
processJoinWithComma(elem.getExceptionTypes(), context);
}
process(elem.getBody(), context);
context.declaration(EmitDirection.END);
return null;
}
@Override
public Void visitContinueStatement(ContinueStatement elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
context.keyword("continue"); //$NON-NLS-1$
process(elem.getTarget(), context);
context.separator(";"); //$NON-NLS-1$
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitDoStatement(DoStatement elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
context.keyword("do"); //$NON-NLS-1$
process(elem.getBody(), context);
context.keyword("while"); //$NON-NLS-1$
context.symbol("("); //$NON-NLS-1$
process(elem.getCondition(), context);
context.separator(")"); //$NON-NLS-1$
context.separator(";"); //$NON-NLS-1$
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitEmptyStatement(EmptyStatement elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
context.separator(";"); //$NON-NLS-1$
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitEnhancedForStatement(EnhancedForStatement elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
context.keyword("for"); //$NON-NLS-1$
context.symbol("("); //$NON-NLS-1$
process(elem.getParameter(), context);
context.separator(":"); //$NON-NLS-1$
process(elem.getExpression(), context);
context.separator(")"); //$NON-NLS-1$
process(elem.getBody(), context);
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitEnumConstantDeclaration(EnumConstantDeclaration elem, EmitContext context) {
begin(elem, context);
context.declaration(EmitDirection.BEGIN);
process(elem.getJavadoc(), context);
processBlockComment(elem, context);
processAttributes(elem.getModifiers(), context);
process(elem.getName(), context);
if (appears(elem.getArguments())) {
processParameters(elem.getArguments(), context);
}
process(elem.getBody(), context);
context.separator(","); //$NON-NLS-1$
context.declaration(EmitDirection.END);
return null;
}
@Override
public Void visitEnumDeclaration(EnumDeclaration elem, EmitContext context) {
begin(elem, context);
context.declaration(EmitDirection.BEGIN);
process(elem.getJavadoc(), context);
processBlockComment(elem, context);
processAttributes(elem.getModifiers(), context);
context.keyword("enum"); //$NON-NLS-1$
process(elem.getName(), context);
if (appears(elem.getSuperInterfaceTypes())) {
context.keyword("implements"); //$NON-NLS-1$
processJoinWithComma(elem.getSuperInterfaceTypes(), context);
}
context.classBlock(EmitDirection.BEGIN);
process(elem.getConstantDeclarations(), context);
if (appears(elem.getBodyDeclarations())) {
context.declaration(EmitDirection.BEGIN);
context.separator(";"); //$NON-NLS-1$
context.declaration(EmitDirection.END);
process(elem.getBodyDeclarations(), context);
}
context.classBlock(EmitDirection.END);
context.declaration(EmitDirection.END);
return null;
}
@Override
public Void visitExpressionStatement(ExpressionStatement elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
process(elem.getExpression(), context);
context.separator(";"); //$NON-NLS-1$
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitFieldAccessExpression(FieldAccessExpression elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getQualifier(), context);
context.symbol("."); //$NON-NLS-1$
process(elem.getName(), context);
return null;
}
@Override
public Void visitFieldDeclaration(FieldDeclaration elem, EmitContext context) {
begin(elem, context);
context.declaration(EmitDirection.BEGIN);
process(elem.getJavadoc(), context);
processBlockComment(elem, context);
processAttributes(elem.getModifiers(), context);
process(elem.getType(), context);
processJoinWithComma(elem.getVariableDeclarators(), context);
context.separator(";"); //$NON-NLS-1$
context.declaration(EmitDirection.END);
return null;
}
@Override
public Void visitFormalParameterDeclaration(FormalParameterDeclaration elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getModifiers(), context); // one liner instead of processAttributes(...)
process(elem.getType(), context);
if (elem.isVariableArity()) {
context.separator("..."); //$NON-NLS-1$
}
process(elem.getName(), context);
for (int i = 0, n = elem.getExtraDimensions(); i < n; i++) {
context.symbol("["); //$NON-NLS-1$
context.separator("]"); //$NON-NLS-1$
}
return null;
}
@Override
public Void visitForStatement(ForStatement elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
context.keyword("for"); //$NON-NLS-1$
context.symbol("("); //$NON-NLS-1$
if (elem.getInitialization() instanceof LocalVariableDeclaration) {
LocalVariableDeclaration decl = (LocalVariableDeclaration) elem.getInitialization();
processLocalVaribale(decl, context);
} else {
process(elem.getInitialization(), context);
}
context.separator(";"); //$NON-NLS-1$
process(elem.getCondition(), context);
context.separator(";"); //$NON-NLS-1$
process(elem.getUpdate(), context);
context.separator(")"); //$NON-NLS-1$
process(elem.getBody(), context);
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitIfStatement(IfStatement elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
context.keyword("if"); //$NON-NLS-1$
context.symbol("("); //$NON-NLS-1$
process(elem.getCondition(), context);
context.separator(")"); //$NON-NLS-1$
process(elem.getThenStatement(), context);
if (appears(elem.getElseStatement())) {
context.keyword("else"); //$NON-NLS-1$
process(elem.getElseStatement(), context);
}
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitImportDeclaration(ImportDeclaration elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.declaration(EmitDirection.BEGIN);
context.keyword("import"); //$NON-NLS-1$
if (elem.getImportKind().getTarget() == Target.MEMBER) {
context.keyword("static"); //$NON-NLS-1$
}
process(elem.getName(), context);
if (elem.getImportKind().getRange() == Range.ON_DEMAND) {
context.symbol("."); //$NON-NLS-1$
context.symbol("*"); //$NON-NLS-1$
}
context.separator(";"); //$NON-NLS-1$
context.declaration(EmitDirection.END);
return null;
}
@Override
public Void visitInfixExpression(InfixExpression elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getLeftOperand(), context);
context.operator(elem.getOperator().getSymbol());
process(elem.getRightOperand(), context);
return null;
}
@Override
public Void visitInitializerDeclaration(InitializerDeclaration elem, EmitContext context) {
begin(elem, context);
context.declaration(EmitDirection.BEGIN);
processBlockComment(elem, context);
processAttributes(elem.getModifiers(), context);
process(elem.getBody(), context);
context.declaration(EmitDirection.END);
return null;
}
@Override
public Void visitInstanceofExpression(InstanceofExpression elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getExpression(), context);
context.keyword("instanceof"); //$NON-NLS-1$
process(elem.getType(), context);
return null;
}
@Override
public Void visitInterfaceDeclaration(InterfaceDeclaration elem, EmitContext context) {
begin(elem, context);
context.declaration(EmitDirection.BEGIN);
process(elem.getJavadoc(), context);
processBlockComment(elem, context);
processAttributes(elem.getModifiers(), context);
context.keyword("interface"); //$NON-NLS-1$
process(elem.getName(), context);
processTypeParameters(elem.getTypeParameters(), context);
if (appears(elem.getSuperInterfaceTypes())) {
context.keyword("extends"); //$NON-NLS-1$
processJoinWithComma(elem.getSuperInterfaceTypes(), context);
}
context.classBlock(EmitDirection.BEGIN);
process(elem.getBodyDeclarations(), context);
context.classBlock(EmitDirection.END);
context.declaration(EmitDirection.END);
return null;
}
@Override
public Void visitLabeledStatement(LabeledStatement elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
process(elem.getLabel(), context);
context.separator(":"); //$NON-NLS-1$
process(elem.getBody(), context);
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitLambdaExpression(LambdaExpression elem, EmitContext context) {
List<? extends LambdaParameter> parameters = elem.getParameters();
if (parameters.size() == 1 && parameters.get(0).getModelKind() == ModelKind.SIMPLE_NAME) {
process(parameters.get(0), context);
} else {
context.symbol("("); //$NON-NLS-1$
processJoinWithComma(parameters, context);
context.separator(")"); //$NON-NLS-1$
}
context.operator("->"); //$NON-NLS-1$
process(elem.getBody(), context);
return null;
}
@Override
public Void visitLineComment(LineComment elem, EmitContext context) {
String body = elem.getString();
if (body.startsWith("//")) { //$NON-NLS-1$
body = body.substring(2);
}
if (body.startsWith(" ")) { //$NON-NLS-1$
body = body.substring(1);
}
context.putLineComment(body);
return null;
}
@Override
public Void visitLiteral(Literal elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
context.immediate(elem.getToken());
return null;
}
@Override
public Void visitLocalClassDeclaration(LocalClassDeclaration elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
process(elem.getDeclaration(), context);
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitLocalVariableDeclaration(LocalVariableDeclaration elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
processLocalVaribale(elem, context);
context.separator(";"); //$NON-NLS-1$
context.statement(EmitDirection.END);
return null;
}
private void processLocalVaribale(LocalVariableDeclaration elem, EmitContext context) {
begin(elem, context);
processAttributes(elem.getModifiers(), context);
process(elem.getType(), context);
processJoinWithComma(elem.getVariableDeclarators(), context);
}
@Override
public Void visitMarkerAnnotation(MarkerAnnotation elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
context.symbol("@"); //$NON-NLS-1$
process(elem.getType(), context);
return null;
}
@Override
public Void visitMethodDeclaration(MethodDeclaration elem, EmitContext context) {
begin(elem, context);
context.declaration(EmitDirection.BEGIN);
process(elem.getJavadoc(), context);
processBlockComment(elem, context);
processAttributes(elem.getModifiers(), context);
processTypeParameters(elem.getTypeParameters(), context);
process(elem.getReturnType(), context);
process(elem.getName(), context);
processParameters(elem.getFormalParameters(), context);
for (int i = 0, n = elem.getExtraDimensions(); i < n; i++) {
context.symbol("["); //$NON-NLS-1$
context.separator("]"); //$NON-NLS-1$
}
if (appears(elem.getExceptionTypes())) {
context.keyword("throws"); //$NON-NLS-1$
processJoinWithComma(elem.getExceptionTypes(), context);
}
if (appears(elem.getBody())) {
process(elem.getBody(), context);
} else {
context.separator(";"); //$NON-NLS-1$
}
context.declaration(EmitDirection.END);
return null;
}
@Override
public Void visitMethodInvocationExpression(MethodInvocationExpression elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
if (process(elem.getQualifier(), context)) {
context.symbol("."); //$NON-NLS-1$
}
processTypeParameters(elem.getTypeArguments(), context);
process(elem.getName(), context);
processParameters(elem.getArguments(), context);
return null;
}
@Override
public Void visitConstructorReferenceExpression(ConstructorReferenceExpression elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getQualifier(), context);
context.symbol("::"); //$NON-NLS-1$
processTypeParameters(elem.getTypeArguments(), context);
context.keyword("new"); //$NON-NLS-1$
return null;
}
@Override
public Void visitMethodReferenceExpression(MethodReferenceExpression elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getQualifier(), context);
context.symbol("::"); //$NON-NLS-1$
processTypeParameters(elem.getTypeArguments(), context);
process(elem.getName(), context);
return null;
}
@Override
public Void visitModifier(Modifier elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
context.keyword(elem.getModifierKind().getKeyword());
return null;
}
@Override
public Void visitNamedType(NamedType elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getName(), context);
return null;
}
@Override
public Void visitNormalAnnotation(NormalAnnotation elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
context.symbol("@"); //$NON-NLS-1$
process(elem.getType(), context);
processParameters(elem.getElements(), context);
return null;
}
@Override
public Void visitPackageDeclaration(PackageDeclaration elem, EmitContext context) {
begin(elem, context);
context.declaration(EmitDirection.BEGIN);
process(elem.getJavadoc(), context);
processBlockComment(elem, context);
context.keyword("package"); //$NON-NLS-1$
process(elem.getName(), context);
context.separator(";"); //$NON-NLS-1$
context.declaration(EmitDirection.END);
return null;
}
@Override
public Void visitParameterizedType(ParameterizedType elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getType(), context);
// may be diamond operator
context.symbol("<"); //$NON-NLS-1$
processJoinWithComma(elem.getTypeArguments(), context);
context.separator(">"); //$NON-NLS-1$
return null;
}
@Override
public Void visitParenthesizedExpression(ParenthesizedExpression elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
context.symbol("("); //$NON-NLS-1$
process(elem.getExpression(), context);
context.separator(")"); //$NON-NLS-1$
return null;
}
@Override
public Void visitPostfixExpression(PostfixExpression elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getOperand(), context);
context.operator(elem.getOperator().getSymbol());
return null;
}
@Override
public Void visitQualifiedName(QualifiedName elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getQualifier(), context);
context.symbol("."); //$NON-NLS-1$
process(elem.getSimpleName(), context);
return null;
}
@Override
public Void visitQualifiedType(QualifiedType elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getQualifier(), context);
context.symbol("."); //$NON-NLS-1$
process(elem.getSimpleName(), context);
return null;
}
@Override
public Void visitReturnStatement(ReturnStatement elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
context.keyword("return"); //$NON-NLS-1$
process(elem.getExpression(), context);
context.separator(";"); //$NON-NLS-1$
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitSimpleName(SimpleName elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
context.immediate(elem.getToken());
return null;
}
@Override
public Void visitSingleElementAnnotation(SingleElementAnnotation elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
context.symbol("@"); //$NON-NLS-1$
process(elem.getType(), context);
context.symbol("("); //$NON-NLS-1$
process(elem.getExpression(), context);
context.separator(")"); //$NON-NLS-1$
return null;
}
@Override
public Void visitStatementExpressionList(StatementExpressionList elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
processJoinWithComma(elem.getExpressions(), context);
return null;
}
@Override
public Void visitSuper(Super elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
if (process(elem.getQualifier(), context)) {
context.symbol("."); //$NON-NLS-1$
}
context.keyword("super"); //$NON-NLS-1$
return null;
}
@Override
public Void visitSuperConstructorInvocation(SuperConstructorInvocation elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
if (process(elem.getQualifier(), context)) {
context.symbol("."); //$NON-NLS-1$
}
processTypeParameters(elem.getTypeArguments(), context);
context.keyword("super"); //$NON-NLS-1$
processParameters(elem.getArguments(), context);
context.separator(";"); //$NON-NLS-1$
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitSwitchCaseLabel(SwitchCaseLabel elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
context.keyword("case"); //$NON-NLS-1$
process(elem.getExpression(), context);
context.symbol(":"); //$NON-NLS-1$
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitSwitchDefaultLabel(SwitchDefaultLabel elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
context.keyword("default"); //$NON-NLS-1$
context.symbol(":"); //$NON-NLS-1$
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitSwitchStatement(SwitchStatement elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
context.keyword("switch"); //$NON-NLS-1$
context.symbol("("); //$NON-NLS-1$
process(elem.getExpression(), context);
context.separator(")"); //$NON-NLS-1$
context.statementBlock(EmitDirection.BEGIN);
processSwitchBody(elem, context);
context.statementBlock(EmitDirection.END);
context.statement(EmitDirection.END);
return null;
}
private void processSwitchBody(SwitchStatement elem, EmitContext context) {
if (appears(elem.getStatements()) == false) {
return;
}
boolean inLabel = false;
for (Statement stmt : elem.getStatements()) {
if (stmt instanceof SwitchLabel) {
if (inLabel) {
context.switchLabel(EmitDirection.END);
}
process(stmt, context);
context.switchLabel(EmitDirection.BEGIN);
inLabel = true;
} else {
process(stmt, context);
}
}
if (inLabel) {
context.switchLabel(EmitDirection.END);
}
}
@Override
public Void visitSynchronizedStatement(SynchronizedStatement elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
context.keyword("synchronized"); //$NON-NLS-1$
context.symbol("("); //$NON-NLS-1$
process(elem.getExpression(), context);
context.separator(")"); //$NON-NLS-1$
process(elem.getBody(), context);
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitThis(This elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
if (process(elem.getQualifier(), context)) {
context.symbol("."); //$NON-NLS-1$
}
context.keyword("this"); //$NON-NLS-1$
return null;
}
@Override
public Void visitThrowStatement(ThrowStatement elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
context.keyword("throw"); //$NON-NLS-1$
process(elem.getExpression(), context);
context.separator(";"); //$NON-NLS-1$
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitTryResource(TryResource elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getParameter(), context);
context.operator("="); //$NON-NLS-1$
process(elem.getInitializer(), context);
return null;
}
@Override
public Void visitTryStatement(TryStatement elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
context.keyword("try"); //$NON-NLS-1$
if (appears(elem.getResources())) {
context.symbol("("); //$NON-NLS-1$
boolean first = true;
for (TryResource resource : elem.getResources()) {
if (first) {
first = false;
} else {
context.separator(";"); //$NON-NLS-1$
context.blockPadding();
}
process(resource, context);
}
context.separator(")");
}
process(elem.getTryBlock(), context);
process(elem.getCatchClauses(), context);
if (appears(elem.getFinallyBlock())) {
context.keyword("finally"); //$NON-NLS-1$
process(elem.getFinallyBlock(), context);
}
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitTypeParameterDeclaration(TypeParameterDeclaration elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getName(), context);
Iterator<? extends Type> iter = elem.getTypeBounds().iterator();
if (iter.hasNext()) {
context.keyword("extends"); //$NON-NLS-1$
process(iter.next(), context);
while (iter.hasNext()) {
context.separator("&"); //$NON-NLS-1$
process(iter.next(), context);
}
}
return null;
}
@Override
public Void visitUnaryExpression(UnaryExpression elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
context.operator(elem.getOperator().getSymbol());
process(elem.getOperand(), context);
return null;
}
@Override
public Void visitUnionType(UnionType elem, EmitContext context) {
begin(elem, context);
processJoin("|", elem.getAlternativeTypes(), context); //$NON-NLS-1$
return null;
}
@Override
public Void visitVariableDeclarator(VariableDeclarator elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
process(elem.getName(), context);
for (int i = 0, n = elem.getExtraDimensions(); i < n; i++) {
context.symbol("["); //$NON-NLS-1$
context.separator("]"); //$NON-NLS-1$
}
if (appears(elem.getInitializer())) {
context.operator("="); //$NON-NLS-1$
process(elem.getInitializer(), context);
}
return null;
}
@Override
public Void visitWhileStatement(WhileStatement elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.statement(EmitDirection.BEGIN);
context.keyword("while"); //$NON-NLS-1$
context.symbol("("); //$NON-NLS-1$
process(elem.getCondition(), context);
context.separator(")"); //$NON-NLS-1$
process(elem.getBody(), context);
context.statement(EmitDirection.END);
return null;
}
@Override
public Void visitWildcard(Wildcard elem, EmitContext context) {
begin(elem, context);
processInlineComment(elem, context);
context.keyword("?"); //$NON-NLS-1$
if (elem.getBoundKind() == WildcardBoundKind.UPPER_BOUNDED) {
context.keyword("extends"); //$NON-NLS-1$
process(elem.getTypeBound(), context);
} else if (elem.getBoundKind() == WildcardBoundKind.LOWER_BOUNDED) {
context.keyword("super"); //$NON-NLS-1$
process(elem.getTypeBound(), context);
}
return null;
}
@Override
public Void visitJavadoc(Javadoc elem, EmitContext context) {
begin(elem, context);
processBlockComment(elem, context);
context.docComment(EmitDirection.BEGIN);
for (DocBlock block : elem.getBlocks()) {
context.docBlock(EmitDirection.BEGIN);
process(block, context);
context.docBlock(EmitDirection.END);
}
context.docComment(EmitDirection.END);
return null;
}
@Override
public Void visitDocBlock(DocBlock elem, EmitContext context) {
begin(elem, context);
String tag = elem.getTag();
if (tag.length() != 0) {
context.separator(tag);
context.padding();
}
int offset = 0;
List<? extends DocElement> elements = elem.getElements();
if (tag.equals("@param") && isDocTypeParameter(elements)) { //$NON-NLS-1$
// @param <T>
context.symbol("<"); //$NON-NLS-1$
context.symbol(((SimpleName) elements.get(1)).getToken());
context.symbol(">"); //$NON-NLS-1$
context.padding();
offset = 3;
}
for (int i = offset, n = elements.size(); i < n; i++) {
processDocInlineElement(elements.get(i), i == n - 1, context);
}
return null;
}
private boolean isDocTypeParameter(List<? extends DocElement> elements) {
if (elements.size() < 3) {
return false;
}
if (elements.get(0).getModelKind() != ModelKind.DOC_TEXT) {
return false;
}
if (elements.get(1).getModelKind() != ModelKind.SIMPLE_NAME) {
return false;
}
if (elements.get(2).getModelKind() != ModelKind.DOC_TEXT) {
return false;
}
if (((DocText) elements.get(0)).getString().equals("<") == false) { //$NON-NLS-1$
return false;
}
if (((DocText) elements.get(2)).getString().equals(">") == false) { //$NON-NLS-1$
return false;
}
return true;
}
private void processDocInlineElement(DocElement elem, boolean last, EmitContext context) {
if (elem.getModelKind() == ModelKind.DOC_BLOCK) {
context.docInlineBlock(EmitDirection.BEGIN);
process(elem, context);
context.docInlineBlock(EmitDirection.END);
} else if (elem.getModelKind() == ModelKind.DOC_TEXT) {
process(elem, context);
} else {
context.padding();
process(elem, context);
if (last == false) {
context.padding();
}
}
}
@Override
public Void visitDocField(DocField elem, EmitContext context) {
begin(elem, context);
process(elem.getType(), context);
context.symbol("#"); //$NON-NLS-1$
process(elem.getName(), context);
return null;
}
@Override
public Void visitDocMethod(DocMethod elem, EmitContext context) {
begin(elem, context);
process(elem.getType(), context);
context.symbol("#"); //$NON-NLS-1$
process(elem.getName(), context);
context.symbol("("); //$NON-NLS-1$
processJoinWithComma(elem.getFormalParameters(), context);
context.separator(")"); //$NON-NLS-1$
return null;
}
@Override
public Void visitDocMethodParameter(DocMethodParameter elem, EmitContext context) {
begin(elem, context);
process(elem.getType(), context);
if (elem.isVariableArity()) {
context.separator("..."); //$NON-NLS-1$
}
process(elem.getName(), context);
return null;
}
@Override
public Void visitDocText(DocText elem, EmitContext context) {
begin(elem, context);
if (elem.getString().startsWith(" ")) { //$NON-NLS-1$
context.symbol(elem.getString());
} else {
context.immediate(elem.getString());
}
return null;
}
private void begin(Model elem, EmitContext context) {
return;
}
private boolean appears(Model element) {
return element != null;
}
private boolean appears(List<? extends Model> elements) {
return elements.isEmpty() == false;
}
private boolean process(Model element, EmitContext context) {
if (element == null) {
return false;
}
element.accept(this, context);
return true;
}
private boolean process(List<? extends Model> elements, EmitContext context) {
for (Model element : elements) {
element.accept(this, context);
}
return true;
}
private void processJoinWithComma(List<? extends Model> elements, EmitContext context) {
processJoin(",", elements, context); //$NON-NLS-1$
}
private void processJoin(String separator, List<? extends Model> elements, EmitContext context) {
Iterator<? extends Model> iter = elements.iterator();
if (iter.hasNext()) {
process(iter.next(), context);
while (iter.hasNext()) {
context.separator(separator);
process(iter.next(), context);
}
}
}
private void processAttributes(List<? extends Attribute> elements, EmitContext context) {
for (Attribute element : elements) {
element.accept(this, context);
switch (element.getModelKind()) {
case MARKER_ANNOTATION:
case SINGLE_ELEMENT_ANNOTATION:
case NORMAL_ANNOTATION:
context.blockPadding();
break;
default:
break;
}
}
}
private void processParameters(List<? extends Model> elements, EmitContext context) {
context.symbol("("); //$NON-NLS-1$
processJoinWithComma(elements, context);
context.separator(")"); //$NON-NLS-1$
}
private void processTypeParameters(List<? extends Model> elements, EmitContext context) {
if (appears(elements)) {
context.symbol("<"); //$NON-NLS-1$
processJoinWithComma(elements, context);
context.separator(">"); //$NON-NLS-1$
}
}
private void processCompilationUnitComment(CompilationUnit elem, EmitContext context) {
CommentEmitTrait comment = elem.findModelTrait(CommentEmitTrait.class);
if (comment == null) {
return;
}
context.putBlockComment(comment.getContents());
}
private void processBlockComment(Model elem, EmitContext context) {
CommentEmitTrait comment = elem.findModelTrait(CommentEmitTrait.class);
if (comment == null) {
return;
}
for (String line : comment.getContents()) {
context.putLineComment(line);
}
}
private void processInlineComment(Model elem, EmitContext context) {
CommentEmitTrait comment = elem.findModelTrait(CommentEmitTrait.class);
if (comment == null) {
return;
}
for (String line : comment.getContents()) {
context.putInlineComment(line);
}
}
}