Java tutorial
/******************************************************************************* * Copyright (c) 2006, 2008 Abstratt Technologies * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Rafael Chaves (Abstratt Technologies) - initial API and implementation *******************************************************************************/ package com.abstratt.mdd.internal.frontend.textuml; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.beanutils.MethodUtils; import com.abstratt.mdd.frontend.core.ASTNode; import com.abstratt.mdd.frontend.textuml.core.TextUMLCore; import com.abstratt.mdd.internal.frontend.textuml.analysis.Analysis; import com.abstratt.mdd.internal.frontend.textuml.analysis.DepthFirstAdapter; import com.abstratt.mdd.internal.frontend.textuml.node.AAnnotations; import com.abstratt.mdd.internal.frontend.textuml.node.AAssociationDef; import com.abstratt.mdd.internal.frontend.textuml.node.AAssociationRoleDecl; import com.abstratt.mdd.internal.frontend.textuml.node.AAttributeInvariant; import com.abstratt.mdd.internal.frontend.textuml.node.ABehavioralFeatureBody; import com.abstratt.mdd.internal.frontend.textuml.node.ABinaryExpression; import com.abstratt.mdd.internal.frontend.textuml.node.ABlockNonIfStatement; import com.abstratt.mdd.internal.frontend.textuml.node.ACast; import com.abstratt.mdd.internal.frontend.textuml.node.ACatchSection; import com.abstratt.mdd.internal.frontend.textuml.node.AClassDef; import com.abstratt.mdd.internal.frontend.textuml.node.AClauseBody; import com.abstratt.mdd.internal.frontend.textuml.node.AClosure; import com.abstratt.mdd.internal.frontend.textuml.node.AElseRestIf; import com.abstratt.mdd.internal.frontend.textuml.node.AElseifRestIf; import com.abstratt.mdd.internal.frontend.textuml.node.AFeatureDecl; import com.abstratt.mdd.internal.frontend.textuml.node.AGlobalDirectiveSection; import com.abstratt.mdd.internal.frontend.textuml.node.AIfClause; import com.abstratt.mdd.internal.frontend.textuml.node.AIfStatement; import com.abstratt.mdd.internal.frontend.textuml.node.AInvariantKernel; import com.abstratt.mdd.internal.frontend.textuml.node.AIsClassifiedExpression; import com.abstratt.mdd.internal.frontend.textuml.node.ANamedArgument; import com.abstratt.mdd.internal.frontend.textuml.node.ANoIfStatementResolved; import com.abstratt.mdd.internal.frontend.textuml.node.ANonBlockNonIfStatement; import com.abstratt.mdd.internal.frontend.textuml.node.AOperationDecl; import com.abstratt.mdd.internal.frontend.textuml.node.AOperationPrecondition; import com.abstratt.mdd.internal.frontend.textuml.node.AParamDecl; import com.abstratt.mdd.internal.frontend.textuml.node.ARootExpression; import com.abstratt.mdd.internal.frontend.textuml.node.ASendSpecificStatement; import com.abstratt.mdd.internal.frontend.textuml.node.ASignature; import com.abstratt.mdd.internal.frontend.textuml.node.ASimpleExpressionBlock; import com.abstratt.mdd.internal.frontend.textuml.node.ASimpleStatementBlock; import com.abstratt.mdd.internal.frontend.textuml.node.AStart; import com.abstratt.mdd.internal.frontend.textuml.node.AStateBehavior; import com.abstratt.mdd.internal.frontend.textuml.node.AStateDecl; import com.abstratt.mdd.internal.frontend.textuml.node.AStateMachineDecl; import com.abstratt.mdd.internal.frontend.textuml.node.AStatement; import com.abstratt.mdd.internal.frontend.textuml.node.AStereotypeDef; import com.abstratt.mdd.internal.frontend.textuml.node.AStereotypePropertyDecl; import com.abstratt.mdd.internal.frontend.textuml.node.ASubNamespace; import com.abstratt.mdd.internal.frontend.textuml.node.ATopLevelElement; import com.abstratt.mdd.internal.frontend.textuml.node.ATransitionDecl; import com.abstratt.mdd.internal.frontend.textuml.node.ATransitionGuard; import com.abstratt.mdd.internal.frontend.textuml.node.ATryStatement; import com.abstratt.mdd.internal.frontend.textuml.node.ATupleComponentValue; import com.abstratt.mdd.internal.frontend.textuml.node.ATupleComponentValueTail; import com.abstratt.mdd.internal.frontend.textuml.node.ATupleConstructor; import com.abstratt.mdd.internal.frontend.textuml.node.AUnaryExpression; import com.abstratt.mdd.internal.frontend.textuml.node.AVarDeclSection; import com.abstratt.mdd.internal.frontend.textuml.node.AWhileStatement; import com.abstratt.mdd.internal.frontend.textuml.node.AWithIfStatementResolved; import com.abstratt.mdd.internal.frontend.textuml.node.AWordyBlock; import com.abstratt.mdd.internal.frontend.textuml.node.Node; import com.abstratt.mdd.internal.frontend.textuml.node.PGlobalDirective; import com.abstratt.mdd.internal.frontend.textuml.node.TAssignop; import com.abstratt.mdd.internal.frontend.textuml.node.TColon; import com.abstratt.mdd.internal.frontend.textuml.node.TComma; import com.abstratt.mdd.internal.frontend.textuml.node.TComment; import com.abstratt.mdd.internal.frontend.textuml.node.TModelComment; import com.abstratt.mdd.internal.frontend.textuml.node.TWhiteSpace; import com.abstratt.mdd.internal.frontend.textuml.node.Token; import com.abstratt.pluginutils.LogUtils; /** * Produces a formatted output from the parsed node. * * Implementation notes: * */ public class TextUMLFormatter { private static String LINE_ENDING = System.getProperty("line.separator"); private static int LINE_ENDING_LENGTH = LINE_ENDING.length(); public static void main(String[] args) { // String toFormat = // "package mypak; import yourpack1; import yourpack2; class // ExampleClass extends AnotherClass implements Interface1, Interface2, // Interface3 attribute attr1 : Integer; operation op1(); operation // op2(); begin if false then x := 1 else while true do begin x := x + // 1; end; end; operation op3() : Integer; begin var x : Integer, y : // Integer; x := 10; y := 1; while y < x do begin x := x + 1; end; y := // self.compute(15,60); return x + y; end; end; operation // ExampleClass.op1;begin end; end."; // String toFormat = // "package mypak; import yourpack1; import yourpack2; [persistent]class // ExampleClass specializes AnotherClass implements Interface1, // Interface2, Interface3 [transient] attribute attr1 : Integer; [ // transactional ]operation op1(); operation op2(); end; end."; String toFormat = "model bank;apply foo;import xyz;apply java;[persistent ,external ( language= \"Java\" , className\n= \n\"Foo\")]class Account attribute accountNumber : base::String; [ transient] attribute balance : base::Real; attribute changes : AccountChange[0,*]; operation withdraw(amount : Real); operation deposit(amount : Real); operation balance() : Real; operation transfer(other : Account, amount : Real); end;[persistent]class Client operation getAccounts() : Account[0, *];end;enumeration TestEnum VALUE1, VALUE2, VALUE3 end;class DateTime /* attribute day : Integer; attribute month : Integer; attribute year : Integer; attribute hour : Integer; attribute minute : Integer; attribute second : Integer; */ end; [persistent]abstract class AccountChange attribute date : DateTime;end;composition AccountAccountChange navigable role account : Account[1]; navigable role Account.changes;end;class SimpleAccountChange specializes AccountChange attribute amount : Real; end;[persistent]class Deposit specializes SimpleAccountChange end;[persistent]class Withdrawal specializes SimpleAccountChange end;[persistent]class Transfer specializes AccountChange attribute secondAccount : Account;end; [persistent]class BusinessClient specializes Client attribute businessNumber : String;end;aggregation ClientAccount navigable role owner : Client[1]; navigable role account : Account[0,*];end;end."; System.out.println(new TextUMLCompiler().format(toFormat)); } private Analysis ignoredTokens; /** * Reflection-based formatter lookup is slow, so we cache formatter methods based on the node. */ private Map<Class<? extends Node>, Method> formatters = new HashMap<Class<? extends Node>, Method>(); public void format(AAnnotations node, StringBuilder output, int indentation) { boolean newLine = isAtNewLine(output); boolean returnTypeAnnotation = !newLine && isAt(output, ')'); if (returnTypeAnnotation) addWhitespace(output); doGenericFormat(node, output, indentation); if (newLine) newLine(output); else if (!returnTypeAnnotation) addWhitespace(output); } public void format(ASubNamespace node, StringBuilder output, int indentation) { format(node.getPackageHeading(), output, indentation); newLine(output); format(node.getNamespaceContents(), output, indentation + 1); newLine(output); format(node.getEnd(), output, indentation); format(node.getSemicolon(), output, indentation); } public void format(AAssociationDef node, StringBuilder output, int indentation) { format(node.getAnnotations(), output, indentation); format(node.getAssociationHeader(), output, indentation); newLine(output); newLine(output); format(node.getAssociationRoleDeclList(), output, indentation + 1); format(node.getEnd(), output, indentation); format(node.getSemicolon(), output, indentation); } public void format(AAssociationRoleDecl node, StringBuilder output, int indentation) { doGenericFormat(node, output, indentation); newLine(output); newLine(output); } public void format(ABinaryExpression node, StringBuilder output, int indentation) { format(node.getExpression(), output, indentation); addWhitespace(output); format(node.getBinaryOperator(), output, indentation); addWhitespace(output); format(node.getOperand(), output, indentation); } public void format(AIsClassifiedExpression node, StringBuilder output, int indentation) { format(node.getExpression(), output, indentation); addWhitespace(output); format(node.getIs(), output, indentation); addWhitespace(output); format(node.getQualifiedIdentifier(), output, indentation); } public void format(ARootExpression node, StringBuilder output, int indentation) { addWhitespace(output); format(node.getExpression(), output, indentation); } public void format(ATupleConstructor node, StringBuilder output, int indentation) { format(node.getLCurlyBracket(), output, indentation); newLine(output); format(node.getTupleComponentValue(), output, indentation); format(node.getTupleComponentValueTail(), output, indentation); newLine(output); format(node.getRCurlyBracket(), output, indentation); } public void format(ATupleComponentValue node, StringBuilder output, int indentation) { format(node.getIdentifier(), output, indentation + 1); format(node.getAssignop(), output, indentation + 1); format(node.getExpression(), output, indentation + 1); } public void format(ATupleComponentValueTail node, StringBuilder output, int indentation) { format(node.getComma(), output, indentation); newLine(output); format(node.getTupleComponentValue(), output, indentation); } public void format(AClassDef node, StringBuilder output, int indentation) { format(node.getAnnotations(), output, indentation); format(node.getClassHeader(), output, indentation); format(node.getFeatureDeclList(), output, indentation + 1); newLine(output); format(node.getEnd(), output, indentation); format(node.getSemicolon(), output, indentation); } public void format(AStereotypeDef node, StringBuilder output, int indentation) { format(node.getAnnotations(), output, indentation); format(node.getStereotypeDefHeader(), output, indentation); format(node.getStereotypePropertyDecl(), output, indentation + 1); newLine(output); format(node.getEnd(), output, indentation); format(node.getSemicolon(), output, indentation); } public void format(AFeatureDecl node, StringBuilder output, int indentation) { newLine(output); newLine(output); doGenericFormat(node, output, indentation); } public void format(AStereotypePropertyDecl node, StringBuilder output, int indentation) { newLine(output); doGenericFormat(node, output, indentation); } public void format(AOperationDecl node, StringBuilder output, int indentation) { format(node.getOperationHeader(), output, indentation); format(node.getOperationPrecondition(), output, indentation + 1); format(node.getSemicolon(), output, indentation); format(node.getOptionalBehavioralFeatureBody(), output, indentation); } public void format(ASignature node, StringBuilder output, int indentation) { format(node.getLParen(), output, indentation); format(node.getParamDeclList(), output, indentation); format(node.getRParen(), output, indentation); format(node.getOptionalReturnType(), output, indentation); if (node.getOptionalRaisesSection() != null) { addWhitespace(output); format(node.getOptionalRaisesSection(), output, indentation); } } public void format(AParamDecl node, StringBuilder output, int indentation) { if (getColumn(output) > 80) { indentation += 2; newLine(output); } doGenericFormat(node, output, indentation); } private void breakIfNeeded(Node node, StringBuilder output, int indentation, int limit, boolean space) { if (getColumn(output) + node.toString().length() > limit) { newLine(output); format(node, output, indentation + 1); newLine(output); } else { if (space) addWhitespace(output); format(node, output, indentation); if (space) addWhitespace(output); } } public void format(AAttributeInvariant node, StringBuilder output, int indentation) { newLine(output); format(node.getModelComment(), output, indentation + 1); format(node.getAnnotations(), output, indentation + 1); format(node.getInvariantKernel(), output, indentation + 1); } public void format(AOperationPrecondition node, StringBuilder output, int indentation) { newLine(output); format(node.getModelComment(), output, indentation); format(node.getPrecondition(), output, indentation); addWhitespace(output); format(node.getIdentifier(), output, indentation); format(node.getPreconditionSignature(), output, indentation); format(node.getConstraintException(), output, indentation); addWhitespace(output); format(node.getExpressionBlock(), output, indentation); } protected void addWhitespace(StringBuilder output) { if (!isAtWhitespace(output) && !isAtNewLine(output)) output.append(' '); } public void format(AInvariantKernel node, StringBuilder output, int indentation) { format(node.getConstraintKeyword(), output, indentation); format(node.getIdentifier(), output, indentation); format(node.getConstraintException(), output, indentation); addWhitespace(output); format(node.getExpressionBlock(), output, indentation); } public void format(ACast node, StringBuilder output, int indentation) { addWhitespace(output); format(node.getAs(), output, indentation); addWhitespace(output); format(node.getSingleTypeIdentifier(), output, indentation); } public void format(AClosure node, StringBuilder output, int indentation) { format(node.getSimpleSignature(), output, indentation); addWhitespace(output); format(node.getBlock(), output, indentation); } public void format(ABehavioralFeatureBody node, StringBuilder output, int indentation) { newLine(output); format(node.getBlock(), output, indentation); format(node.getSemicolon(), output, indentation); } public void format(ASendSpecificStatement node, StringBuilder output, int indentation) { format(node.getSend(), output, indentation); format(node.getSignal(), output, indentation); format(node.getLParen(), output, indentation); format(node.getNamedArgumentList(), output, indentation + 1); format(node.getRParen(), output, indentation); addWhitespace(output); format(node.getTo(), output, indentation); addWhitespace(output); format(node.getTarget(), output, indentation); } public void format(ANamedArgument node, StringBuilder output, int indentation) { newLine(output); format(node.getIdentifier(), output, indentation); format(node.getAssignop(), output, indentation); addWhitespace(output); format(node.getExpression(), output, indentation); } public void format(ASimpleStatementBlock node, StringBuilder output, int indentation) { format(node.getLCurlyBracket(), output, indentation); newLine(output); format(node.getBlockKernel(), output, indentation + 1); if (!isAtNewLine(output)) newLine(output); format(node.getRCurlyBracket(), output, indentation); } public void format(ASimpleExpressionBlock node, StringBuilder output, int indentation) { format(node.getLCurlyBracket(), output, indentation); breakIfNeeded(node.getRootExpression(), output, indentation, 80, true); format(node.getRCurlyBracket(), output, indentation); } public void format(AStateMachineDecl node, StringBuilder output, int indentation) { format(node.getStatemachine(), output, indentation); format(node.getIdentifier(), output, indentation); newLine(output); format(node.getStateDecl(), output, indentation + 1); newLine(output); format(node.getEnd(), output, indentation); format(node.getSemicolon(), output, indentation); newLine(output); } public void format(AStateDecl node, StringBuilder output, int indentation) { newLine(output); format(node.getModelComment(), output, indentation); format(node.getStateModifierList(), output, indentation); format(node.getState(), output, indentation); format(node.getIdentifier(), output, indentation); if (!node.getStateBehavior().isEmpty()) { format(node.getStateBehavior(), output, indentation + 1); newLine(output); } if (!node.getTransitionDecl().isEmpty()) { format(node.getTransitionDecl(), output, indentation + 1); newLine(output); } addWhitespace(output); format(node.getEnd(), output, indentation); format(node.getSemicolon(), output, indentation); newLine(output); } public void format(AStateBehavior node, StringBuilder output, int indentation) { newLine(output); format(node.getStateBehaviorModifier(), output, indentation); addWhitespace(output); format(node.getStateBehaviorDefinition(), output, indentation); format(node.getSemicolon(), output, indentation); } public void format(AUnaryExpression node, StringBuilder output, int indentation) { format(node.getUnaryOperator(), output, indentation); addWhitespace(output); format(node.getOperand(), output, indentation); } public void format(ATransitionDecl node, StringBuilder output, int indentation) { newLine(output); format(node.getModelComment(), output, indentation); format(node.getTransition(), output, indentation); format(node.getTransitionTriggers(), output, indentation); addWhitespace(output); format(node.getTo(), output, indentation); format(node.getDestination(), output, indentation); format(node.getTransitionGuard(), output, indentation); format(node.getTransitionEffect(), output, indentation); format(node.getSemicolon(), output, indentation); } public void format(ATransitionGuard node, StringBuilder output, int indentation) { format(node.getWhen(), output, indentation); addWhitespace(output); format(node.getExpressionBlock(), output, indentation); } public void format(AStart node, StringBuilder output, int indentation) { format(node.getPackageHeading(), output, indentation); newLine(output); format(node.getGlobalDirectiveSection(), output, indentation); format(node.getNamespaceContents(), output, indentation); newLine(output); format(node.getEnd(), output, indentation); format(node.getDot(), output, indentation); } public void format(AStatement node, StringBuilder output, int indentation) { format(node.getStatementResolved(), output, indentation); // omit semicolon/newline - leave it to specific formatters to handle it } public void format(AIfStatement node, StringBuilder output, int indentation) { format(node.getIf(), output, indentation); addWhitespace(output); format(node.getIfClause(), output, indentation); format(node.getRestIf(), output, indentation); } public void format(AElseRestIf node, StringBuilder output, int indentation) { newLine(output); format(node.getElse(), output, indentation); format(node.getClauseBody(), output, indentation); } public void format(AIfClause node, StringBuilder output, int indentation) { format(node.getTest(), output, indentation); addWhitespace(output); format(node.getThen(), output, indentation); format(node.getClauseBody(), output, indentation); } public void format(AClauseBody node, StringBuilder output, int indentation) { newLine(output); doGenericFormat(node, output, isBlock(node) ? indentation : indentation + 1); } public void format(AElseifRestIf node, StringBuilder output, int indentation) { newLine(output); format(node.getElseif(), output, indentation); addWhitespace(output); format(node.getIfClause(), output, indentation); format(node.getRestIf(), output, indentation); } public void format(ANoIfStatementResolved node, StringBuilder output, int indentation) { format(node.getNonIfStatement(), output, indentation); addSemicolonAndNewline(output); } protected void addSemicolonAndNewline(StringBuilder output) { addSemicolon(output); newLine(output); } protected void addSemicolon(StringBuilder output) { output.append(";"); } public void format(AWithIfStatementResolved node, StringBuilder output, int indentation) { format(node.getIfStatement(), output, indentation); addSemicolonAndNewline(output); } public void format(ATopLevelElement node, StringBuilder output, int indentation) { newLine(output); if (node.getModelComment() != null) format(node.getModelComment(), output, indentation); format(node.getTopLevelElementChoice(), output, indentation); newLine(output); } public void format(ATryStatement node, StringBuilder output, int indentation) { format(node.getTry(), output, indentation); newLine(output); format(node.getProtectedBlock(), output, indentation + 1); newLine(output); format(node.getCatchSection(), output, indentation); format(node.getFinallySection(), output, indentation); format(node.getEnd(), output, indentation); newLine(output); } public void format(ACatchSection node, StringBuilder output, int indentation) { format(node.getCatch(), output, indentation); format(node.getLParen(), output, indentation); format(node.getVarDecl(), output, indentation); format(node.getRParen(), output, indentation); newLine(output); format(node.getHandlerBlock(), output, indentation + 1); newLine(output); } public void format(AVarDeclSection node, StringBuilder output, int indentation) { doGenericFormat(node, output, indentation); newLine(output); } public void format(AWhileStatement node, StringBuilder output, int indentation) { format(node.getWhile(), output, indentation); format(node.getLoopTest(), output, indentation); format(node.getDo(), output, indentation); newLine(output); boolean block = isBlock(node.getWhileLoopBody()); format(node.getWhileLoopBody(), output, block ? indentation : indentation + 1); } public void format(AWordyBlock node, StringBuilder output, int indentation) { format(node.getBegin(), output, indentation); newLine(output); format(node.getBlockKernel(), output, indentation + 1); format(node.getEnd(), output, indentation); } public String format(Node toFormat, Analysis ignoredTokens) { this.ignoredTokens = ignoredTokens; StringBuilder result = new StringBuilder(); format(toFormat, result, 0); return result.toString(); } private void format(List<? extends Node> nodes, StringBuilder output, int indentation) { for (Node node : nodes) format(node, output, indentation); } /** * Tries to find a more specific formatter for the given node type. If none * can be found, falls back to the generic formatter. * * @param node * @param output * @param indentation */ private void format(Node node, StringBuilder output, int indentation) { if (node == null) return; if (!formatters.containsKey(node.getClass())) { Method method = MethodUtils.getMatchingAccessibleMethod(this.getClass(), "format", new Class[] { node.getClass(), StringBuilder.class, Integer.class }); formatters.put(node.getClass(), method); } Method formatterMethod = formatters.get(node.getClass()); if (formatterMethod == null) { doGenericFormat(node, output, indentation); return; } try { formatterMethod.invoke(this, new Object[] { node, output, indentation }); } catch (IllegalArgumentException e) { LogUtils.logError(TextUMLCore.PLUGIN_ID, "Unexpected exception", e); } catch (IllegalAccessException e) { LogUtils.logError(TextUMLCore.PLUGIN_ID, "Unexpected exception", e); } catch (InvocationTargetException e) { LogUtils.logError(TextUMLCore.PLUGIN_ID, "Unexpected exception", e); } } public void format(AGlobalDirectiveSection node, StringBuilder output, int indentation) { List<PGlobalDirective> sortedDirectives = new ArrayList<PGlobalDirective>(node.getGlobalDirective()); Collections.sort(sortedDirectives, new Comparator<PGlobalDirective>() { public int compare(PGlobalDirective o1, PGlobalDirective o2) { return o1.toString().compareTo(o2.toString()); } }); Class<?> previousClass = null; for (PGlobalDirective globalDirective : sortedDirectives) { if (globalDirective.getClass() != previousClass) newLine(output); doGenericFormat(globalDirective, output, indentation); newLine(output); previousClass = globalDirective.getClass(); } } public void format(TAssignop node, StringBuilder output, int indentation) { output.append(" "); output.append(node.getText()); output.append(" "); } public void format(TColon node, StringBuilder output, int indentation) { output.append(" "); output.append(node.getText()); output.append(" "); } public void format(TComma node, StringBuilder output, int indentation) { output.append(node.getText()); output.append(" "); } public void format(TComment node, StringBuilder output, int indentation) { boolean newLine = isAtNewLine(output); if (!newLine && !isAtWhitespace(output)) addWhitespace(output); doGenericFormat(node, output, indentation); if (newLine) newLine(output); else addWhitespace(output); } public void format(TWhiteSpace node, StringBuilder output, int indentation) { // omit whitespaces } public void format(TModelComment node, StringBuilder output, int indentation) { if (node != null) { doGenericFormat(node, output, indentation); newLine(output); } } private boolean isBlock(Node node) { final boolean[] block = { false }; node.apply(new DepthFirstAdapter() { @Override public void caseABlockNonIfStatement(ABlockNonIfStatement node) { block[0] = true; } @Override public void caseANonBlockNonIfStatement(ANonBlockNonIfStatement node) { // not really necessary block[0] = false; } }); return block[0]; } private static boolean isPunctuation(String text) { return !Character.isJavaIdentifierStart(text.charAt(0)) && text.charAt(0) != '\\'; } private static void newLine(StringBuilder output) { output.append(LINE_ENDING); } @SuppressWarnings("unchecked") private void doGenericFormat(Node node, StringBuilder output, int indentation) { if (node == null) return; List<Node> ignored = (List<Node>) ignoredTokens.getIn(node); if (ignored != null) for (Node ignoredNode : ignored) format(ignoredNode, output, indentation); if (node instanceof Token) { final Token token = ((Token) node); final boolean newLine = isAtNewLine(output); if (newLine) for (int i = 0; i < indentation; i++) output.append(" "); else if (!isPunctuation(token.getText()) && !isPunctuation("" + output.charAt(output.length() - 1))) addWhitespace(output); output.append(token.getText()); } else { ASTNode<Token, Node> astNode = ASTNode.<Token, Node>buildTree(node); List<ASTNode<Token, Node>> children = astNode.getChildren(); for (Iterator<ASTNode<Token, Node>> i = children.iterator(); i.hasNext();) { ASTNode<Token, Node> element = (ASTNode<Token, Node>) i.next(); format((Node) element.getBaseNode(), output, indentation); } } } private static boolean isAtNewLine(StringBuilder output) { return output.length() == 0 || endsWith(output, LINE_ENDING); } private static int getColumn(StringBuilder output) { int last = output.length() - LINE_ENDING_LENGTH; int column = 0; while (!LINE_ENDING.equals(output.substring(last - column, last - column + LINE_ENDING_LENGTH))) column++; return column; } private static boolean isAtWhitespace(StringBuilder output) { return output.length() > 0 && Character.isWhitespace(output.charAt(output.length() - 1)); } private static boolean isAt(StringBuilder output, char... chars) { if (output.length() <= 0) return false; for (char c : chars) if (output.charAt(output.length() - 1) == c) return true; return false; } private static boolean endsWith(StringBuilder toCheck, String ending) { final int suffixLength = ending.length(); final int bufferLength = toCheck.length(); int bufferOffset = bufferLength - suffixLength; if (bufferOffset < 0) return false; for (int i = 0; i < suffixLength; i++) if (ending.charAt(i) != toCheck.charAt(bufferOffset + i)) return false; return true; } }