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 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 }
510 break;
511
512 }
513 if (!modifiers.isEmpty()) {
514 writer.print(modifiers.stream()
515 .map(Modifier::toString)
516 .collect(Collectors.joining(" ", "", " ")));
517 }
518 }
519
520 private void printFormalTypeParameters(Parameterizable e,
521 boolean pad) {
522 List<? extends TypeParameterElement> typeParams = e.getTypeParameters();
523 if (!typeParams.isEmpty()) {
524 writer.print(typeParams.stream()
525 .map(tpe -> annotationsToString(tpe) + tpe.toString() + printTypeVariableBoundsIfNeeded(tpe))
526 .collect(Collectors.joining(", ", "<", ">")));
527 if (pad)
528 writer.print(" ");
529 }
530 }
531
532 private String printTypeVariableBoundsIfNeeded(TypeParameterElement tpe) {
533 List<? extends TypeMirror> printableBounds =
534 tpe.getBounds()
535 .stream()
536 .filter(type -> isImportantType(type))
537 .toList();
538
539 if (printableBounds.isEmpty()) {
540 return "";
541 }
542
543 return " extends " + printableBounds.stream()
544 .map(t -> t.toString())
545 .collect(Collectors.joining(" & "));
546 }
547
548 private String annotationsToString(Element e) {
549 List<? extends AnnotationMirror> annotations = e.getAnnotationMirrors();
550 return annotations.isEmpty() ?
551 "" :
552 annotations.stream()
553 .map(AnnotationMirror::toString)
554 .collect(Collectors.joining(" ", "", " "));
555 }
556
557 private void printAnnotations(Element e) {
558 List<? extends AnnotationMirror> annots = e.getAnnotationMirrors();
559 for(AnnotationMirror annotationMirror : annots) {
560 // Handle compiler-generated container annotations specially
561 if (!printedContainerAnnotation(e, annotationMirror)) {
562 indent();
563 writer.println(annotationMirror);
564 }
565 }
566 }
567
568 private boolean printedContainerAnnotation(Element e,
569 AnnotationMirror annotationMirror) {
570 /*
571 * If the annotation mirror is marked as mandated and
572 * looks like a container annotation, elide printing the
573 * container and just print the wrapped contents.
574 */
575 if (elementUtils.getOrigin(e, annotationMirror) == Elements.Origin.MANDATED) {
576 // From JLS Chapter 9, an annotation interface AC is a
577 // containing annotation interface of A if AC declares
578 // a value() method whose return type is A[] and any
579 // methods declared by AC other than value() have a
580 // default value. As an implementation choice, if more
581 // than one annotation element is found on the outer
582 // annotation, in other words, something besides a
583 // "value" method, the annotation will not be treated
584 // as a wrapper for the purposes of printing. These
585 // checks are intended to preserve correctness in the
586 // face of some other kind of annotation being marked
587 // as mandated.
588
589 var entries = annotationMirror.getElementValues().entrySet();
590 if (entries.size() == 1) {
591 var annotationType = annotationMirror.getAnnotationType();
592 var annotationTypeAsElement = annotationType.asElement();
593
594 var entry = entries.iterator().next();
595 var annotationElements = entry.getValue();
596
597 // Check that the annotation type declaration has
598 // a single method named "value" and that it
599 // returns an array. A stricter check would be
600 // that it is an array of an annotation type and
601 // that annotation type in turn was repeatable.
602 if (annotationTypeAsElement.getKind() == ElementKind.ANNOTATION_TYPE) {
603 var annotationMethods =
604 ElementFilter.methodsIn(annotationTypeAsElement.getEnclosedElements());
605 if (annotationMethods.size() == 1) {
606 var valueMethod = annotationMethods.get(0);
607 var returnType = valueMethod.getReturnType();
608
609 if ("value".equals(valueMethod.getSimpleName().toString()) &&
610 returnType.getKind() == TypeKind.ARRAY) {
611 // Use annotation value visitor that
612 // returns a boolean if it prints out
613 // contained annotations as expected
614 // and false otherwise
615
616 return (new SimpleAnnotationValueVisitor14<Boolean, Void>(false) {
617 @Override
618 public Boolean visitArray(List<? extends AnnotationValue> vals, Void p) {
619 if (vals.size() < 2) {
620 return false;
621 } else {
622 for (var annotValue: vals) {
623 indent();
624 writer.println(annotValue.toString());
625 }
626 return true;
627 }
628 }
629 }).visit(annotationElements);
630 }
631 }
632 }
633 }
634 }
635 return false;
636 }
637
638 // TODO: Refactor
639 private void printParameters(ExecutableElement e) {
640 List<? extends VariableElement> parameters = e.getParameters();
641 int size = parameters.size();
642
643 switch (size) {
644 case 0:
645 break;
646
647 case 1:
648 for(VariableElement parameter: parameters) {
649 printModifiers(parameter);
650
651 if (e.isVarArgs() ) {
652 TypeMirror tm = parameter.asType();
653 if (tm.getKind() != TypeKind.ARRAY)
654 throw new AssertionError("Var-args parameter is not an array type: " + tm);
655 writer.print((ArrayType.class.cast(tm)).getComponentType() );
656 writer.print("...");
657 } else
658 writer.print(parameter.asType());
659 writer.print(" " + parameter.getSimpleName());
660 }
661 break;
662
663 default:
664 {
665 int i = 1;
666 for(VariableElement parameter: parameters) {
667 if (i == 2)
668 indentation++;
669
670 if (i > 1)
671 indent();
672
673 printModifiers(parameter);
674
675 if (i == size && e.isVarArgs() ) {
676 TypeMirror tm = parameter.asType();
677 if (tm.getKind() != TypeKind.ARRAY)
678 throw new AssertionError("Var-args parameter is not an array type: " + tm);
679 writer.print((ArrayType.class.cast(tm)).getComponentType() );
680
681 writer.print("...");
682 } else
683 writer.print(parameter.asType());
684 writer.print(" " + parameter.getSimpleName());
685
686 if (i < size)
687 writer.println(",");
688
689 i++;
690 }
691
692 if (parameters.size() >= 2)
693 indentation--;
694 }
695 break;
696 }
697 }
698
699 private void printInterfaces(TypeElement e) {
700 ElementKind kind = e.getKind();
701
702 if(kind != ANNOTATION_TYPE) {
703 List<? extends TypeMirror> interfaces = e.getInterfaces();
704 if (!interfaces.isEmpty()) {
705 writer.print((kind.isClass() ? " implements " : " extends "));
706 writer.print(interfaces.stream()
707 .map(TypeMirror::toString)
708 .collect(Collectors.joining(", ")));
709 }
710 }
711 }
712
713 private void printPermittedSubclasses(TypeElement e) {
714 if (e.getKind() == ENUM) {
715 // any permitted classes on an enum are anonymous
716 // classes for enum bodies, elide.
717 return;
718 }
719
720 List<? extends TypeMirror> subtypes = e.getPermittedSubclasses();
721 if (!subtypes.isEmpty()) { // could remove this check with more complicated joining call
722 writer.print(" permits ");
723 writer.print(subtypes
724 .stream()
725 .map(subtype -> subtype.toString())
726 .collect(Collectors.joining(", ")));
727 }
728 }
729
730 private void printThrows(ExecutableElement e) {
731 List<? extends TypeMirror> thrownTypes = e.getThrownTypes();
732 final int size = thrownTypes.size();
733 if (size != 0) {
734 writer.print(" throws");
735
736 int i = 1;
737 for(TypeMirror thrownType: thrownTypes) {
738 if (i == 1)
739 writer.print(" ");
740
741 if (i == 2)
742 indentation++;
743
744 if (i >= 2)
745 indent();
746
747 writer.print(thrownType);
748
749 if (i != size)
750 writer.println(", ");
751
752 i++;
753 }
754
755 if (size >= 2)
756 indentation--;
757 }
758 }
759
760 private static final String [] spaces = {
761 "",
762 " ",
763 " ",
764 " ",
765 " ",
766 " ",
767 " ",
768 " ",
769 " ",
770 " ",
771 " "
772 };
773
774 private void indent() {
775 int indentation = this.indentation;
776 if (indentation < 0)
777 return;
778 final int maxIndex = spaces.length - 1;
779
780 while (indentation > maxIndex) {
781 writer.print(spaces[maxIndex]);
782 indentation -= maxIndex;
783 }
784 writer.print(spaces[indentation]);
785 }
786
787 /**{@return true if this type is either not {@code java.lang.Object},
788 * or is annotated, and hence needs to be included in the output,
789 * even for cases where there's implicit {@code java.lang.Object} type.}
790 *
791 * @param type the type to check.
792 */
793 private boolean isImportantType(TypeMirror type) {
794 if (!type.getAnnotationMirrors().isEmpty()) {
795 return true;
796 }
797 TypeElement e2 = (TypeElement)
798 ((DeclaredType) type).asElement();
799 if (!e2.getKind().isClass()) {
800 return true;
801 }
802 return e2.getSuperclass().getKind() != TypeKind.NONE;
803 }
804 }
805 }