/*
* This file is part of the OpenJML project.
* Author: David R. Cok
*/
package org.jmlspecs.openjml;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Iterator;
import org.jmlspecs.annotation.NonNull;
import org.jmlspecs.openjml.JmlTree.JmlBinary;
import org.jmlspecs.openjml.JmlTree.JmlBlock;
import org.jmlspecs.openjml.JmlTree.JmlChoose;
import org.jmlspecs.openjml.JmlTree.JmlClassDecl;
import org.jmlspecs.openjml.JmlTree.JmlCompilationUnit;
import org.jmlspecs.openjml.JmlTree.JmlDoWhileLoop;
import org.jmlspecs.openjml.JmlTree.JmlEnhancedForLoop;
import org.jmlspecs.openjml.JmlTree.JmlForLoop;
import org.jmlspecs.openjml.JmlTree.JmlGroupName;
import org.jmlspecs.openjml.JmlTree.JmlImport;
import org.jmlspecs.openjml.JmlTree.JmlLabeledStatement;
import org.jmlspecs.openjml.JmlTree.JmlLblExpression;
import org.jmlspecs.openjml.JmlTree.JmlMethodClause;
import org.jmlspecs.openjml.JmlTree.JmlMethodClauseCallable;
import org.jmlspecs.openjml.JmlTree.JmlMethodClauseConditional;
import org.jmlspecs.openjml.JmlTree.JmlMethodClauseDecl;
import org.jmlspecs.openjml.JmlTree.JmlMethodClauseExpr;
import org.jmlspecs.openjml.JmlTree.JmlMethodClauseGroup;
import org.jmlspecs.openjml.JmlTree.JmlMethodClauseSignals;
import org.jmlspecs.openjml.JmlTree.JmlMethodClauseSignalsOnly;
import org.jmlspecs.openjml.JmlTree.JmlMethodClauseStoreRef;
import org.jmlspecs.openjml.JmlTree.JmlMethodDecl;
import org.jmlspecs.openjml.JmlTree.JmlMethodInvocation;
import org.jmlspecs.openjml.JmlTree.JmlMethodSig;
import org.jmlspecs.openjml.JmlTree.JmlMethodSpecs;
import org.jmlspecs.openjml.JmlTree.JmlModelProgramStatement;
import org.jmlspecs.openjml.JmlTree.JmlPrimitiveTypeTree;
import org.jmlspecs.openjml.JmlTree.JmlQuantifiedExpr;
import org.jmlspecs.openjml.JmlTree.JmlSetComprehension;
import org.jmlspecs.openjml.JmlTree.JmlSingleton;
import org.jmlspecs.openjml.JmlTree.JmlSpecificationCase;
import org.jmlspecs.openjml.JmlTree.JmlStatement;
import org.jmlspecs.openjml.JmlTree.JmlStatementDecls;
import org.jmlspecs.openjml.JmlTree.JmlStatementExpr;
import org.jmlspecs.openjml.JmlTree.JmlStatementHavoc;
import org.jmlspecs.openjml.JmlTree.JmlStatementLoop;
import org.jmlspecs.openjml.JmlTree.JmlStatementSpec;
import org.jmlspecs.openjml.JmlTree.JmlStoreRefArrayRange;
import org.jmlspecs.openjml.JmlTree.JmlStoreRefKeyword;
import org.jmlspecs.openjml.JmlTree.JmlStoreRefListExpression;
import org.jmlspecs.openjml.JmlTree.JmlTypeClause;
import org.jmlspecs.openjml.JmlTree.JmlTypeClauseConditional;
import org.jmlspecs.openjml.JmlTree.JmlTypeClauseConstraint;
import org.jmlspecs.openjml.JmlTree.JmlTypeClauseDecl;
import org.jmlspecs.openjml.JmlTree.JmlTypeClauseExpr;
import org.jmlspecs.openjml.JmlTree.JmlTypeClauseIn;
import org.jmlspecs.openjml.JmlTree.JmlTypeClauseInitializer;
import org.jmlspecs.openjml.JmlTree.JmlTypeClauseMaps;
import org.jmlspecs.openjml.JmlTree.JmlTypeClauseMonitorsFor;
import org.jmlspecs.openjml.JmlTree.JmlTypeClauseRepresents;
import org.jmlspecs.openjml.JmlTree.JmlVariableDecl;
import org.jmlspecs.openjml.JmlTree.JmlWhileLoop;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.tree.*;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCImport;
import com.sun.tools.javac.tree.JCTree.JCLiteral;
import com.sun.tools.javac.tree.JCTree.JCNewClass;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
//import com.sun.tools.javac.tree.Pretty.UncheckedIOException;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
/** This class does pretty-printing of JML ASTs. */
public class JmlPretty extends Pretty implements IJmlVisitor {
// Pretty was not originally a registered tool. In order to be able to
// slide in JmlPretty, I added a registration mechanism, but see the comments
// in Pretty: it does not depend on context (so compiler options are not
// available).
public static void preRegister(final Context context) {
cachedInstance = new JmlPretty(null,false);
}
/** Returns a new pretty printer of the dynamic type of the receiver.
* @param out the writer to pretty print to
* @param sourceOutput if true, writes out compilable source code
* (if false, there may be additional, uncompilable, information)
* */
@Override
protected JmlPretty inst(Writer out, boolean sourceOutput) {
return new JmlPretty(out,sourceOutput);
}
/** If true, then wrap JML statements in JML comments */
public boolean useJMLComments;
/** Instantiates a pretty-printer for Jml nodes with default indentation
* @param out the Write to which output is to be put
* @param sourceOutput whether to produce output that is equivalent to source code
* (if false, there may be additional, uncompilable, information)
*/
public JmlPretty(/*@non_null*/Writer out, boolean sourceOutput) {
super(out, sourceOutput);
this.out = out;
this.width = 2;
this.useJMLComments = sourceOutput;
}
/** Writes out a tree in pretty-printed fashion, with two-character indentation
*
* @param tree the tree to print
* @return the resulting text
*/
static public @NonNull String write(@NonNull JCTree tree) {
return write(tree,true);
}
/** Writes out a tree in pretty-printed fashion, with two-character indentation
*
* @param tree the tree to print
* @param source if true then put out compilable source
* @return the resulting text
*/
static public @NonNull String write(@NonNull JCTree tree, boolean source) {
StringWriter sw = new StringWriter();
JmlPretty p = new JmlPretty(sw,source);
p.width = 2;
tree.accept(p);
return sw.toString();
}
/** Writes out a tree in pretty-printed fashion, with two-character indentation,
* but ignoring any JML.
*
* @param tree the tree to print
* @param source if true then put out compilable source
* @return the resulting text
*/
static public String writeJava(JCTree tree, boolean source) { // FIXME - source is ignored
try {
// Here we use the Pretty constructor because we specifically
// want only Java, not any JML
StringWriter sw = new StringWriter();
tree.accept(new Pretty(sw,true));
return sw.toString();
} catch(Exception e) {}
return "<Exception>";
}
/** Flush the output Writer */
public void flush() throws IOException {
out.flush();
}
/** Adds to the indentation amount and realigns the indentation;
* call this when increasing the indentation after align() has already been called.
* @throws IOException
*/
public void indentAndRealign() throws IOException {
indent();
for (int i=width; i>0; --i) print(" ");
}
/** A method used for those pretty-printing methods that are not yet
* implemented; it just prints the class type.
* @param that a non-null AST node
* @throws IOException if there is a problem writing to the writer
*/
protected void notImpl(/*@non_null*/JCTree that) throws IOException {
print("<");
print(that.getClass());
print(">");
}
/** A method used to report exceptions that happen on writing to the writer
*
* @param that a non-null AST node
* @param e the exception that is being reported
*/
protected void perr(/*@ non_null*/JCTree that, /*@ non_null*/Exception e) {
System.err.println(e.getClass() + " error in JMLPretty: " + that.getClass());
e.printStackTrace(System.err);
}
//-------------- VISITOR METHODS -------------------------------------------
public void visitJmlBinary(JmlBinary that) {
try {
that.lhs.accept(this);
print(" ");
print(that.op.internedName());
print(" ");
that.rhs.accept(this);
} catch (IOException e) { perr(that,e); }
}
public void visitJmlBlock(JmlBlock that) {
visitBlock(that);
}
public void visitJmlMethodInvocation(JmlMethodInvocation that) {
try {
if (that.token == null) {
visitApply(that);
} else {
print(that.token.internedName());
if (that.javaType &&
(that.token == JmlTokenKind.BSTYPELC || that.token == JmlTokenKind.BSTYPEOF)
) print("j");
print("(");
printExprs(that.args);
print(")");
}
} catch (IOException e) { perr(that,e); }
}
public void visitJmlLabeledStatement(JmlLabeledStatement that) {
try {
// printStats(that.extraStatements.toList());
print(that.label + ":");
printStat(that.body);
} catch (IOException e) {
perr(that,e);
}
}
public void visitJmlLblExpression(JmlLblExpression that) {
try {
// NOTE: JML requires that a lbl expression be in parentheses.
// In this parser the outer parentheses are a JCParens expression.
print(that.token.internedName());
print(" ");
print(that.label.toString());
print(" ");
that.expression.accept(this);
} catch (IOException e) { perr(that,e); }
}
public void visitJmlImport(JmlImport that) {
try {
if (useJMLComments && that.isModel) print("//@ ");
if (that.isModel) print("model ");
print("import ");
if (that.staticImport) print("static ");
printExpr(that.qualid);
print(";");
println();
} catch (IOException e) { perr(that,e); }
}
public void visitJmlMethodClauseGroup(JmlMethodClauseGroup that) {
// Presumes already aligned; does not call println at end
try {
if (that.cases.size() == 1) {
that.cases.get(0).accept(this);
} else {
print("{|"); println();
// Internal specification cases are always 'lightweight'
// and so have an automatic indentation
boolean first = true;
for (JmlSpecificationCase t: that.cases) {
if (first) first = false;
else {
align(); print("also"); println();
}
align();
t.accept(this); // indents and outdents itself for lightweight clauses
println();
}
align(); print("|}");
}
} catch (IOException e) { perr(that,e); }
}
public void visitJmlMethodClauseDecl(JmlMethodClauseDecl that) {
try {
for (JCTree.JCVariableDecl s: that.decls) {
print(that.token.internedName());
print(" ");
s.accept(this);
print("; ");
}
} catch (IOException e) { perr(that,e); }
}
public void visitJmlMethodClauseExpr(JmlMethodClauseExpr that) {
try {
print(that.token.internedName());
print(" ");
that.expression.accept(this);
print("; ");
} catch (IOException e) { perr(that,e); }
}
public void visitJmlMethodClauseCallable(JmlMethodClauseCallable that) {
try {
print(JmlTokenKind.CALLABLE.internedName());
print(" ");
if (that.keyword != null) {
that.keyword.accept(this);
} else {
Iterator<JmlMethodSig> iter = that.methodSignatures.iterator();
iter.next().accept(this);
while (iter.hasNext()) {
print(", ");
iter.next().accept(this);
}
}
print("; ");
} catch (IOException e) { perr(that,e); }
}
public void visitJmlMethodClauseConditional(JmlMethodClauseConditional that) {
try {
print(that.token.internedName());
print(" ");
that.expression.accept(this);
if (that.predicate != null) {
print(" if ");
that.predicate.accept(this);
}
print("; ");
} catch (IOException e) { perr(that,e); }
}
public void visitJmlMethodClauseSigOnly(JmlMethodClauseSignalsOnly that) {
try {
print(that.token.internedName());
print(" ");
if (that.list.isEmpty()) {
print("\\nothing");
} else {
boolean first = true;
for (JCExpression item: that.list) {
if (first) first = false; else print(", ");
item.accept(this);
}
}
print("; ");
} catch (IOException e) { perr(that,e); }
}
public void visitJmlMethodClauseStoreRef(JmlMethodClauseStoreRef that) {
try {
print(that.token.internedName());
print(" ");
boolean first = true;
for (JCTree item: that.list) {
if (first) first = false; else print(", ");
item.accept(this);
}
print("; ");
} catch (IOException e) { perr(that,e); }
}
public void visitJmlMethodClauseSignals(JmlMethodClauseSignals that) {
try {
print(that.token.internedName());
print(" (");
if (that.vardef != null) {
that.vardef.vartype.accept(this);
if (that.vardef.name != null) {
String s = that.vardef.name.toString();
if (!Strings.syntheticExceptionID.equals(s)) {
print(" ");
print(that.vardef.name.toString());
}
}
}
print(") ");
that.expression.accept(this);
print("; ");
} catch (IOException e) { perr(that,e); }
}
public void visitJmlMethodSpecs(JmlMethodSpecs that) {
if (that.cases.isEmpty()) return;
try {
if (useJMLComments) { align(); print("/*@"); println(); }
boolean first = true;
for (JmlSpecificationCase c: that.cases) {
if (first) first = false;
else {
print("also"); //$NON-NLS-1$
println();
}
indent();
align();
c.accept(this); // presumes already aligned; does not end with println
println();
undent();
}
if (useJMLComments) { align(); print(" */"); println(); }
} catch (Exception e) {
perr(that,e);
}
}
public void visitJmlQuantifiedExpr(JmlQuantifiedExpr that) {
try {
// FIXME - it appears that the enclosing parentheses are parsed as a Parens
// expression - is this really right?
print(that.op.internedName());
print(" ");
boolean first = true;
for (JCTree.JCVariableDecl n: that.decls) {
if (!first) print(", ");
else first = false;
n.accept(this);
}
print("; ");
if (that.range != null) that.range.accept(this);
print("; ");
if (that.value != null) that.value.accept(this);
else print("????:");
} catch (IOException e) { perr(that,e); }
}
public void visitJmlSetComprehension(JmlSetComprehension that) {
int oldprec = prec;
prec = 0;
try {
print("new ");
that.newtype.accept(this);
print(" { ");
that.variable.accept(this);
print(" | ");
that.predicate.accept(this);
print(" }");
}
catch (IOException e) { perr(that,e); }
finally {
prec = oldprec;
}
}
public void visitJmlSingleton(JmlSingleton that) {
try {
if (that.token == JmlTokenKind.INFORMAL_COMMENT) {
print("(*");
print(that.info);
print("*)");
} else {
print(that);
}
} catch (IOException e) { perr(that,e); }
}
public void visitJmlSpecificationCase(JmlSpecificationCase that) {
// Presumes the output is already aligned before the call
// Presumes the caller does any needed println() afterwards
try {
if (that.modifiers != null &&
(that.modifiers.flags != 0 || that.modifiers.annotations != null)) {
that.modifiers.accept(this); // presumes that this call adds a trailing space
}
if (that.code) {
print(JmlTokenKind.CODE.internedName());
print(" ");
}
if (that.token == JmlTokenKind.MODEL_PROGRAM) {
print(that.token.internedName());
print(" ");
that.block.accept(this);
return;
}
if (that.token == null) {
// lightweight
} else {
print(that.token.internedName());
println();
align();
}
try {
// Note - the output is already aligned, so we have to bump up the alignment
indentAndRealign();
boolean first = true;
for (JmlMethodClause c: that.clauses) {
if (first) first = false; else { println(); align(); }
c.accept(this);
}
} finally {
undent();
}
} catch (IOException e) { perr(that,e); }
}
/** debug and set statements */
public void visitJmlStatement(JmlStatement that) {
try {
if (useJMLComments) print ("/*@ ");
print(that.token.internedName());
print(" ");
that.statement.accept(this);
if (useJMLComments) print("*/");
} catch (IOException e) { perr(that,e); }
}
public void visitJmlStatementLoop(JmlStatementLoop that) {
try {
if (useJMLComments) print("//@ ");
print(that.token.internedName());
print(" ");
that.expression.accept(this);
print(";");
} catch (IOException e) { perr(that,e); }
}
public void visitJmlStatementSpec(JmlStatementSpec that) {
that.statementSpecs.accept(this);
}
public void visitJmlStatementExpr(JmlStatementExpr that) {
try {
if (that.token == JmlTokenKind.COMMENT) {
print("// ");
if (that.expression != null) print(((JCLiteral)that.expression).value); // FIXME - can the comment span more than one line?
else {
print("[ERROR: NULL COMMENT EXPRESSION]");
}
} else {
if (useJMLComments) print ("/*@ "); // FIXME - this is needed in lots more places
print(that.token.internedName());
print(" ");
if (that.label != null && !sourceOutput) {
print(that.label);
print(" ");
}
printExpr(that.expression);
if (that.optionalExpression != null) {
print(" : ");
printExpr(that.optionalExpression);
}
print(";");
if (useJMLComments) print("*/");
//println();
}
} catch (IOException e) { perr(that,e); }
}
public void visitJmlStatementHavoc(JmlStatementHavoc that) {
try {
if (useJMLComments) print ("//@ ");
print(that.token.internedName());
print(" ");
boolean first = true;
for (JCTree item: that.storerefs) {
if (first) first = false; else print(", ");
item.accept(this);
}
print("; ");
} catch (IOException e) { perr(that,e); }
}
public void visitJmlStatementDecls(JmlStatementDecls that) {
try { notImpl(that); // FIXME
} catch (IOException e) { perr(that,e); }
}
public void visitJmlTypeClauseExpr(JmlTypeClauseExpr that) {
try {
if (useJMLComments) print("//@ ");
if (that.modifiers != null) that.modifiers.accept(this); // includes trailing space if non-empty
print(that.token.internedName());
print(" ");
printExpr(that.expression);
print("; ");
} catch (IOException e) { perr(that,e); }
}
public void visitJmlTypeClauseDecl(JmlTypeClauseDecl that) {
that.decl.accept(this); // FIXME - //@?
}
public void visitJmlTypeClauseIn(JmlTypeClauseIn that) {
try {
if (useJMLComments) print("//@ ");
print(that.token.internedName());
Iterator<JmlGroupName> g = that.list.iterator();
print(" ");
g.next().accept(this);
while(g.hasNext()) {
print(", ");
g.next().accept(this);
}
print("; ");
} catch (IOException e) { perr(that,e); }
}
public void visitJmlTypeClauseMaps(JmlTypeClauseMaps that) {
try {
if (useJMLComments) print("//@ ");
print(that.token.internedName());
print(" ");
print(that.expression);
print(" \\into");
Iterator<JmlGroupName> g = that.list.iterator();
print(" ");
g.next().accept(this);
while(g.hasNext()) {
print(", ");
g.next().accept(this);
}
print("; ");
} catch (IOException e) { perr(that,e); }
}
public void visitJmlGroupName(JmlGroupName that) {
// try {
// // FIXME - this was changed - is it right?
// if (that.selection != null) {
that.selection.accept(this);
// print(".");
// }
// print(that.sym);
// }
// catch (IOException e) { perr(that,e); }
}
public void visitJmlTypeClauseInitializer(JmlTypeClauseInitializer that) {
try {
if (that.specs != null) that.specs.accept(this);
align();
if (that.modifiers != null) that.modifiers.accept(this);
print(that.token.internedName());
print(" {}");
println();
} catch (IOException e) { perr(that,e); }
}
public void visitJmlTypeClauseConstraint(JmlTypeClauseConstraint that) {
try {
if (useJMLComments) print("//@ ");
if (that.modifiers != null) that.modifiers.accept(this); // includes trailing space if non-empty
print(that.token.internedName());
print(" ");
that.expression.accept(this);
if (that.sigs != null && !that.sigs.isEmpty()) {
print(" for ");
if (that.notlist) print("! ");
if (that.sigs.isEmpty()) print(" \\nothing");
else {
boolean first = true;
for (JmlMethodSig sig: that.sigs) {
if (first) first = false; else print(", ");
sig.accept(this);
}
}
}
print("; ");
} catch (IOException e) { perr(that,e); }
}
public void visitJmlTypeClauseRepresents(JmlTypeClauseRepresents that) {
try {
if (useJMLComments) print("//@ ");
if (that.modifiers != null) that.modifiers.accept(this); // trailing space if non-empty
print(that.token.internedName());
print(" ");
that.ident.accept(this);
print(" = ");
that.expression.accept(this);
print("; ");
} catch (IOException e) { perr(that,e); }
}
public void visitJmlTypeClauseConditional(JmlTypeClauseConditional that) {
try {
if (useJMLComments) print("//@ ");
if (that.modifiers != null) that.modifiers.accept(this); // trailing space if non-empty
print(that.token.internedName());
print(" ");
that.identifier.accept(this);
if (that.expression != null) {
print(" if ");
that.expression.accept(this);
}
print("; ");
} catch (IOException e) { perr(that,e); }
}
public void visitJmlTypeClauseMonitorsFor(JmlTypeClauseMonitorsFor that) {
try {
if (useJMLComments) print("//@ ");
if (that.modifiers != null) that.modifiers.accept(this); // trailing space if non-empty
print(that.token.internedName());
print(" ");
that.identifier.accept(this);
print(" = ");
boolean first = true;
for (JCExpression item: that.list) {
if (first) first = false; else print(", ");
item.accept(this);
}
print("; ");
} catch (IOException e) { perr(that,e); }
}
public void visitJmlPrimitiveTypeTree(JmlPrimitiveTypeTree that) {
try { print(that.token.internedName());
} catch (IOException e) { perr(that,e); }
}
public void visitJmlStoreRefArrayRange(JmlStoreRefArrayRange that) {
try {
that.expression.accept(this);
print('[');
if (that.lo == null) {
print('*');
} else if (that.hi == that.lo) {
that.lo.accept(this);
} else if (that.hi == null) {
that.lo.accept(this);
print(" .. *");
} else {
that.lo.accept(this);
print(" .. ");
that.hi.accept(this);
}
print(']');
} catch (IOException e) { perr(that,e); }
}
public void visitJmlStoreRefKeyword(JmlStoreRefKeyword that) {
try {
print(that.token.internedName());
} catch (IOException e) { perr(that,e); }
}
public void visitJmlStoreRefListExpression(JmlStoreRefListExpression that) {
try {
print(that.token.internedName());
print('(');
boolean first = true;
for (JCTree expr : that.list) {
if (first) first = false; else print(',');
expr.accept(this);
}
print(')');
} catch (IOException e) { perr(that,e); }
}
/** This is overridden simply to exclude the package name from JML annotations,
* in order to conserve space. [FIXME - this will actually create illegal
* source if there is no import statement for the annotations. ]
*/
@Override
public void visitAnnotation(JCAnnotation tree) {
try {
// We prefer to use tree.type since it will be the fully resolved
// type name, including the package, even if the use of the annotation
// itself does not use the package name. However, if types have not
// yet been attributed (e.g. this is a pure parse tree), then
// tree.type is null.
String s = (tree.type != null) ? tree.type.toString() :
tree.annotationType.toString();
if (s.startsWith(Strings.jmlAnnotationPackage)) {
s = s.substring(Strings.jmlAnnotationPackage.length()+1); // +1 for the extra period
print("@");
print(s);
} else {
super.visitAnnotation(tree);
}
} catch (IOException e) { perr(tree,e); }
}
@Override
public void printAnnotations(List<JCAnnotation> trees) throws IOException {
for (List<JCAnnotation> l = trees; l.nonEmpty(); l = l.tail) {
printStat(l.head);
print(" ");
}
if (!trees.isEmpty()) { // This test is needed for example in quantified expressions
println();
align();
}
}
public void visitJmlDoWhileLoop(JmlDoWhileLoop that) {
if (that.loopSpecs != null) for (JmlStatementLoop s: that.loopSpecs) {
s.accept(this);
}
super.visitDoLoop(that);
}
public void visitJmlEnhancedForLoop(JmlEnhancedForLoop that) {
try {
if (that.loopSpecs != null) for (JmlStatementLoop s: that.loopSpecs) {
s.accept(this);
println();
align();
}
super.visitForeachLoop(that);
} catch (IOException e) { perr(that,e); }
}
public void visitJmlForLoop(JmlForLoop that) {
try {
if (that.loopSpecs != null) {
for (JmlStatementLoop s: that.loopSpecs) {
s.accept(this);
println();
align();
}
}
super.visitForLoop(that);
} catch (IOException e) { perr(that,e); }
}
public void visitJmlWhileLoop(JmlWhileLoop that) {
try {
if (that.loopSpecs != null) {
for (JmlStatementLoop s: that.loopSpecs) {
s.accept(this);
println();
align();
}
}
super.visitWhileLoop(that);
} catch (IOException e) { perr(that,e); }
}
public void visitJmlChoose(JmlChoose that) {
try { // FIXME - this needs testing
align();
print(JmlTokenKind.CHOOSE.internedName());
println();
indent();
Iterator<JCBlock> iter = that.orBlocks.iterator();
align();
iter.next().accept(this);
while (iter.hasNext()) {
align();
print(JmlTokenKind.CHOOSE.internedName());
println();
align();
iter.next().accept(this);
}
} catch (IOException e) { perr(that,e); }
// FIXME - fill in
}
// FIXME - clean this up
JmlSpecs.TypeSpecs specsToPrint = null;
public void visitJmlClassDecl(JmlClassDecl that) {
if (that.typeSpecs != null) {
specsToPrint = that.typeSpecs;
}
visitClassDef(that);
}
public void printStats(List<? extends JCTree> stats) throws IOException {
JmlSpecs.TypeSpecs toPrint = specsToPrint;
specsToPrint = null;
super.printStats(stats);
if (toPrint != null && (!toPrint.clauses.isEmpty() || !toPrint.decls.isEmpty())) {
align(); print("// JML specifications"); println();
for (JmlTypeClause t: toPrint.clauses) {
align();
t.accept(this);
println();
}
for (JmlTypeClause t: toPrint.decls) {
align();
t.accept(this);
println();
}
}
}
// FIXME - document
protected boolean inSequence = false;
public void visitJmlCompilationUnit(JmlCompilationUnit tree) {
// FIXME - can we call Pretty.printUnit - or do we not get model imports then
// FIXME - review this for sourceOutput true and false
try {
printDocComment(tree);
if (tree.pid != null) {
print("package ");
printExpr(tree.pid);
print(";");
println();
}
boolean firstImport = true;
for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
if (l.head.getTag() == JCTree.Tag.IMPORT) {
JCImport imp = (JCImport)l.head;
if (true) {
if (firstImport) {
firstImport = false;
println();
}
printStat(imp);
}
} else {
printStat(l.head);
}
}
if (!inSequence && tree.specsCompilationUnit != null) {
boolean prevInSequence = inSequence; // should always be false, since we don't call this more than one level deep
inSequence = true;
try {
println();
print("// Specifications: ");
print(tree.specsCompilationUnit.sourcefile.getName());
println();
JmlCompilationUnit jcu = tree.specsCompilationUnit;
print("// Specification file: " + jcu.sourcefile.getName());
println();
jcu.accept(this);
println();
} finally {
inSequence = prevInSequence;
}
}
} catch (IOException e) {
perr(tree,e);
}
}
public void visitJmlMethodDecl(JmlMethodDecl that) {
// FIXME //@? model?
if (that.methodSpecsCombined != null) {
that.methodSpecsCombined.cases.accept(this);
}
else if (that.cases != null) that.cases.accept(this);
// FIXME - visitMethodDef will print the Java modifiers
// and annotations that are on the Java declaration
// We need the following to get the combined annotations
// but we don't want both?
// if (that.methodSpecsCombined != null) that.methodSpecsCombined.mods.accept(this);
// Do some shenanigans with sourceOuput to get default constructors printed
boolean wasSourceOutput = sourceOutput;
if (that.name == that.name.table.names.init &&
sourceOutput) sourceOutput = false;
visitMethodDef(that);
sourceOutput = wasSourceOutput;
}
public void visitJmlVariableDecl(JmlVariableDecl that) {
// FIXME //@?
visitVarDef(that);
}
@Override
public void visitVarDef(JCVariableDecl that) {
super.visitVarDef(that);
if (!(that instanceof JmlVariableDecl)) return;
JmlVariableDecl jmlthat = (JmlVariableDecl)that;
if (jmlthat.fieldSpecsCombined != null) printFieldSpecs(jmlthat.fieldSpecsCombined);
else if (jmlthat.fieldSpecs != null) printFieldSpecs(jmlthat.fieldSpecs);
}
public void printFieldSpecs(JmlSpecs.FieldSpecs fspecs) {
// FIXME - may need to add a println at the beginning and omit one at the end
// FIXME //@?
indent();
for (JmlTypeClause t: fspecs.list) {
try {
if (t.token == JmlTokenKind.IN || t.token == JmlTokenKind.MAPS) {
align(); t.accept(this); println();
}
} catch (IOException e) {
perr(t,e);
}
}
for (JmlTypeClause t: fspecs.list) {
try{
if (t.token == JmlTokenKind.IN || t.token == JmlTokenKind.MAPS) continue;
align(); t.accept(this); println(); // FIXME - what might theses be?
} catch (IOException e) {
perr(t,e);
}
}
undent();
}
@Override
public void visitLiteral(JCLiteral that) {
try {
if (that.value instanceof Type) {
print(that.value.toString());
} else if (that.value instanceof Long) {
print(that.value.toString() + "L");
} else {
super.visitLiteral(that);
}
} catch (IOException e) {
perr(that,e);
}
}
/** Overridden in order to handle the case of a null selection field - this
* means a * in a store-ref expression.
*/
@Override
public void visitSelect(JCFieldAccess tree) {
try {
printExpr(tree.selected, TreeInfo.postfixPrec);
if (tree.name == null) print(".*");
else {
// This special case is here because of a bug in JDK that
// put a parent with an empty name before each package name.
// We also put a fix in TreeMaker.QualIdent, but am not sure it
// is correct or will be kept.
if(tree.selected instanceof JCIdent
&& ((JCIdent)(tree.selected)).name!=null
&& ((JCIdent)(tree.selected)).name.isEmpty())
print(tree.name);
else print("." + tree.name);
}
} catch (IOException e) {
perr(tree,e);
}
}
/** Overridden to try to cleanup a little bit the printing of an anonymous
* class expression.
*/
@Override
public void visitNewClass(JCNewClass tree) { // FIXME - review
indent(); indent(); indent(); indent(); // indent
super.visitNewClass(tree);
undent(); undent(); undent(); undent(); // indent
}
public void visitJmlMethodSig(JmlMethodSig that) {
try {
printExpr(that.expression);
print("(");
boolean first = true;
for (JCExpression t: that.argtypes) {
if (first) first = false; else print(",");
printExpr(t);
}
print(")");
} catch (IOException e) { perr(that,e); }
}
public void visitJmlModelProgramStatement(JmlModelProgramStatement that) {
that.item.accept(this);
}
// Enables printing of things like:
//
// [jmlrac:../demos/Test.java:1]\t %line
//
// This makes reading the -show output easier.
public static String toFancyLineFormat(String file, JmlPrettyFormatter fmt, String old)
{
String pieces [] = old.split(System.getProperty("line.separator"));
StringBuffer sb = new StringBuffer();
int line=1;
for(String p : pieces){
sb.append(fmt.formatLine(file, line, p)).append(System.getProperty("line.separator"));
line++;
}
return sb.toString();
}
public static String toFancyLineFormat(String file, JmlPrettyFormatter fmt, String prefix, String old)
{
return toFancyLineFormat(file, fmt, prefix + System.getProperty("line.separator") + old );
}
public static JmlPrettyFormatter racFormatter;
interface JmlPrettyFormatter {
public String formatLine(String file, int lineNumber, String line);
}
static {
racFormatter = new JmlPrettyFormatter() {
@Override
public String formatLine(String file, int lineNumber, String line) {
return String.format("[jmlrac:%s:%d] %s", file, lineNumber, line);
}
};
}
}