aoqi@0: /*
aoqi@0: * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0: *
aoqi@0: * This code is free software; you can redistribute it and/or modify it
aoqi@0: * under the terms of the GNU General Public License version 2 only, as
aoqi@0: * published by the Free Software Foundation. Oracle designates this
aoqi@0: * particular file as subject to the "Classpath" exception as provided
aoqi@0: * by Oracle in the LICENSE file that accompanied this code.
aoqi@0: *
aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0: * accompanied this code).
aoqi@0: *
aoqi@0: * You should have received a copy of the GNU General Public License version
aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0: *
aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0: * or visit www.oracle.com if you need additional information or have any
aoqi@0: * questions.
aoqi@0: */
aoqi@0:
aoqi@0: package com.sun.tools.javac.processing;
aoqi@0:
aoqi@0: import javax.annotation.processing.*;
aoqi@0: import javax.lang.model.*;
aoqi@0: import javax.lang.model.element.*;
aoqi@0: import static javax.lang.model.element.ElementKind.*;
aoqi@0: import static javax.lang.model.element.NestingKind.*;
aoqi@0: import javax.lang.model.type.*;
aoqi@0: import javax.lang.model.util.*;
aoqi@0:
aoqi@0: import java.io.PrintWriter;
aoqi@0: import java.io.Writer;
aoqi@0: import java.util.*;
aoqi@0: import com.sun.tools.javac.util.StringUtils;
aoqi@0:
aoqi@0: /**
aoqi@0: * A processor which prints out elements. Used to implement the
aoqi@0: * -Xprint option; the included visitor class is used to implement
aoqi@0: * Elements.printElements.
aoqi@0: *
aoqi@0: *
This is NOT part of any supported API.
aoqi@0: * If you write code that depends on this, you do so at your own risk.
aoqi@0: * This code and its internal interfaces are subject to change or
aoqi@0: * deletion without notice.
aoqi@0: */
aoqi@0: @SupportedAnnotationTypes("*")
aoqi@0: @SupportedSourceVersion(SourceVersion.RELEASE_8)
aoqi@0: public class PrintingProcessor extends AbstractProcessor {
aoqi@0: PrintWriter writer;
aoqi@0:
aoqi@0: public PrintingProcessor() {
aoqi@0: super();
aoqi@0: writer = new PrintWriter(System.out);
aoqi@0: }
aoqi@0:
aoqi@0: public void setWriter(Writer w) {
aoqi@0: writer = new PrintWriter(w);
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public boolean process(Set extends TypeElement> tes,
aoqi@0: RoundEnvironment renv) {
aoqi@0:
aoqi@0: for(Element element : renv.getRootElements()) {
aoqi@0: print(element);
aoqi@0: }
aoqi@0:
aoqi@0: // Just print the elements, nothing more to do.
aoqi@0: return true;
aoqi@0: }
aoqi@0:
aoqi@0: void print(Element element) {
aoqi@0: new PrintingElementVisitor(writer, processingEnv.getElementUtils()).
aoqi@0: visit(element).flush();
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Used for the -Xprint option and called by Elements.printElements
aoqi@0: */
aoqi@0: public static class PrintingElementVisitor
aoqi@0: extends SimpleElementVisitor8 {
aoqi@0: int indentation; // Indentation level;
aoqi@0: final PrintWriter writer;
aoqi@0: final Elements elementUtils;
aoqi@0:
aoqi@0: public PrintingElementVisitor(Writer w, Elements elementUtils) {
aoqi@0: super();
aoqi@0: this.writer = new PrintWriter(w);
aoqi@0: this.elementUtils = elementUtils;
aoqi@0: indentation = 0;
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: protected PrintingElementVisitor defaultAction(Element e, Boolean newLine) {
aoqi@0: if (newLine != null && newLine)
aoqi@0: writer.println();
aoqi@0: printDocComment(e);
aoqi@0: printModifiers(e);
aoqi@0: return this;
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public PrintingElementVisitor visitExecutable(ExecutableElement e, Boolean p) {
aoqi@0: ElementKind kind = e.getKind();
aoqi@0:
aoqi@0: if (kind != STATIC_INIT &&
aoqi@0: kind != INSTANCE_INIT) {
aoqi@0: Element enclosing = e.getEnclosingElement();
aoqi@0:
aoqi@0: // Don't print out the constructor of an anonymous class
aoqi@0: if (kind == CONSTRUCTOR &&
aoqi@0: enclosing != null &&
aoqi@0: NestingKind.ANONYMOUS ==
aoqi@0: // Use an anonymous class to determine anonymity!
aoqi@0: (new SimpleElementVisitor7() {
aoqi@0: @Override
aoqi@0: public NestingKind visitType(TypeElement e, Void p) {
aoqi@0: return e.getNestingKind();
aoqi@0: }
aoqi@0: }).visit(enclosing))
aoqi@0: return this;
aoqi@0:
aoqi@0: defaultAction(e, true);
aoqi@0: printFormalTypeParameters(e, true);
aoqi@0:
aoqi@0: switch(kind) {
aoqi@0: case CONSTRUCTOR:
aoqi@0: // Print out simple name of the class
aoqi@0: writer.print(e.getEnclosingElement().getSimpleName());
aoqi@0: break;
aoqi@0:
aoqi@0: case METHOD:
aoqi@0: writer.print(e.getReturnType().toString());
aoqi@0: writer.print(" ");
aoqi@0: writer.print(e.getSimpleName().toString());
aoqi@0: break;
aoqi@0: }
aoqi@0:
aoqi@0: writer.print("(");
aoqi@0: printParameters(e);
aoqi@0: writer.print(")");
aoqi@0: AnnotationValue defaultValue = e.getDefaultValue();
aoqi@0: if (defaultValue != null)
aoqi@0: writer.print(" default " + defaultValue);
aoqi@0:
aoqi@0: printThrows(e);
aoqi@0: writer.println(";");
aoqi@0: }
aoqi@0: return this;
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: @Override
aoqi@0: public PrintingElementVisitor visitType(TypeElement e, Boolean p) {
aoqi@0: ElementKind kind = e.getKind();
aoqi@0: NestingKind nestingKind = e.getNestingKind();
aoqi@0:
aoqi@0: if (NestingKind.ANONYMOUS == nestingKind) {
aoqi@0: // Print out an anonymous class in the style of a
aoqi@0: // class instance creation expression rather than a
aoqi@0: // class declaration.
aoqi@0: writer.print("new ");
aoqi@0:
aoqi@0: // If the anonymous class implements an interface
aoqi@0: // print that name, otherwise print the superclass.
aoqi@0: List extends TypeMirror> interfaces = e.getInterfaces();
aoqi@0: if (!interfaces.isEmpty())
aoqi@0: writer.print(interfaces.get(0));
aoqi@0: else
aoqi@0: writer.print(e.getSuperclass());
aoqi@0:
aoqi@0: writer.print("(");
aoqi@0: // Anonymous classes that implement an interface can't
aoqi@0: // have any constructor arguments.
aoqi@0: if (interfaces.isEmpty()) {
aoqi@0: // Print out the parameter list from the sole
aoqi@0: // constructor. For now, don't try to elide any
aoqi@0: // synthetic parameters by determining if the
aoqi@0: // anonymous class is in a static context, etc.
aoqi@0: List extends ExecutableElement> constructors =
aoqi@0: ElementFilter.constructorsIn(e.getEnclosedElements());
aoqi@0:
aoqi@0: if (!constructors.isEmpty())
aoqi@0: printParameters(constructors.get(0));
aoqi@0: }
aoqi@0: writer.print(")");
aoqi@0: } else {
aoqi@0: if (nestingKind == TOP_LEVEL) {
aoqi@0: PackageElement pkg = elementUtils.getPackageOf(e);
aoqi@0: if (!pkg.isUnnamed())
aoqi@0: writer.print("package " + pkg.getQualifiedName() + ";\n");
aoqi@0: }
aoqi@0:
aoqi@0: defaultAction(e, true);
aoqi@0:
aoqi@0: switch(kind) {
aoqi@0: case ANNOTATION_TYPE:
aoqi@0: writer.print("@interface");
aoqi@0: break;
aoqi@0: default:
aoqi@0: writer.print(StringUtils.toLowerCase(kind.toString()));
aoqi@0: }
aoqi@0: writer.print(" ");
aoqi@0: writer.print(e.getSimpleName());
aoqi@0:
aoqi@0: printFormalTypeParameters(e, false);
aoqi@0:
aoqi@0: // Print superclass information if informative
aoqi@0: if (kind == CLASS) {
aoqi@0: TypeMirror supertype = e.getSuperclass();
aoqi@0: if (supertype.getKind() != TypeKind.NONE) {
aoqi@0: TypeElement e2 = (TypeElement)
aoqi@0: ((DeclaredType) supertype).asElement();
aoqi@0: if (e2.getSuperclass().getKind() != TypeKind.NONE)
aoqi@0: writer.print(" extends " + supertype);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: printInterfaces(e);
aoqi@0: }
aoqi@0: writer.println(" {");
aoqi@0: indentation++;
aoqi@0:
aoqi@0: if (kind == ENUM) {
aoqi@0: List enclosedElements =
aoqi@0: new ArrayList(e.getEnclosedElements());
aoqi@0: // Handle any enum constants specially before other entities.
aoqi@0: List enumConstants = new ArrayList();
aoqi@0: for(Element element : enclosedElements) {
aoqi@0: if (element.getKind() == ENUM_CONSTANT)
aoqi@0: enumConstants.add(element);
aoqi@0: }
aoqi@0: if (!enumConstants.isEmpty()) {
aoqi@0: int i;
aoqi@0: for(i = 0; i < enumConstants.size()-1; i++) {
aoqi@0: this.visit(enumConstants.get(i), true);
aoqi@0: writer.print(",");
aoqi@0: }
aoqi@0: this.visit(enumConstants.get(i), true);
aoqi@0: writer.println(";\n");
aoqi@0:
aoqi@0: enclosedElements.removeAll(enumConstants);
aoqi@0: }
aoqi@0:
aoqi@0: for(Element element : enclosedElements)
aoqi@0: this.visit(element);
aoqi@0: } else {
aoqi@0: for(Element element : e.getEnclosedElements())
aoqi@0: this.visit(element);
aoqi@0: }
aoqi@0:
aoqi@0: indentation--;
aoqi@0: indent();
aoqi@0: writer.println("}");
aoqi@0: return this;
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public PrintingElementVisitor visitVariable(VariableElement e, Boolean newLine) {
aoqi@0: ElementKind kind = e.getKind();
aoqi@0: defaultAction(e, newLine);
aoqi@0:
aoqi@0: if (kind == ENUM_CONSTANT)
aoqi@0: writer.print(e.getSimpleName());
aoqi@0: else {
aoqi@0: writer.print(e.asType().toString() + " " + e.getSimpleName() );
aoqi@0: Object constantValue = e.getConstantValue();
aoqi@0: if (constantValue != null) {
aoqi@0: writer.print(" = ");
aoqi@0: writer.print(elementUtils.getConstantExpression(constantValue));
aoqi@0: }
aoqi@0: writer.println(";");
aoqi@0: }
aoqi@0: return this;
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public PrintingElementVisitor visitTypeParameter(TypeParameterElement e, Boolean p) {
aoqi@0: writer.print(e.getSimpleName());
aoqi@0: return this;
aoqi@0: }
aoqi@0:
aoqi@0: // Should we do more here?
aoqi@0: @Override
aoqi@0: public PrintingElementVisitor visitPackage(PackageElement e, Boolean p) {
aoqi@0: defaultAction(e, false);
aoqi@0: if (!e.isUnnamed())
aoqi@0: writer.println("package " + e.getQualifiedName() + ";");
aoqi@0: else
aoqi@0: writer.println("// Unnamed package");
aoqi@0: return this;
aoqi@0: }
aoqi@0:
aoqi@0: public void flush() {
aoqi@0: writer.flush();
aoqi@0: }
aoqi@0:
aoqi@0: private void printDocComment(Element e) {
aoqi@0: String docComment = elementUtils.getDocComment(e);
aoqi@0:
aoqi@0: if (docComment != null) {
aoqi@0: // Break comment into lines
aoqi@0: java.util.StringTokenizer st = new StringTokenizer(docComment,
aoqi@0: "\n\r");
aoqi@0: indent();
aoqi@0: writer.println("/**");
aoqi@0:
aoqi@0: while(st.hasMoreTokens()) {
aoqi@0: indent();
aoqi@0: writer.print(" *");
aoqi@0: writer.println(st.nextToken());
aoqi@0: }
aoqi@0:
aoqi@0: indent();
aoqi@0: writer.println(" */");
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: private void printModifiers(Element e) {
aoqi@0: ElementKind kind = e.getKind();
aoqi@0: if (kind == PARAMETER) {
aoqi@0: printAnnotationsInline(e);
aoqi@0: } else {
aoqi@0: printAnnotations(e);
aoqi@0: indent();
aoqi@0: }
aoqi@0:
aoqi@0: if (kind == ENUM_CONSTANT)
aoqi@0: return;
aoqi@0:
aoqi@0: Set modifiers = new LinkedHashSet();
aoqi@0: modifiers.addAll(e.getModifiers());
aoqi@0:
aoqi@0: switch (kind) {
aoqi@0: case ANNOTATION_TYPE:
aoqi@0: case INTERFACE:
aoqi@0: modifiers.remove(Modifier.ABSTRACT);
aoqi@0: break;
aoqi@0:
aoqi@0: case ENUM:
aoqi@0: modifiers.remove(Modifier.FINAL);
aoqi@0: modifiers.remove(Modifier.ABSTRACT);
aoqi@0: break;
aoqi@0:
aoqi@0: case METHOD:
aoqi@0: case FIELD:
aoqi@0: Element enclosingElement = e.getEnclosingElement();
aoqi@0: if (enclosingElement != null &&
aoqi@0: enclosingElement.getKind().isInterface()) {
aoqi@0: modifiers.remove(Modifier.PUBLIC);
aoqi@0: modifiers.remove(Modifier.ABSTRACT); // only for methods
aoqi@0: modifiers.remove(Modifier.STATIC); // only for fields
aoqi@0: modifiers.remove(Modifier.FINAL); // only for fields
aoqi@0: }
aoqi@0: break;
aoqi@0:
aoqi@0: }
aoqi@0:
aoqi@0: for(Modifier m: modifiers) {
aoqi@0: writer.print(m.toString() + " ");
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: private void printFormalTypeParameters(Parameterizable e,
aoqi@0: boolean pad) {
aoqi@0: List extends TypeParameterElement> typeParams = e.getTypeParameters();
aoqi@0: if (typeParams.size() > 0) {
aoqi@0: writer.print("<");
aoqi@0:
aoqi@0: boolean first = true;
aoqi@0: for(TypeParameterElement tpe: typeParams) {
aoqi@0: if (!first)
aoqi@0: writer.print(", ");
aoqi@0: printAnnotationsInline(tpe);
aoqi@0: writer.print(tpe.toString());
aoqi@0: first = false;
aoqi@0: }
aoqi@0:
aoqi@0: writer.print(">");
aoqi@0: if (pad)
aoqi@0: writer.print(" ");
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: private void printAnnotationsInline(Element e) {
aoqi@0: List extends AnnotationMirror> annots = e.getAnnotationMirrors();
aoqi@0: for(AnnotationMirror annotationMirror : annots) {
aoqi@0: writer.print(annotationMirror);
aoqi@0: writer.print(" ");
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: private void printAnnotations(Element e) {
aoqi@0: List extends AnnotationMirror> annots = e.getAnnotationMirrors();
aoqi@0: for(AnnotationMirror annotationMirror : annots) {
aoqi@0: indent();
aoqi@0: writer.println(annotationMirror);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: // TODO: Refactor
aoqi@0: private void printParameters(ExecutableElement e) {
aoqi@0: List extends VariableElement> parameters = e.getParameters();
aoqi@0: int size = parameters.size();
aoqi@0:
aoqi@0: switch (size) {
aoqi@0: case 0:
aoqi@0: break;
aoqi@0:
aoqi@0: case 1:
aoqi@0: for(VariableElement parameter: parameters) {
aoqi@0: printModifiers(parameter);
aoqi@0:
aoqi@0: if (e.isVarArgs() ) {
aoqi@0: TypeMirror tm = parameter.asType();
aoqi@0: if (tm.getKind() != TypeKind.ARRAY)
aoqi@0: throw new AssertionError("Var-args parameter is not an array type: " + tm);
aoqi@0: writer.print((ArrayType.class.cast(tm)).getComponentType() );
aoqi@0: writer.print("...");
aoqi@0: } else
aoqi@0: writer.print(parameter.asType());
aoqi@0: writer.print(" " + parameter.getSimpleName());
aoqi@0: }
aoqi@0: break;
aoqi@0:
aoqi@0: default:
aoqi@0: {
aoqi@0: int i = 1;
aoqi@0: for(VariableElement parameter: parameters) {
aoqi@0: if (i == 2)
aoqi@0: indentation++;
aoqi@0:
aoqi@0: if (i > 1)
aoqi@0: indent();
aoqi@0:
aoqi@0: printModifiers(parameter);
aoqi@0:
aoqi@0: if (i == size && e.isVarArgs() ) {
aoqi@0: TypeMirror tm = parameter.asType();
aoqi@0: if (tm.getKind() != TypeKind.ARRAY)
aoqi@0: throw new AssertionError("Var-args parameter is not an array type: " + tm);
aoqi@0: writer.print((ArrayType.class.cast(tm)).getComponentType() );
aoqi@0:
aoqi@0: writer.print("...");
aoqi@0: } else
aoqi@0: writer.print(parameter.asType());
aoqi@0: writer.print(" " + parameter.getSimpleName());
aoqi@0:
aoqi@0: if (i < size)
aoqi@0: writer.println(",");
aoqi@0:
aoqi@0: i++;
aoqi@0: }
aoqi@0:
aoqi@0: if (parameters.size() >= 2)
aoqi@0: indentation--;
aoqi@0: }
aoqi@0: break;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: private void printInterfaces(TypeElement e) {
aoqi@0: ElementKind kind = e.getKind();
aoqi@0:
aoqi@0: if(kind != ANNOTATION_TYPE) {
aoqi@0: List extends TypeMirror> interfaces = e.getInterfaces();
aoqi@0: if (interfaces.size() > 0) {
aoqi@0: writer.print((kind.isClass() ? " implements" : " extends"));
aoqi@0:
aoqi@0: boolean first = true;
aoqi@0: for(TypeMirror interf: interfaces) {
aoqi@0: if (!first)
aoqi@0: writer.print(",");
aoqi@0: writer.print(" ");
aoqi@0: writer.print(interf.toString());
aoqi@0: first = false;
aoqi@0: }
aoqi@0: }
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: private void printThrows(ExecutableElement e) {
aoqi@0: List extends TypeMirror> thrownTypes = e.getThrownTypes();
aoqi@0: final int size = thrownTypes.size();
aoqi@0: if (size != 0) {
aoqi@0: writer.print(" throws");
aoqi@0:
aoqi@0: int i = 1;
aoqi@0: for(TypeMirror thrownType: thrownTypes) {
aoqi@0: if (i == 1)
aoqi@0: writer.print(" ");
aoqi@0:
aoqi@0: if (i == 2)
aoqi@0: indentation++;
aoqi@0:
aoqi@0: if (i >= 2)
aoqi@0: indent();
aoqi@0:
aoqi@0: writer.print(thrownType);
aoqi@0:
aoqi@0: if (i != size)
aoqi@0: writer.println(", ");
aoqi@0:
aoqi@0: i++;
aoqi@0: }
aoqi@0:
aoqi@0: if (size >= 2)
aoqi@0: indentation--;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: private static final String [] spaces = {
aoqi@0: "",
aoqi@0: " ",
aoqi@0: " ",
aoqi@0: " ",
aoqi@0: " ",
aoqi@0: " ",
aoqi@0: " ",
aoqi@0: " ",
aoqi@0: " ",
aoqi@0: " ",
aoqi@0: " "
aoqi@0: };
aoqi@0:
aoqi@0: private void indent() {
aoqi@0: int indentation = this.indentation;
aoqi@0: if (indentation < 0)
aoqi@0: return;
aoqi@0: final int maxIndex = spaces.length - 1;
aoqi@0:
aoqi@0: while (indentation > maxIndex) {
aoqi@0: writer.print(spaces[maxIndex]);
aoqi@0: indentation -= maxIndex;
aoqi@0: }
aoqi@0: writer.print(spaces[indentation]);
aoqi@0: }
aoqi@0:
aoqi@0: }
aoqi@0: }