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 }