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