1 /* 2 * Copyright (c) 2005, 2025, 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.*; 34 import javax.lang.model.type.*; 35 import javax.lang.model.util.*; 36 37 import java.io.PrintWriter; 38 import java.io.Writer; 39 import java.util.*; 40 import java.util.stream.Collectors; 41 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_26) 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 SimpleElementVisitor14<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 visitRecordComponent(RecordComponentElement e, Boolean p) { 115 // Do nothing; printing of component information done by 116 // printing the record type itself 117 return this; 118 } 119 120 @Override @DefinedBy(Api.LANGUAGE_MODEL) 121 public PrintingElementVisitor visitExecutable(ExecutableElement e, Boolean p) { 122 ElementKind kind = e.getKind(); 123 124 if (kind != STATIC_INIT && 125 kind != INSTANCE_INIT) { 126 Element enclosing = e.getEnclosingElement(); 127 128 // Don't print out the constructor of an anonymous class 129 if (kind == CONSTRUCTOR && 130 enclosing != null && 131 (NestingKind.ANONYMOUS == 132 // Use an anonymous class to determine anonymity! 133 (new SimpleElementVisitor14<NestingKind, Void>() { 134 @Override @DefinedBy(Api.LANGUAGE_MODEL) 135 public NestingKind visitType(TypeElement e, Void p) { 136 return e.getNestingKind(); 137 } 138 }).visit(enclosing)) ) { 139 return this; 140 } 141 142 defaultAction(e, true); 143 printFormalTypeParameters(e, true); 144 145 switch(kind) { 146 case CONSTRUCTOR: 147 // Print out simple name of the class 148 writer.print(e.getEnclosingElement().getSimpleName()); 149 break; 150 151 case METHOD: 152 writer.print(e.getReturnType().toString()); 153 writer.print(" "); 154 writer.print(e.getSimpleName().toString()); 155 break; 156 } 157 158 if (elementUtils.isCompactConstructor(e)) { 159 // A record's compact constructor by definition 160 // lacks source-explicit parameters and lacks a 161 // throws clause. 162 writer.print(" {} /* compact constructor */ "); 163 } else { 164 writer.print("("); 165 printParameters(e); 166 writer.print(")"); 167 168 // Display any default values for an annotation 169 // interface element 170 AnnotationValue defaultValue = e.getDefaultValue(); 171 if (defaultValue != null) 172 writer.print(" default " + defaultValue); 173 174 printThrows(e); 175 } 176 177 writer.println(";"); 178 } 179 return this; 180 } 181 182 183 @Override @DefinedBy(Api.LANGUAGE_MODEL) 184 public PrintingElementVisitor visitType(TypeElement e, Boolean p) { 185 ElementKind kind = e.getKind(); 186 NestingKind nestingKind = e.getNestingKind(); 187 188 if (NestingKind.ANONYMOUS == nestingKind) { 189 // Print nothing for an anonymous class used for an 190 // enum constant body. 191 TypeMirror supertype = e.getSuperclass(); 192 if (supertype.getKind() != TypeKind.NONE) { 193 TypeElement superClass = (TypeElement)(((DeclaredType)supertype).asElement()); 194 if (superClass.getKind() == ENUM) { 195 return this; 196 } 197 } 198 199 // Print out an anonymous class in the style of a 200 // class instance creation expression rather than a 201 // class declaration. 202 writer.print("new "); 203 204 // If the anonymous class implements an interface 205 // print that name, otherwise print the superclass. 206 List<? extends TypeMirror> interfaces = e.getInterfaces(); 207 if (!interfaces.isEmpty()) 208 writer.print(interfaces.get(0)); 209 else 210 writer.print(e.getSuperclass()); 211 212 writer.print("("); 213 // Anonymous classes that implement an interface can't 214 // have any constructor arguments. 215 if (interfaces.isEmpty()) { 216 // Print out the parameter list from the sole 217 // constructor. For now, don't try to elide any 218 // synthetic parameters by determining if the 219 // anonymous class is in a static context, etc. 220 List<? extends ExecutableElement> constructors = 221 ElementFilter.constructorsIn(e.getEnclosedElements()); 222 223 if (!constructors.isEmpty()) 224 printParameters(constructors.get(0)); 225 } 226 writer.print(")"); 227 } else { 228 if (nestingKind == TOP_LEVEL) { 229 PackageElement pkg = elementUtils.getPackageOf(e); 230 if (!pkg.isUnnamed()) 231 writer.print("package " + pkg.getQualifiedName() + ";\n"); 232 } 233 234 defaultAction(e, true); 235 236 switch(kind) { 237 case ANNOTATION_TYPE: 238 writer.print("@interface"); 239 break; 240 default: 241 writer.print(StringUtils.toLowerCase(kind.toString())); 242 } 243 writer.print(" "); 244 writer.print(e.getSimpleName()); 245 246 printFormalTypeParameters(e, false); 247 248 if (kind == RECORD) { 249 // Print out record components 250 writer.print("("); 251 writer.print(e.getRecordComponents() 252 .stream() 253 .map(recordDes -> annotationsToString(recordDes) + recordDes.asType().toString() + " " + recordDes.getSimpleName()) 254 .collect(Collectors.joining(", "))); 255 writer.print(")"); 256 } 257 258 // Print superclass information if informative 259 if (kind == CLASS) { 260 TypeMirror supertype = e.getSuperclass(); 261 if (supertype.getKind() != TypeKind.NONE) { 262 if (isImportantType(supertype)) 263 writer.print(" extends " + supertype); 264 } 265 } 266 267 printInterfaces(e); 268 printPermittedSubclasses(e); 269 } 270 writer.println(" {"); 271 indentation++; 272 273 if (kind == ENUM) { 274 List<Element> enclosedElements = new ArrayList<>(e.getEnclosedElements()); 275 // Handle any enum constants specially before other entities. 276 List<Element> enumConstants = new ArrayList<>(); 277 for(Element element : enclosedElements) { 278 if (element.getKind() == ENUM_CONSTANT) 279 enumConstants.add(element); 280 } 281 if (!enumConstants.isEmpty()) { 282 int i; 283 for(i = 0; i < enumConstants.size()-1; i++) { 284 this.visit(enumConstants.get(i), true); 285 writer.print(","); 286 } 287 this.visit(enumConstants.get(i), true); 288 writer.println(";\n"); 289 290 enclosedElements.removeAll(enumConstants); 291 } 292 293 for(Element element : enclosedElements) 294 this.visit(element); 295 } else { 296 for(Element element : 297 (kind != RECORD ? 298 e.getEnclosedElements() : 299 e.getEnclosedElements() 300 .stream() 301 .filter(elt -> elementUtils.getOrigin(elt) == Elements.Origin.EXPLICIT ) 302 .toList() ) ) 303 this.visit(element); 304 } 305 306 indentation--; 307 indent(); 308 writer.println("}"); 309 return this; 310 } 311 312 @Override @DefinedBy(Api.LANGUAGE_MODEL) 313 public PrintingElementVisitor visitVariable(VariableElement e, Boolean newLine) { 314 ElementKind kind = e.getKind(); 315 defaultAction(e, newLine); 316 317 if (kind == ENUM_CONSTANT) 318 writer.print(e.getSimpleName()); 319 else { 320 writer.print(e.asType().toString() + " " + (e.getSimpleName().isEmpty() ? "_" : e.getSimpleName())); 321 Object constantValue = e.getConstantValue(); 322 if (constantValue != null) { 323 writer.print(" = "); 324 writer.print(elementUtils.getConstantExpression(constantValue)); 325 } 326 writer.println(";"); 327 } 328 return this; 329 } 330 331 @Override @DefinedBy(Api.LANGUAGE_MODEL) 332 public PrintingElementVisitor visitTypeParameter(TypeParameterElement e, Boolean p) { 333 writer.print(e.getSimpleName()); 334 return this; 335 } 336 337 // Should we do more here? 338 @Override @DefinedBy(Api.LANGUAGE_MODEL) 339 public PrintingElementVisitor visitPackage(PackageElement e, Boolean p) { 340 defaultAction(e, false); 341 if (!e.isUnnamed()) 342 writer.println("package " + e.getQualifiedName() + ";"); 343 else 344 writer.println("// Unnamed package"); 345 return this; 346 } 347 348 @Override @DefinedBy(Api.LANGUAGE_MODEL) 349 public PrintingElementVisitor visitModule(ModuleElement e, Boolean p) { 350 defaultAction(e, false); 351 352 if (!e.isUnnamed()) { 353 if (e.isOpen()) { 354 writer.print("open "); 355 } 356 writer.println("module " + e.getQualifiedName() + " {"); 357 indentation++; 358 for (ModuleElement.Directive directive : e.getDirectives()) { 359 printDirective(directive); 360 } 361 indentation--; 362 writer.println("}"); 363 } else 364 writer.println("// Unnamed module"); // Should we do more here? 365 return this; 366 } 367 368 private void printDirective(ModuleElement.Directive directive) { 369 indent(); 370 (new PrintDirective(writer)).visit(directive); 371 writer.println(";"); 372 } 373 374 private static class PrintDirective implements ModuleElement.DirectiveVisitor<Void, Void> { 375 private final PrintWriter writer; 376 377 PrintDirective(PrintWriter writer) { 378 this.writer = writer; 379 } 380 381 @Override @DefinedBy(Api.LANGUAGE_MODEL) 382 public Void visitExports(ExportsDirective d, Void p) { 383 // "exports package-name [to module-name-list]" 384 writer.print("exports "); 385 writer.print(d.getPackage().getQualifiedName()); 386 printModuleList(d.getTargetModules()); 387 return null; 388 } 389 390 @Override @DefinedBy(Api.LANGUAGE_MODEL) 391 public Void visitOpens(OpensDirective d, Void p) { 392 // opens package-name [to module-name-list] 393 writer.print("opens "); 394 writer.print(d.getPackage().getQualifiedName()); 395 printModuleList(d.getTargetModules()); 396 return null; 397 } 398 399 @Override @DefinedBy(Api.LANGUAGE_MODEL) 400 public Void visitProvides(ProvidesDirective d, Void p) { 401 // provides service-name with implementation-name 402 writer.print("provides "); 403 writer.print(d.getService().getQualifiedName()); 404 writer.print(" with "); 405 printNameableList(d.getImplementations()); 406 return null; 407 } 408 409 @Override @DefinedBy(Api.LANGUAGE_MODEL) 410 public Void visitRequires(RequiresDirective d, Void p) { 411 // requires (static|transitive)* module-name 412 writer.print("requires "); 413 if (d.isStatic()) 414 writer.print("static "); 415 if (d.isTransitive()) 416 writer.print("transitive "); 417 writer.print(d.getDependency().getQualifiedName()); 418 return null; 419 } 420 421 @Override @DefinedBy(Api.LANGUAGE_MODEL) 422 public Void visitUses(UsesDirective d, Void p) { 423 // uses service-name 424 writer.print("uses "); 425 writer.print(d.getService().getQualifiedName()); 426 return null; 427 } 428 429 private void printModuleList(List<? extends ModuleElement> modules) { 430 if (modules != null) { 431 writer.print(" to "); 432 printNameableList(modules); 433 } 434 } 435 436 private void printNameableList(List<? extends QualifiedNameable> nameables) { 437 writer.print(nameables.stream(). 438 map(QualifiedNameable::getQualifiedName). 439 collect(Collectors.joining(", "))); 440 } 441 } 442 443 public void flush() { 444 writer.flush(); 445 } 446 447 private void printDocComment(Element e) { 448 String docComment = elementUtils.getDocComment(e); 449 450 if (docComment != null) { 451 // Break comment into lines 452 java.util.StringTokenizer st = new StringTokenizer(docComment, 453 "\n\r"); 454 indent(); 455 writer.println("/**"); 456 457 while(st.hasMoreTokens()) { 458 indent(); 459 writer.print(" *"); 460 writer.println(st.nextToken()); 461 } 462 463 indent(); 464 writer.println(" */"); 465 } 466 } 467 468 private void printModifiers(Element e) { 469 ElementKind kind = e.getKind(); 470 if (kind == PARAMETER || kind == RECORD_COMPONENT) { 471 // Print annotation inline 472 writer.print(annotationsToString(e)); 473 } else { 474 printAnnotations(e); 475 indent(); 476 } 477 478 if (kind == ENUM_CONSTANT || kind == RECORD_COMPONENT) 479 return; 480 481 Set<Modifier> modifiers = new LinkedHashSet<>(); 482 modifiers.addAll(e.getModifiers()); 483 484 switch (kind) { 485 case ANNOTATION_TYPE: 486 case INTERFACE: 487 modifiers.remove(Modifier.ABSTRACT); 488 break; 489 490 case ENUM: 491 modifiers.remove(Modifier.FINAL); 492 modifiers.remove(Modifier.ABSTRACT); 493 modifiers.remove(Modifier.SEALED); 494 break; 495 496 case RECORD: 497 modifiers.remove(Modifier.FINAL); 498 break; 499 500 case METHOD: 501 case FIELD: 502 Element enclosingElement = e.getEnclosingElement(); 503 if (enclosingElement != null) { 504 if (enclosingElement.getKind().isInterface()) { 505 modifiers.remove(Modifier.PUBLIC); 506 modifiers.remove(Modifier.ABSTRACT); // only for methods 507 modifiers.remove(Modifier.STATIC); // only for fields 508 modifiers.remove(Modifier.FINAL); // only for fields 509 } else if (enclosingElement.getKind() == RECORD) { 510 modifiers.remove(Modifier.STRICTFP); 511 } 512 } 513 break; 514 515 } 516 if (!modifiers.isEmpty()) { 517 writer.print(modifiers.stream() 518 .map(Modifier::toString) 519 .collect(Collectors.joining(" ", "", " "))); 520 } 521 } 522 523 private void printFormalTypeParameters(Parameterizable e, 524 boolean pad) { 525 List<? extends TypeParameterElement> typeParams = e.getTypeParameters(); 526 if (!typeParams.isEmpty()) { 527 writer.print(typeParams.stream() 528 .map(tpe -> annotationsToString(tpe) + tpe.toString() + printTypeVariableBoundsIfNeeded(tpe)) 529 .collect(Collectors.joining(", ", "<", ">"))); 530 if (pad) 531 writer.print(" "); 532 } 533 } 534 535 private String printTypeVariableBoundsIfNeeded(TypeParameterElement tpe) { 536 List<? extends TypeMirror> printableBounds = 537 tpe.getBounds() 538 .stream() 539 .filter(type -> isImportantType(type)) 540 .toList(); 541 542 if (printableBounds.isEmpty()) { 543 return ""; 544 } 545 546 return " extends " + printableBounds.stream() 547 .map(t -> t.toString()) 548 .collect(Collectors.joining(" & ")); 549 } 550 551 private String annotationsToString(Element e) { 552 List<? extends AnnotationMirror> annotations = e.getAnnotationMirrors(); 553 return annotations.isEmpty() ? 554 "" : 555 annotations.stream() 556 .map(AnnotationMirror::toString) 557 .collect(Collectors.joining(" ", "", " ")); 558 } 559 560 private void printAnnotations(Element e) { 561 List<? extends AnnotationMirror> annots = e.getAnnotationMirrors(); 562 for(AnnotationMirror annotationMirror : annots) { 563 // Handle compiler-generated container annotations specially 564 if (!printedContainerAnnotation(e, annotationMirror)) { 565 indent(); 566 writer.println(annotationMirror); 567 } 568 } 569 } 570 571 private boolean printedContainerAnnotation(Element e, 572 AnnotationMirror annotationMirror) { 573 /* 574 * If the annotation mirror is marked as mandated and 575 * looks like a container annotation, elide printing the 576 * container and just print the wrapped contents. 577 */ 578 if (elementUtils.getOrigin(e, annotationMirror) == Elements.Origin.MANDATED) { 579 // From JLS Chapter 9, an annotation interface AC is a 580 // containing annotation interface of A if AC declares 581 // a value() method whose return type is A[] and any 582 // methods declared by AC other than value() have a 583 // default value. As an implementation choice, if more 584 // than one annotation element is found on the outer 585 // annotation, in other words, something besides a 586 // "value" method, the annotation will not be treated 587 // as a wrapper for the purposes of printing. These 588 // checks are intended to preserve correctness in the 589 // face of some other kind of annotation being marked 590 // as mandated. 591 592 var entries = annotationMirror.getElementValues().entrySet(); 593 if (entries.size() == 1) { 594 var annotationType = annotationMirror.getAnnotationType(); 595 var annotationTypeAsElement = annotationType.asElement(); 596 597 var entry = entries.iterator().next(); 598 var annotationElements = entry.getValue(); 599 600 // Check that the annotation type declaration has 601 // a single method named "value" and that it 602 // returns an array. A stricter check would be 603 // that it is an array of an annotation type and 604 // that annotation type in turn was repeatable. 605 if (annotationTypeAsElement.getKind() == ElementKind.ANNOTATION_TYPE) { 606 var annotationMethods = 607 ElementFilter.methodsIn(annotationTypeAsElement.getEnclosedElements()); 608 if (annotationMethods.size() == 1) { 609 var valueMethod = annotationMethods.get(0); 610 var returnType = valueMethod.getReturnType(); 611 612 if ("value".equals(valueMethod.getSimpleName().toString()) && 613 returnType.getKind() == TypeKind.ARRAY) { 614 // Use annotation value visitor that 615 // returns a boolean if it prints out 616 // contained annotations as expected 617 // and false otherwise 618 619 return (new SimpleAnnotationValueVisitor14<Boolean, Void>(false) { 620 @Override 621 public Boolean visitArray(List<? extends AnnotationValue> vals, Void p) { 622 if (vals.size() < 2) { 623 return false; 624 } else { 625 for (var annotValue: vals) { 626 indent(); 627 writer.println(annotValue.toString()); 628 } 629 return true; 630 } 631 } 632 }).visit(annotationElements); 633 } 634 } 635 } 636 } 637 } 638 return false; 639 } 640 641 // TODO: Refactor 642 private void printParameters(ExecutableElement e) { 643 List<? extends VariableElement> parameters = e.getParameters(); 644 int size = parameters.size(); 645 646 switch (size) { 647 case 0: 648 break; 649 650 case 1: 651 for(VariableElement parameter: parameters) { 652 printModifiers(parameter); 653 654 if (e.isVarArgs() ) { 655 TypeMirror tm = parameter.asType(); 656 if (tm.getKind() != TypeKind.ARRAY) 657 throw new AssertionError("Var-args parameter is not an array type: " + tm); 658 writer.print((ArrayType.class.cast(tm)).getComponentType() ); 659 writer.print("..."); 660 } else 661 writer.print(parameter.asType()); 662 writer.print(" " + parameter.getSimpleName()); 663 } 664 break; 665 666 default: 667 { 668 int i = 1; 669 for(VariableElement parameter: parameters) { 670 if (i == 2) 671 indentation++; 672 673 if (i > 1) 674 indent(); 675 676 printModifiers(parameter); 677 678 if (i == size && e.isVarArgs() ) { 679 TypeMirror tm = parameter.asType(); 680 if (tm.getKind() != TypeKind.ARRAY) 681 throw new AssertionError("Var-args parameter is not an array type: " + tm); 682 writer.print((ArrayType.class.cast(tm)).getComponentType() ); 683 684 writer.print("..."); 685 } else 686 writer.print(parameter.asType()); 687 writer.print(" " + parameter.getSimpleName()); 688 689 if (i < size) 690 writer.println(","); 691 692 i++; 693 } 694 695 if (parameters.size() >= 2) 696 indentation--; 697 } 698 break; 699 } 700 } 701 702 private void printInterfaces(TypeElement e) { 703 ElementKind kind = e.getKind(); 704 705 if(kind != ANNOTATION_TYPE) { 706 List<? extends TypeMirror> interfaces = e.getInterfaces(); 707 if (!interfaces.isEmpty()) { 708 writer.print((kind.isClass() ? " implements " : " extends ")); 709 writer.print(interfaces.stream() 710 .map(TypeMirror::toString) 711 .collect(Collectors.joining(", "))); 712 } 713 } 714 } 715 716 private void printPermittedSubclasses(TypeElement e) { 717 if (e.getKind() == ENUM) { 718 // any permitted classes on an enum are anonymous 719 // classes for enum bodies, elide. 720 return; 721 } 722 723 List<? extends TypeMirror> subtypes = e.getPermittedSubclasses(); 724 if (!subtypes.isEmpty()) { // could remove this check with more complicated joining call 725 writer.print(" permits "); 726 writer.print(subtypes 727 .stream() 728 .map(subtype -> subtype.toString()) 729 .collect(Collectors.joining(", "))); 730 } 731 } 732 733 private void printThrows(ExecutableElement e) { 734 List<? extends TypeMirror> thrownTypes = e.getThrownTypes(); 735 final int size = thrownTypes.size(); 736 if (size != 0) { 737 writer.print(" throws"); 738 739 int i = 1; 740 for(TypeMirror thrownType: thrownTypes) { 741 if (i == 1) 742 writer.print(" "); 743 744 if (i == 2) 745 indentation++; 746 747 if (i >= 2) 748 indent(); 749 750 writer.print(thrownType); 751 752 if (i != size) 753 writer.println(", "); 754 755 i++; 756 } 757 758 if (size >= 2) 759 indentation--; 760 } 761 } 762 763 private static final String [] spaces = { 764 "", 765 " ", 766 " ", 767 " ", 768 " ", 769 " ", 770 " ", 771 " ", 772 " ", 773 " ", 774 " " 775 }; 776 777 private void indent() { 778 int indentation = this.indentation; 779 if (indentation < 0) 780 return; 781 final int maxIndex = spaces.length - 1; 782 783 while (indentation > maxIndex) { 784 writer.print(spaces[maxIndex]); 785 indentation -= maxIndex; 786 } 787 writer.print(spaces[indentation]); 788 } 789 790 /**{@return true if this type is either not {@code java.lang.Object}, 791 * or is annotated, and hence needs to be included in the output, 792 * even for cases where there's implicit {@code java.lang.Object} type.} 793 * 794 * @param type the type to check. 795 */ 796 private boolean isImportantType(TypeMirror type) { 797 if (!type.getAnnotationMirrors().isEmpty()) { 798 return true; 799 } 800 TypeElement e2 = (TypeElement) 801 ((DeclaredType) type).asElement(); 802 if (!e2.getKind().isClass()) { 803 return true; 804 } 805 return e2.getSuperclass().getKind() != TypeKind.NONE; 806 } 807 } 808 }