/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * JavaSourceGenerator.java * Creation date: Sep 11, 2003. * By: Edward Lam */ package org.openquark.cal.internal.javamodel; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import org.openquark.cal.compiler.StringEncoder; import org.openquark.cal.internal.javamodel.JavaExpression.ArrayAccess; import org.openquark.cal.internal.javamodel.JavaExpression.ArrayCreationExpression; import org.openquark.cal.internal.javamodel.JavaExpression.ArrayLength; import org.openquark.cal.internal.javamodel.JavaExpression.Assignment; import org.openquark.cal.internal.javamodel.JavaExpression.CastExpression; import org.openquark.cal.internal.javamodel.JavaExpression.ClassInstanceCreationExpression; import org.openquark.cal.internal.javamodel.JavaExpression.ClassLiteral; import org.openquark.cal.internal.javamodel.JavaExpression.InstanceOf; import org.openquark.cal.internal.javamodel.JavaExpression.LiteralWrapper; import org.openquark.cal.internal.javamodel.JavaExpression.LocalName; import org.openquark.cal.internal.javamodel.JavaExpression.LocalVariable; import org.openquark.cal.internal.javamodel.JavaExpression.MethodInvocation; import org.openquark.cal.internal.javamodel.JavaExpression.MethodVariable; import org.openquark.cal.internal.javamodel.JavaExpression.OperatorExpression; import org.openquark.cal.internal.javamodel.JavaExpression.PlaceHolder; import org.openquark.cal.internal.javamodel.JavaExpression.JavaField.Instance; import org.openquark.cal.internal.javamodel.JavaExpression.JavaField.Static; import org.openquark.cal.internal.javamodel.JavaExpression.JavaField.This; import org.openquark.cal.internal.javamodel.JavaExpression.OperatorExpression.Binary; import org.openquark.cal.internal.javamodel.JavaExpression.OperatorExpression.Ternary; import org.openquark.cal.internal.javamodel.JavaExpression.OperatorExpression.Unary; import org.openquark.cal.internal.javamodel.JavaStatement.AssertStatement; import org.openquark.cal.internal.javamodel.JavaStatement.Block; import org.openquark.cal.internal.javamodel.JavaStatement.ExpressionStatement; import org.openquark.cal.internal.javamodel.JavaStatement.IfThenElseStatement; import org.openquark.cal.internal.javamodel.JavaStatement.JavaDocComment; import org.openquark.cal.internal.javamodel.JavaStatement.LabelledContinue; import org.openquark.cal.internal.javamodel.JavaStatement.LineComment; import org.openquark.cal.internal.javamodel.JavaStatement.LocalVariableDeclaration; import org.openquark.cal.internal.javamodel.JavaStatement.MultiLineComment; import org.openquark.cal.internal.javamodel.JavaStatement.ReturnStatement; import org.openquark.cal.internal.javamodel.JavaStatement.SwitchStatement; import org.openquark.cal.internal.javamodel.JavaStatement.SynchronizedMethodInvocation; import org.openquark.cal.internal.javamodel.JavaStatement.ThrowStatement; import org.openquark.cal.internal.javamodel.JavaStatement.UnconditionalLoop; import org.openquark.cal.internal.javamodel.JavaStatement.SwitchStatement.IntCaseGroup; import org.openquark.cal.util.ArrayStack; /** * A class to generate Java source code given an object representation. * @author Edward Lam */ public class JavaSourceGenerator { /** The length of an indent. */ private static final int INDENT_LENGTH = 4; /** Line separator. */ private static final String EOL = System.getProperty("line.separator"); /** * Constructor for a JavaSourceGenerator */ JavaSourceGenerator() { } /** * Holds class names used, etc. * TODOEL: manage namespace collisions. * @author Edward Lam */ static class GenerationContext { private final JavaTypeName classTypeName; private Map<String, JavaTypeName> unqualifiedNameToTypeNameMap = new TreeMap<String, JavaTypeName>(); // TODOEL: resolve conflicts. /** * Constructor for a GenerationContext * @param classTypeName */ GenerationContext(JavaTypeName classTypeName) { this.classTypeName = classTypeName; } /** * Get the name that can be used in java source for a given type (class). * @param typeName * @return String */ String getSourceName(JavaTypeName typeName) { // We can declare an import and return the short class name if: // 1) There isn't another class with the same name imported from another module. // 2) The imported class doesn't have the same name as the class we are creating. switch (typeName.getTag()) { case JavaTypeName.VOID_TAG: case JavaTypeName.BOOLEAN_TAG: case JavaTypeName.BYTE_TAG: case JavaTypeName.SHORT_TAG: case JavaTypeName.CHAR_TAG: case JavaTypeName.INT_TAG: case JavaTypeName.LONG_TAG: case JavaTypeName.DOUBLE_TAG: case JavaTypeName.FLOAT_TAG: return typeName.getName(); case JavaTypeName.OBJECT_TAG: { JavaTypeName.Reference.Object referenceType = (JavaTypeName.Reference.Object)typeName; // Same type name as this class. if (typeName.equals(classTypeName)) { return referenceType.getBaseName(); } // if this type is in java.lang we will need to fully qualify it. // This prevents conflicts with other classes with the same name // ex. mypackage.String if (typeName.getPackageName().equals("java.lang")) { return referenceType.getFullJavaSourceName(); } JavaTypeName.Reference.Object classType = (JavaTypeName.Reference.Object)classTypeName; String importName = referenceType.getImportName(); // Same enclosing class (ie. for nested classes..). if (importName.equals(classType.getImportName())) { return referenceType.getBaseName(); } String objectPackageName = referenceType.getPackageName(); String unqualifiedName = referenceType.getUnqualifiedJavaSourceName(); // Same unqualified name as this class, different module. if (unqualifiedName.equals(classType.getUnqualifiedJavaSourceName())) { return referenceType.getFullJavaSourceName(); } // Inner class, where outer class has same name as this class. String tempName = unqualifiedName; int index = tempName.indexOf ("."); while (index >= 0) { String tempUnqualifiedName = tempName.substring(0, index); // Same unqualified name as this class, different module. if (tempUnqualifiedName.equals(classType.getUnqualifiedJavaSourceName())) { return referenceType.getFullJavaSourceName(); } tempName = tempName.substring(index + 1); index = tempName.indexOf ("."); } // We want to key the unqualified name to typename map off of the containing class name // in cases where we are dealing with an inner class. int dotIndex = unqualifiedName.indexOf ("."); String lookupName = (dotIndex != -1) ? unqualifiedName.substring(0, dotIndex): unqualifiedName; JavaTypeName existingTypeName = unqualifiedNameToTypeNameMap.get(lookupName); if (existingTypeName == null) { unqualifiedNameToTypeNameMap.put(lookupName, typeName); return referenceType.getUnqualifiedJavaSourceName(); } if (objectPackageName.equals(existingTypeName.getPackageName())) { // There is already a match for this type, or the containing type if this is // an inner class, simply return the unqualifiedName return unqualifiedName; } // There is a match on the unqualifiedName but it is not the same type. return referenceType.getFullJavaSourceName(); } case JavaTypeName.ARRAY_TAG: { JavaTypeName.Reference.Array arrayType = (JavaTypeName.Reference.Array)typeName; int nDimensions = arrayType.getNDimensions(); JavaTypeName elementType = arrayType.getElementType(); // Get the source for the element type, and append "[]" for each dimension. StringBuilder sb = new StringBuilder(getSourceName(elementType)); for (int i = 0; i < nDimensions; i++) { sb.append("[]"); } return sb.toString(); } default: { throw new NullPointerException("Unrecognized class: " + typeName.getName()); } } } /** * Get the type names for which unqualified names were output in source. * @return (List of JavaTypeName) */ List<JavaTypeName> getTypeNames() { return new ArrayList<JavaTypeName>(unqualifiedNameToTypeNameMap.values()); } } /** * Emit import statements. * @param leccPackageName * @param topLevelClassName * @param context * @return String */ private static String genS_Imports(String leccPackageName, String topLevelClassName, GenerationContext context) { // Get the sorted set of import names. Set<String> importNameSet = new TreeSet<String>(); for (final JavaTypeName typeName : context.getTypeNames()) { String packageName = typeName.getPackageName(); if (packageName.equals("") || packageName.equals("java.lang") || packageName.equals(leccPackageName)) { continue; } // Check to see if the class is an inner class of the current class. if (packageName.equals(topLevelClassName)) { continue; } importNameSet.add(((JavaTypeName.Reference.Object)typeName).getImportName()); } // Emit the imports. StringBuilder sb = new StringBuilder(); for (final String importName : importNameSet) { emitLine(sb, 0, "import " + importName + ";"); } return sb.toString(); } /** * Get the Java source code for the given top-level class. * @param classRep the representation for the top-level class. * @return the java source * @throws JavaGenerationException */ public static String generateSourceCode(JavaClassRep classRep) throws JavaGenerationException { StringBuilder sb = new StringBuilder(); JavaTypeName classTypeName = classRep.getClassName(); String packageName = classTypeName.getPackageName(); // Create a generation context.. GenerationContext context = new GenerationContext(classTypeName); // Emit the class comemnt if any. if (classRep.getComment() != null) { sb.append(getSource(classRep.getComment(), context, 0)); emitLine(sb); } // Emit SC package and banner emitLine(sb, 0, "package " + packageName + ";"); emitLine(sb); // Next we need to walk the java model building up the import information. ImportGenerator ig = new ImportGenerator(context); classRep.accept(ig, null); // Emit imports and class comment. String imports = genS_Imports(packageName, classTypeName.getFullJavaSourceName(), context); sb.append(imports); emitLine(sb); sb.append(getSource(classRep, context, 0)); return sb.toString(); } /** * Get a String consisting of the opening line for a Java declaration for the given Java class rep. * eg. "public static class Foo extends Bar implements Baz {" * * @param classRep the Java class representation * @param context the generation context * @return the opening line of the Java class, ending in an open paren. */ private static String getClassDeclaration(JavaClassRep classRep, GenerationContext context) { String unqualifiedClassName = ((JavaTypeName.Reference.Object)classRep.getClassName()).getBaseName(); StringBuilder classDeclaration = new StringBuilder(Modifier.toString(classRep.getModifiers())); classDeclaration.append(" class " + unqualifiedClassName + " "); JavaTypeName superclassName = classRep.getSuperclassName(); if (!superclassName.equals(JavaTypeName.OBJECT)) { classDeclaration.append("extends " + context.getSourceName(superclassName) + " "); } final int nInterfaces = classRep.getNInterfaces(); if (nInterfaces > 0) { classDeclaration.append("implements "); for (int i = 0; i < nInterfaces; i++) { if (i > 0) { classDeclaration.append(", "); } classDeclaration.append(context.getSourceName(classRep.getInterface(i))); } classDeclaration.append(" "); } classDeclaration.append("{"); return classDeclaration.toString(); } /** * Get a String consisting of the opening line for a Java declaration for the given Java constructor. * eg. "public static Bar() {" * * @param javaConstructor * @param context the generation context * @return the opening line of the Java constructor, ending in an open paren. */ private static String getConstructorDeclaration(JavaConstructor javaConstructor, GenerationContext context) { // Initialize with access flags. StringBuilder constructorDeclaration = new StringBuilder(Modifier.toString(javaConstructor.getModifiers())).append(' '); // Method name constructorDeclaration.append(javaConstructor.getConstructorName() + "("); // Parameters final int nParams = javaConstructor.getNParams(); for (int i = 0; i < nParams; i++) { if (i > 0) { constructorDeclaration.append(", "); } constructorDeclaration.append(context.getSourceName(javaConstructor.getParamType(i)) + " " + javaConstructor.getParamName(i)); } // Close paren, open brace constructorDeclaration.append(") {"); return constructorDeclaration.toString(); } /** * Get a String consisting of the opening line for a Java declaration for the given Java method. * eg. "public static void foo throws Bar {" * * @param javaMethod the Java method representation * @param context the generation context * @return the opening line of the Java method, ending in an open paren. */ private static String getMethodDeclaration(JavaMethod javaMethod, GenerationContext context) { // Initialize with access flags. StringBuilder methodDeclaration = new StringBuilder(Modifier.toString(javaMethod.getModifiers())).append(' '); // Return type methodDeclaration.append(context.getSourceName(javaMethod.getReturnType()) + " "); // Method name methodDeclaration.append(javaMethod.getMethodName() + "("); // Parameters for (int i = 0, nParams = javaMethod.getNParams(); i < nParams; i++) { if (i > 0) { methodDeclaration.append(", "); } if (javaMethod.isParamFinal(i)) { methodDeclaration.append("final "); } methodDeclaration.append(context.getSourceName(javaMethod.getParamType(i))).append(" ").append(javaMethod.getParamName(i)); } // Close paren methodDeclaration.append(") "); // throws final int nThrownExceptions = javaMethod.getNThrownExceptions(); if (nThrownExceptions > 0) { methodDeclaration.append("throws "); for (int i = 0; i < nThrownExceptions; ++i) { JavaTypeName thrownExceptionName = javaMethod.getThrownException(i); methodDeclaration.append(context.getSourceName(thrownExceptionName)); if (i < nThrownExceptions - 1) { methodDeclaration.append(","); } methodDeclaration.append(" "); } } // Open brace methodDeclaration.append("{"); return methodDeclaration.toString(); } /** * Get the Java source for a given java class representation. * @param classRep the java class representation * @param context the generation context * @param indent the indent level of the source * @return the java source * @throws JavaGenerationException */ private static String getSource(JavaClassRep classRep, GenerationContext context, int indent) throws JavaGenerationException{ StringBuilder sb = new StringBuilder(); // Emit class JavaDoc if (classRep.getJavaDoc() != null) { sb.append(getSource(classRep.getJavaDoc(), context, indent).toString()); } // Emit class declaration { emitLine(sb, indent, getClassDeclaration(classRep, context)); } Iterator<Object> elements = classRep.getClassContent().iterator(); while (elements.hasNext()) { Object element = elements.next(); if (element instanceof JavaFieldDeclaration) { // Emit a field declaration. JavaFieldDeclaration fieldDeclaration = (JavaFieldDeclaration)element; sb.append(getSource(fieldDeclaration, context, indent+1)); } else if (element instanceof JavaConstructor) { // Emit constructor JavaConstructor javaConstructor = (JavaConstructor)element; sb.append(getSource(javaConstructor, context, indent + 1)); // extra EOL emitLine(sb); } else if (element instanceof JavaMethod) { // Emit method JavaMethod javaMethod = (JavaMethod)element; sb.append(getSource(javaMethod, context, indent + 1)); // extra EOL emitLine(sb); } else if (element instanceof JavaClassRep) { // Emit inner classe JavaClassRep innerClassRep = (JavaClassRep)element; sb.append(getSource(innerClassRep, context, indent + 1)); // extra EOL } else if (element instanceof MultiLineComment) { sb.append(getSource((MultiLineComment)element, context, indent + 1)); emitLine(sb); } else { throw new JavaGenerationException ("Unhandled top level element in JavaClassRep: " + element.getClass().getName()); } } // Close paren. { emitLine(sb, indent, "}"); } return sb.toString(); } /** * Get the Java source for a given java field declaration. * @param fieldDeclaration the java field declaration * @param context the generation context * @param indent the indent level of the source * @return String * @throws JavaGenerationException */ private static String getSource(JavaFieldDeclaration fieldDeclaration, GenerationContext context, int indent) throws JavaGenerationException { StringBuilder block = new StringBuilder(); JavaDocComment jdc = fieldDeclaration.getJavaDoc(); if (jdc != null) { block.append(getSource(jdc, context, indent)); } String fieldName = fieldDeclaration.getFieldName(); JavaTypeName fieldType = fieldDeclaration.getFieldType(); JavaExpression initializer = fieldDeclaration.getInitializer(); // flags, type, name final StringBuilder declaration; { final String modifiers = Modifier.toString(fieldDeclaration.getModifiers()); if (modifiers.length() > 0) { declaration = new StringBuilder(modifiers).append(' '); } else { declaration = new StringBuilder(); } } declaration.append(context.getSourceName(fieldType)).append(' ').append(fieldName); // " = " , initializer. if (initializer != null) { declaration.append(" = "); declaration.append(getSource(initializer, indent, declaration.length(), context)); } declaration.append(";"); emitLine(block, indent, declaration.toString()); emitLine(block); return block.toString(); } /** * Get the Java source for a given java constructor. * @param javaConstructor the Java constructor representation * @param context the generation context * @param indent the indent level of the source * @return String * @throws JavaGenerationException */ private static String getSource(JavaConstructor javaConstructor, GenerationContext context, int indent) throws JavaGenerationException { StringBuilder sb = new StringBuilder(); emitLine(sb, indent, getConstructorDeclaration(javaConstructor, context)); if (javaConstructor.getSuperConstructorParamValues().length > 0) { StringBuilder superCall = new StringBuilder(); superCall.append("super ("); for (int i = 0, n = javaConstructor.getSuperConstructorParamValues().length; i < n; ++i) { superCall.append(getSource(javaConstructor.getSuperConstructorParamValues()[i], indent + 1, superCall.length(), context)); if (i < (n-1)) { superCall.append(", "); } } superCall.append(");"); emitLine(sb, indent+1, superCall.toString()); } sb.append(getSource(javaConstructor.getBodyCode(), context, indent + 1)); emitLine(sb, indent, "}"); return sb.toString(); } /** * Get the Java source for a given java method. * @param javaMethod the Java method representation * @param context the generation context * @param indent the indent level of the source * @return String * @throws JavaGenerationException */ private static String getSource(JavaMethod javaMethod, GenerationContext context, int indent) throws JavaGenerationException { StringBuilder sb = new StringBuilder(); if (javaMethod.getJavaDocComment() != null) { sb.append(getSource(javaMethod.getJavaDocComment(), context, indent)); } emitLine(sb, indent, getMethodDeclaration(javaMethod, context)); sb.append(getSource(javaMethod.getBodyCode(), context, indent + 1)); emitLine(sb, indent, "}"); return sb.toString(); } /** * Get the Java source for a given java statement. * @param statement the java statement for which to generate source. * @param context the generation context. * @param indent the indent level of the source * @return StringBuilder * @throws JavaGenerationException */ private static StringBuilder getSource(JavaStatement statement, GenerationContext context, int indent) throws JavaGenerationException { StringBuilder sb = new StringBuilder(); if (statement instanceof LocalVariableDeclaration) { LocalVariableDeclaration declaration = (LocalVariableDeclaration)statement; LocalVariable localVariable = declaration.getLocalVariable(); emitIndent(sb, indent); // final if (declaration.isFinal()) { sb.append("final "); } // variable type and name sb.append(context.getSourceName(localVariable.getTypeName()) + " " + localVariable.getName()); // " = " + initializer JavaExpression initializer = declaration.getInitializer(); if (initializer != null) { sb.append(" = "); sb.append(getSource(initializer, indent, sb.length(), context)); } // ; sb.append(";" + EOL); } else if (statement instanceof ExpressionStatement) { ExpressionStatement expressionStatement = (ExpressionStatement)statement; String expressionSource = getSource(expressionStatement.getJavaExpression(), indent, 0, context); emitLine(sb, 0, expressionSource + ";"); } else if (statement instanceof LineComment) { LineComment lineComment = (LineComment)statement; emitLine(sb, indent, "// " + lineComment.getCommentText()); } else if (statement instanceof MultiLineComment) { boolean isJavaDoc = (statement instanceof JavaDocComment); MultiLineComment jdc = (MultiLineComment)statement; Iterator<String> it = jdc.getCommentLines().iterator(); String firstLine = it.next(); boolean nd = !firstLine.startsWith("/*"); if (nd) { emitLine(sb, indent, isJavaDoc ? "/**" : "/*"); } it = jdc.getCommentLines().iterator(); while (it.hasNext()) { String line = it.next(); if (nd) { emitLine(sb, indent, " * " + line); } else { emitLine(sb, indent, line); } } if (nd) { emitLine(sb, indent, " */"); } } else if (statement instanceof ReturnStatement) { ReturnStatement returnStatement = (ReturnStatement)statement; if (returnStatement.getReturnExpression() == null) { emitLine(sb, indent, "return;"); } else { emitLine(sb, indent, "return " + getSource(returnStatement.getReturnExpression(),indent, 7, context) + ";"); } } else if (statement instanceof IfThenElseStatement) { IfThenElseStatement iteStatement = (IfThenElseStatement)statement; emitLine(sb, indent, "if (" + getSource(iteStatement.getCondition(), indent, 4, context) + ") {"); sb.append(getSource(iteStatement.getThenStatement(), context, indent + 1)); // Emit the else clause only if there is one. StringBuilder elseClause = getSource(iteStatement.getElseStatement(), context, indent + 1); if (elseClause.length() > 0) { emitLine(sb, indent, "} else {"); sb.append(elseClause); } emitLine(sb, indent, "}"); } else if (statement instanceof SwitchStatement) { SwitchStatement switchStatement = (SwitchStatement)statement; JavaExpression condition = switchStatement.getCondition(); List<IntCaseGroup> caseGroups = switchStatement.getCaseGroups(); JavaStatement defaultStatementGroup = switchStatement.getDefaultStatement(); // Sort cases by the first case label in each group. Collections.sort(caseGroups, new Comparator<IntCaseGroup>() { public int compare(IntCaseGroup o1, IntCaseGroup o2) { int int1 = o1.getNthCaseLabel(0); int int2 = o2.getNthCaseLabel(0); if (int1 < int2) { return -1; } if (int1 > int2) { return 1; } return 0; } }); emitIndent (sb, indent); int length = sb.length(); sb.append("switch ("); String conditionString = getSource(condition, 0, 0, context, true); conditionString = conditionString.replaceAll(EOL," "); sb.append(conditionString); if (sb.lastIndexOf(EOL, length) >= 0) { emitLine(sb); emitIndent(sb, indent); for (int i = 0; i < 7; ++i) { sb.append(" "); } sb.append (") {"); emitLine (sb); } else { sb.append(") {"); emitLine(sb); } emitLine(sb); // case labels and their statement groups. for (final IntCaseGroup switchCaseGroup : caseGroups) { JavaStatement caseStatementGroup = switchCaseGroup.getStatement(); int nCaseLabels = switchCaseGroup.getNCaseLabels(); for (int i = 0; i < nCaseLabels - 1; i++) { emitLine(sb, indent + 1, "case " + switchCaseGroup.getNthCaseLabel(i) + ":"); } emitLine(sb, indent + 1, "case " + switchCaseGroup.getNthCaseLabel(nCaseLabels - 1) + ": {"); sb.append(getSource(caseStatementGroup, context, indent + 2)); emitLine(sb, indent + 1, "}"); emitLine(sb); } // default label and statement group. if (defaultStatementGroup != null) { emitLine(sb, indent + 1, "default: {"); sb.append(getSource(defaultStatementGroup, context, indent + 2)); emitLine(sb, indent + 1, "}"); } emitLine(sb, indent, "}"); } else if (statement instanceof Block) { Block block = (Block)statement; int blockIndent = indent; List<JavaExceptionHandler> exceptionHandlers = block.getExceptionHandlers(); if (!exceptionHandlers.isEmpty()) { emitLine(sb, indent, "try {"); blockIndent++; } boolean doingLocalVarDeclarations = false; boolean wasLineComment = false; int nStatements = block.getNBlockStatements(); for (int i = 0; i < nStatements; i++) { JavaStatement blockStatement = block.getNthBlockStatement(i); // Try to separate local var declarations from other types of block statements. boolean isLocalVarDeclaration = blockStatement instanceof LocalVariableDeclaration; if (!wasLineComment && isLocalVarDeclaration != doingLocalVarDeclarations && i > 0) { sb.append(EOL); } doingLocalVarDeclarations = isLocalVarDeclaration; wasLineComment = blockStatement instanceof LineComment; // Now append the source. sb.append(getSource(blockStatement, context, blockIndent)); } if (!exceptionHandlers.isEmpty()) { for (final JavaExceptionHandler eh : exceptionHandlers) { emitLine(sb, indent, "} catch (" + fixupClassName(eh.getExceptionClass().getName()) + " " + eh.getExceptionVarName() + ") {"); sb.append(getSource(eh.getHandlerCode(), context, blockIndent)); } emitLine(sb, indent, "}"); } } else if (statement instanceof ThrowStatement) { ThrowStatement throwStatement = (ThrowStatement)statement; emitLine(sb, indent, "throw " + getSource(throwStatement.getThrownExpression(), indent, 6, context) + ";"); } else if (statement instanceof UnconditionalLoop) { UnconditionalLoop whileStatement = (UnconditionalLoop)statement; emitLine (sb, indent, whileStatement.getLabel() + ": while (true) {"); sb.append(getSource(whileStatement.getBody(), context, indent + 1)); emitLine (sb, indent, "}"); } else if (statement instanceof LabelledContinue) { LabelledContinue lc = (LabelledContinue)statement; emitLine (sb, indent, "continue " + lc.getLabel() + ";"); } else if (statement instanceof SynchronizedMethodInvocation){ // A method invocation wrapped in a synchronization block. SynchronizedMethodInvocation sof = (SynchronizedMethodInvocation)statement; // Start the synchronized block. emitLine (sb, indent, "synchronized (" + getSource(sof.getSynchronizingObject(), indent, 14, context) + ") {"); // Add the method invocation. sb.append (getSource(new ExpressionStatement(sof.getMethodInvocation()), context, indent + 1)); // Finish the block. emitLine (sb, indent, "}"); } else if (statement instanceof AssertStatement) { AssertStatement ast = (AssertStatement)statement; JavaExpression conditionExpression = ast.getConditionExpr(); JavaExpression failureExpression = ast.getOnFailureExpr(); if (failureExpression != null) { emitIndent(sb, indent); sb.append("assert ("); sb.append(getSource(conditionExpression, indent, 8, context)); sb.append(") : ("); sb.append(getSource(failureExpression, indent+1, 5, context)); sb.append(");"); emitLine(sb); } else { emitLine (sb, indent, "assert (" + getSource(conditionExpression, indent, 8, context) + ");"); } } else { throw new JavaGenerationException("Unrecognized statement type: " + statement.getClass()); } return sb; } /** * Get the Java source for a given java expression. * @param expression the java expression * @param context the generation context * @param indentLevel * @param startOffset * @return String */ private static String getSource(JavaExpression expression, int indentLevel, int startOffset, GenerationContext context) { return getSource (expression, indentLevel, startOffset, context, false); } private static String getSource(JavaExpression expression, int indentLevel, int startOffset, GenerationContext context, boolean singleLine) { ExpressionTextGenerator etg = new ExpressionTextGenerator(context); expression.accept(etg, null); ExpressionTextNode rootNode = etg.getCurrentNode().getNthChild(0); StringBuilder sb = new StringBuilder(); if (!singleLine) { rootNode.formatExpressionText(sb, indentLevel, startOffset); } else { rootNode.appendExpressionText(sb); } return sb.toString(); } /** * Emit an indent. * @param sb the StringBuilder to which to add an indent. * @param indent the number of indents to add. * @return StringBuilder sb, returned for convenience. */ private static StringBuilder emitIndent(StringBuilder sb, int indent) { // NOTE: we use the tab character '\t' instead of adding spaces // because of memory issues. // When using spaces instead of '\t' some of our generated java source // files were large enough to cause out-of-memory errors. for (int i = 0; i < indent; i++) { sb.append('\t'); } return sb; } /** * Emit an empty line. * @param sb the StringBuilder to which to add the empty line. */ private static void emitLine(StringBuilder sb) { sb.append(EOL); } /** * Emit an indent, some text, and an EOL. * @param sb the StringBuilder to which to add an indent. * @param text the text to add. * @param indent the number of indents to add. */ private static void emitLine(StringBuilder sb, int indent,String text) { emitIndent(sb, indent); sb.append(text + EOL); } /** * A textual representation of a JavaClassRep to be used for debugging purposes only. * @param classRep * @return String */ static String toDebugString(JavaClassRep classRep) { try { return generateSourceCode(classRep); } catch (JavaGenerationException e) { JavaTypeName classTypeName = classRep.getClassName(); return "JavaGenerationException generating source for JavaClassRep: " + classTypeName + "."; } } /** * A textual representation of a JavaMethod to be used for debugging purposes only. * With respect to use of qualified symbols, the method is treated as being defined in the java.lang.Object class. * @param javaMethod * @return String */ static String toDebugString(JavaMethod javaMethod) { try { return getSource(javaMethod, new GenerationContext(JavaTypeName.OBJECT), 0); } catch (JavaGenerationException cge) { return "JavaGenerationException generating source for JavaMethod: " + javaMethod.getMethodName() + "."; } } /** * A textual representation of a JavaConstructor to be used for debugging purposes only. * With respect to use of qualified symbols, the constructor is treated as being defined in the java.lang.Object class. * @param javaConstructor * @return String */ static String toDebugString(JavaConstructor javaConstructor) { try { return getSource(javaConstructor, new GenerationContext(JavaTypeName.OBJECT), 0); } catch (JavaGenerationException cge) { return "JavaGenerationException generating source for JavaConstructor: " + javaConstructor.getConstructorName() + "."; } } /** * A textual representation of a JavaStatement to be used for debugging purposes only. * With respect to use of qualified symbols, the statement is treated as being defined in the java.lang.Object class. * @param javaStatement * @return String */ static String toDebugString(JavaStatement javaStatement) { try { return getSource(javaStatement, new GenerationContext(JavaTypeName.OBJECT), 0).toString(); } catch (JavaGenerationException cge) { return "JavaGenerationException generating source for JavaStatement: " + javaStatement.getClass() + "."; } } /** * A textual representation of a JavaExpression to be used for debugging purposes only. * With respect to use of qualified symbols, the expression is treated as being defined in the java.lang.Object class. * @param javaExpression * @return String */ static String toDebugString(JavaExpression javaExpression) { return getSource(javaExpression, 0, 0, new GenerationContext(JavaTypeName.OBJECT)); } /** * A textual representation of a JavaFieldDeclaration to be used for debugging purposes only. * With respect to use of qualified symbols, the field declaration is treated as being defined in the java.lang.Object class. * @param javaFieldDeclaration * @return String */ static String toDebugString(JavaFieldDeclaration javaFieldDeclaration) { try { return getSource(javaFieldDeclaration, new GenerationContext(JavaTypeName.OBJECT), 0); } catch (JavaGenerationException cge) { return "JavaGenerationException generating source for JavaFieldDeclaration: " + javaFieldDeclaration.getClass() + "."; } } /** * Return the name of the class, in a form that can be used in source code. * eg. [[B ==> byte[][]. * CALExecutor$ForeignFunctionException ==> CALExecutor.ForiegnFunctionException. * @param name * @return String */ static String fixupClassName (String name) { // Count the number of array dimensions (if any). int i = 0; while (name.startsWith("[")) { i++; name = name.substring(1); } if (name.startsWith ("L") && name.endsWith(";")) { // This is a fully qualified class name. name = name.substring (1, name.length() - 1); } else if (name.equals ("Z")) { // boolean name = "boolean"; } else if (name.equals ("B")) { name = "byte"; } else if (name.equals ("C")) { name = "char"; } else if (name.equals("S")) { name = "short"; } else if (name.equals ("I")) { name = "integer"; } else if (name.equals ("J")) { name = "long"; } else if (name.equals ("F")) { name = "float"; } else if (name.equals("D")) { name = "double"; } for (int j = 0; j < i; ++j) { name = name + "[]"; } // Substitute . for $ name = name.replace ('$', '.'); return name; } /** * A class used for generating Java source code from a * JavaExpression. * * @author rcypher * */ private static class ExpressionTextNode { /** Max length of a generated line of source. */ private static final int MAX_LINE_LENGTH = 80; /** The text associated with this node. */ private String myText = ""; /** Child nodes. */ private List<ExpressionTextNode> children = new ArrayList<ExpressionTextNode>(); /** The type of formatting for this node. */ private final FormatType formatType; /** * Create an ExpressionTextNode * @param formatType */ ExpressionTextNode (FormatType formatType) { if (formatType == null) { throw new NullPointerException("Invalid null value for format type."); } this.formatType = formatType; } /** * Create an ExpressionTextNode * @param myText * @param formatType */ ExpressionTextNode (String myText, FormatType formatType) { if (formatType == null) { throw new NullPointerException("Invalid null value for format type."); } this.myText = myText; this.formatType = formatType; } @Override public String toString () { StringBuilder sb = new StringBuilder(); appendExpressionText(sb); return sb.toString(); } String getThisText() { return myText; } void setThisText (String newText) { myText = newText; } int getNChildren () { return children.size(); } ExpressionTextNode getNthChild (int index) { return children.get(index); } int getTotalTextLength () { int totalLength = getThisText().length(); for (final ExpressionTextNode expressionTextNode : children) { totalLength += expressionTextNode.getTotalTextLength(); } return totalLength; } void addChild (ExpressionTextNode node) { children.add(node); } /** * Prefix the supplied text to the text of * this node, if there is any. Otherwise prefix * it to the leftmost child node. * @param prefixText */ void prefixText (String prefixText) { if(myText != null && myText.length() > 0) { myText = prefixText + myText; } else if (getNChildren() > 0) { getNthChild(0).prefixText(prefixText); } } /** * Suffix the given text to this nodes text if * there are no children. Otherwise suffix to the * rightmost child. * @param suffixText */ void suffixText (String suffixText) { if (getNChildren() == 0) { myText = myText + suffixText; } else { children.get(children.size()-1).suffixText(suffixText); } } /** * Format the Java source expression represented by this ExpressionTextNode * @param sb - the StringBuilder to put the formatted source in. * @param indentLevel * @param offset - offset from the current indent */ private void formatExpressionText ( StringBuilder sb, int indentLevel, int offset) { // Update the offset if necessary. if (offset == 0) { offset = sb.length() - sb.lastIndexOf(EOL) - EOL.length(); if (offset < 0) { offset = 0; } } // Find starting position on current line. int startPos = offset + (indentLevel * INDENT_LENGTH); // If the expression fits on the remainder of the current line // we can simply append the text. if (getTotalTextLength() + startPos <= MAX_LINE_LENGTH) { // If we're at the beginning of a line we need to indent. if (offset == 0) { emitIndent(sb, indentLevel); } appendExpressionText(sb); return; } // If switching to the next line will give more space // emit a line break and increase the indent level. if (offset > 0 && offset > INDENT_LENGTH) { emitLine(sb); formatExpressionText(sb, ++indentLevel, 0); return; } // We need to break up the expression. // The expression is broken up based on the format type if (getFormatType().equals(FormatType.ATOMIC)) { if (offset == 0) { emitIndent(sb, indentLevel); } appendExpressionText(sb); } else if (getFormatType().equals(FormatType.CHILDREN_AT_SAME_LEVEL)) { if (getThisText().length() > 0) { if (offset == 0) { emitIndent(sb, indentLevel); } sb.append(getThisText()); emitLine(sb); offset = 0; } for (int i = 0, n = getNChildren(); i < n; ++i) { getNthChild(i).formatExpressionText(sb, indentLevel, i == 0 ? offset : 0); if (i < n-1) { emitLine(sb); } } } else if (getFormatType().equals(FormatType.CHILDREN_IN_ONE_LEVEL)) { if (getThisText().length() > 0) { if (offset == 0) { emitLine(sb, indentLevel, getThisText()); } else { sb.append(getThisText()); emitLine(sb); offset = 0; } } // Format each child on a new line. for (int i = 0, n = getNChildren(); i < n; ++i) { getNthChild(i).formatExpressionText(sb, indentLevel+1, 0); if (i < n-1) { emitLine(sb); } } } else if (getFormatType().equals(FormatType.FIRST_CHILD_AT_LEVEL)) { if (getThisText().length() > 0) { if (offset == 0) { emitLine(sb, indentLevel, getThisText()); } else { sb.append(getThisText()); emitLine(sb); offset = 0; } } // Format first child at the same indent level. getNthChild(0).formatExpressionText(sb, indentLevel, offset); if (getNChildren() > 1) { emitLine(sb); } // Format other children on new lines. for (int i = 1, n = getNChildren(); i < n; ++i) { getNthChild(i).formatExpressionText(sb, indentLevel+1, 0); if (i < n-1) { emitLine(sb); } } } else { // Default behaviour is to place this nodes text on the current line and then // format each child starting on a new line with the same indent. if (getThisText().length() > 0) { if (offset == 0) { emitIndent(sb, indentLevel); } sb.append(getThisText()); emitLine(sb); offset = 0; } for (int i = 0, n = getNChildren(); i < n; ++i) { ExpressionTextNode child = getNthChild(i); child.formatExpressionText(sb, indentLevel, offset); if (!sb.toString().endsWith(EOL)) { sb.append(EOL); } } } } /** * Build up the concatenation of the text contained in * this node and all its children. * @param sb */ private void appendExpressionText (StringBuilder sb) { sb.append(getThisText()); for (int i = 0, n = getNChildren(); i < n; ++i) { getNthChild(i).appendExpressionText(sb); } } private FormatType getFormatType () { return formatType; } /** * A class used to indicate the type of formatting associated * with an ExpressionTextNode. * @author rcypher */ static class FormatType { /** The text of the sub-tree cannot be broken apart. */ static final FormatType ATOMIC = new FormatType(1, "ATOMIC"); /** The text of the sub-tree can be broken between nodes. * The children should have the same indent level as the root. */ static final FormatType CHILDREN_AT_SAME_LEVEL = new FormatType(2, "CHILDREN_AT_SAME_LEVEL"); /** The text of the sub-tree can be broken between nodes. * The children should indent one more level than the root. */ static final FormatType CHILDREN_IN_ONE_LEVEL = new FormatType(3, "CHILDREN_IN_ONE_LEVEL"); /** The text of the sub-tree can be broken between nodes. * The first child should have the same indent level as the root. * Other children should be indented an additional level.*/ static final FormatType FIRST_CHILD_AT_LEVEL = new FormatType(4, "FIRST_CHILD_AT_LEVEL"); private final int type; private final String name; private FormatType (int type, String name) { this.type = type; this.name = name; } @Override public boolean equals(Object other) { return (other != null && other instanceof FormatType && ((FormatType)other).type == this.type); } @Override public String toString () { return name; } } } /** * This class is an extension of the JavaModelTraverser which * is used in generating Java source for from a JavaExpression. * * The JavaExpression is traversed and a tree of ExpressionTextNode * instances is created. The text in each ExpressionTextNode is * considered to be an atomic part of the generated source (i.e. * it cannot be broken apart for formatting purposes) * * @author rcypher * */ private static class ExpressionTextGenerator extends JavaModelTraverser<Void, Void> { ArrayStack<ExpressionTextNode> stack = ArrayStack.make(); GenerationContext context; ExpressionTextGenerator (GenerationContext context) { this.context = context; stack.push(new ExpressionTextNode(ExpressionTextNode.FormatType.ATOMIC)); } ExpressionTextNode getCurrentNode () { return stack.peek(); } boolean doOptionalParenthesis (JavaExpression expression) { if (expression instanceof PlaceHolder) { expression = ((PlaceHolder)expression).getActualExpression(); } boolean parenthesize = (expression instanceof Assignment) || (expression instanceof CastExpression) || (expression instanceof ClassInstanceCreationExpression) || (expression instanceof ArrayCreationExpression) || (expression instanceof InstanceOf) || (expression instanceof OperatorExpression); // sometimes not parenthesizing these are ok too.. // parenthesize if the literal is a -ve number. if (!parenthesize && expression instanceof LiteralWrapper) { Object literalObject = ((LiteralWrapper)expression).getLiteralObject(); if (literalObject instanceof Number && ((Number)literalObject).doubleValue() < 0) { parenthesize = true; } } // don't parenthesize: // (expression instanceof UnknownJavaExpression) // (expression instanceof MethodInvocation) // (expression instanceof JavaTypeName) // (expression instanceof JavaField) // (expression instanceof LocalVariable) // (expression instanceof MethodVariable) // (expression instanceof ArrayAccess) return parenthesize; } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitArrayAccessExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.ArrayAccess, java.lang.Object) */ @Override public Void visitArrayAccessExpression(ArrayAccess arrayAccess, Void arg) { ExpressionTextNode node = new ExpressionTextNode(ExpressionTextNode.FormatType.CHILDREN_IN_ONE_LEVEL); stack.peek().addChild(node); stack.push(node); super.visitArrayAccessExpression(arrayAccess, arg); assert node.getNChildren() == 2; if (doOptionalParenthesis(arrayAccess.getArrayReference())) { node.getNthChild(0).prefixText("("); node.getNthChild(0).suffixText(")"); } node.getNthChild(0).suffixText("["); node.getNthChild(1).suffixText("]"); stack.pop(); return null; } /** {@inheritDoc} */ @Override public Void visitArrayLengthExpression( ArrayLength arrayLength, Void arg) { ExpressionTextNode node = new ExpressionTextNode(ExpressionTextNode.FormatType.CHILDREN_AT_SAME_LEVEL); stack.peek().addChild(node); stack.push(node); super.visitArrayLengthExpression(arrayLength, arg); if (doOptionalParenthesis(arrayLength.getArrayReference())) { node.getNthChild(0).prefixText("("); node.getNthChild(0).suffixText(")"); } node.getNthChild(0).suffixText(".length"); stack.pop(); return null; } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitArrayCreationExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.ArrayCreationExpression, java.lang.Object) */ @Override public Void visitArrayCreationExpression( ArrayCreationExpression arrayCreation, Void arg) { ExpressionTextNode node = new ExpressionTextNode(ExpressionTextNode.FormatType.ATOMIC); stack.peek().addChild(node); stack.push(node); try { String elementClassNameString = context.getSourceName(arrayCreation.getArrayElementTypeName()); node.setThisText("new " + elementClassNameString + "[] {"); } catch (Exception e) { } super.visitArrayCreationExpression(arrayCreation, arg); for (int i = 0, n = node.getNChildren()-1; i < n; ++i) { node.getNthChild(i).suffixText(", "); } node.suffixText("}"); stack.pop(); return null; } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitAssignmentExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.Assignment, java.lang.Object) */ @Override public Void visitAssignmentExpression( Assignment assignment, Void arg) { ExpressionTextNode node = new ExpressionTextNode(ExpressionTextNode.FormatType.CHILDREN_IN_ONE_LEVEL); stack.peek().addChild(node); stack.push(node); super.visitAssignmentExpression(assignment, arg); node.getNthChild(0).suffixText(" = "); if (doOptionalParenthesis(assignment.getValue())) { node.getNthChild(1).prefixText("("); node.getNthChild(1).suffixText(")"); } stack.pop(); return null; } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitCastExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.CastExpression, java.lang.Object) */ @Override public Void visitCastExpression( CastExpression cast, Void arg) { // Check for redundant casts. i.e. (RTRecordValue)(RTRecordValue)expressionToCast if (cast.getExpressionToCast() instanceof CastExpression) { CastExpression innerCast = (CastExpression)cast.getExpressionToCast(); if (cast.getCastType().equals(innerCast.getCastType())) { return super.visitCastExpression(cast, arg); } } ExpressionTextNode node = new ExpressionTextNode(ExpressionTextNode.FormatType.CHILDREN_IN_ONE_LEVEL); stack.peek().addChild(node); stack.push(node); String classNameString = context.getSourceName(cast.getCastType()); if (cast.getCastType() instanceof JavaTypeName.Primitive) { node.setThisText("((" + classNameString + ")"); } else { // This is a reference/array type cast, and we need to first upcast to java.lang.Object // so that the Java compiler does not complain about incompatible types in situations like this: // Suppose there are classes A, B, and C, where B and C are subclasses of A. // The cast is from A to B. The actual expression being cast has a static Java type of C. // While the JVM allows such a checkcast operation, the Java language does not, because // C is not compatible with B for a cast operation. node.setThisText("((" + classNameString + ")(" + context.getSourceName(JavaTypeName.OBJECT) + ")"); } super.visitCastExpression(cast, arg); if (doOptionalParenthesis(cast.getExpressionToCast())) { node.getNthChild(0).prefixText("("); node.getNthChild(0).suffixText(")"); } node.suffixText(")"); stack.pop(); return null; } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitClassInstanceCreationExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.ClassInstanceCreationExpression, java.lang.Object) */ @Override public Void visitClassInstanceCreationExpression( ClassInstanceCreationExpression instanceCreation, Void arg) { ExpressionTextNode node = new ExpressionTextNode(ExpressionTextNode.FormatType.CHILDREN_IN_ONE_LEVEL); stack.peek().addChild(node); stack.push(node); node.setThisText("new "); JavaTypeName className = instanceCreation.getClassName(); if (instanceCreation.isArrayCreationExpression()) { // Creating an array. // Get the source for the array element type final JavaTypeName.Reference.Array arrayType = ((JavaTypeName.Reference.Array)className); final int nDims = arrayType.getNDimensions(); final JavaTypeName arrayElementType = arrayType.getElementType(); final String classNameString = context.getSourceName(arrayElementType); node.suffixText(classNameString); super.visitClassInstanceCreationExpression(instanceCreation, arg); final int nArgs = instanceCreation.getNArgs(); // Get the source for the args whose dimensions are specified. for (int i = 0; i < nArgs; i++) { node.getNthChild(i).prefixText("["); node.getNthChild(i).suffixText("]"); } //the renaming dimensions (needed to specify the array type correctly) for (int i = 0, nRemaining = nDims - nArgs; i < nRemaining; ++i) { node.addChild(new ExpressionTextNode("[]", ExpressionTextNode.FormatType.ATOMIC)); } } else { // Creating a non-array object. // Get the source for the object type. String classNameString = context.getSourceName(className); node.suffixText(classNameString + "("); // Get the source for the args. super.visitClassInstanceCreationExpression(instanceCreation, arg); for (int i = 0, nArgs = instanceCreation.getNArgs()-1; i < nArgs; i++) { node.getNthChild(i).suffixText(", "); } node.suffixText(")"); } stack.pop(); return null; } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitInstanceOfExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.InstanceOf, java.lang.Object) */ @Override public Void visitInstanceOfExpression( InstanceOf instanceOf, Void arg) { ExpressionTextNode node = new ExpressionTextNode(ExpressionTextNode.FormatType.CHILDREN_IN_ONE_LEVEL); stack.peek().addChild(node); stack.push(node); super.visitInstanceOfExpression(instanceOf, arg); JavaTypeName referenceType = instanceOf.getReferenceType(); // We need to first upcast to java.lang.Object // so that the Java compiler does not complain about incompatible types in situations like this: // Suppose there are classes A, B, and C, where B and C are subclasses of A. // The instanceof foreign function takes an argument of CAL type JA, whose Java implementation type is A. // The instanceof is a check for type B. The actual expression being checked has a static Java type of C. // While the JVM allows such an instanceof check, the Java language does not, because // C is not compatible with B as B is neither a subclass nor a superclass of C. node.setThisText("((" + context.getSourceName(JavaTypeName.OBJECT) + ")"); if (doOptionalParenthesis(instanceOf.getJavaExpression())) { node.getNthChild(0).prefixText("("); node.getNthChild(0).suffixText(")"); } node.suffixText(")"); node.addChild( new ExpressionTextNode( " instanceof " + context.getSourceName(referenceType), ExpressionTextNode.FormatType.ATOMIC)); stack.pop (); return null; } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitInstanceFieldExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.JavaField.Instance, java.lang.Object) */ @Override public Void visitInstanceFieldExpression( Instance instanceField, Void arg) { ExpressionTextNode node = new ExpressionTextNode(ExpressionTextNode.FormatType.ATOMIC); stack.peek().addChild(node); stack.push(node); super.visitInstanceFieldExpression(instanceField, arg); String fieldName = instanceField.getFieldName(); if (node.getNChildren() > 0) { if (doOptionalParenthesis(instanceField.getInstance())) { node.getNthChild(0).prefixText("("); node.getNthChild(0).suffixText(")"); } node.suffixText("." + fieldName); } else { node.setThisText(fieldName); } stack.pop(); return null; } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitStaticFieldExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.JavaField.Static, java.lang.Object) */ @Override public Void visitStaticFieldExpression( Static staticField, Void arg) { ExpressionTextNode node = new ExpressionTextNode( context.getSourceName(staticField.getInvocationClass()) + "." + staticField.getFieldName(), ExpressionTextNode.FormatType.ATOMIC); stack.peek().addChild(node); return null; } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitThisFieldExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.JavaField.This, java.lang.Object) */ @Override public Void visitThisFieldExpression( This thisField, Void arg) { ExpressionTextNode node = new ExpressionTextNode( "this", ExpressionTextNode.FormatType.ATOMIC); stack.peek().addChild(node); return null; } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitLiteralWrapperExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.LiteralWrapper, java.lang.Object) */ @Override public Void visitLiteralWrapperExpression( LiteralWrapper literalWrapper, Void arg) { ExpressionTextNode node = new ExpressionTextNode( ExpressionTextNode.FormatType.ATOMIC); stack.peek().addChild(node); stack.push(node); Object literalObject = literalWrapper.getLiteralObject(); if (literalObject instanceof Integer || literalObject instanceof Double || literalObject instanceof Boolean) { node.setThisText(literalObject.toString()); } else if (literalObject instanceof Byte) { node.setThisText("(byte)" + literalObject.toString()); } else if (literalObject instanceof Short) { node.setThisText("(short)" + literalObject.toString()); } else if (literalObject instanceof Long) { node.setThisText(literalObject.toString() + "L"); } else if (literalObject instanceof Float) { node.setThisText(literalObject.toString() + "F"); } else if (literalObject instanceof String) { node.setThisText(StringEncoder.encodeString(literalObject.toString())); } else if (literalObject instanceof Character) { node.setThisText(StringEncoder.encodeChar(((Character)literalObject).charValue())); } else if (literalObject == null) { node.setThisText("null"); } stack.pop(); return null; } /** * {@inheritDoc} */ @Override public Void visitClassLiteralExpression(ClassLiteral classLiteral, Void arg) { ExpressionTextNode node = new ExpressionTextNode( context.getSourceName(classLiteral.getReferentType()) + ".class", ExpressionTextNode.FormatType.ATOMIC); stack.peek().addChild(node); return null; } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitLocalNameExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.LocalName, java.lang.Object) */ @Override public Void visitLocalNameExpression( LocalName localName, Void arg) { ExpressionTextNode node = new ExpressionTextNode( localName.getName(), ExpressionTextNode.FormatType.ATOMIC); stack.peek().addChild(node); return null; } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitLocalVariableExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.LocalVariable, java.lang.Object) */ @Override public Void visitLocalVariableExpression( LocalVariable localVariable, Void arg) { ExpressionTextNode node = new ExpressionTextNode( localVariable.getName(), ExpressionTextNode.FormatType.ATOMIC); stack.peek().addChild(node); return null; } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitInstanceMethodInvocationExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.MethodInvocation.Instance, java.lang.Object) */ @Override public Void visitInstanceMethodInvocationExpression( MethodInvocation.Instance instanceInvocation, Void arg) { final ExpressionTextNode node; if (instanceInvocation.getInvocationTarget() == null) { node = new ExpressionTextNode(ExpressionTextNode.FormatType.CHILDREN_IN_ONE_LEVEL); } else { node = new ExpressionTextNode(ExpressionTextNode.FormatType.FIRST_CHILD_AT_LEVEL); } stack.peek().addChild(node); stack.push(node); super.visitInstanceMethodInvocationExpression(instanceInvocation, arg); int firstArg = 0; // The invocation target can be null, which means use "this" or "super" (depending on invocationType). // since "this" is optional in Java source in this situation, we just omit it. if (instanceInvocation.getInvocationTarget() == null) { if (instanceInvocation.getInvocationType() == MethodInvocation.InvocationType.SPECIAL) { node.setThisText("super." + instanceInvocation.getMethodName() + "("); } else { node.setThisText(instanceInvocation.getMethodName() + "("); } } else { if (doOptionalParenthesis(instanceInvocation.getInvocationTarget())) { node.getNthChild(0).prefixText("("); node.getNthChild(0).suffixText(")"); } node.getNthChild(0).suffixText("." + instanceInvocation.getMethodName() + "("); firstArg++; } for(int i = firstArg, n = node.getNChildren() - 1; i < n; ++i) { node.getNthChild(i).suffixText(", "); } node.suffixText(")"); stack.pop(); return null; } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitStaticMethodInvocationExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.MethodInvocation.Static, java.lang.Object) */ @Override public Void visitStaticMethodInvocationExpression( JavaExpression.MethodInvocation.Static staticInvocation, Void arg) { ExpressionTextNode node = new ExpressionTextNode(ExpressionTextNode.FormatType.CHILDREN_IN_ONE_LEVEL); stack.peek().addChild(node); stack.push(node); node.setThisText(context.getSourceName(staticInvocation.getInvocationClass()) + "." + staticInvocation.getMethodName() + "("); super.visitStaticMethodInvocationExpression(staticInvocation, arg); for (int i = 0, nArgs = staticInvocation.getNArgs() - 1; i < nArgs; i++) { node.getNthChild(i).suffixText(", "); } node.suffixText(")"); stack.pop(); return null; } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitMethodVariableExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.MethodVariable, java.lang.Object) */ @Override public Void visitMethodVariableExpression( MethodVariable methodVariable, Void arg) { ExpressionTextNode node = new ExpressionTextNode( methodVariable.getName(), ExpressionTextNode.FormatType.ATOMIC); stack.peek().addChild(node); return null; } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitBinaryOperatorExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.OperatorExpression.Binary, java.lang.Object) */ @Override public Void visitBinaryOperatorExpression( Binary binaryOperator, Void arg) { ExpressionTextNode node = new ExpressionTextNode( ExpressionTextNode.FormatType.CHILDREN_AT_SAME_LEVEL); stack.peek().addChild(node); stack.push(node); super.visitBinaryOperatorExpression(binaryOperator, arg); if (doOptionalParenthesis(binaryOperator.getArgument(0))) { node.getNthChild(0).prefixText("("); node.getNthChild(0).suffixText(")"); } if (doOptionalParenthesis(binaryOperator.getArgument(1))) { node.getNthChild(1).prefixText("("); node.getNthChild(1).suffixText(")"); } node.getNthChild(0).suffixText(" " + binaryOperator.getJavaOperator().getSymbol() + " "); stack.pop(); return null; } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitTernaryOperatorExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.OperatorExpression.Ternary, java.lang.Object) */ @Override public Void visitTernaryOperatorExpression( Ternary ternaryOperator, Void arg) { ExpressionTextNode node = new ExpressionTextNode( ExpressionTextNode.FormatType.CHILDREN_AT_SAME_LEVEL); stack.peek().addChild(node); stack.push(node); super.visitTernaryOperatorExpression(ternaryOperator, arg); if (doOptionalParenthesis(ternaryOperator.getArgument(0))) { node.getNthChild(0).prefixText("("); node.getNthChild(0).suffixText(")"); } if (doOptionalParenthesis(ternaryOperator.getArgument(1))) { node.getNthChild(1).prefixText("("); node.getNthChild(1).suffixText(")"); } if (doOptionalParenthesis(ternaryOperator.getArgument(2))) { node.getNthChild(2).prefixText("("); node.getNthChild(2).suffixText(")"); } node.getNthChild(0).suffixText(" ? " ); node.getNthChild(1).suffixText(" : "); stack.pop(); return null; } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitUnaryOperatorExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.OperatorExpression.Unary, java.lang.Object) */ @Override public Void visitUnaryOperatorExpression( Unary unaryOperator, Void arg) { ExpressionTextNode node = new ExpressionTextNode( ExpressionTextNode.FormatType.CHILDREN_AT_SAME_LEVEL); stack.peek().addChild(node); stack.push(node); super.visitUnaryOperatorExpression(unaryOperator, arg); if (doOptionalParenthesis(unaryOperator.getArgument(0))) { node.getNthChild(0).prefixText("("); node.getNthChild(0).suffixText(")"); } node.prefixText(unaryOperator.getJavaOperator().getSymbol()); stack.pop(); return null; } } /** * An extension of JavaModelTraverser which traverses a JavaModel * construct and builds up the context of imported classes. * * @author rcypher * */ private static class ImportGenerator extends JavaModelTraverser<Void, Void> { /** The context of imported classes. */ GenerationContext context; /** * Create an ImportGenerator. * @param context - the context of imported classes. */ ImportGenerator (GenerationContext context) { this.context = context; } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitArrayCreationExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.ArrayCreationExpression, java.lang.Object) */ @Override public Void visitArrayCreationExpression( ArrayCreationExpression arrayCreation, Void arg) { context.getSourceName(arrayCreation.getArrayElementTypeName()); return super.visitArrayCreationExpression(arrayCreation, arg); } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitCastExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.CastExpression, java.lang.Object) */ @Override public Void visitCastExpression( CastExpression cast, Void arg) { context.getSourceName(cast.getCastType()); return super.visitCastExpression(cast, arg); } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitClassInstanceCreationExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.ClassInstanceCreationExpression, java.lang.Object) */ @Override public Void visitClassInstanceCreationExpression( ClassInstanceCreationExpression instanceCreation, Void arg) { JavaTypeName className = instanceCreation.getClassName(); if (instanceCreation.isArrayCreationExpression()) { // Creating an array. // Get the source for the array element type final JavaTypeName.Reference.Array arrayType = ((JavaTypeName.Reference.Array)className); final JavaTypeName arrayElementType = arrayType.getElementType(); context.getSourceName(arrayElementType); } else { // Creating a non-array object. // Get the source for the object type. context.getSourceName(className); } return super.visitClassInstanceCreationExpression(instanceCreation, arg); } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitInstanceOfExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.InstanceOf, java.lang.Object) */ @Override public Void visitInstanceOfExpression( InstanceOf instanceOf, Void arg) { JavaTypeName referenceType = instanceOf.getReferenceType(); context.getSourceName(referenceType); return super.visitInstanceOfExpression(instanceOf, arg); } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitStaticFieldExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.JavaField.Static, java.lang.Object) */ @Override public Void visitStaticFieldExpression( Static staticField, Void arg) { context.getSourceName(staticField.getInvocationClass()); return super.visitStaticFieldExpression(staticField, arg); } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitStaticMethodInvocationExpression(org.openquark.cal.internal.runtime.lecc.JavaExpression.MethodInvocation.Static, java.lang.Object) */ @Override public Void visitStaticMethodInvocationExpression( JavaExpression.MethodInvocation.Static staticInvocation, Void arg) { context.getSourceName(staticInvocation.getInvocationClass()); return super.visitStaticMethodInvocationExpression(staticInvocation, arg); } /** * {@inheritDoc} */ @Override public Void visitClassLiteralExpression(ClassLiteral classLiteral, Void arg) { context.getSourceName(classLiteral.getReferentType()); return super.visitClassLiteralExpression(classLiteral, arg); } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitJavaClassRep(org.openquark.cal.internal.runtime.lecc.JavaClassRep, java.lang.Object) */ @Override public Void visitJavaClassRep(JavaClassRep classRep, Void arg) { JavaTypeName superclassName = classRep.getSuperclassName(); if (!superclassName.equals(JavaTypeName.OBJECT)) { context.getSourceName(superclassName); } for (int i = 0, n = classRep.getNInterfaces(); i < n; i++) { context.getSourceName(classRep.getInterface(i)); } return super.visitJavaClassRep(classRep, arg); } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitLocalVariableDeclarationStatement(org.openquark.cal.internal.runtime.lecc.JavaStatement.LocalVariableDeclaration, java.lang.Object) */ @Override public Void visitLocalVariableDeclarationStatement( LocalVariableDeclaration localVariableDeclaration, Void arg) { LocalVariable localVariable = localVariableDeclaration.getLocalVariable(); context.getSourceName(localVariable.getTypeName()); return super.visitLocalVariableDeclarationStatement(localVariableDeclaration, arg); } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitJavaFieldDeclaration(org.openquark.cal.internal.runtime.lecc.JavaFieldDeclaration, java.lang.Object) */ @Override public Void visitJavaFieldDeclaration( JavaFieldDeclaration fieldDeclaration, Void arg) { JavaTypeName fieldType = fieldDeclaration.getFieldType(); context.getSourceName(fieldType); return super.visitJavaFieldDeclaration(fieldDeclaration, arg); } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitJavaMethod(org.openquark.cal.internal.runtime.lecc.JavaMethod, java.lang.Object) */ @Override public Void visitJavaMethod(JavaMethod javaMethod, Void arg) { // Return type context.getSourceName(javaMethod.getReturnType()); // Parameters for (int i = 0, nParams = javaMethod.getNParams(); i < nParams; i++) { context.getSourceName(javaMethod.getParamType(i)); } // throws for (int i = 0, n = javaMethod.getNThrownExceptions(); i < n; ++i) { JavaTypeName thrownExceptionName = javaMethod.getThrownException(i); context.getSourceName(thrownExceptionName); } return super.visitJavaMethod(javaMethod, arg); } /* (non-Javadoc) * @see org.openquark.cal.internal.runtime.lecc.JavaModelVisitor#visitJavaConstructor(org.openquark.cal.internal.runtime.lecc.JavaConstructor, java.lang.Object) */ @Override public Void visitJavaConstructor(JavaConstructor javaConstructor, Void arg) { for (int i = 0, n = javaConstructor.getNParams(); i < n; ++i) { context.getSourceName(javaConstructor.getParamType(i)); } return super.visitJavaConstructor(javaConstructor, arg); } } }