Beispiel #1
0
public class PrettyPrinter extends JCTree.Visitor {
  private static final String LINE_SEP = System.getProperty("line.separator");
  private static final Map<TreeTag, String> OPERATORS;

  static {
    Map<TreeTag, String> map = new HashMap<TreeTag, String>();

    map.put(treeTag("POS"), "+");
    map.put(treeTag("NEG"), "-");
    map.put(treeTag("NOT"), "!");
    map.put(treeTag("COMPL"), "~");
    map.put(treeTag("PREINC"), "++");
    map.put(treeTag("PREDEC"), "--");
    map.put(treeTag("POSTINC"), "++");
    map.put(treeTag("POSTDEC"), "--");
    map.put(treeTag("NULLCHK"), "<*nullchk*>");
    map.put(treeTag("OR"), "||");
    map.put(treeTag("AND"), "&&");
    map.put(treeTag("EQ"), "==");
    map.put(treeTag("NE"), "!=");
    map.put(treeTag("LT"), "<");
    map.put(treeTag("GT"), ">");
    map.put(treeTag("LE"), "<=");
    map.put(treeTag("GE"), ">=");
    map.put(treeTag("BITOR"), "|");
    map.put(treeTag("BITXOR"), "^");
    map.put(treeTag("BITAND"), "&");
    map.put(treeTag("SL"), "<<");
    map.put(treeTag("SR"), ">>");
    map.put(treeTag("USR"), ">>>");
    map.put(treeTag("PLUS"), "+");
    map.put(treeTag("MINUS"), "-");
    map.put(treeTag("MUL"), "*");
    map.put(treeTag("DIV"), "/");
    map.put(treeTag("MOD"), "%");

    map.put(treeTag("BITOR_ASG"), "|=");
    map.put(treeTag("BITXOR_ASG"), "^=");
    map.put(treeTag("BITAND_ASG"), "&=");
    map.put(treeTag("SL_ASG"), "<<=");
    map.put(treeTag("SR_ASG"), ">>=");
    map.put(treeTag("USR_ASG"), ">>>=");
    map.put(treeTag("PLUS_ASG"), "+=");
    map.put(treeTag("MINUS_ASG"), "-=");
    map.put(treeTag("MUL_ASG"), "*=");
    map.put(treeTag("DIV_ASG"), "/=");
    map.put(treeTag("MOD_ASG"), "%=");

    OPERATORS = map;
  }

  private final Writer out;
  private final JCCompilationUnit compilationUnit;
  private List<CommentInfo> comments;
  private final FormatPreferences formatPreferences;

  private final Map<JCTree, String> docComments;
  private final DocCommentTable docTable;
  private int indent = 0;

  @SuppressWarnings({"unchecked", "rawtypes"})
  public PrettyPrinter(
      Writer out, JCCompilationUnit cu, List<CommentInfo> comments, FormatPreferences preferences) {
    this.out = out;
    this.comments = comments;
    this.compilationUnit = cu;
    this.formatPreferences = preferences;

    /* load doc comments */ {
      Object dc = getDocComments(compilationUnit);
      if (dc instanceof Map<?, ?>) {
        this.docComments = (Map) dc;
        this.docTable = null;
      } else if (dc instanceof DocCommentTable) {
        this.docComments = null;
        this.docTable = (DocCommentTable) dc;
      } else {
        this.docComments = null;
        this.docTable = null;
      }
    }
  }

  private int endPos(JCTree tree) {
    return getEndPosition(tree, compilationUnit);
  }

  private static int lineEndPos(String s, int start) {
    int pos = s.indexOf('\n', start);
    if (pos < 0) pos = s.length();
    return pos;
  }

  private boolean needsAlign, needsNewLine, onNewLine = true, needsSpace, aligned;

  public static final class UncheckedIOException extends RuntimeException {
    UncheckedIOException(IOException source) {
      super(toMsg(source));
      setStackTrace(source.getStackTrace());
    }

    private static String toMsg(Throwable t) {
      String msg = t.getMessage();
      String n = t.getClass().getSimpleName();
      if (msg == null || msg.isEmpty()) return n;
      return n + ": " + msg;
    }
  }

  private void align() {
    if (!onNewLine) return;
    try {
      for (int i = 0; i < indent; i++) out.write(formatPreferences.indent());
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }

    onNewLine = false;
    aligned = true;
    needsAlign = false;
  }

  private void print(JCTree tree) {
    if (tree == null) {
      print("/*missing*/");
      return;
    }

    consumeComments(tree);
    tree.accept(this);
    consumeTrailingComments(endPos(tree));
  }

  private void print(List<? extends JCTree> trees, String infix) {
    boolean first = true;
    JCTree prev = null;
    for (JCTree tree : trees) {
      if (suppress(tree)) continue;
      if (!first && infix != null && !infix.isEmpty()) {
        if ("\n".equals(infix)) println(prev);
        else print(infix);
      }
      first = false;
      print(tree);
      prev = tree;
    }
  }

  private boolean suppress(JCTree tree) {
    if (tree instanceof JCBlock) {
      JCBlock block = (JCBlock) tree;
      return (Position.NOPOS == block.pos) && block.stats.isEmpty();
    }

    if (tree instanceof JCExpressionStatement) {
      JCExpression expr = ((JCExpressionStatement) tree).expr;
      if (expr instanceof JCMethodInvocation) {
        JCMethodInvocation inv = (JCMethodInvocation) expr;
        if (!inv.typeargs.isEmpty() || !inv.args.isEmpty()) return false;
        if (!(inv.meth instanceof JCIdent)) return false;
        return ((JCIdent) inv.meth).name.toString().equals("super");
      }
    }

    return false;
  }

  private void print(CharSequence s) {
    boolean align = needsAlign;
    if (needsNewLine && !onNewLine) println();
    if (align && !aligned) align();
    try {
      if (needsSpace && !onNewLine && !aligned) out.write(' ');
      out.write(s.toString());
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }

    needsSpace = false;
    onNewLine = false;
    aligned = false;
  }

  private void println() {
    try {
      out.write(LINE_SEP);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }

    onNewLine = true;
    aligned = false;
    needsNewLine = false;
  }

  private void println(JCTree completed) {
    if (completed != null) {
      int endPos = endPos(completed);
      consumeTrailingComments(endPos);
    }
    try {
      out.write(LINE_SEP);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }

    onNewLine = true;
    aligned = false;
    needsNewLine = false;
  }

  private void println(CharSequence s) {
    print(s);
    println();
  }

  private void println(CharSequence s, JCTree completed) {
    print(s);
    println(completed);
  }

  private void aPrint(CharSequence s) {
    align();
    print(s);
  }

  private void aPrintln(CharSequence s) {
    align();
    print(s);
    println();
  }

  private void aPrintln(CharSequence s, JCTree completed) {
    align();
    print(s);
    println(completed);
  }

  private void consumeComments(int until) {
    CommentInfo head = comments.head;
    while (comments.nonEmpty() && head.pos < until) {
      printComment(head);
      comments = comments.tail;
      head = comments.head;
    }
  }

  private void consumeComments(JCTree tree) {
    consumeComments(tree.pos);
  }

  private void consumeTrailingComments(int from) {
    boolean prevNewLine = onNewLine;
    CommentInfo head = comments.head;
    boolean stop = false;

    while (comments.nonEmpty()
        && head.prevEndPos == from
        && !stop
        && !(head.start == StartConnection.ON_NEXT_LINE
            || head.start == StartConnection.START_OF_LINE)) {
      from = head.endPos;
      printComment(head);
      stop = (head.end == EndConnection.ON_NEXT_LINE);
      comments = comments.tail;
      head = comments.head;
    }

    if (!onNewLine && prevNewLine) {
      println();
    }
  }

  private String getJavadocFor(JCTree node) {
    if (docComments != null) return docComments.get(node);
    if (docTable != null) return docTable.getCommentText(node);
    return null;
  }

  private int dims(JCExpression vartype) {
    if (vartype instanceof JCArrayTypeTree) {
      return 1 + dims(((JCArrayTypeTree) vartype).elemtype);
    }

    return 0;
  }

  private void printComment(CommentInfo comment) {
    switch (comment.start) {
      case DIRECT_AFTER_PREVIOUS:
        needsSpace = false;
        break;
      case AFTER_PREVIOUS:
        needsSpace = true;
        break;
      case START_OF_LINE:
        needsNewLine = true;
        needsAlign = false;
        break;
      case ON_NEXT_LINE:
        if (!onNewLine) {
          needsNewLine = true;
          needsAlign = true;
        } else if (!aligned) {
          needsAlign = true;
        }
        break;
    }

    if (onNewLine && !aligned && comment.start != StartConnection.START_OF_LINE) needsAlign = true;

    print(comment.content);

    switch (comment.end) {
      case ON_NEXT_LINE:
        if (!aligned) {
          needsNewLine = true;
          needsAlign = true;
        }
        break;
      case AFTER_COMMENT:
        needsSpace = true;
        break;
      case DIRECT_AFTER_COMMENT:
        // do nothing
        break;
    }
  }

  private void printDocComment(JCTree tree) {
    String dc = getJavadocFor(tree);
    if (dc == null) return;
    aPrintln("/**");
    int pos = 0;
    int endpos = lineEndPos(dc, pos);
    boolean atStart = true;
    while (pos < dc.length()) {
      String line = dc.substring(pos, endpos);
      if (line.trim().isEmpty() && atStart) {
        atStart = false;
        continue;
      }
      atStart = false;
      aPrint(" *");
      if (pos < dc.length() && dc.charAt(pos) > ' ') print(" ");
      println(dc.substring(pos, endpos));
      pos = endpos + 1;
      endpos = lineEndPos(dc, pos);
    }
    aPrintln(" */");
  }

  private Name __INIT__, __VALUE__;

  private Name name_init(Name someName) {
    if (__INIT__ == null) __INIT__ = someName.table.fromChars("<init>".toCharArray(), 0, 6);
    return __INIT__;
  }

  private Name name_value(Name someName) {
    if (__VALUE__ == null) __VALUE__ = someName.table.fromChars("value".toCharArray(), 0, 5);
    return __VALUE__;
  }

  @Override
  public void visitTopLevel(JCCompilationUnit tree) {
    printDocComment(tree);
    if (tree.pid != null) {
      consumeComments(tree);
      aPrint("package ");
      print(tree.pid);
      println(";", tree.pid);
    }

    boolean first = true;

    for (JCTree child : tree.defs) {
      if (!(child instanceof JCImport)) continue;
      if (first) println();
      first = false;
      print(child);
    }

    for (JCTree child : tree.defs) {
      if (child instanceof JCImport) continue;
      print(child);
    }
    consumeComments(Integer.MAX_VALUE);
  }

  @Override
  public void visitImport(JCImport tree) {
    aPrint("import ");
    if (tree.staticImport) print("static ");
    print(tree.qualid);
    println(";", tree);
  }

  private Name currentTypeName;

  @Override
  public void visitClassDef(JCClassDecl tree) {
    println();
    printDocComment(tree);
    align();
    print(tree.mods);

    boolean isInterface = (tree.mods.flags & INTERFACE) != 0;
    boolean isAnnotationInterface = isInterface && (tree.mods.flags & ANNOTATION) != 0;
    boolean isEnum = (tree.mods.flags & ENUM) != 0;

    if (isAnnotationInterface) print("@interface ");
    else if (isInterface) print("interface ");
    else if (isEnum) print("enum ");
    else print("class ");

    print(tree.name);
    Name prevTypeName = currentTypeName;
    currentTypeName = tree.name;

    if (tree.typarams.nonEmpty()) {
      print("<");
      print(tree.typarams, ", ");
      print(">");
    }
    JCTree extendsClause = getExtendsClause(tree);
    if (extendsClause != null) {
      print(" extends ");
      print(extendsClause);
    }

    if (tree.implementing.nonEmpty()) {
      print(isInterface ? " extends " : " implements ");
      print(tree.implementing, ", ");
    }

    println(" {");
    indent++;
    printClassMembers(tree.defs, isEnum, isInterface);
    consumeComments(endPos(tree));
    indent--;
    aPrintln("}", tree);
    currentTypeName = prevTypeName;
  }

  private void printClassMembers(List<JCTree> members, boolean isEnum, boolean isInterface) {
    Class<?> prefType = null;
    int typeOfPrevEnumMember = isEnum ? 3 : 0; // 1 = normal, 2 = with body, 3 = no enum field yet.
    boolean prevWasEnumMember = isEnum;

    for (JCTree member : members) {
      if (typeOfPrevEnumMember == 3
          && member instanceof JCMethodDecl
          && (((JCMethodDecl) member).mods.flags & GENERATEDCONSTR) != 0) continue;
      boolean isEnumVar =
          isEnum
              && member instanceof JCVariableDecl
              && (((JCVariableDecl) member).mods.flags & ENUM) != 0;
      if (!isEnumVar && prevWasEnumMember) {
        prevWasEnumMember = false;
        if (typeOfPrevEnumMember == 3) align();
        println(";");
      }

      if (isEnumVar) {
        if (prefType != null && prefType != JCVariableDecl.class) println();
        switch (typeOfPrevEnumMember) {
          case 1:
            print(", ");
            break;
          case 2:
            println(",");
            align();
            break;
        }
        print(member);
        JCTree init = ((JCVariableDecl) member).init;
        typeOfPrevEnumMember =
            init instanceof JCNewClass && ((JCNewClass) init).def != null ? 2 : 1;
      } else if (member instanceof JCVariableDecl) {
        if (prefType != null && prefType != JCVariableDecl.class) println();
        if (isInterface) flagMod = -1L & ~(PUBLIC | STATIC | FINAL);
        print(member);
      } else if (member instanceof JCMethodDecl) {
        if ((((JCMethodDecl) member).mods.flags & GENERATEDCONSTR) != 0) continue;
        if (prefType != null) println();
        if (isInterface) flagMod = -1L & ~(PUBLIC | ABSTRACT);
        print(member);
      } else if (member instanceof JCClassDecl) {
        if (prefType != null) println();
        if (isInterface) flagMod = -1L & ~(PUBLIC | STATIC);
        print(member);
      } else {
        if (prefType != null) println();
        print(member);
      }

      prefType = member.getClass();
    }

    if (prevWasEnumMember) {
      prevWasEnumMember = false;
      if (typeOfPrevEnumMember == 3) align();
      println(";");
    }
  }

  @Override
  public void visitTypeParameter(JCTypeParameter tree) {
    List<JCExpression> annotations = readObject(tree, "annotations", List.<JCExpression>nil());
    if (!annotations.isEmpty()) {
      print(annotations, " ");
      print(" ");
    }
    print(tree.name);
    if (tree.bounds.nonEmpty()) {
      print(" extends ");
      print(tree.bounds, " & ");
    }
    consumeComments(tree);
  }

  @Override
  public void visitVarDef(JCVariableDecl tree) {
    printDocComment(tree);
    align();
    if ((tree.mods.flags & ENUM) != 0) {
      printEnumMember(tree);
      return;
    }
    printAnnotations(tree.mods.annotations, true);
    printModifierKeywords(tree.mods);
    printVarDef0(tree);
    println(";", tree);
  }

  private void printVarDefInline(JCVariableDecl tree) {
    printAnnotations(tree.mods.annotations, false);
    printModifierKeywords(tree.mods);
    printVarDef0(tree);
  }

  private void printVarDef0(JCVariableDecl tree) {
    boolean varargs = (tree.mods.flags & VARARGS) != 0;
    if (varargs && tree.vartype instanceof JCArrayTypeTree) {
      print(((JCArrayTypeTree) tree.vartype).elemtype);
      print("...");
    } else {
      print(tree.vartype);
    }
    print(" ");
    print(tree.name);
    if (tree.init != null) {
      print(" = ");
      print(tree.init);
    }
  }

  private void printEnumMember(JCVariableDecl tree) {
    printAnnotations(tree.mods.annotations, true);
    print(tree.name);
    if (tree.init instanceof JCNewClass) {
      JCNewClass constructor = (JCNewClass) tree.init;
      if (constructor.args != null && constructor.args.nonEmpty()) {
        print("(");
        print(constructor.args, ", ");
        print(")");
      }

      if (constructor.def != null && constructor.def.defs != null) {
        println(" {");
        indent++;
        printClassMembers(constructor.def.defs, false, false);
        consumeComments(endPos(tree));
        indent--;
        aPrint("}");
      }
    }
  }

  // TODO: Test postfix syntax for methods (?), for decls. Multiline vardefs, possibly with
  // comments. enums with bodies. constructor-local generics, method-local generics, also do/while,
  // finally, try-with-resources, lambdas, annotations in java8 places...
  // TODO: Whatever is JCAnnotatedType? We handle it in the 7+ bucket in the old one...

  @Override
  public void visitTypeApply(JCTypeApply tree) {
    print(tree.clazz);
    print("<");
    print(tree.arguments, ", ");
    print(">");
  }

  @Override
  public void visitWildcard(JCWildcard tree) {
    switch (tree.getKind()) {
      default:
      case UNBOUNDED_WILDCARD:
        print("?");
        return;
      case EXTENDS_WILDCARD:
        print("? extends ");
        print(tree.inner);
        return;
      case SUPER_WILDCARD:
        print("? super ");
        print(tree.inner);
        return;
    }
  }

  @Override
  public void visitLiteral(JCLiteral tree) {
    TypeTag typeTag = typeTag(tree);
    if (CTC_INT.equals(typeTag)) print("" + tree.value);
    else if (CTC_LONG.equals(typeTag)) print(tree.value + "L");
    else if (CTC_FLOAT.equals(typeTag)) print(tree.value + "F");
    else if (CTC_DOUBLE.equals(typeTag)) print("" + tree.value);
    else if (CTC_CHAR.equals(typeTag)) {
      print("\'" + quoteChar((char) ((Number) tree.value).intValue()) + "\'");
    } else if (CTC_BOOLEAN.equals(typeTag))
      print(((Number) tree.value).intValue() == 1 ? "true" : "false");
    else if (CTC_BOT.equals(typeTag)) print("null");
    else print("\"" + quoteChars(tree.value.toString()) + "\"");
  }

  @Override
  public void visitMethodDef(JCMethodDecl tree) {
    boolean isConstructor = tree.name == name_init(tree.name);
    if (isConstructor && (tree.mods.flags & GENERATEDCONSTR) != 0) return;
    printDocComment(tree);
    align();
    print(tree.mods);
    if (tree.typarams != null && tree.typarams.nonEmpty()) {
      print("<");
      print(tree.typarams, ", ");
      print("> ");
    }

    if (isConstructor) {
      print(currentTypeName == null ? "<init>" : currentTypeName);
    } else {
      print(tree.restype);
      print(" ");
      print(tree.name);
    }

    print("(");
    boolean first = true;
    for (JCVariableDecl param : tree.params) {
      if (!first) print(", ");
      first = false;
      printVarDefInline(param);
    }
    print(")");

    if (tree.thrown.nonEmpty()) {
      print(" throws ");
      print(tree.thrown, ", ");
    }

    if (tree.defaultValue != null) {
      print(" default ");
      print(tree.defaultValue);
    }

    if (tree.body != null) {
      print(" ");
      print(tree.body);
    } else println(";", tree);
  }

  @Override
  public void visitSkip(JCSkip that) {
    if (onNewLine && !aligned) {
      align();
    }
    println(";");
  }

  @Override
  public void visitAnnotation(JCAnnotation tree) {
    print("@");
    print(tree.annotationType);
    if (tree.args.isEmpty()) return;
    print("(");
    boolean done = false;
    if (tree.args.length() == 1 && tree.args.get(0) instanceof JCAssign) {
      JCAssign arg1 = (JCAssign) tree.args.get(0);
      JCIdent arg1Name = arg1.lhs instanceof JCIdent ? ((JCIdent) arg1.lhs) : null;
      if (arg1Name != null && arg1Name.name == name_value(arg1Name.name)) {
        print(arg1.rhs);
        done = true;
      }
    }
    if (!done) print(tree.args, ", ");
    print(")");
  }

  @Override
  public void visitTypeArray(JCArrayTypeTree tree) {
    JCTree elem = tree.elemtype;
    while (elem instanceof JCWildcard) elem = ((JCWildcard) elem).inner;
    print(elem);
    print("[]");
  }

  @Override
  public void visitNewArray(JCNewArray tree) {
    JCTree elem = tree.elemtype;
    int dims = 0;
    if (elem != null) {
      print("new ");

      while (elem instanceof JCArrayTypeTree) {
        dims++;
        elem = ((JCArrayTypeTree) elem).elemtype;
      }
      print(elem);

      for (JCExpression expr : tree.dims) {
        print("[");
        print(expr);
        print("]");
      }
    }

    for (int i = 0; i < dims; i++) print("[]");

    if (tree.elems != null) {
      if (elem != null) print("[] ");
      print("{");
      print(tree.elems, ", ");
      print("}");
    }
  }

  @Override
  public void visitNewClass(JCNewClass tree) {
    if (tree.encl != null) {
      print(tree.encl);
      print(".");
    }

    print("new ");
    if (!tree.typeargs.isEmpty()) {
      print("<");
      print(tree.typeargs, ", ");
      print(">");
    }
    print(tree.clazz);
    print("(");
    print(tree.args, ", ");
    print(")");
    if (tree.def != null) {
      Name previousTypeName = currentTypeName;
      currentTypeName = null;
      println(" {");
      indent++;
      print(tree.def.defs, "");
      indent--;
      aPrint("}");
      currentTypeName = previousTypeName;
    }
  }

  @Override
  public void visitIndexed(JCArrayAccess tree) {
    print(tree.indexed);
    print("[");
    print(tree.index);
    print("]");
  }

  @Override
  public void visitTypeIdent(JCPrimitiveTypeTree tree) {
    TypeTag typeTag = typeTag(tree);

    if (CTC_BYTE.equals(typeTag)) print("byte");
    else if (CTC_CHAR.equals(typeTag)) print("char");
    else if (CTC_SHORT.equals(typeTag)) print("short");
    else if (CTC_INT.equals(typeTag)) print("int");
    else if (CTC_LONG.equals(typeTag)) print("long");
    else if (CTC_FLOAT.equals(typeTag)) print("float");
    else if (CTC_DOUBLE.equals(typeTag)) print("double");
    else if (CTC_BOOLEAN.equals(typeTag)) print("boolean");
    else if (CTC_VOID.equals(typeTag)) print("void");
    else print("error");
  }

  @Override
  public void visitLabelled(JCLabeledStatement tree) {
    aPrint(tree.label);
    print(":");
    if (tree.body instanceof JCSkip || suppress(tree)) {
      println(" ;", tree);
    } else if (tree.body instanceof JCBlock) {
      print(" ");
      print(tree.body);
    } else {
      println(tree);
      print(tree.body);
    }
  }

  private long flagMod = -1L;
  private static final long DEFAULT = 1L << 43;

  @Override
  public void visitModifiers(JCModifiers tree) {
    printAnnotations(tree.annotations, true);
    printModifierKeywords(tree);
  }

  private void printAnnotations(List<JCAnnotation> annotations, boolean newlines) {
    for (JCAnnotation ann : annotations) {
      print(ann);
      if (newlines) {
        println();
        align();
      } else print(" ");
    }
  }

  private void printModifierKeywords(JCModifiers tree) {
    long v = flagMod & tree.flags;
    flagMod = -1L;

    if ((v & SYNTHETIC) != 0) print("/* synthetic */ ");
    if ((v & PUBLIC) != 0) print("public ");
    if ((v & PRIVATE) != 0) print("private ");
    if ((v & PROTECTED) != 0) print("protected ");
    if ((v & STATIC) != 0) print("static ");
    if ((v & FINAL) != 0) print("final ");
    if ((v & SYNCHRONIZED) != 0) print("synchronized ");
    if ((v & VOLATILE) != 0) print("volatile ");
    if ((v & TRANSIENT) != 0) print("transient ");
    if ((v & NATIVE) != 0) print("native ");
    if ((v & ABSTRACT) != 0) print("abstract ");
    if ((v & STRICTFP) != 0) print("strictfp ");
    if ((v & DEFAULT) != 0 && (v & INTERFACE) == 0) print("default ");
  }

  @Override
  public void visitSelect(JCFieldAccess tree) {
    print(tree.selected);
    print(".");
    print(tree.name);
  }

  @Override
  public void visitIdent(JCIdent tree) {
    print(tree.name);
  }

  @Override
  public void visitApply(JCMethodInvocation tree) {
    if (tree.typeargs.nonEmpty()) {
      if (tree.meth instanceof JCFieldAccess) {
        JCFieldAccess fa = (JCFieldAccess) tree.meth;
        print(fa.selected);
        print(".<");
        print(tree.typeargs, ", ");
        print(">");
        print(fa.name);
      } else {
        print("<");
        print(tree.typeargs, ", ");
        print(">");
        print(tree.meth);
      }
    } else {
      print(tree.meth);
    }

    print("(");
    print(tree.args, ", ");
    print(")");
  }

  @Override
  public void visitAssert(JCAssert tree) {
    aPrint("assert ");
    print(tree.cond);
    if (tree.detail != null) {
      print(" : ");
      print(tree.detail);
    }
    println(";", tree);
  }

  @Override
  public void visitAssign(JCAssign tree) {
    print(tree.lhs);
    print(" = ");
    print(tree.rhs);
  }

  @Override
  public void visitAssignop(JCAssignOp tree) {
    print(tree.lhs);
    String opname = operator(treeTag(tree));
    print(" " + opname + " ");
    print(tree.rhs);
  }

  private static final int PREFIX = 14;

  @Override
  public void visitUnary(JCUnary tree) {
    String op = operator(treeTag(tree));
    if (treeTag(tree).getOperatorPrecedenceLevel() == PREFIX) {
      print(op);
      print(tree.arg);
    } else {
      print(tree.arg);
      print(op);
    }
  }

  @Override
  public void visitBinary(JCBinary tree) {
    String op = operator(treeTag(tree));
    print(tree.lhs);
    print(" ");
    print(op);
    print(" ");
    print(tree.rhs);
  }

  @Override
  public void visitTypeTest(JCInstanceOf tree) {
    print(tree.expr);
    print(" instanceof ");
    print(tree.clazz);
  }

  @Override
  public void visitTypeCast(JCTypeCast tree) {
    print("(");
    print(tree.clazz);
    print(") ");
    print(tree.expr);
  }

  @Override
  public void visitBlock(JCBlock tree) {
    if (tree.pos == Position.NOPOS && tree.stats.isEmpty()) return;
    if (onNewLine) align();
    if ((tree.flags & STATIC) != 0) print("static ");
    println("{");
    indent++;
    print(tree.stats, "");
    consumeComments(endPos(tree));
    indent--;
    aPrintln("}", tree);
  }

  @Override
  public void visitBreak(JCBreak tree) {
    aPrint("break");
    if (tree.label != null) {
      print(" ");
      print(tree.label);
    }
    println(";", tree);
  }

  @Override
  public void visitContinue(JCContinue tree) {
    aPrint("continue");
    if (tree.label != null) {
      print(" ");
      print(tree.label);
    }
    println(";", tree);
  }

  @Override
  public void visitConditional(JCConditional tree) {
    print(tree.cond);
    print(" ? ");
    print(tree.truepart);
    print(" : ");
    print(tree.falsepart);
  }

  @Override
  public void visitParens(JCParens tree) {
    print("(");
    print(tree.expr);
    print(")");
  }

  @Override
  public void visitReturn(JCReturn tree) {
    aPrint("return");
    if (tree.expr != null) {
      print(" ");
      print(tree.expr);
    }
    println(";", tree);
  }

  @Override
  public void visitThrow(JCThrow tree) {
    aPrint("throw ");
    print(tree.expr);
    println(";", tree);
  }

  @Override
  public void visitWhileLoop(JCWhileLoop tree) {
    aPrint("while ");
    if (tree.cond instanceof JCParens) {
      print(tree.cond);
    } else {
      print("(");
      print(tree.cond);
      print(")");
    }
    print(" ");
    print(tree.body);
    // make sure to test while (true) ; and while(true){} and while(true) x = 5;
  }

  @Override
  public void visitForLoop(JCForLoop tree) {
    aPrint("for (");
    if (tree.init.nonEmpty()) {
      // ForInit is either a StatementExpressionList or a LocalVariableDeclaration
      if (tree.init.head instanceof JCVariableDecl) {
        boolean first = true;
        int dims = 0;
        for (JCStatement i : tree.init) {
          JCVariableDecl vd = (JCVariableDecl) i;
          if (first) {
            printVarDefInline(vd);
            dims = dims(vd.vartype);
          } else {
            print(", ");
            print(vd.name);
            int dimDiff = dims(vd.vartype) - dims;
            for (int j = 0; j < dimDiff; j++) print("[]");
            if (vd.init != null) {
              print(" = ");
              print(vd.init);
            }
          }
          first = false;
        }
      } else {
        boolean first = true;
        for (JCStatement exprStatement : tree.init) {
          if (!first) print(", ");
          first = false;
          print(((JCExpressionStatement) exprStatement).expr);
        }
      }
    }
    print("; ");
    if (tree.cond != null) print(tree.cond);
    print("; ");
    boolean first = true;
    for (JCExpressionStatement exprStatement : tree.step) {
      if (!first) print(", ");
      first = false;
      print(exprStatement.expr);
    }
    print(") ");
    print(tree.body);
  }

  @Override
  public void visitForeachLoop(JCEnhancedForLoop tree) {
    aPrint("for (");
    printVarDefInline(tree.var);
    print(" : ");
    print(tree.expr);
    print(") ");
    print(tree.body);
  }

  @Override
  public void visitIf(JCIf tree) {
    aPrint("if ");
    if (tree.cond instanceof JCParens) {
      print(tree.cond);
    } else {
      print("(");
      print(tree.cond);
      print(")");
    }
    print(" ");
    if (tree.thenpart instanceof JCBlock) {
      println("{");
      indent++;
      print(((JCBlock) tree.thenpart).stats, "");
      indent--;
      if (tree.elsepart == null) {
        aPrintln("}", tree);
      } else {
        aPrint("}");
      }
    } else {
      print(tree.thenpart);
    }
    if (tree.elsepart != null) {
      aPrint(" else ");
      print(tree.elsepart);
    }
  }

  @Override
  public void visitExec(JCExpressionStatement tree) {
    align();
    print(tree.expr);
    println(";", tree);
  }

  @Override
  public void visitDoLoop(JCDoWhileLoop tree) {
    aPrint("do ");
    if (tree.body instanceof JCBlock) {
      println("{");
      indent++;
      print(((JCBlock) tree.body).stats, "");
      indent--;
      aPrint("}");

    } else print(tree.body);
    print(" while ");
    if (tree.cond instanceof JCParens) {
      print(tree.cond);
    } else {
      print("(");
      print(tree.cond);
      print(")");
    }
    println(";", tree);
  }

  @Override
  public void visitSynchronized(JCSynchronized tree) {
    aPrint("synchronized ");
    if (tree.lock instanceof JCParens) {
      print(tree.lock);
    } else {
      print("(");
      print(tree.lock);
      print(")");
    }
    print(" ");
    print(tree.body);
  }

  @Override
  public void visitCase(JCCase tree) {
    if (tree.pat == null) {
      aPrint("default");
    } else {
      aPrint("case ");
      print(tree.pat);
    }
    println(": ");
    indent++;
    print(tree.stats, "");
    indent--;
  }

  @Override
  public void visitCatch(JCCatch tree) {
    print(" catch (");
    print(tree.param);
    print(") ");
    print(tree.body);
  }

  @Override
  public void visitSwitch(JCSwitch tree) {
    aPrint("switch ");
    if (tree.selector instanceof JCParens) {
      print(tree.selector);
    } else {
      print("(");
      print(tree.selector);
      print(")");
    }
    println(" {");
    print(tree.cases, "\n");
    aPrintln("}", tree);
  }

  @Override
  public void visitTry(JCTry tree) {
    aPrint("try ");
    List<?> resources = readObject(tree, "resources", List.nil());
    int len = resources.length();
    switch (len) {
      case 0:
        break;
      case 1:
        print("(");
        JCVariableDecl decl = (JCVariableDecl) resources.get(0);
        flagMod = -1L & ~FINAL;
        printVarDefInline(decl);
        print(") ");
        break;
      default:
        println("(");
        indent++;
        int c = 0;
        for (Object i : resources) {
          align();
          flagMod = -1L & ~FINAL;
          printVarDefInline((JCVariableDecl) i);
          if (++c == len) {
            print(") ");
          } else {
            println(";", (JCTree) i);
          }
        }
        indent--;
    }
    println("{");
    indent++;
    for (JCStatement stat : tree.body.stats) print(stat);
    indent--;
    aPrint("}");
    for (JCCatch catchBlock : tree.catchers) {
      printCatch(catchBlock);
    }
    if (tree.finalizer != null) {
      println(" finally {");
      indent++;
      for (JCStatement stat : tree.finalizer.stats) print(stat);
      indent--;
      aPrint("}");
    }
    println(tree);
  }

  private void printCatch(JCCatch catchBlock) {
    print(" catch (");
    printVarDefInline(catchBlock.param); // ExprType1 | ExprType2 handled via JCTypeUnion.
    println(") {");
    indent++;
    for (JCStatement stat : catchBlock.body.stats) print(stat);
    indent--;
    aPrint("}");
  }

  public void visitErroneous(JCErroneous tree) {
    print("(ERROR)");
  }

  private static String operator(TreeTag tag) {
    String op = OPERATORS.get(tag);
    if (op == null) return "(?op?)";
    return op;
  }

  private static String quoteChars(String s) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < s.length(); i++) sb.append(quoteChar(s.charAt(i)));
    return sb.toString();
  }

  private static String quoteChar(char ch) {
    switch (ch) {
      case '\b':
        return "\\b";
      case '\f':
        return "\\f";
      case '\n':
        return "\\n";
      case '\r':
        return "\\r";
      case '\t':
        return "\\t";
      case '\'':
        return "\\'";
      case '\"':
        return "\\\"";
      case '\\':
        return "\\\\";
      default:
        if (ch < 32) return String.format("\\%03o", (int) ch);
        return String.valueOf(ch);
    }
  }

  private static final Method getExtendsClause, getEndPosition, storeEnd;

  static {
    getExtendsClause = getMethod(JCClassDecl.class, "getExtendsClause", new Class<?>[0]);
    getExtendsClause.setAccessible(true);

    if (getJavaCompilerVersion() < 8) {
      getEndPosition = getMethod(DiagnosticPosition.class, "getEndPosition", java.util.Map.class);
      storeEnd = getMethod(java.util.Map.class, "put", Object.class, Object.class);
    } else {
      getEndPosition =
          getMethod(
              DiagnosticPosition.class, "getEndPosition", "com.sun.tools.javac.tree.EndPosTable");
      Method storeEndMethodTemp;
      Class<?> endPosTable;
      try {
        endPosTable = Class.forName("com.sun.tools.javac.tree.EndPosTable");
      } catch (ClassNotFoundException ex) {
        throw sneakyThrow(ex);
      }
      try {
        storeEndMethodTemp = endPosTable.getMethod("storeEnd", JCTree.class, int.class);
      } catch (NoSuchMethodException e) {
        try {
          endPosTable = Class.forName("com.sun.tools.javac.parser.JavacParser$AbstractEndPosTable");
          storeEndMethodTemp = endPosTable.getDeclaredMethod("storeEnd", JCTree.class, int.class);
        } catch (NoSuchMethodException ex) {
          throw sneakyThrow(ex);
        } catch (ClassNotFoundException ex) {
          throw sneakyThrow(ex);
        }
      }
      storeEnd = storeEndMethodTemp;
    }
    getEndPosition.setAccessible(true);
    storeEnd.setAccessible(true);
  }

  private static Method getMethod(Class<?> clazz, String name, Class<?>... paramTypes) {
    try {
      return clazz.getMethod(name, paramTypes);
    } catch (NoSuchMethodException e) {
      throw sneakyThrow(e);
    }
  }

  private static Method getMethod(Class<?> clazz, String name, String... paramTypes) {
    try {
      Class<?>[] c = new Class[paramTypes.length];
      for (int i = 0; i < paramTypes.length; i++) c[i] = Class.forName(paramTypes[i]);
      return clazz.getMethod(name, c);
    } catch (NoSuchMethodException e) {
      throw sneakyThrow(e);
    } catch (ClassNotFoundException e) {
      throw sneakyThrow(e);
    }
  }

  public static JCTree getExtendsClause(JCClassDecl decl) {
    try {
      return (JCTree) getExtendsClause.invoke(decl);
    } catch (IllegalAccessException e) {
      throw sneakyThrow(e);
    } catch (InvocationTargetException e) {
      throw sneakyThrow(e.getCause());
    }
  }

  static RuntimeException sneakyThrow(Throwable t) {
    if (t == null) throw new NullPointerException("t");
    PrettyPrinter.<RuntimeException>sneakyThrow0(t);
    return null;
  }

  @SuppressWarnings("unchecked")
  private static <T extends Throwable> void sneakyThrow0(Throwable t) throws T {
    throw (T) t;
  }

  private static final Map<Class<?>, Map<String, Field>> reflectionCache =
      new HashMap<Class<?>, Map<String, Field>>();

  @SuppressWarnings("unchecked")
  private <T> T readObject(JCTree tree, String fieldName, T defaultValue) {
    Class<?> tClass = tree.getClass();
    Map<String, Field> c = reflectionCache.get(tClass);
    if (c == null) reflectionCache.put(tClass, c = new HashMap<String, Field>());
    Field f = c.get(fieldName);
    if (f == null) {
      try {
        f = tClass.getDeclaredField(fieldName);
      } catch (Exception e) {
        return defaultValue;
      }
      f.setAccessible(true);
      c.put(fieldName, f);
    }

    try {
      return (T) f.get(tree);
    } catch (Exception e) {
      return defaultValue;
    }
  }

  public void visitTypeBoundKind(TypeBoundKind tree) {
    print(String.valueOf(tree.kind));
  }

  @Override
  public void visitTree(JCTree tree) {
    String simpleName = tree.getClass().getSimpleName();
    if ("JCTypeUnion".equals(simpleName)) {
      List<JCExpression> types = readObject(tree, "alternatives", List.<JCExpression>nil());
      print(types, " | ");
      return;
    } else if ("JCTypeIntersection".equals(simpleName)) {
      print(readObject(tree, "bounds", List.<JCExpression>nil()), " & ");
      return;
    } else if ("JCMemberReference".equals(simpleName)) {
      printMemberReference0(tree);
      return;
    } else if ("JCLambda".equals(simpleName)) {
      printLambda0(tree);
      return;
    } else if ("JCAnnotatedType".equals(simpleName)) {
      printAnnotatedType0(tree);
      return;
    }

    throw new AssertionError("Unhandled tree type: " + tree.getClass() + ": " + tree);
  }

  private void printMemberReference0(JCTree tree) {
    print(readObject(tree, "expr", (JCExpression) null));
    print("::");
    List<JCExpression> typeArgs = readObject(tree, "typeargs", List.<JCExpression>nil());
    if (typeArgs != null && !typeArgs.isEmpty()) {
      print("<");
      print(typeArgs, ", ");
      print(">");
    }
    print(
        readObject(tree, "mode", new Object()).toString().equals("INVOKE")
            ? readObject(tree, "name", (Name) null)
            : "new");
  }

  private void printLambda0(JCTree tree) {
    List<JCVariableDecl> params = readObject(tree, "params", List.<JCVariableDecl>nil());
    boolean explicit = true;
    int paramLength = params.size();
    try {
      explicit = readObject(tree, "paramKind", new Object()).toString().equals("EXPLICIT");
    } catch (Exception e) {
    }
    boolean useParens = paramLength != 1 || explicit;
    if (useParens) print("(");
    if (explicit) {
      boolean first = true;
      for (JCVariableDecl vd : params) {
        if (!first) print(", ");
        first = false;
        printVarDefInline(vd);
      }
    } else {
      String sep = "";
      for (JCVariableDecl param : params) {
        print(sep);
        print(param.name);
        sep = ", ";
      }
    }
    if (useParens) print(")");
    print(" -> ");
    JCTree body = readObject(tree, "body", (JCTree) null);
    if (body instanceof JCBlock) {
      println("{");
      indent++;
      print(((JCBlock) body).stats, "");
      indent--;
      aPrint("}");
    } else {
      print(body);
    }
  }

  private void printAnnotatedType0(JCTree tree) {
    JCTree underlyingType = readObject(tree, "underlyingType", (JCTree) null);
    if (underlyingType instanceof JCFieldAccess) {
      print(((JCFieldAccess) underlyingType).selected);
      print(".");
      print(readObject(tree, "annotations", List.<JCExpression>nil()), " ");
      print(" ");
      print(((JCFieldAccess) underlyingType).name);
    } else {
      print(readObject(tree, "annotations", List.<JCExpression>nil()), " ");
      print(" ");
      print(underlyingType);
    }
  }
}
Beispiel #2
0
/**
 * Prints out a tree as an indented Java source program.
 *
 * <p>
 *
 * <p><b>This is NOT part of any supported API. If you write code that depends on this, you do so at
 * your own risk. This code and its internal interfaces are subject to change or deletion without
 * notice.</b>
 */
public class Pretty extends JCTree.Visitor {

  public Pretty(Writer out, boolean sourceOutput) {
    this.out = out;
    this.sourceOutput = sourceOutput;
  }

  /**
   * Set when we are producing source output. If we're not producing source output, we can sometimes
   * give more detail in the output even though that detail would not be valid java soruce.
   */
  private final boolean sourceOutput;

  /** The output stream on which trees are printed. */
  Writer out;

  /** Indentation width (can be reassigned from outside). */
  public int width = 4;

  /** The current left margin. */
  int lmargin = 0;

  /** The enclosing class name. */
  Name enclClassName;

  /** A hashtable mapping trees to their documentation comments (can be null) */
  Map<JCTree, String> docComments = null;

  /** Align code to be indented to left margin. */
  void align() throws IOException {
    for (int i = 0; i < lmargin; i++) out.write(" ");
  }

  /** Increase left margin by indentation width. */
  void indent() {
    lmargin = lmargin + width;
  }

  /** Decrease left margin by indentation width. */
  void undent() {
    lmargin = lmargin - width;
  }

  /**
   * Enter a new precedence level. Emit a `(' if new precedence level is less than precedence level
   * so far.
   *
   * @param contextPrec The precedence level in force so far.
   * @param ownPrec The new precedence level.
   */
  void open(int contextPrec, int ownPrec) throws IOException {
    if (ownPrec < contextPrec) out.write("(");
  }

  /**
   * Leave precedence level. Emit a `(' if inner precedence level is less than precedence level we
   * revert to.
   *
   * @param contextPrec The precedence level we revert to.
   * @param ownPrec The inner precedence level.
   */
  void close(int contextPrec, int ownPrec) throws IOException {
    if (ownPrec < contextPrec) out.write(")");
  }

  /** Print string, replacing all non-ascii character with unicode escapes. */
  public void print(Object s) throws IOException {
    out.write(Convert.escapeUnicode(s.toString()));
  }

  /** Print new line. */
  public void println() throws IOException {
    out.write(lineSep);
  }

  String lineSep = System.getProperty("line.separator");

  /**
   * ************************************************************************ Traversal methods
   * ***********************************************************************
   */

  /** Exception to propogate IOException through visitXXX methods */
  private static class UncheckedIOException extends Error {
    static final long serialVersionUID = -4032692679158424751L;

    UncheckedIOException(IOException e) {
      super(e.getMessage(), e);
    }
  }

  /** Visitor argument: the current precedence level. */
  int prec;

  /**
   * Visitor method: print expression tree.
   *
   * @param prec The current precedence level.
   */
  public void printExpr(JCTree tree, int prec) throws IOException {
    int prevPrec = this.prec;
    try {
      this.prec = prec;
      if (tree == null) print("/*missing*/");
      else {
        tree.accept(this);
      }
    } catch (UncheckedIOException ex) {
      IOException e = new IOException(ex.getMessage());
      e.initCause(ex);
      throw e;
    } finally {
      this.prec = prevPrec;
    }
  }

  /** Derived visitor method: print expression tree at minimum precedence level for expression. */
  public void printExpr(JCTree tree) throws IOException {
    printExpr(tree, TreeInfo.noPrec);
  }

  /** Derived visitor method: print statement tree. */
  public void printStat(JCTree tree) throws IOException {
    printExpr(tree, TreeInfo.notExpression);
  }

  /**
   * Derived visitor method: print list of expression trees, separated by given string.
   *
   * @param sep the separator string
   */
  public <T extends JCTree> void printExprs(List<T> trees, String sep) throws IOException {
    if (trees.nonEmpty()) {
      printExpr(trees.head);
      for (List<T> l = trees.tail; l.nonEmpty(); l = l.tail) {
        print(sep);
        printExpr(l.head);
      }
    }
  }

  /** Derived visitor method: print list of expression trees, separated by commas. */
  public <T extends JCTree> void printExprs(List<T> trees) throws IOException {
    printExprs(trees, ", ");
  }

  /** Derived visitor method: print list of statements, each on a separate line. */
  public void printStats(List<? extends JCTree> trees) throws IOException {
    for (List<? extends JCTree> l = trees; l.nonEmpty(); l = l.tail) {
      align();
      printStat(l.head);
      println();
    }
  }

  /** Print a set of modifiers. */
  public void printFlags(long flags) throws IOException {
    if ((flags & SYNTHETIC) != 0) print("/*synthetic*/ ");
    print(TreeInfo.flagNames(flags));
    if ((flags & StandardFlags) != 0) print(" ");
    if ((flags & ANNOTATION) != 0) print("@");
  }

  public void printAnnotations(List<JCAnnotation> trees) throws IOException {
    for (List<JCAnnotation> l = trees; l.nonEmpty(); l = l.tail) {
      printStat(l.head);
      println();
      align();
    }
  }

  /**
   * Print documentation comment, if it exists
   *
   * @param tree The tree for which a documentation comment should be printed.
   */
  public void printDocComment(JCTree tree) throws IOException {
    if (docComments != null) {
      String dc = docComments.get(tree);
      if (dc != null) {
        print("/**");
        println();
        int pos = 0;
        int endpos = lineEndPos(dc, pos);
        while (pos < dc.length()) {
          align();
          print(" *");
          if (pos < dc.length() && dc.charAt(pos) > ' ') print(" ");
          print(dc.substring(pos, endpos));
          println();
          pos = endpos + 1;
          endpos = lineEndPos(dc, pos);
        }
        align();
        print(" */");
        println();
        align();
      }
    }
  }

  // where
  static int lineEndPos(String s, int start) {
    int pos = s.indexOf('\n', start);
    if (pos < 0) pos = s.length();
    return pos;
  }

  /** If type parameter list is non-empty, print it enclosed in "<...>" brackets. */
  public void printTypeParameters(List<JCTypeParameter> trees) throws IOException {
    if (trees.nonEmpty()) {
      print("<");
      printExprs(trees);
      print(">");
    }
  }

  /** Print a block. */
  public void printBlock(List<? extends JCTree> stats) throws IOException {
    print("{");
    println();
    indent();
    printStats(stats);
    undent();
    align();
    print("}");
  }

  /** Print a block. */
  public void printEnumBody(List<JCTree> stats) throws IOException {
    print("{");
    println();
    indent();
    boolean first = true;
    for (List<JCTree> l = stats; l.nonEmpty(); l = l.tail) {
      if (isEnumerator(l.head)) {
        if (!first) {
          print(",");
          println();
        }
        align();
        printStat(l.head);
        first = false;
      }
    }
    print(";");
    println();
    for (List<JCTree> l = stats; l.nonEmpty(); l = l.tail) {
      if (!isEnumerator(l.head)) {
        align();
        printStat(l.head);
        println();
      }
    }
    undent();
    align();
    print("}");
  }

  /** Is the given tree an enumerator definition? */
  boolean isEnumerator(JCTree t) {
    return t.getTag() == JCTree.VARDEF && (((JCVariableDecl) t).mods.flags & ENUM) != 0;
  }

  /**
   * Print unit consisting of package clause and import statements in toplevel, followed by class
   * definition. if class definition == null, print all definitions in toplevel.
   *
   * @param tree The toplevel tree
   * @param cdef The class definition, which is assumed to be part of the toplevel tree.
   */
  public void printUnit(JCCompilationUnit tree, JCClassDecl cdef) throws IOException {
    docComments = tree.docComments;
    printDocComment(tree);
    if (tree.pid != null) {
      print("package ");
      printExpr(tree.pid);
      print(";");
      println();
    }
    boolean firstImport = true;
    for (List<JCTree> l = tree.defs;
        l.nonEmpty() && (cdef == null || l.head.getTag() == JCTree.IMPORT);
        l = l.tail) {
      if (l.head.getTag() == JCTree.IMPORT) {
        JCImport imp = (JCImport) l.head;
        Name name = TreeInfo.name(imp.qualid);
        if (name == name.table.asterisk
            || cdef == null
            || isUsed(TreeInfo.symbol(imp.qualid), cdef)) {
          if (firstImport) {
            firstImport = false;
            println();
          }
          printStat(imp);
        }
      } else {
        printStat(l.head);
      }
    }
    if (cdef != null) {
      printStat(cdef);
      println();
    }
  }

  // where
  boolean isUsed(final Symbol t, JCTree cdef) {
    class UsedVisitor extends TreeScanner {
      public void scan(JCTree tree) {
        if (tree != null && !result) tree.accept(this);
      }

      boolean result = false;

      public void visitIdent(JCIdent tree) {
        if (tree.sym == t) result = true;
      }
    }
    UsedVisitor v = new UsedVisitor();
    v.scan(cdef);
    return v.result;
  }

  /**
   * ************************************************************************ Visitor methods
   * ***********************************************************************
   */
  public void visitTopLevel(JCCompilationUnit tree) {
    try {
      printUnit(tree, null);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitImport(JCImport tree) {
    try {
      print("import ");
      if (tree.staticImport) print("static ");
      printExpr(tree.qualid);
      print(";");
      println();
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitClassDef(JCClassDecl tree) {
    try {
      println();
      align();
      printDocComment(tree);
      printAnnotations(tree.mods.annotations);
      printFlags(tree.mods.flags & ~INTERFACE);
      Name enclClassNamePrev = enclClassName;
      enclClassName = tree.name;
      if ((tree.mods.flags & INTERFACE) != 0) {
        print("interface " + tree.name);
        printTypeParameters(tree.typarams);
        if (tree.implementing.nonEmpty()) {
          print(" extends ");
          printExprs(tree.implementing);
        }
      } else {
        if ((tree.mods.flags & ENUM) != 0) print("enum " + tree.name);
        else print("class " + tree.name);
        printTypeParameters(tree.typarams);
        if (tree.extending != null) {
          print(" extends ");
          printExpr(tree.extending);
        }
        if (tree.implementing.nonEmpty()) {
          print(" implements ");
          printExprs(tree.implementing);
        }
      }
      print(" ");
      if ((tree.mods.flags & ENUM) != 0) {
        printEnumBody(tree.defs);
      } else {
        printBlock(tree.defs);
      }
      enclClassName = enclClassNamePrev;
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitMethodDef(JCMethodDecl tree) {
    try {
      // when producing source output, omit anonymous constructors
      if (tree.name == tree.name.table.init && enclClassName == null && sourceOutput) return;
      println();
      align();
      printDocComment(tree);
      printExpr(tree.mods);
      printTypeParameters(tree.typarams);
      if (tree.name == tree.name.table.init) {
        print(enclClassName != null ? enclClassName : tree.name);
      } else {
        printExpr(tree.restype);
        print(" " + tree.name);
      }
      print("(");
      printExprs(tree.params);
      print(")");
      if (tree.thrown.nonEmpty()) {
        print(" throws ");
        printExprs(tree.thrown);
      }
      if (tree.body != null) {
        print(" ");
        printStat(tree.body);
      } else {
        print(";");
      }
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitVarDef(JCVariableDecl tree) {
    try {
      if (docComments != null && docComments.get(tree) != null) {
        println();
        align();
      }
      printDocComment(tree);
      if ((tree.mods.flags & ENUM) != 0) {
        print("/*public static final*/ ");
        print(tree.name);
        if (tree.init != null) {
          print(" /* = ");
          printExpr(tree.init);
          print(" */");
        }
      } else {
        printExpr(tree.mods);
        if ((tree.mods.flags & VARARGS) != 0) {
          printExpr(((JCArrayTypeTree) tree.vartype).elemtype);
          print("... " + tree.name);
        } else {
          printExpr(tree.vartype);
          print(" " + tree.name);
        }
        if (tree.init != null) {
          print(" = ");
          printExpr(tree.init);
        }
        if (prec == TreeInfo.notExpression) print(";");
      }
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitSkip(JCSkip tree) {
    try {
      print(";");
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitBlock(JCBlock tree) {
    try {
      printFlags(tree.flags);
      printBlock(tree.stats);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitDoLoop(JCDoWhileLoop tree) {
    try {
      print("do ");
      printStat(tree.body);
      align();
      print(" while ");
      if (tree.cond.getTag() == JCTree.PARENS) {
        printExpr(tree.cond);
      } else {
        print("(");
        printExpr(tree.cond);
        print(")");
      }
      print(";");
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitWhileLoop(JCWhileLoop tree) {
    try {
      print("while ");
      if (tree.cond.getTag() == JCTree.PARENS) {
        printExpr(tree.cond);
      } else {
        print("(");
        printExpr(tree.cond);
        print(")");
      }
      print(" ");
      printStat(tree.body);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitForLoop(JCForLoop tree) {
    try {
      print("for (");
      if (tree.init.nonEmpty()) {
        if (tree.init.head.getTag() == JCTree.VARDEF) {
          printExpr(tree.init.head);
          for (List<JCStatement> l = tree.init.tail; l.nonEmpty(); l = l.tail) {
            JCVariableDecl vdef = (JCVariableDecl) l.head;
            print(", " + vdef.name + " = ");
            printExpr(vdef.init);
          }
        } else {
          printExprs(tree.init);
        }
      }
      print("; ");
      if (tree.cond != null) printExpr(tree.cond);
      print("; ");
      printExprs(tree.step);
      print(") ");
      printStat(tree.body);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitForeachLoop(JCEnhancedForLoop tree) {
    try {
      print("for (");
      printExpr(tree.var);
      print(" : ");
      printExpr(tree.expr);
      print(") ");
      printStat(tree.body);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitLabelled(JCLabeledStatement tree) {
    try {
      print(tree.label + ": ");
      printStat(tree.body);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitSwitch(JCSwitch tree) {
    try {
      print("switch ");
      if (tree.selector.getTag() == JCTree.PARENS) {
        printExpr(tree.selector);
      } else {
        print("(");
        printExpr(tree.selector);
        print(")");
      }
      print(" {");
      println();
      printStats(tree.cases);
      align();
      print("}");
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitCase(JCCase tree) {
    try {
      if (tree.pat == null) {
        print("default");
      } else {
        print("case ");
        printExpr(tree.pat);
      }
      print(": ");
      println();
      indent();
      printStats(tree.stats);
      undent();
      align();
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitSynchronized(JCSynchronized tree) {
    try {
      print("synchronized ");
      if (tree.lock.getTag() == JCTree.PARENS) {
        printExpr(tree.lock);
      } else {
        print("(");
        printExpr(tree.lock);
        print(")");
      }
      print(" ");
      printStat(tree.body);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitTry(JCTry tree) {
    try {
      print("try ");
      printStat(tree.body);
      for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
        printStat(l.head);
      }
      if (tree.finalizer != null) {
        print(" finally ");
        printStat(tree.finalizer);
      }
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitCatch(JCCatch tree) {
    try {
      print(" catch (");
      printExpr(tree.param);
      print(") ");
      printStat(tree.body);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitConditional(JCConditional tree) {
    try {
      open(prec, TreeInfo.condPrec);
      printExpr(tree.cond, TreeInfo.condPrec);
      print(" ? ");
      printExpr(tree.truepart, TreeInfo.condPrec);
      print(" : ");
      printExpr(tree.falsepart, TreeInfo.condPrec);
      close(prec, TreeInfo.condPrec);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitIf(JCIf tree) {
    try {
      print("if ");
      if (tree.cond.getTag() == JCTree.PARENS) {
        printExpr(tree.cond);
      } else {
        print("(");
        printExpr(tree.cond);
        print(")");
      }
      print(" ");
      printStat(tree.thenpart);
      if (tree.elsepart != null) {
        print(" else ");
        printStat(tree.elsepart);
      }
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitExec(JCExpressionStatement tree) {
    try {
      printExpr(tree.expr);
      if (prec == TreeInfo.notExpression) print(";");
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitBreak(JCBreak tree) {
    try {
      print("break");
      if (tree.label != null) print(" " + tree.label);
      print(";");
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitContinue(JCContinue tree) {
    try {
      print("continue");
      if (tree.label != null) print(" " + tree.label);
      print(";");
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitReturn(JCReturn tree) {
    try {
      print("return");
      if (tree.expr != null) {
        print(" ");
        printExpr(tree.expr);
      }
      print(";");
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitThrow(JCThrow tree) {
    try {
      print("throw ");
      printExpr(tree.expr);
      print(";");
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitAssert(JCAssert tree) {
    try {
      print("assert ");
      printExpr(tree.cond);
      if (tree.detail != null) {
        print(" : ");
        printExpr(tree.detail);
      }
      print(";");
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitApply(JCMethodInvocation tree) {
    try {
      if (!tree.typeargs.isEmpty()) {
        if (tree.meth.getTag() == JCTree.SELECT) {
          JCFieldAccess left = (JCFieldAccess) tree.meth;
          printExpr(left.selected);
          print(".<");
          printExprs(tree.typeargs);
          print(">" + left.name);
        } else {
          print("<");
          printExprs(tree.typeargs);
          print(">");
          printExpr(tree.meth);
        }
      } else {
        printExpr(tree.meth);
      }
      print("(");
      printExprs(tree.args);
      print(")");
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitNewClass(JCNewClass tree) {
    try {
      if (tree.encl != null) {
        printExpr(tree.encl);
        print(".");
      }
      print("new ");
      if (!tree.typeargs.isEmpty()) {
        print("<");
        printExprs(tree.typeargs);
        print(">");
      }
      printExpr(tree.clazz);
      print("(");
      printExprs(tree.args);
      print(")");
      if (tree.def != null) {
        Name enclClassNamePrev = enclClassName;
        enclClassName =
            tree.def.name != null
                ? tree.def.name
                : tree.type != null && tree.type.tsym.name != tree.type.tsym.name.table.empty
                    ? tree.type.tsym.name
                    : null;
        if ((tree.def.mods.flags & Flags.ENUM) != 0) print("/*enum*/");
        printBlock(tree.def.defs);
        enclClassName = enclClassNamePrev;
      }
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitNewArray(JCNewArray tree) {
    try {
      if (tree.elemtype != null) {
        print("new ");
        JCTree elem = tree.elemtype;
        if (elem instanceof JCArrayTypeTree) printBaseElementType((JCArrayTypeTree) elem);
        else printExpr(elem);
        for (List<JCExpression> l = tree.dims; l.nonEmpty(); l = l.tail) {
          print("[");
          printExpr(l.head);
          print("]");
        }
        if (elem instanceof JCArrayTypeTree) printBrackets((JCArrayTypeTree) elem);
      }
      if (tree.elems != null) {
        if (tree.elemtype != null) print("[]");
        print("{");
        printExprs(tree.elems);
        print("}");
      }
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitParens(JCParens tree) {
    try {
      print("(");
      printExpr(tree.expr);
      print(")");
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitAssign(JCAssign tree) {
    try {
      open(prec, TreeInfo.assignPrec);
      printExpr(tree.lhs, TreeInfo.assignPrec + 1);
      print(" = ");
      printExpr(tree.rhs, TreeInfo.assignPrec);
      close(prec, TreeInfo.assignPrec);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public String operatorName(int tag) {
    switch (tag) {
      case JCTree.POS:
        return "+";
      case JCTree.NEG:
        return "-";
      case JCTree.NOT:
        return "!";
      case JCTree.COMPL:
        return "~";
      case JCTree.PREINC:
        return "++";
      case JCTree.PREDEC:
        return "--";
      case JCTree.POSTINC:
        return "++";
      case JCTree.POSTDEC:
        return "--";
      case JCTree.NULLCHK:
        return "<*nullchk*>";
      case JCTree.OR:
        return "||";
      case JCTree.AND:
        return "&&";
      case JCTree.EQ:
        return "==";
      case JCTree.NE:
        return "!=";
      case JCTree.LT:
        return "<";
      case JCTree.GT:
        return ">";
      case JCTree.LE:
        return "<=";
      case JCTree.GE:
        return ">=";
      case JCTree.BITOR:
        return "|";
      case JCTree.BITXOR:
        return "^";
      case JCTree.BITAND:
        return "&";
      case JCTree.SL:
        return "<<";
      case JCTree.SR:
        return ">>";
      case JCTree.USR:
        return ">>>";
      case JCTree.PLUS:
        return "+";
      case JCTree.MINUS:
        return "-";
      case JCTree.MUL:
        return "*";
      case JCTree.DIV:
        return "/";
      case JCTree.MOD:
        return "%";
      default:
        throw new Error();
    }
  }

  public void visitAssignop(JCAssignOp tree) {
    try {
      open(prec, TreeInfo.assignopPrec);
      printExpr(tree.lhs, TreeInfo.assignopPrec + 1);
      print(" " + operatorName(tree.getTag() - JCTree.ASGOffset) + "= ");
      printExpr(tree.rhs, TreeInfo.assignopPrec);
      close(prec, TreeInfo.assignopPrec);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitUnary(JCUnary tree) {
    try {
      int ownprec = TreeInfo.opPrec(tree.getTag());
      String opname = operatorName(tree.getTag());
      open(prec, ownprec);
      if (tree.getTag() <= JCTree.PREDEC) {
        print(opname);
        printExpr(tree.arg, ownprec);
      } else {
        printExpr(tree.arg, ownprec);
        print(opname);
      }
      close(prec, ownprec);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitBinary(JCBinary tree) {
    try {
      int ownprec = TreeInfo.opPrec(tree.getTag());
      String opname = operatorName(tree.getTag());
      open(prec, ownprec);
      printExpr(tree.lhs, ownprec);
      print(" " + opname + " ");
      printExpr(tree.rhs, ownprec + 1);
      close(prec, ownprec);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitTypeCast(JCTypeCast tree) {
    try {
      open(prec, TreeInfo.prefixPrec);
      print("(");
      printExpr(tree.clazz);
      print(")");
      printExpr(tree.expr, TreeInfo.prefixPrec);
      close(prec, TreeInfo.prefixPrec);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitTypeTest(JCInstanceOf tree) {
    try {
      open(prec, TreeInfo.ordPrec);
      printExpr(tree.expr, TreeInfo.ordPrec);
      print(" instanceof ");
      printExpr(tree.clazz, TreeInfo.ordPrec + 1);
      close(prec, TreeInfo.ordPrec);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitIndexed(JCArrayAccess tree) {
    try {
      printExpr(tree.indexed, TreeInfo.postfixPrec);
      print("[");
      printExpr(tree.index);
      print("]");
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitSelect(JCFieldAccess tree) {
    try {
      printExpr(tree.selected, TreeInfo.postfixPrec);
      print("." + tree.name);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitIdent(JCIdent tree) {
    try {
      print(tree.name);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitLiteral(JCLiteral tree) {
    try {
      switch (tree.typetag) {
        case TypeTags.INT:
          print(tree.value.toString());
          break;
        case TypeTags.LONG:
          print(tree.value + "L");
          break;
        case TypeTags.FLOAT:
          print(tree.value + "F");
          break;
        case TypeTags.DOUBLE:
          print(tree.value.toString());
          break;
        case TypeTags.CHAR:
          print(
              "\'" + Convert.quote(String.valueOf((char) ((Number) tree.value).intValue())) + "\'");
          break;
        case TypeTags.BOOLEAN:
          print(((Number) tree.value).intValue() == 1 ? "true" : "false");
          break;
        case TypeTags.BOT:
          print("null");
          break;
        default:
          print("\"" + Convert.quote(tree.value.toString()) + "\"");
          break;
      }
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitTypeIdent(JCPrimitiveTypeTree tree) {
    try {
      switch (tree.typetag) {
        case TypeTags.BYTE:
          print("byte");
          break;
        case TypeTags.CHAR:
          print("char");
          break;
        case TypeTags.SHORT:
          print("short");
          break;
        case TypeTags.INT:
          print("int");
          break;
        case TypeTags.LONG:
          print("long");
          break;
        case TypeTags.FLOAT:
          print("float");
          break;
        case TypeTags.DOUBLE:
          print("double");
          break;
        case TypeTags.BOOLEAN:
          print("boolean");
          break;
        case TypeTags.VOID:
          print("void");
          break;
        default:
          print("error");
          break;
      }
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitTypeArray(JCArrayTypeTree tree) {
    try {
      printBaseElementType(tree);
      printBrackets(tree);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  // Prints the inner element type of a nested array
  private void printBaseElementType(JCArrayTypeTree tree) throws IOException {
    JCTree elem = tree.elemtype;
    while (elem instanceof JCWildcard) elem = ((JCWildcard) elem).inner;
    if (elem instanceof JCArrayTypeTree) printBaseElementType((JCArrayTypeTree) elem);
    else printExpr(elem);
  }

  // prints the brackets of a nested array in reverse order
  private void printBrackets(JCArrayTypeTree tree) throws IOException {
    JCTree elem;
    while (true) {
      elem = tree.elemtype;
      print("[]");
      if (!(elem instanceof JCArrayTypeTree)) break;
      tree = (JCArrayTypeTree) elem;
    }
  }

  public void visitTypeApply(JCTypeApply tree) {
    try {
      printExpr(tree.clazz);
      print("<");
      printExprs(tree.arguments);
      print(">");
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitTypeParameter(JCTypeParameter tree) {
    try {
      print(tree.name);
      if (tree.bounds.nonEmpty()) {
        print(" extends ");
        printExprs(tree.bounds, " & ");
      }
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  @Override
  public void visitWildcard(JCWildcard tree) {
    try {
      print(tree.kind);
      if (tree.kind.kind != BoundKind.UNBOUND) printExpr(tree.inner);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  @Override
  public void visitTypeBoundKind(TypeBoundKind tree) {
    try {
      print(String.valueOf(tree.kind));
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitErroneous(JCErroneous tree) {
    try {
      print("(ERROR)");
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitLetExpr(LetExpr tree) {
    try {
      print("(let " + tree.defs + " in " + tree.expr + ")");
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitModifiers(JCModifiers mods) {
    try {
      printAnnotations(mods.annotations);
      printFlags(mods.flags);
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitAnnotation(JCAnnotation tree) {
    try {
      print("@");
      printExpr(tree.annotationType);
      print("(");
      printExprs(tree.args);
      print(")");
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  public void visitTree(JCTree tree) {
    try {
      print("(UNKNOWN: " + tree + ")");
      println();
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }
}