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