1 /*
   2  * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.tools.javac.processing;
  27 
  28 import javax.annotation.processing.*;
  29 import javax.lang.model.*;
  30 import javax.lang.model.element.*;
  31 import static javax.lang.model.element.ElementKind.*;
  32 import static javax.lang.model.element.NestingKind.*;
  33 import static javax.lang.model.element.ModuleElement.DirectiveKind.*;
  34 import static javax.lang.model.element.ModuleElement.*;
  35 import javax.lang.model.type.*;
  36 import javax.lang.model.util.*;
  37 
  38 import java.io.PrintWriter;
  39 import java.io.Writer;
  40 import java.util.*;
  41 import java.util.stream.Collectors;
  42 
  43 import com.sun.tools.javac.util.DefinedBy;
  44 import com.sun.tools.javac.util.DefinedBy.Api;
  45 import com.sun.tools.javac.util.StringUtils;
  46 
  47 /**
  48  * A processor which prints out elements.  Used to implement the
  49  * -Xprint option; the included visitor class is used to implement
  50  * Elements.printElements.
  51  *
  52  * <p><b>This is NOT part of any supported API.
  53  * If you write code that depends on this, you do so at your own risk.
  54  * This code and its internal interfaces are subject to change or
  55  * deletion without notice.</b>
  56  */
  57 @SupportedAnnotationTypes("*")
  58 @SupportedSourceVersion(SourceVersion.RELEASE_9)
  59 public class PrintingProcessor extends AbstractProcessor {
  60     PrintWriter writer;
  61 
  62     public PrintingProcessor() {
  63         super();
  64         writer = new PrintWriter(System.out);
  65     }
  66 
  67     public void setWriter(Writer w) {
  68         writer = new PrintWriter(w);
  69     }
  70 
  71     @Override @DefinedBy(Api.ANNOTATION_PROCESSING)
  72     public boolean process(Set<? extends TypeElement> tes,
  73                            RoundEnvironment renv) {
  74 
  75         for(Element element : renv.getRootElements()) {
  76             print(element);
  77         }
  78 
  79         // Just print the elements, nothing more to do.
  80         return true;
  81     }
  82 
  83     void print(Element element) {
  84         new PrintingElementVisitor(writer, processingEnv.getElementUtils()).
  85             visit(element).flush();
  86     }
  87 
  88     /**
  89      * Used for the -Xprint option and called by Elements.printElements
  90      */
  91     public static class PrintingElementVisitor
  92         extends SimpleElementVisitor9<PrintingElementVisitor, Boolean> {
  93         int indentation; // Indentation level;
  94         final PrintWriter writer;
  95         final Elements elementUtils;
  96 
  97         public PrintingElementVisitor(Writer w, Elements elementUtils) {
  98             super();
  99             this.writer = new PrintWriter(w);
 100             this.elementUtils = elementUtils;
 101             indentation = 0;
 102         }
 103 
 104         @Override @DefinedBy(Api.LANGUAGE_MODEL)
 105         protected PrintingElementVisitor defaultAction(Element e, Boolean newLine) {
 106             if (newLine != null && newLine)
 107                 writer.println();
 108             printDocComment(e);
 109             printModifiers(e);
 110             return this;
 111         }
 112 
 113         @Override @DefinedBy(Api.LANGUAGE_MODEL)
 114         public PrintingElementVisitor visitExecutable(ExecutableElement e, Boolean p) {
 115             ElementKind kind = e.getKind();
 116 
 117             if (kind != STATIC_INIT &&
 118                 kind != INSTANCE_INIT) {
 119                 Element enclosing = e.getEnclosingElement();
 120 
 121                 // Don't print out the constructor of an anonymous class
 122                 if (kind == CONSTRUCTOR &&
 123                     enclosing != null &&
 124                     NestingKind.ANONYMOUS ==
 125                     // Use an anonymous class to determine anonymity!
 126                     (new SimpleElementVisitor7<NestingKind, Void>() {
 127                         @Override @DefinedBy(Api.LANGUAGE_MODEL)
 128                         public NestingKind visitType(TypeElement e, Void p) {
 129                             return e.getNestingKind();
 130                         }
 131                     }).visit(enclosing))
 132                     return this;
 133 
 134                 defaultAction(e, true);
 135                 printFormalTypeParameters(e, true);
 136 
 137                 switch(kind) {
 138                     case CONSTRUCTOR:
 139                     // Print out simple name of the class
 140                     writer.print(e.getEnclosingElement().getSimpleName());
 141                     break;
 142 
 143                     case METHOD:
 144                     writer.print(e.getReturnType().toString());
 145                     writer.print(" ");
 146                     writer.print(e.getSimpleName().toString());
 147                     break;
 148                 }
 149 
 150                 writer.print("(");
 151                 printParameters(e);
 152                 writer.print(")");
 153                 AnnotationValue defaultValue = e.getDefaultValue();
 154                 if (defaultValue != null)
 155                     writer.print(" default " + defaultValue);
 156 
 157                 printThrows(e);
 158                 writer.println(";");
 159             }
 160             return this;
 161         }
 162 
 163 
 164         @Override @DefinedBy(Api.LANGUAGE_MODEL)
 165         public PrintingElementVisitor visitType(TypeElement e, Boolean p) {
 166             ElementKind kind = e.getKind();
 167             NestingKind nestingKind = e.getNestingKind();
 168 
 169             if (NestingKind.ANONYMOUS == nestingKind) {
 170                 // Print out an anonymous class in the style of a
 171                 // class instance creation expression rather than a
 172                 // class declaration.
 173                 writer.print("new ");
 174 
 175                 // If the anonymous class implements an interface
 176                 // print that name, otherwise print the superclass.
 177                 List<? extends TypeMirror> interfaces = e.getInterfaces();
 178                 if (!interfaces.isEmpty())
 179                     writer.print(interfaces.get(0));
 180                 else
 181                     writer.print(e.getSuperclass());
 182 
 183                 writer.print("(");
 184                 // Anonymous classes that implement an interface can't
 185                 // have any constructor arguments.
 186                 if (interfaces.isEmpty()) {
 187                     // Print out the parameter list from the sole
 188                     // constructor.  For now, don't try to elide any
 189                     // synthetic parameters by determining if the
 190                     // anonymous class is in a static context, etc.
 191                     List<? extends ExecutableElement> constructors =
 192                         ElementFilter.constructorsIn(e.getEnclosedElements());
 193 
 194                     if (!constructors.isEmpty())
 195                         printParameters(constructors.get(0));
 196                 }
 197                 writer.print(")");
 198             } else {
 199                 if (nestingKind == TOP_LEVEL) {
 200                     PackageElement pkg = elementUtils.getPackageOf(e);
 201                     if (!pkg.isUnnamed())
 202                         writer.print("package " + pkg.getQualifiedName() + ";\n");
 203                 }
 204 
 205                 defaultAction(e, true);
 206 
 207                 switch(kind) {
 208                 case ANNOTATION_TYPE:
 209                     writer.print("@interface");
 210                     break;
 211                 default:
 212                     writer.print(StringUtils.toLowerCase(kind.toString()));
 213                 }
 214                 writer.print(" ");
 215                 writer.print(e.getSimpleName());
 216 
 217                 printFormalTypeParameters(e, false);
 218 
 219                 // Print superclass information if informative
 220                 if (kind == CLASS) {
 221                     TypeMirror supertype = e.getSuperclass();
 222                     if (supertype.getKind() != TypeKind.NONE) {
 223                         TypeElement e2 = (TypeElement)
 224                             ((DeclaredType) supertype).asElement();
 225                         if (e2.getSuperclass().getKind() != TypeKind.NONE)
 226                             writer.print(" extends " + supertype);
 227                     }
 228                 }
 229 
 230                 printInterfaces(e);
 231             }
 232             writer.println(" {");
 233             indentation++;
 234 
 235             if (kind == ENUM) {
 236                 List<Element> enclosedElements = new ArrayList<>(e.getEnclosedElements());
 237                 // Handle any enum constants specially before other entities.
 238                 List<Element> enumConstants = new ArrayList<>();
 239                 for(Element element : enclosedElements) {
 240                     if (element.getKind() == ENUM_CONSTANT)
 241                         enumConstants.add(element);
 242                 }
 243                 if (!enumConstants.isEmpty()) {
 244                     int i;
 245                     for(i = 0; i < enumConstants.size()-1; i++) {
 246                         this.visit(enumConstants.get(i), true);
 247                         writer.print(",");
 248                     }
 249                     this.visit(enumConstants.get(i), true);
 250                     writer.println(";\n");
 251 
 252                     enclosedElements.removeAll(enumConstants);
 253                 }
 254 
 255                 for(Element element : enclosedElements)
 256                     this.visit(element);
 257             } else {
 258                 for(Element element : e.getEnclosedElements())
 259                     this.visit(element);
 260             }
 261 
 262             indentation--;
 263             indent();
 264             writer.println("}");
 265             return this;
 266         }
 267 
 268         @Override @DefinedBy(Api.LANGUAGE_MODEL)
 269         public PrintingElementVisitor visitVariable(VariableElement e, Boolean newLine) {
 270             ElementKind kind = e.getKind();
 271             defaultAction(e, newLine);
 272 
 273             if (kind == ENUM_CONSTANT)
 274                 writer.print(e.getSimpleName());
 275             else {
 276                 writer.print(e.asType().toString() + " " + e.getSimpleName() );
 277                 Object constantValue  = e.getConstantValue();
 278                 if (constantValue != null) {
 279                     writer.print(" = ");
 280                     writer.print(elementUtils.getConstantExpression(constantValue));
 281                 }
 282                 writer.println(";");
 283             }
 284             return this;
 285         }
 286 
 287         @Override @DefinedBy(Api.LANGUAGE_MODEL)
 288         public PrintingElementVisitor visitTypeParameter(TypeParameterElement e, Boolean p) {
 289             writer.print(e.getSimpleName());
 290             return this;
 291         }
 292 
 293         // Should we do more here?
 294         @Override @DefinedBy(Api.LANGUAGE_MODEL)
 295         public PrintingElementVisitor visitPackage(PackageElement e, Boolean p) {
 296             defaultAction(e, false);
 297             if (!e.isUnnamed())
 298                 writer.println("package " + e.getQualifiedName() + ";");
 299             else
 300                 writer.println("// Unnamed package");
 301             return this;
 302         }
 303 
 304         @Override @DefinedBy(Api.LANGUAGE_MODEL)
 305         public PrintingElementVisitor visitModule(ModuleElement e, Boolean p) {
 306             defaultAction(e, false);
 307 
 308             if (!e.isUnnamed()) {
 309                 if (e.isOpen()) {
 310                     writer.print("open ");
 311                 }
 312                 writer.println("module " + e.getQualifiedName() + " {");
 313                 indentation++;
 314                 for (ModuleElement.Directive directive : e.getDirectives()) {
 315                     printDirective(directive);
 316                 }
 317                 indentation--;
 318                 writer.println("}");
 319             } else
 320                 writer.println("// Unnamed module"); // Should we do more here?
 321             return this;
 322         }
 323 
 324         private void printDirective(ModuleElement.Directive directive) {
 325             indent();
 326             (new PrintDirective(writer)).visit(directive);
 327             writer.println(";");
 328         }
 329 
 330         private static class PrintDirective implements ModuleElement.DirectiveVisitor<Void, Void> {
 331             private final PrintWriter writer;
 332 
 333             PrintDirective(PrintWriter writer) {
 334                 this.writer = writer;
 335             }
 336 
 337             @Override
 338             public Void visitExports(ExportsDirective d, Void p) {
 339                 // "exports package-name [to module-name-list]"
 340                 writer.print("exports ");
 341                 writer.print(d.getPackage().getQualifiedName());
 342                 printModuleList(d.getTargetModules());
 343                 return null;
 344             }
 345 
 346             @Override
 347             public Void visitOpens(OpensDirective d, Void p) {
 348                 // opens package-name [to module-name-list]
 349                 writer.print("opens ");
 350                 writer.print(d.getPackage().getQualifiedName());
 351                 printModuleList(d.getTargetModules());
 352                 return null;
 353             }
 354 
 355             @Override
 356             public Void visitProvides(ProvidesDirective d, Void p) {
 357                 // provides service-name with implementation-name
 358                 writer.print("provides ");
 359                 writer.print(d.getService().getQualifiedName());
 360                 writer.print(" with ");
 361                 printNameableList(d.getImplementations());
 362                 return null;
 363             }
 364 
 365             @Override
 366             public Void visitRequires(RequiresDirective d, Void p) {
 367                 // requires (static|transitive)* module-name
 368                 writer.print("requires ");
 369                 if (d.isStatic())
 370                     writer.print("static ");
 371                 if (d.isTransitive())
 372                     writer.print("transitive ");
 373                 writer.print(d.getDependency().getQualifiedName());
 374                 return null;
 375             }
 376 
 377             @Override
 378             public Void visitUses(UsesDirective d, Void p) {
 379                 // uses service-name
 380                 writer.print("uses ");
 381                 writer.print(d.getService().getQualifiedName());
 382                 return null;
 383             }
 384 
 385             private void printModuleList(List<? extends ModuleElement> modules) {
 386                 if (modules != null) {
 387                     writer.print(" to ");
 388                     printNameableList(modules);
 389                 }
 390             }
 391 
 392             private void printNameableList(List<? extends QualifiedNameable> nameables) {
 393                 writer.print(nameables.stream().
 394                              map(QualifiedNameable::getQualifiedName).
 395                              collect(Collectors.joining(", ")));
 396             }
 397         }
 398 
 399         public void flush() {
 400             writer.flush();
 401         }
 402 
 403         private void printDocComment(Element e) {
 404             String docComment = elementUtils.getDocComment(e);
 405 
 406             if (docComment != null) {
 407                 // Break comment into lines
 408                 java.util.StringTokenizer st = new StringTokenizer(docComment,
 409                                                                   "\n\r");
 410                 indent();
 411                 writer.println("/**");
 412 
 413                 while(st.hasMoreTokens()) {
 414                     indent();
 415                     writer.print(" *");
 416                     writer.println(st.nextToken());
 417                 }
 418 
 419                 indent();
 420                 writer.println(" */");
 421             }
 422         }
 423 
 424         private void printModifiers(Element e) {
 425             ElementKind kind = e.getKind();
 426             if (kind == PARAMETER) {
 427                 printAnnotationsInline(e);
 428             } else {
 429                 printAnnotations(e);
 430                 indent();
 431             }
 432 
 433             if (kind == ENUM_CONSTANT)
 434                 return;
 435 
 436             Set<Modifier> modifiers = new LinkedHashSet<>();
 437             modifiers.addAll(e.getModifiers());
 438 
 439             switch (kind) {
 440             case ANNOTATION_TYPE:
 441             case INTERFACE:
 442                 modifiers.remove(Modifier.ABSTRACT);
 443                 break;
 444 
 445             case ENUM:
 446                 modifiers.remove(Modifier.FINAL);
 447                 modifiers.remove(Modifier.ABSTRACT);
 448                 break;
 449 
 450             case METHOD:
 451             case FIELD:
 452                 Element enclosingElement = e.getEnclosingElement();
 453                 if (enclosingElement != null &&
 454                     enclosingElement.getKind().isInterface()) {
 455                     modifiers.remove(Modifier.PUBLIC);
 456                     modifiers.remove(Modifier.ABSTRACT); // only for methods
 457                     modifiers.remove(Modifier.STATIC);   // only for fields
 458                     modifiers.remove(Modifier.FINAL);    // only for fields
 459                 }
 460                 break;
 461 
 462             }
 463 
 464             for(Modifier m: modifiers) {
 465                 writer.print(m.toString() + " ");
 466             }
 467         }
 468 
 469         private void printFormalTypeParameters(Parameterizable e,
 470                                                boolean pad) {
 471             List<? extends TypeParameterElement> typeParams = e.getTypeParameters();
 472             if (typeParams.size() > 0) {
 473                 writer.print("<");
 474 
 475                 boolean first = true;
 476                 for(TypeParameterElement tpe: typeParams) {
 477                     if (!first)
 478                         writer.print(", ");
 479                     printAnnotationsInline(tpe);
 480                     writer.print(tpe.toString());
 481                     first = false;
 482                 }
 483 
 484                 writer.print(">");
 485                 if (pad)
 486                     writer.print(" ");
 487             }
 488         }
 489 
 490         private void printAnnotationsInline(Element e) {
 491             List<? extends AnnotationMirror> annots = e.getAnnotationMirrors();
 492             for(AnnotationMirror annotationMirror : annots) {
 493                 writer.print(annotationMirror);
 494                 writer.print(" ");
 495             }
 496         }
 497 
 498         private void printAnnotations(Element e) {
 499             List<? extends AnnotationMirror> annots = e.getAnnotationMirrors();
 500             for(AnnotationMirror annotationMirror : annots) {
 501                 indent();
 502                 writer.println(annotationMirror);
 503             }
 504         }
 505 
 506         // TODO: Refactor
 507         private void printParameters(ExecutableElement e) {
 508             List<? extends VariableElement> parameters = e.getParameters();
 509             int size = parameters.size();
 510 
 511             switch (size) {
 512             case 0:
 513                 break;
 514 
 515             case 1:
 516                 for(VariableElement parameter: parameters) {
 517                     printModifiers(parameter);
 518 
 519                     if (e.isVarArgs() ) {
 520                         TypeMirror tm = parameter.asType();
 521                         if (tm.getKind() != TypeKind.ARRAY)
 522                             throw new AssertionError("Var-args parameter is not an array type: " + tm);
 523                         writer.print((ArrayType.class.cast(tm)).getComponentType() );
 524                         writer.print("...");
 525                     } else
 526                         writer.print(parameter.asType());
 527                     writer.print(" " + parameter.getSimpleName());
 528                 }
 529                 break;
 530 
 531             default:
 532                 {
 533                     int i = 1;
 534                     for(VariableElement parameter: parameters) {
 535                         if (i == 2)
 536                             indentation++;
 537 
 538                         if (i > 1)
 539                             indent();
 540 
 541                         printModifiers(parameter);
 542 
 543                         if (i == size && e.isVarArgs() ) {
 544                             TypeMirror tm = parameter.asType();
 545                             if (tm.getKind() != TypeKind.ARRAY)
 546                                 throw new AssertionError("Var-args parameter is not an array type: " + tm);
 547                                     writer.print((ArrayType.class.cast(tm)).getComponentType() );
 548 
 549                             writer.print("...");
 550                         } else
 551                             writer.print(parameter.asType());
 552                         writer.print(" " + parameter.getSimpleName());
 553 
 554                         if (i < size)
 555                             writer.println(",");
 556 
 557                         i++;
 558                     }
 559 
 560                     if (parameters.size() >= 2)
 561                         indentation--;
 562                 }
 563                 break;
 564             }
 565         }
 566 
 567         private void printInterfaces(TypeElement e) {
 568             ElementKind kind = e.getKind();
 569 
 570             if(kind != ANNOTATION_TYPE) {
 571                 List<? extends TypeMirror> interfaces = e.getInterfaces();
 572                 if (interfaces.size() > 0) {
 573                     writer.print((kind.isClass() ? " implements" : " extends"));
 574 
 575                     boolean first = true;
 576                     for(TypeMirror interf: interfaces) {
 577                         if (!first)
 578                             writer.print(",");
 579                         writer.print(" ");
 580                         writer.print(interf.toString());
 581                         first = false;
 582                     }
 583                 }
 584             }
 585         }
 586 
 587         private void printThrows(ExecutableElement e) {
 588             List<? extends TypeMirror> thrownTypes = e.getThrownTypes();
 589             final int size = thrownTypes.size();
 590             if (size != 0) {
 591                 writer.print(" throws");
 592 
 593                 int i = 1;
 594                 for(TypeMirror thrownType: thrownTypes) {
 595                     if (i == 1)
 596                         writer.print(" ");
 597 
 598                     if (i == 2)
 599                         indentation++;
 600 
 601                     if (i >= 2)
 602                         indent();
 603 
 604                     writer.print(thrownType);
 605 
 606                     if (i != size)
 607                         writer.println(", ");
 608 
 609                     i++;
 610                 }
 611 
 612                 if (size >= 2)
 613                     indentation--;
 614             }
 615         }
 616 
 617         private static final String [] spaces = {
 618             "",
 619             "  ",
 620             "    ",
 621             "      ",
 622             "        ",
 623             "          ",
 624             "            ",
 625             "              ",
 626             "                ",
 627             "                  ",
 628             "                    "
 629         };
 630 
 631         private void indent() {
 632             int indentation = this.indentation;
 633             if (indentation < 0)
 634                 return;
 635             final int maxIndex = spaces.length - 1;
 636 
 637             while (indentation > maxIndex) {
 638                 writer.print(spaces[maxIndex]);
 639                 indentation -= maxIndex;
 640             }
 641             writer.print(spaces[indentation]);
 642         }
 643 
 644     }
 645 }