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