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 }