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             switch (directive.getKind()) {
 327             case EXPORTS: // "exports package-name [to module-name-list]"
 328                 {
 329                     ExportsDirective exportsDirective = (ExportsDirective) directive;
 330                     writer.print("exports ");
 331                     writer.print(exportsDirective.getPackage().getQualifiedName());
 332                     printModuleList(exportsDirective.getTargetModules());
 333                 }
 334                 break;
 335 
 336             case OPENS: // opens package-name [to module-name-list]
 337                 {
 338                     OpensDirective opensDirective = (OpensDirective) directive;
 339                     writer.print("opens ");
 340                     writer.print(opensDirective.getPackage().getQualifiedName());
 341                     printModuleList(opensDirective.getTargetModules());
 342                 }
 343                 break;
 344 
 345             case PROVIDES: // provides service-name with implementation-name
 346                 {
 347                     ProvidesDirective providesDirective = (ProvidesDirective) directive;
 348                     writer.print("provides ");
 349                     writer.print(providesDirective.getService().getQualifiedName());
 350                     writer.print(" with ");
 351                     printNameableList(providesDirective.getImplementations());
 352                 }
 353                 break;
 354 
 355             case REQUIRES: // requires (static|transitive)* module-name
 356                 {
 357                     RequiresDirective requiresDirective = (RequiresDirective) directive;
 358                     writer.print("requires ");
 359                     if (requiresDirective.isStatic())
 360                         writer.print("static ");
 361                     if (requiresDirective.isTransitive())
 362                         writer.print("transitive ");
 363                     writer.print(requiresDirective.getDependency().getQualifiedName());
 364                 }
 365                 break;
 366 
 367             case USES: // uses service-name
 368                 {
 369                     UsesDirective usesDirective = (UsesDirective) directive;
 370                     writer.print("uses ");
 371                     writer.print(usesDirective.getService().getQualifiedName());
 372                 }
 373                 break;
 374 
 375             default:
 376                 throw new UnsupportedOperationException("unknown directive " + directive);
 377             }
 378             writer.println(";");
 379         }
 380 
 381         private void printModuleList(List<? extends ModuleElement> modules) {
 382             if (modules != null) {
 383                 writer.print(" to ");
 384                 printNameableList(modules);
 385             }
 386         }
 387 
 388         private void printNameableList(List<? extends QualifiedNameable> nameables) {
 389                 writer.print(nameables.stream().
 390                              map(QualifiedNameable::getQualifiedName).
 391                              collect(Collectors.joining(", ")));
 392         }
 393 
 394         public void flush() {
 395             writer.flush();
 396         }
 397 
 398         private void printDocComment(Element e) {
 399             String docComment = elementUtils.getDocComment(e);
 400 
 401             if (docComment != null) {
 402                 // Break comment into lines
 403                 java.util.StringTokenizer st = new StringTokenizer(docComment,
 404                                                                   "\n\r");
 405                 indent();
 406                 writer.println("/**");
 407 
 408                 while(st.hasMoreTokens()) {
 409                     indent();
 410                     writer.print(" *");
 411                     writer.println(st.nextToken());
 412                 }
 413 
 414                 indent();
 415                 writer.println(" */");
 416             }
 417         }
 418 
 419         private void printModifiers(Element e) {
 420             ElementKind kind = e.getKind();
 421             if (kind == PARAMETER) {
 422                 printAnnotationsInline(e);
 423             } else {
 424                 printAnnotations(e);
 425                 indent();
 426             }
 427 
 428             if (kind == ENUM_CONSTANT)
 429                 return;
 430 
 431             Set<Modifier> modifiers = new LinkedHashSet<>();
 432             modifiers.addAll(e.getModifiers());
 433 
 434             switch (kind) {
 435             case ANNOTATION_TYPE:
 436             case INTERFACE:
 437                 modifiers.remove(Modifier.ABSTRACT);
 438                 break;
 439 
 440             case ENUM:
 441                 modifiers.remove(Modifier.FINAL);
 442                 modifiers.remove(Modifier.ABSTRACT);
 443                 break;
 444 
 445             case METHOD:
 446             case FIELD:
 447                 Element enclosingElement = e.getEnclosingElement();
 448                 if (enclosingElement != null &&
 449                     enclosingElement.getKind().isInterface()) {
 450                     modifiers.remove(Modifier.PUBLIC);
 451                     modifiers.remove(Modifier.ABSTRACT); // only for methods
 452                     modifiers.remove(Modifier.STATIC);   // only for fields
 453                     modifiers.remove(Modifier.FINAL);    // only for fields
 454                 }
 455                 break;
 456 
 457             }
 458 
 459             for(Modifier m: modifiers) {
 460                 writer.print(m.toString() + " ");
 461             }
 462         }
 463 
 464         private void printFormalTypeParameters(Parameterizable e,
 465                                                boolean pad) {
 466             List<? extends TypeParameterElement> typeParams = e.getTypeParameters();
 467             if (typeParams.size() > 0) {
 468                 writer.print("<");
 469 
 470                 boolean first = true;
 471                 for(TypeParameterElement tpe: typeParams) {
 472                     if (!first)
 473                         writer.print(", ");
 474                     printAnnotationsInline(tpe);
 475                     writer.print(tpe.toString());
 476                     first = false;
 477                 }
 478 
 479                 writer.print(">");
 480                 if (pad)
 481                     writer.print(" ");
 482             }
 483         }
 484 
 485         private void printAnnotationsInline(Element e) {
 486             List<? extends AnnotationMirror> annots = e.getAnnotationMirrors();
 487             for(AnnotationMirror annotationMirror : annots) {
 488                 writer.print(annotationMirror);
 489                 writer.print(" ");
 490             }
 491         }
 492 
 493         private void printAnnotations(Element e) {
 494             List<? extends AnnotationMirror> annots = e.getAnnotationMirrors();
 495             for(AnnotationMirror annotationMirror : annots) {
 496                 indent();
 497                 writer.println(annotationMirror);
 498             }
 499         }
 500 
 501         // TODO: Refactor
 502         private void printParameters(ExecutableElement e) {
 503             List<? extends VariableElement> parameters = e.getParameters();
 504             int size = parameters.size();
 505 
 506             switch (size) {
 507             case 0:
 508                 break;
 509 
 510             case 1:
 511                 for(VariableElement parameter: parameters) {
 512                     printModifiers(parameter);
 513 
 514                     if (e.isVarArgs() ) {
 515                         TypeMirror tm = parameter.asType();
 516                         if (tm.getKind() != TypeKind.ARRAY)
 517                             throw new AssertionError("Var-args parameter is not an array type: " + tm);
 518                         writer.print((ArrayType.class.cast(tm)).getComponentType() );
 519                         writer.print("...");
 520                     } else
 521                         writer.print(parameter.asType());
 522                     writer.print(" " + parameter.getSimpleName());
 523                 }
 524                 break;
 525 
 526             default:
 527                 {
 528                     int i = 1;
 529                     for(VariableElement parameter: parameters) {
 530                         if (i == 2)
 531                             indentation++;
 532 
 533                         if (i > 1)
 534                             indent();
 535 
 536                         printModifiers(parameter);
 537 
 538                         if (i == size && e.isVarArgs() ) {
 539                             TypeMirror tm = parameter.asType();
 540                             if (tm.getKind() != TypeKind.ARRAY)
 541                                 throw new AssertionError("Var-args parameter is not an array type: " + tm);
 542                                     writer.print((ArrayType.class.cast(tm)).getComponentType() );
 543 
 544                             writer.print("...");
 545                         } else
 546                             writer.print(parameter.asType());
 547                         writer.print(" " + parameter.getSimpleName());
 548 
 549                         if (i < size)
 550                             writer.println(",");
 551 
 552                         i++;
 553                     }
 554 
 555                     if (parameters.size() >= 2)
 556                         indentation--;
 557                 }
 558                 break;
 559             }
 560         }
 561 
 562         private void printInterfaces(TypeElement e) {
 563             ElementKind kind = e.getKind();
 564 
 565             if(kind != ANNOTATION_TYPE) {
 566                 List<? extends TypeMirror> interfaces = e.getInterfaces();
 567                 if (interfaces.size() > 0) {
 568                     writer.print((kind.isClass() ? " implements" : " extends"));
 569 
 570                     boolean first = true;
 571                     for(TypeMirror interf: interfaces) {
 572                         if (!first)
 573                             writer.print(",");
 574                         writer.print(" ");
 575                         writer.print(interf.toString());
 576                         first = false;
 577                     }
 578                 }
 579             }
 580         }
 581 
 582         private void printThrows(ExecutableElement e) {
 583             List<? extends TypeMirror> thrownTypes = e.getThrownTypes();
 584             final int size = thrownTypes.size();
 585             if (size != 0) {
 586                 writer.print(" throws");
 587 
 588                 int i = 1;
 589                 for(TypeMirror thrownType: thrownTypes) {
 590                     if (i == 1)
 591                         writer.print(" ");
 592 
 593                     if (i == 2)
 594                         indentation++;
 595 
 596                     if (i >= 2)
 597                         indent();
 598 
 599                     writer.print(thrownType);
 600 
 601                     if (i != size)
 602                         writer.println(", ");
 603 
 604                     i++;
 605                 }
 606 
 607                 if (size >= 2)
 608                     indentation--;
 609             }
 610         }
 611 
 612         private static final String [] spaces = {
 613             "",
 614             "  ",
 615             "    ",
 616             "      ",
 617             "        ",
 618             "          ",
 619             "            ",
 620             "              ",
 621             "                ",
 622             "                  ",
 623             "                    "
 624         };
 625 
 626         private void indent() {
 627             int indentation = this.indentation;
 628             if (indentation < 0)
 629                 return;
 630             final int maxIndex = spaces.length - 1;
 631 
 632             while (indentation > maxIndex) {
 633                 writer.print(spaces[maxIndex]);
 634                 indentation -= maxIndex;
 635             }
 636             writer.print(spaces[indentation]);
 637         }
 638 
 639     }
 640 }