/* * Copyright 2013 Guidewire Software, Inc. */ package gw.plugin.ij.util.transform.java.Visitor; import com.sun.source.tree.*; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.util.Convert; import javax.lang.model.element.Modifier; import javax.lang.model.element.Name; import java.util.*; import java.util.regex.Pattern; public class GosuVisitor implements TreeVisitor<String, Void> { StringBuilder output; private int ident = 0; private static int TAB_SIZE ; private static String TAB; private SymbolTable symTable; private String currentEnumIdent; private List<String> currentResourcesIdents; private Mode mode; private boolean isEnum; private boolean isInterface; boolean skipBlockScope; private boolean skipSymConversion; private enum Mode {NORMAL, USING_NO_MODIFIERS, CATCH_PARAM, USING_CAST, METHOD_PARAM, ADD_RESOURCES_FINALLY_BLOCK, CLASS_VAR} public StringBuilder getOutput() { return output; } public GosuVisitor(int tabSize) { TAB_SIZE = tabSize; TAB = genTabSpaces(TAB_SIZE); mode = Mode.NORMAL; symTable = new SymbolTable(); output = new StringBuilder(); skipBlockScope = false; skipSymConversion = false; } private String genTabSpaces(int x) { String tab = ""; while(x > 0) { tab += " "; x--; } return tab; } public String visitImport(ImportTree node, Void v) { StringBuilder out = new StringBuilder(); Tree qualId = node.getQualifiedIdentifier(); out.append("uses "); out.append(qualId.accept(this, v)); out.append("\n"); return out.toString(); } public String visitCompilationUnit(CompilationUnitTree node, Void v) { ident = 0; if (!node.getPackageAnnotations().isEmpty()) { throw new AssertionError("Annotations on packages not supported"); } ExpressionTree packageName = node.getPackageName(); if (packageName != null) { output.append("package "); output.append(packageName); output.append("\n\n"); } output.append("uses java.lang.*\n"); List<? extends ImportTree> imports = node.getImports(); for (ImportTree imp : imports) { output.append(imp.accept(this, v)); } output.append("\n"); List<? extends Tree> typeDecls = node.getTypeDecls(); for (Tree typeDecl : typeDecls) { output.append(typeDecl.accept(this, v)); } return output.toString(); } public String visitClass(ClassTree node, Void v) { StringBuilder out = new StringBuilder(); mode = Mode.NORMAL; Name name = node.getSimpleName(); List<? extends TypeParameterTree> typeParameters = node.getTypeParameters(); String declType = null; Tree.Kind kind = node.getKind(); isEnum = false; isInterface = false; switch (kind) { case CLASS: declType = "class"; break; case ENUM: declType = "enum"; isEnum = true; currentEnumIdent = name.toString(); break; case INTERFACE: declType = "interface"; isInterface = true; break; case ANNOTATION_TYPE: appendAsComment(out, node.toString()); //throw new AssertionError("Annotation declaration not supported in the conversion"); return out.toString(); //break; default: declType = "???"; break; } ModifiersTree modifiers = node.getModifiers(); out.append(modifiers.accept(this, v)); out.append(declType); out.append(" "); out.append(name); if(!typeParameters.isEmpty()) { out.append("<"); boolean first = true; for(Tree t : typeParameters) { if(!first) { out.append(", "); } first = false; out.append(t.accept(this, v)); } out.append(">"); } out.append(" "); Tree extendsClause = node.getExtendsClause(); if (extendsClause != null) { out.append("extends "); out.append(extendsClause.accept(this, v)); out.append(" "); } List<? extends Tree> implementsClause = node.getImplementsClause(); if (!implementsClause.isEmpty()) { if (isInterface) { out.append("extends "); } else { out.append("implements "); } boolean first = true; for (Tree implement : implementsClause) { if (first) { out.append(implement.accept(this, v)); first = false; } else { out.append(", "); out.append(implement.accept(this, v)); } } } out.append(" {\n"); pushIndent(); symTable.pushGlobalScope(name.toString()); List<? extends Tree> members = node.getMembers(); addGlobalVariables(members); boolean first = true; boolean isLastVar = false; for (Tree member : members) { isLastVar = false; if (isEnum && isAEnumConstant(member)) { if (!first) { out.append(",\n"); } first = false; appendIndent(out); String enumConst = member.accept(this, v); out.append(enumConst); } else { out.append("\n"); appendIndent(out); if(member instanceof VariableTree) { Mode old = mode; mode = Mode.CLASS_VAR; out.append(member.accept(this, v)); mode = old; isLastVar = true; } else if (member instanceof BlockTree){ appendAsComment(out, member.toString()); } else { out.append(member.accept(this, v)); } } } if(isLastVar) { out.append("\n"); } if (isEnum) { out.append("\n"); isEnum = false; } if (isInterface) { isInterface = false; } mode = Mode.NORMAL; symTable.popGlobalScope(); popIndent(); out.append("\n"); appendIndent(out); out.append("}\n"); return out.toString(); } private void addGlobalVariables(List<? extends Tree> members) { for (Tree member : members) { if (member instanceof VariableTree) { String ident = ((VariableTree) member).getName().toString(); symTable.addGlobally(ident); } } } public String visitReturn(ReturnTree node, Void v) { StringBuilder out = new StringBuilder(); ExpressionTree expression = node.getExpression(); out.append("return"); if(expression != null) { out.append(" "); out.append(expression.accept(this, v)); } return out.toString(); } public String visitTry(TryTree node, Void v) { StringBuilder out = new StringBuilder(); List<? extends Tree> resources = node.getResources(); BlockTree block = node.getBlock(); List<? extends CatchTree> catches = node.getCatches(); BlockTree finallyBlock = node.getFinallyBlock(); List<String> resIdents = new ArrayList<String>(); if(catches.isEmpty() && !resources.isEmpty()) { out.append("using ("); boolean first = true; Mode oldMode = mode; mode = Mode.USING_NO_MODIFIERS; for(Tree res : resources) { if (!first) { out.append(", "); } first = false; out.append(res.accept(this, v)); } mode = oldMode; out.append(")"); out.append(block.accept(this, v)); if(finallyBlock != null) { appendIndent(out); out.append("finally "); out.append(finallyBlock.accept(this, v)); } return out.toString(); } if(!resources.isEmpty()) { for(Tree res : resources) { out.append("\n"); appendIndent(out); out.append(res.accept(this, v)); String ident = ((JCTree.JCVariableDecl) res).getName().toString(); resIdents.add(ident); } } out.append("\n"); appendIndent(out); out.append("try"); out.append(block.accept(this, v)); if(!catches.isEmpty()) { for(CatchTree c : catches) { out.append(c.accept(this, v)); } } if(finallyBlock != null || !resIdents.isEmpty()) { Mode oldMode = mode; mode = Mode.ADD_RESOURCES_FINALLY_BLOCK; currentResourcesIdents = resIdents; appendIndent(out); out.append("finally "); if(finallyBlock == null) { finallyBlock = new DummyBlock(); } out.append(finallyBlock.accept(this, v)); mode = oldMode; } return out.toString(); } public String visitCatch(CatchTree node, Void v) { StringBuilder out = new StringBuilder(); VariableTree parameter = node.getParameter(); BlockTree block = node.getBlock(); Tree type = parameter.getType(); if(type instanceof JCTree.JCTypeUnion) { String name = parameter.getName().toString(); for(JCTree.JCExpression expr : ((JCTree.JCTypeUnion) type).getTypeAlternatives()) { appendIndent(out); out.append("catch ("); skipBlockScope = true; symTable.pushLocalScope(); String newName = symTable.addLocally(name); out.append(newName).append(" : "); out.append(expr.accept(this, v)); out.append(")"); out.append(block.accept(this, v)); symTable.popLocalScope(); } } else { appendIndent(out); out.append("catch ("); skipBlockScope = true; Mode oldMode = mode; mode = Mode.CATCH_PARAM; symTable.pushLocalScope(); out.append(parameter.accept(this, v)); mode = oldMode; out.append(")"); out.append(block.accept(this, v)); symTable.popLocalScope(); } return out.toString(); } public String visitLabeledStatement(LabeledStatementTree node, Void v) { StringBuilder out = new StringBuilder(); StatementTree stmt = node.getStatement(); String label = node.getLabel().toString() + ": "; appendAsComment(out, label); appendIndent(out); out.append(stmt.accept(this, v)); return out.toString(); } public String visitBlock(BlockTree node, Void v) { StringBuilder out = new StringBuilder(); // todo handle static blocks if (node.isStatic()) { //throw new AssertionError("Static blocks not supported in Gosu"); appendAsComment(out, node.toString()); return out.toString(); } out.append(" {\n"); pushIndent(); boolean skipPopScope = false; if (!skipBlockScope) { symTable.pushLocalScope(); } else { skipBlockScope = false; skipPopScope = true; } if(mode == Mode.ADD_RESOURCES_FINALLY_BLOCK) { int i = currentResourcesIdents.size()-1; while(i >= 0) { String id = currentResourcesIdents.get(i); appendIndent(out); String newId = symTable.convertLocalSymbol(id); out.append("if (").append(newId).append(" != null) ").append(newId).append(".close()\n"); i--; } } List<? extends StatementTree> statements = node.getStatements(); for (StatementTree stm : statements) { appendIndent(out); out.append(stm.accept(this, v)); out.append("\n"); } if (!skipPopScope) { symTable.popLocalScope(); } popIndent(); appendIndent(out); out.append("}\n"); return out.toString(); } public String visitSwitch(SwitchTree node, Void v) { StringBuilder out = new StringBuilder(); List<? extends CaseTree> cases = node.getCases(); ExpressionTree expression = node.getExpression(); out.append("switch "); out.append(expression.accept(this, v)); out.append(" {\n"); pushIndent(); symTable.pushLocalScope(); for(CaseTree c : cases) { out.append(c.accept(this, v)); out.append("\n"); } symTable.popLocalScope(); popIndent(); appendIndent(out); out.append("}\n"); return out.toString(); } public String visitCase(CaseTree node, Void v) { StringBuilder out = new StringBuilder(); ExpressionTree expr = node.getExpression(); List<? extends StatementTree> statements = node.getStatements(); appendIndent(out); if(expr != null) { out.append("case "); out.append(expr.accept(this, v)); } else { out.append("default"); } out.append(":\n"); pushIndent(); for(StatementTree stm : statements) { appendIndent(out); out.append(stm.accept(this, v)); out.append("\n"); } popIndent(); appendIndent(out); return out.toString(); } public String visitEnhancedForLoop(EnhancedForLoopTree node, Void v) { StringBuilder out = new StringBuilder(); String variablename = node.getVariable().getName().toString(); ExpressionTree expr = node.getExpression(); StatementTree stmt = node.getStatement(); skipBlockScope = true; symTable.pushLocalScope(); out.append("for ("); String newName = symTable.addLocally(variablename); out.append(newName); out.append(" in "); out.append(expr.accept(this, v)); out.append(")"); if(stmt instanceof BlockTree) { out.append(stmt.accept(this, v)); } else { out.append(" {\n"); pushIndent(); appendIndent(out); out.append(stmt.accept(this, v)); popIndent(); out.append("\n"); appendIndent(out); out.append("}\n"); } symTable.popLocalScope(); return out.toString(); } public String visitEmptyStatement(EmptyStatementTree node, Void v) { return ""; } public String visitExpressionStatement(ExpressionStatementTree node, Void v) { StringBuilder out = new StringBuilder(); out.append(node.getExpression().accept(this, v)); return out.toString(); } public String visitArrayAccess(ArrayAccessTree node, Void v) { StringBuilder out = new StringBuilder(); ExpressionTree expr = node.getExpression(); ExpressionTree index = node.getIndex(); out.append(expr.accept(this, v)); out.append("["); out.append(index.accept(this, v)); out.append("]"); return out.toString(); } public String visitArrayType(ArrayTypeTree node, Void v) { return node.getType().accept(this, v) + "[]"; } public String visitThrow(ThrowTree node, Void v) { return "throw " + node.getExpression().accept(this, v); } public String visitVariable(VariableTree node, Void v) { StringBuilder out = new StringBuilder(); String name = node.getName().toString(); ExpressionTree initializer = node.getInitializer(); if (mode != Mode.CLASS_VAR && !isAEnumConstant(node)) { name = symTable.addLocally(name); } if (isEnum && isAEnumConstant(node)) { out.append(name); if (initializer != null) { out.append(initializer.accept(this, v)); } return out.toString(); } if (mode != Mode.CATCH_PARAM && mode != Mode.USING_NO_MODIFIERS) { ModifiersTree modifiers = node.getModifiers(); out.append(modifiers.accept(this, v)); } Tree type = node.getType(); boolean appedVar = !(mode == Mode.METHOD_PARAM || mode == Mode.CATCH_PARAM); if (appedVar) { out.append("var "); } out.append(name); skipSymConversion = true; String varType = type.accept(this, v); skipSymConversion = false; String iniz = null; String genType = null; if (initializer != null) { iniz = " = " + initializer.accept(this, v); if (iniz.contains("<>")) { genType = extractGenericType(varType); iniz = iniz.replaceAll("<>", genType); } } if (appedVar && initializer != null && mode != Mode.CLASS_VAR) { String infer = typeInference(initializer, varType, genType, iniz); if(infer != null) { out.append(infer); return out.toString(); } } out.append(" : "); out.append(varType); if (iniz != null) { out.append(iniz); } return out.toString(); } private String extractGenericType(String varType) { String out = varType; int b = varType.indexOf("<"); int e = varType.lastIndexOf(">"); if(b != -1 && e != -1) { out = varType.substring(b, e + 1); } return out; } private String typeInference(ExpressionTree initializer, String varType, String genType, String iniz) { String initType = null; boolean literal = false; if (initializer instanceof NewArrayTree) { Tree arrayType = ((NewArrayTree) initializer).getType(); if(arrayType != null) { initType = arrayType.accept(this, null); initType = initType.replaceAll(Pattern.quote("[]"), ""); varType = varType.replaceAll(Pattern.quote("[]"), ""); } } else if (initializer instanceof NewClassTree) { initType = ((NewClassTree) initializer).getIdentifier().accept(this, null); if (genType != null) { initType = initType.replaceAll("<>", genType); } } else if(initializer instanceof LiteralTree && initializer.getKind() != Tree.Kind.NULL_LITERAL && !varType.equals("Object")) { literal = true; if(varType.equals("byte")) { iniz = iniz + "b"; } else if(varType.equals("short")) { iniz = iniz + "s"; } else if(varType.equals("float") && initializer.getKind() == Tree.Kind.INT_LITERAL ) { iniz = iniz + "f"; } else if(varType.equals("double") && initializer.getKind() == Tree.Kind.INT_LITERAL ) { iniz = iniz + ".0"; } else if(varType.equals("long") && initializer.getKind() == Tree.Kind.INT_LITERAL ) { iniz = iniz + "L"; } } if((initType != null && varType.equals(initType)) || literal) { return iniz; } return null; } private boolean isAEnumConstant(Tree node) { return ((JCTree)node).getTag() == JCTree.VARDEF && (((JCTree.JCVariableDecl) node).mods.flags & Flags.ENUM) != 0; } public String visitParameterizedType(ParameterizedTypeTree node, Void v) { StringBuilder out = new StringBuilder(); Tree type = node.getType(); List<? extends Tree> typeArguments = node.getTypeArguments(); out.append(type.accept(this, v)); out.append("<"); boolean first = true; for(Tree t : typeArguments) { if(!first) { out.append(", "); } first = false; out.append(t.accept(this, v)); } out.append(">"); return out.toString(); } public String visitMemberSelect(MemberSelectTree node, Void v) { StringBuilder out = new StringBuilder(); String expr = node.getExpression().accept(this, v); String id = node.getIdentifier().toString(); if (expr.endsWith(".this")) { int e = expr.indexOf(".this"); int dot = expr.substring(0, e).lastIndexOf("."); int s = dot != -1 ? dot + 1 : 0; String clazz = expr.substring(s, e); int level = symTable.getClassLevelFromCurrent(clazz); if (level == 0) { return id; } else { out.append("outer"); level--; while (level > 0) { out.append(".outer"); level--; } } } else { out.append(expr); } if (!"class".equals(id)) { out.append("."); out.append(id); } return out.toString(); } public String visitWildcard(WildcardTree node, Void v) { String out; Tree bound = node.getBound(); String boundStr = ""; if(bound != null) { boundStr = bound.accept(this, v); } Tree.Kind kind = node.getKind(); if(kind == Tree.Kind.EXTENDS_WILDCARD) { out = boundStr; } else if(kind == Tree.Kind.SUPER_WILDCARD) { out = "? super " + boundStr; } else {//if(kind == Tree.Kind.UNBOUNDED_WILDCARD) { out = "?"; } return out; } public String visitBinary(BinaryTree node, Void v) { StringBuilder out = new StringBuilder(); ExpressionTree lOp = node.getLeftOperand(); ExpressionTree rOp = node.getRightOperand(); int operator = ((JCTree.JCBinary) node).getTag(); out.append(lOp.accept(this, v)); out.append(" ").append(operatorName(operator)).append(" "); out.append(rOp.accept(this, v)); return out.toString(); } public String visitParenthesized(ParenthesizedTree node, Void v) { StringBuilder out = new StringBuilder(); out.append("("); out.append(node.getExpression().accept(this, v)); if(mode == Mode.USING_CAST) { out.append(" as IMonitorLock"); } out.append(")"); return out.toString(); } public String visitNewArray(NewArrayTree node, Void v) { StringBuilder out = new StringBuilder(); List<? extends ExpressionTree> dim = node.getDimensions(); List<? extends ExpressionTree> inits = node.getInitializers(); Tree type = node.getType(); if (type != null) { out.append("new "); skipSymConversion = true; out.append(type.accept(this, v)); skipSymConversion = false; } if (!dim.isEmpty()) { if(type != null && type instanceof JCTree.JCArrayTypeTree) { int end = out.length(); out.delete(end - 2, end); } for (ExpressionTree d : dim) { out.append("["); out.append(d.accept(this, v)); out.append("]"); } if(type != null && type instanceof JCTree.JCArrayTypeTree) { out.append("[]"); } } else if (type != null) { out.append("[]"); } if (inits != null) { boolean first = true; out.append("{"); for (ExpressionTree init : inits) { if (!first) { out.append(", "); } first = false; out.append(init.accept(this, v)); } out.append("}"); } return out.toString(); } public String visitOther(Tree node, Void v) { return "OTHER"; } public String visitErroneous(ErroneousTree node, Void v) { throw new AssertionError("We don't support erroneous Java files"); } public String visitSynchronized(SynchronizedTree node, Void v) { StringBuilder out = new StringBuilder(); ExpressionTree expr = node.getExpression(); BlockTree block = node.getBlock(); out.append("using "); Mode oldMode = mode; mode = Mode.USING_CAST; out.append(expr.accept(this, v)); mode = oldMode; out.append(block.accept(this, v)); return out.toString(); } public String visitBreak(BreakTree node, Void v) { StringBuilder out = new StringBuilder(); out.append("break"); Name label = node.getLabel(); if(label != null) { out.append(" ").append(label); } return out.toString(); } public String visitContinue(ContinueTree node, Void v) { StringBuilder out = new StringBuilder(); out.append("continue"); Name label = node.getLabel(); if(label != null) { out.append(" ").append(label); } return out.toString(); } public String visitDoWhileLoop(DoWhileLoopTree node, Void v) { StringBuilder out = new StringBuilder(); ExpressionTree condition = node.getCondition(); StatementTree statement = node.getStatement(); out.append("do"); if(statement instanceof BlockTree) { out.append(statement.accept(this, v)); } else { out.append(" {\n"); pushIndent(); symTable.pushLocalScope(); appendIndent(out); out.append(statement.accept(this, v)); symTable.popLocalScope(); popIndent(); out.append("\n"); appendIndent(out); out.append("}\n"); } appendIndent(out); out.append("while "); out.append(condition.accept(this, v)); return out.toString(); } public String visitWhileLoop(WhileLoopTree node, Void v) { StringBuilder out = new StringBuilder(); ExpressionTree condition = node.getCondition(); StatementTree statement = node.getStatement(); out.append("while "); out.append(condition.accept(this, v)); if(statement instanceof BlockTree) { out.append(statement.accept(this, v)); } else { out.append(" {\n"); pushIndent(); symTable.pushLocalScope(); appendIndent(out); out.append(statement.accept(this, v)); symTable.popLocalScope(); popIndent(); out.append("\n"); appendIndent(out); out.append("}\n"); } return out.toString(); } public String visitIf(IfTree node, Void v) { StringBuilder out = new StringBuilder(); ExpressionTree cond = node.getCondition(); StatementTree thenStm = node.getThenStatement(); StatementTree elseStm = node.getElseStatement(); out.append("if "); out.append(cond.accept(this, v)); if(thenStm instanceof BlockTree) { out.append(thenStm.accept(this, v)); out.deleteCharAt(out.length()-1); } else { out.append(" {\n"); pushIndent(); symTable.pushLocalScope(); appendIndent(out); out.append(thenStm.accept(this, v)); symTable.popLocalScope(); popIndent(); out.append("\n"); appendIndent(out); out.append("}"); } if(elseStm != null) { out.append(" else"); if(elseStm instanceof BlockTree || elseStm instanceof IfTree) { if(elseStm instanceof IfTree) { out.append(" "); } out.append(elseStm.accept(this, v)); if(elseStm instanceof BlockTree) { out.deleteCharAt(out.length()-1); } } else { out.append(" {\n"); pushIndent(); symTable.pushLocalScope(); appendIndent(out); out.append(elseStm.accept(this, v)); symTable.popLocalScope(); popIndent(); out.append("\n"); appendIndent(out); out.append("}"); } } return out.toString(); } public String visitInstanceOf(InstanceOfTree node, Void v) { StringBuilder out = new StringBuilder(); ExpressionTree expr = node.getExpression(); Tree type = node.getType(); out.append(expr.accept(this, v)); out.append(" typeis "); out.append(type.accept(this, v)); return out.toString(); } public String visitUnary(UnaryTree node, Void v) { int op = ((JCTree.JCUnary) node).getTag(); String opName = operatorName(op); String expr = node.getExpression().accept(this, v); if(op == JCTree.PREDEC || op == JCTree.PREINC || op == JCTree.POSTDEC || op == JCTree.POSTINC) { return expr + opName; } return opName + expr; } public String visitAssert(AssertTree node, Void v) { StringBuilder out = new StringBuilder(); ExpressionTree condition = node.getCondition(); ExpressionTree detail = node.getDetail(); out.append("assert "); out.append(condition.accept(this, v)); if (detail != null) { out.append(" : "); out.append(detail.accept(this, v)); } return out.toString(); } public String visitForLoop(ForLoopTree node, Void v) { StringBuilder out = new StringBuilder(); List<? extends StatementTree> initializer = node.getInitializer(); ExpressionTree condition = node.getCondition(); StatementTree statement = node.getStatement(); List<? extends ExpressionStatementTree> update = node.getUpdate(); String gosuForLoop = maybeTranfromToGosuFor(node, v); if(gosuForLoop != null) { return gosuForLoop; } if(!initializer.isEmpty()) { for(StatementTree stm : initializer) { out.append(stm.accept(this, v)); out.append("\n"); appendIndent(out); } } out.append("while ("); if(condition == null) { out.append("true"); } else { out.append(condition.accept(this, v)); } out.append(") {\n"); pushIndent(); symTable.pushLocalScope(); if(statement instanceof BlockTree) { List<? extends StatementTree> statements = ((BlockTree) statement).getStatements(); for(StatementTree stm : statements) { appendIndent(out); out.append(stm.accept(this, v)); out.append("\n"); } } else { appendIndent(out); out.append(statement.accept(this, v)); out.append("\n"); } if(!update.isEmpty()) { for(ExpressionStatementTree stm : update) { appendIndent(out); out.append(stm.accept(this, v)); out.append("\n"); } } symTable.popLocalScope(); popIndent(); appendIndent(out); out.append("}\n"); return out.toString(); } private String maybeTranfromToGosuFor(ForLoopTree node, Void v) { StringBuilder out = new StringBuilder(); List<? extends StatementTree> initializer = node.getInitializer(); ExpressionTree condition = node.getCondition(); StatementTree statement = node.getStatement(); List<? extends ExpressionStatementTree> update = node.getUpdate(); if (initializer.size() == 1 && update.size() == 1 && condition instanceof JCTree.JCBinary) { StatementTree inizST = initializer.get(0); ExpressionTree expression = update.get(0).getExpression(); JCTree.JCBinary cond = (JCTree.JCBinary) condition; if (cond.getTag() == JCTree.LT && inizST instanceof JCTree.JCVariableDecl && expression instanceof JCTree.JCUnary) { JCTree.JCVariableDecl iniz = (JCTree.JCVariableDecl) inizST; JCTree.JCUnary up = (JCTree.JCUnary) expression; String var = cond.getLeftOperand().accept(this, v); if (var.equals(up.getExpression().accept(this, v)) && var.equals(iniz.getName().toString()) && (up.getTag() == JCTree.PREINC || up.getTag() == JCTree.POSTINC) && "0".equals(iniz.getInitializer().accept(this, v))) { JCTree.JCExpression condRight = cond.getRightOperand(); boolean validLimit = false; String condR = condRight.accept(this, v); if (condRight instanceof JCTree.JCLiteral) { try { Integer val = Integer.valueOf(condR); if (val >= 0) { validLimit = true; } } catch (NumberFormatException ex) {} } else if (condR.endsWith(".size()") || condR.endsWith(".length()") || condR.endsWith(".length")) { validLimit = true; } if(validLimit) { skipBlockScope = true; symTable.pushLocalScope(); String newName = symTable.addLocally(var); out.append("for ("); out.append(newName); out.append(" in 0..|"); out.append(condR); out.append(")"); if(statement instanceof BlockTree) { out.append(statement.accept(this, v)); } else { out.append(" {\n"); pushIndent(); appendIndent(out); out.append(statement.accept(this, v)); popIndent(); out.append("\n"); appendIndent(out); out.append("}\n"); } symTable.popLocalScope(); return out.toString(); } } } } return null; } public String visitConditionalExpression(ConditionalExpressionTree node, Void v) { StringBuilder out = new StringBuilder(); ExpressionTree cond = node.getCondition(); ExpressionTree falseExp = node.getFalseExpression(); ExpressionTree trueEx = node.getTrueExpression(); out.append(cond.accept(this, v)); out.append(" ? "); out.append(trueEx.accept(this, v)); out.append(" : "); out.append(falseExp.accept(this, v)); return out.toString(); } public String visitMethodInvocation(MethodInvocationTree node, Void v) { StringBuilder out = new StringBuilder(); List<? extends ExpressionTree> arguments = node.getArguments(); ExpressionTree methodSelect = node.getMethodSelect(); if(methodSelect instanceof IdentifierTree) { skipSymConversion = true; out.append(methodSelect.accept(this, v)); skipSymConversion = false; } else { out.append(methodSelect.accept(this, v)); } List<? extends Tree> typeArguments = node.getTypeArguments(); if (!typeArguments.isEmpty()) { out.append("<"); boolean first = true; for(Tree t : typeArguments) { if(!first) { out.append(", "); } first = false; out.append(t.accept(this, v)); } out.append(">"); } out.append("("); if (!arguments.isEmpty()) { boolean first = true; for (ExpressionTree arg : arguments) { if (!first) { out.append(", "); } first = false; out.append(arg.accept(this, v)); } } out.append(")"); return out.toString(); } public String visitNewClass(NewClassTree node, Void v) { StringBuilder out = new StringBuilder(); ExpressionTree identifier = node.getIdentifier(); List<? extends ExpressionTree> arguments = node.getArguments(); ClassTree classBody = node.getClassBody(); ExpressionTree enclosingExpression = node.getEnclosingExpression(); //List<? extends Tree> typeArguments = node.getTypeArguments(); //not needed in gosu if (enclosingExpression != null) { out.append(enclosingExpression.accept(this, v)); out.append("."); //throw new AssertionError("Annotation declaration not supported in the conversion"); } skipSymConversion = true; String identOut = identifier.accept(this, v); skipSymConversion = false; boolean isEnumConst = isEnum && currentEnumIdent.equals(identOut); if (!isEnumConst) { out.append("new "); out.append(identOut); } if (!isEnumConst || !arguments.isEmpty()) { out.append("("); } if (!arguments.isEmpty()) { boolean first = true; for (ExpressionTree arg : arguments) { if (!first) { out.append(", "); } first = false; out.append(arg.accept(this, v)); } } if (!isEnumConst || !arguments.isEmpty()) { out.append(")"); } if (classBody != null) { out.append(" {\n"); pushIndent(); symTable.pushGlobalScope(identOut); List<? extends Tree> members = classBody.getMembers(); addGlobalVariables(members); for (Tree member : members) { out.append("\n"); appendIndent(out); if(member instanceof VariableTree) { Mode old = mode; mode = Mode.CLASS_VAR; out.append(member.accept(this, v)); mode = old; } else if (member instanceof BlockTree){ appendAsComment(out, member.toString()); } else { out.append(member.accept(this, v)); } } symTable.popGlobalScope(); popIndent(); out.append("\n"); appendIndent(out); out.append("}\n"); } return out.toString(); } public String visitPrimitiveType(PrimitiveTypeTree node, Void v) { return node.toString(); } public String visitMethod(MethodTree node, Void v) { StringBuilder out = new StringBuilder(); BlockTree body = node.getBody(); Tree defaultValue = node.getDefaultValue(); if (defaultValue != null) { throw new AssertionError("default values for annotations not supported"); } ModifiersTree modifiers = node.getModifiers(); String name = node.getName().toString(); List<? extends VariableTree> parameters = node.getParameters(); Tree returnType = node.getReturnType(); List<? extends TypeParameterTree> typeParameters = node.getTypeParameters(); //List<? extends ExpressionTree> lThrows = node.getThrows(); not needed HashMap<String, String> constructorTypes = null; out.append(modifiers.accept(this, v)); if (name.equals("<init>")) { if(isEnum && !modifiers.getFlags().contains(Modifier.PRIVATE) ) { out.append("private "); } out.append("construct"); if(!typeParameters.isEmpty()) { constructorTypes = computeConstructorTypes(typeParameters); } } else { out.append("function "); out.append(name); if(!typeParameters.isEmpty()) { out.append("<"); boolean first = true; for(Tree t : typeParameters) { if(!first) { out.append(", "); } first = false; out.append(t.accept(this, v)); } out.append(">"); } } Mode oldMode = mode; mode = Mode.METHOD_PARAM; out.append("("); symTable.pushLocalScope(); boolean first = true; for (VariableTree par : parameters) { if (!first) { out.append(", "); } first = false; out.append(par.accept(this, v)); } out.append(")"); if(constructorTypes != null) { String newOut = out.toString(); for(String key : constructorTypes.keySet()) { newOut = newOut.replace(": " + key, ": " + constructorTypes.get(key)); } out = new StringBuilder(newOut); } mode = oldMode; if (returnType != null) { out.append(" : "); out.append(returnType.accept(this, v)); } if (body != null) { skipBlockScope = true; out.append(body.accept(this, v)); } symTable.popLocalScope(); return out.toString(); } private HashMap<String, String> computeConstructorTypes(List<? extends TypeParameterTree> typeParameters) { HashMap<String, String> constructorTypes = new HashMap<String, String>(); for(TypeParameterTree t : typeParameters) { String key = t.getName().toString(); String value; List<? extends Tree> bounds = t.getBounds(); if(bounds.isEmpty()) { value = "Object"; } else { StringBuilder out = new StringBuilder(); if(!bounds.isEmpty()) { boolean first = true; for(Tree b : bounds) { if(!first) { out.append(" & "); } first = false; out.append(b.accept(this, null)); } } value = out.toString(); } constructorTypes.put(key, value); } return constructorTypes; } public String visitModifiers(ModifiersTree node, Void v) { StringBuilder out = new StringBuilder(); String ann; boolean isOverridden = false; for (AnnotationTree annotation : node.getAnnotations()) { ann = annotation.accept(this, v); if("override".equals(ann)) { isOverridden = true; } else { out.append(ann); out.append("\n"); } } if (!node.getAnnotations().isEmpty() && !isOverridden) { appendIndent(out); } if(isOverridden) { if(node.getAnnotations().size() > 1) { appendIndent(out); } out.append("override "); } for (Modifier modifier : node.getFlags()) { out.append(modifier); out.append(" "); } String modifiers = out.toString(); if(mode == Mode.CLASS_VAR && !isInterface && !modifiers.contains("public") && !modifiers.contains("protected") && !modifiers.contains("private")) { out.append("internal "); } return out.toString(); } public String visitTypeParameter(TypeParameterTree node, Void v) { StringBuilder out = new StringBuilder(); Name name = node.getName(); List<? extends Tree> bounds = node.getBounds(); out.append(name); if(!bounds.isEmpty()) { out.append(" extends "); boolean first = true; for(Tree b : bounds) { if(!first) { out.append(" & "); } first = false; out.append(b.accept(this, v)); } } return out.toString(); } public String visitIdentifier(IdentifierTree node, Void v) { String ident = node.getName().toString(); if(!skipSymConversion) { ident = symTable.convertLocalSymbol(ident); } return ident; } public String visitLiteral(LiteralTree node, Void v) { StringBuilder out = new StringBuilder(); Object value = node.getValue(); switch (node.getKind()) { case INT_LITERAL: out.append(value); break; case LONG_LITERAL: out.append(value); out.append("L"); break; case FLOAT_LITERAL: out.append(value); out.append("f"); break; case DOUBLE_LITERAL: out.append(value); break; case BOOLEAN_LITERAL: case NULL_LITERAL: out.append(value); break; case CHAR_LITERAL: out.append("\'").append(Convert.quote(String.valueOf(value))).append("\'"); break; case STRING_LITERAL: out.append("\"").append(Convert.quote(value.toString())).append("\""); break; } return out.toString(); } public String visitTypeCast(TypeCastTree node, Void v) { StringBuilder out = new StringBuilder(); out.append(node.getExpression().accept(this, v)); out.append(" as "); out.append(node.getType().accept(this, v)); return out.toString(); } public String visitAssignment(AssignmentTree node, Void v) { StringBuilder out = new StringBuilder(); ExpressionTree variable = node.getVariable(); ExpressionTree expression = node.getExpression(); out.append(variable.accept(this, v)); out.append(" = "); out.append(expression.accept(this, v)); return out.toString(); } public String visitCompoundAssignment(CompoundAssignmentTree node, Void v) { StringBuilder out = new StringBuilder(); ExpressionTree variable = node.getVariable(); ExpressionTree expression = node.getExpression(); int operator = ((JCTree.JCAssignOp) node).getTag(); out.append(variable.accept(this, v)); out.append(" ").append(operatorName(operator)).append(" "); out.append(expression.accept(this, v)); return out.toString(); } public String visitAnnotation(AnnotationTree node, Void v) { StringBuilder out = new StringBuilder(); Tree type = node.getAnnotationType(); String typeStr = type.accept(this, v); if("Override".equals(typeStr)) { return "override"; } out.append("@"); out.append(typeStr); List<? extends ExpressionTree> arguments = node.getArguments(); if (!arguments.isEmpty()) { out.append("("); boolean first = true; for (ExpressionTree arg : arguments) { if (!first) { out.append(", "); } first = false; if (arg instanceof AssignmentTree) { out.append(":"); } out.append(arg.accept(this, v)); } out.append(")"); } return out.toString(); } public String visitUnionType(UnionTypeTree node, Void v) { /* handled in the visitCatch()*/ return null; } private void pushIndent() { ident += TAB_SIZE; } private void popIndent() { ident -= TAB_SIZE; } private void appendIndent(StringBuilder out) { for (int i = 0; i < ident; i += TAB_SIZE) { out.append(TAB); } } private void appendAsComment(StringBuilder out, String code) { out.append("/*\n"); pushIndent(); for (String line : code.split("\\r?\\n")) { appendIndent(out); out.append(line); out.append("\n"); } popIndent(); appendIndent(out); out.append("*/\n"); } public String operatorName(int tag) { switch (tag) { case JCTree.POS: return "+"; case JCTree.NEG: return "-"; case JCTree.NOT: return "!"; case JCTree.COMPL: return "~"; case JCTree.PREINC: return "++"; case JCTree.PREDEC: return "--"; case JCTree.POSTINC: return "++"; case JCTree.POSTDEC: return "--"; case JCTree.NULLCHK: return "<*nullchk*>"; case JCTree.OR: return "or"; case JCTree.AND: return "and"; case JCTree.BITOR: return "|"; case JCTree.BITXOR: return "^"; case JCTree.BITAND: return "&"; case JCTree.EQ: return "=="; case JCTree.NE: return "!="; case JCTree.LT: return "<"; case JCTree.GT: return ">"; case JCTree.LE: return "<="; case JCTree.GE: return ">="; case JCTree.SL: return "<<"; case JCTree.SR: return ">>"; case JCTree.USR: return ">>>"; case JCTree.PLUS: return "+"; case JCTree.MINUS: return "-"; case JCTree.MUL: return "*"; case JCTree.DIV: return "/"; case JCTree.MOD: return "%"; case JCTree.BITOR_ASG: return "|="; case JCTree.BITXOR_ASG: return "^="; case JCTree.BITAND_ASG: return "&="; case JCTree.SL_ASG: return "<<="; case JCTree.SR_ASG: return ">>="; case JCTree.USR_ASG: return ">>>="; case JCTree.PLUS_ASG: return "+="; case JCTree.MINUS_ASG: return "-="; case JCTree.MUL_ASG: return "*="; case JCTree.DIV_ASG: return "/="; case JCTree.MOD_ASG: return "%="; default: throw new Error(); } } }