PrintingProcessor.java revision 3929:b7bb3f51028a
1/*
2 * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.tools.javac.processing;
27
28import javax.annotation.processing.*;
29import javax.lang.model.*;
30import javax.lang.model.element.*;
31import static javax.lang.model.element.ElementKind.*;
32import static javax.lang.model.element.NestingKind.*;
33import static javax.lang.model.element.ModuleElement.DirectiveKind.*;
34import static javax.lang.model.element.ModuleElement.*;
35import javax.lang.model.type.*;
36import javax.lang.model.util.*;
37
38import java.io.PrintWriter;
39import java.io.Writer;
40import java.util.*;
41import java.util.stream.Collectors;
42
43import com.sun.tools.javac.util.DefinedBy;
44import com.sun.tools.javac.util.DefinedBy.Api;
45import 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_9)
59public 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 SimpleElementVisitor9<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 visitExecutable(ExecutableElement e, Boolean p) {
115            ElementKind kind = e.getKind();
116
117            if (kind != STATIC_INIT &&
118                kind != INSTANCE_INIT) {
119                Element enclosing = e.getEnclosingElement();
120
121                // Don't print out the constructor of an anonymous class
122                if (kind == CONSTRUCTOR &&
123                    enclosing != null &&
124                    NestingKind.ANONYMOUS ==
125                    // Use an anonymous class to determine anonymity!
126                    (new SimpleElementVisitor7<NestingKind, Void>() {
127                        @Override @DefinedBy(Api.LANGUAGE_MODEL)
128                        public NestingKind visitType(TypeElement e, Void p) {
129                            return e.getNestingKind();
130                        }
131                    }).visit(enclosing))
132                    return this;
133
134                defaultAction(e, true);
135                printFormalTypeParameters(e, true);
136
137                switch(kind) {
138                    case CONSTRUCTOR:
139                    // Print out simple name of the class
140                    writer.print(e.getEnclosingElement().getSimpleName());
141                    break;
142
143                    case METHOD:
144                    writer.print(e.getReturnType().toString());
145                    writer.print(" ");
146                    writer.print(e.getSimpleName().toString());
147                    break;
148                }
149
150                writer.print("(");
151                printParameters(e);
152                writer.print(")");
153                AnnotationValue defaultValue = e.getDefaultValue();
154                if (defaultValue != null)
155                    writer.print(" default " + defaultValue);
156
157                printThrows(e);
158                writer.println(";");
159            }
160            return this;
161        }
162
163
164        @Override @DefinedBy(Api.LANGUAGE_MODEL)
165        public PrintingElementVisitor visitType(TypeElement e, Boolean p) {
166            ElementKind kind = e.getKind();
167            NestingKind nestingKind = e.getNestingKind();
168
169            if (NestingKind.ANONYMOUS == nestingKind) {
170                // Print out an anonymous class in the style of a
171                // class instance creation expression rather than a
172                // class declaration.
173                writer.print("new ");
174
175                // If the anonymous class implements an interface
176                // print that name, otherwise print the superclass.
177                List<? extends TypeMirror> interfaces = e.getInterfaces();
178                if (!interfaces.isEmpty())
179                    writer.print(interfaces.get(0));
180                else
181                    writer.print(e.getSuperclass());
182
183                writer.print("(");
184                // Anonymous classes that implement an interface can't
185                // have any constructor arguments.
186                if (interfaces.isEmpty()) {
187                    // Print out the parameter list from the sole
188                    // constructor.  For now, don't try to elide any
189                    // synthetic parameters by determining if the
190                    // anonymous class is in a static context, etc.
191                    List<? extends ExecutableElement> constructors =
192                        ElementFilter.constructorsIn(e.getEnclosedElements());
193
194                    if (!constructors.isEmpty())
195                        printParameters(constructors.get(0));
196                }
197                writer.print(")");
198            } else {
199                if (nestingKind == TOP_LEVEL) {
200                    PackageElement pkg = elementUtils.getPackageOf(e);
201                    if (!pkg.isUnnamed())
202                        writer.print("package " + pkg.getQualifiedName() + ";\n");
203                }
204
205                defaultAction(e, true);
206
207                switch(kind) {
208                case ANNOTATION_TYPE:
209                    writer.print("@interface");
210                    break;
211                default:
212                    writer.print(StringUtils.toLowerCase(kind.toString()));
213                }
214                writer.print(" ");
215                writer.print(e.getSimpleName());
216
217                printFormalTypeParameters(e, false);
218
219                // Print superclass information if informative
220                if (kind == CLASS) {
221                    TypeMirror supertype = e.getSuperclass();
222                    if (supertype.getKind() != TypeKind.NONE) {
223                        TypeElement e2 = (TypeElement)
224                            ((DeclaredType) supertype).asElement();
225                        if (e2.getSuperclass().getKind() != TypeKind.NONE)
226                            writer.print(" extends " + supertype);
227                    }
228                }
229
230                printInterfaces(e);
231            }
232            writer.println(" {");
233            indentation++;
234
235            if (kind == ENUM) {
236                List<Element> enclosedElements = new ArrayList<>(e.getEnclosedElements());
237                // Handle any enum constants specially before other entities.
238                List<Element> enumConstants = new ArrayList<>();
239                for(Element element : enclosedElements) {
240                    if (element.getKind() == ENUM_CONSTANT)
241                        enumConstants.add(element);
242                }
243                if (!enumConstants.isEmpty()) {
244                    int i;
245                    for(i = 0; i < enumConstants.size()-1; i++) {
246                        this.visit(enumConstants.get(i), true);
247                        writer.print(",");
248                    }
249                    this.visit(enumConstants.get(i), true);
250                    writer.println(";\n");
251
252                    enclosedElements.removeAll(enumConstants);
253                }
254
255                for(Element element : enclosedElements)
256                    this.visit(element);
257            } else {
258                for(Element element : e.getEnclosedElements())
259                    this.visit(element);
260            }
261
262            indentation--;
263            indent();
264            writer.println("}");
265            return this;
266        }
267
268        @Override @DefinedBy(Api.LANGUAGE_MODEL)
269        public PrintingElementVisitor visitVariable(VariableElement e, Boolean newLine) {
270            ElementKind kind = e.getKind();
271            defaultAction(e, newLine);
272
273            if (kind == ENUM_CONSTANT)
274                writer.print(e.getSimpleName());
275            else {
276                writer.print(e.asType().toString() + " " + e.getSimpleName() );
277                Object constantValue  = e.getConstantValue();
278                if (constantValue != null) {
279                    writer.print(" = ");
280                    writer.print(elementUtils.getConstantExpression(constantValue));
281                }
282                writer.println(";");
283            }
284            return this;
285        }
286
287        @Override @DefinedBy(Api.LANGUAGE_MODEL)
288        public PrintingElementVisitor visitTypeParameter(TypeParameterElement e, Boolean p) {
289            writer.print(e.getSimpleName());
290            return this;
291        }
292
293        // Should we do more here?
294        @Override @DefinedBy(Api.LANGUAGE_MODEL)
295        public PrintingElementVisitor visitPackage(PackageElement e, Boolean p) {
296            defaultAction(e, false);
297            if (!e.isUnnamed())
298                writer.println("package " + e.getQualifiedName() + ";");
299            else
300                writer.println("// Unnamed package");
301            return this;
302        }
303
304        @Override @DefinedBy(Api.LANGUAGE_MODEL)
305        public PrintingElementVisitor visitModule(ModuleElement e, Boolean p) {
306            defaultAction(e, false);
307
308            if (!e.isUnnamed()) {
309                // TODO: openness of the module not currently exposed
310                // by the language model API, but should be printed
311                // here once available.
312                writer.println("module " + e.getQualifiedName() + " {");
313                indentation++;
314                for (ModuleElement.Directive directive : e.getDirectives()) {
315                    printDirective(directive);
316                }
317                indentation--;
318                writer.println("}");
319            } else
320                writer.println("// Unnamed module"); // Should we do more here?
321            return this;
322        }
323
324        private void printDirective(ModuleElement.Directive directive) {
325            indent();
326            switch (directive.getKind()) {
327            case EXPORTS: // "exports package-name [to module-name-list]"
328                {
329                    ExportsDirective exportsDirective = (ExportsDirective) directive;
330                    writer.print("exports ");
331                    writer.print(exportsDirective.getPackage().getQualifiedName());
332                    printModuleList(exportsDirective.getTargetModules());
333                }
334                break;
335
336            case OPENS: // opens package-name [to module-name-list]
337                {
338                    OpensDirective opensDirective = (OpensDirective) directive;
339                    writer.print("opens ");
340                    writer.print(opensDirective.getPackage().getQualifiedName());
341                    printModuleList(opensDirective.getTargetModules());
342                }
343                break;
344
345            case PROVIDES: // provides service-name with implementation-name
346                {
347                    ProvidesDirective providesDirective = (ProvidesDirective) directive;
348                    writer.print("provides ");
349                    writer.print(providesDirective.getService().getQualifiedName());
350                    writer.print(" with ");
351                    printNameableList(providesDirective.getImplementations());
352                }
353                break;
354
355            case REQUIRES: // requires (static|transitive)* module-name
356                {
357                    RequiresDirective requiresDirective = (RequiresDirective) directive;
358                    writer.print("requires ");
359                    if (requiresDirective.isStatic())
360                        writer.print("static ");
361                    if (requiresDirective.isTransitive())
362                        writer.print("transitive ");
363                    writer.print(requiresDirective.getDependency().getQualifiedName());
364                }
365                break;
366
367            case USES: // uses service-name
368                {
369                    UsesDirective usesDirective = (UsesDirective) directive;
370                    writer.print("uses ");
371                    writer.print(usesDirective.getService().getQualifiedName());
372                }
373                break;
374
375            default:
376                throw new UnsupportedOperationException("unknown directive " + directive);
377            }
378            writer.println(";");
379        }
380
381        private void printModuleList(List<? extends ModuleElement> modules) {
382            if (modules != null) {
383                writer.print(" to ");
384                printNameableList(modules);
385            }
386        }
387
388        private void printNameableList(List<? extends QualifiedNameable> nameables) {
389                writer.print(nameables.stream().
390                             map(QualifiedNameable::getQualifiedName).
391                             collect(Collectors.joining(", ")));
392        }
393
394        public void flush() {
395            writer.flush();
396        }
397
398        private void printDocComment(Element e) {
399            String docComment = elementUtils.getDocComment(e);
400
401            if (docComment != null) {
402                // Break comment into lines
403                java.util.StringTokenizer st = new StringTokenizer(docComment,
404                                                                  "\n\r");
405                indent();
406                writer.println("/**");
407
408                while(st.hasMoreTokens()) {
409                    indent();
410                    writer.print(" *");
411                    writer.println(st.nextToken());
412                }
413
414                indent();
415                writer.println(" */");
416            }
417        }
418
419        private void printModifiers(Element e) {
420            ElementKind kind = e.getKind();
421            if (kind == PARAMETER) {
422                printAnnotationsInline(e);
423            } else {
424                printAnnotations(e);
425                indent();
426            }
427
428            if (kind == ENUM_CONSTANT)
429                return;
430
431            Set<Modifier> modifiers = new LinkedHashSet<>();
432            modifiers.addAll(e.getModifiers());
433
434            switch (kind) {
435            case ANNOTATION_TYPE:
436            case INTERFACE:
437                modifiers.remove(Modifier.ABSTRACT);
438                break;
439
440            case ENUM:
441                modifiers.remove(Modifier.FINAL);
442                modifiers.remove(Modifier.ABSTRACT);
443                break;
444
445            case METHOD:
446            case FIELD:
447                Element enclosingElement = e.getEnclosingElement();
448                if (enclosingElement != null &&
449                    enclosingElement.getKind().isInterface()) {
450                    modifiers.remove(Modifier.PUBLIC);
451                    modifiers.remove(Modifier.ABSTRACT); // only for methods
452                    modifiers.remove(Modifier.STATIC);   // only for fields
453                    modifiers.remove(Modifier.FINAL);    // only for fields
454                }
455                break;
456
457            }
458
459            for(Modifier m: modifiers) {
460                writer.print(m.toString() + " ");
461            }
462        }
463
464        private void printFormalTypeParameters(Parameterizable e,
465                                               boolean pad) {
466            List<? extends TypeParameterElement> typeParams = e.getTypeParameters();
467            if (typeParams.size() > 0) {
468                writer.print("<");
469
470                boolean first = true;
471                for(TypeParameterElement tpe: typeParams) {
472                    if (!first)
473                        writer.print(", ");
474                    printAnnotationsInline(tpe);
475                    writer.print(tpe.toString());
476                    first = false;
477                }
478
479                writer.print(">");
480                if (pad)
481                    writer.print(" ");
482            }
483        }
484
485        private void printAnnotationsInline(Element e) {
486            List<? extends AnnotationMirror> annots = e.getAnnotationMirrors();
487            for(AnnotationMirror annotationMirror : annots) {
488                writer.print(annotationMirror);
489                writer.print(" ");
490            }
491        }
492
493        private void printAnnotations(Element e) {
494            List<? extends AnnotationMirror> annots = e.getAnnotationMirrors();
495            for(AnnotationMirror annotationMirror : annots) {
496                indent();
497                writer.println(annotationMirror);
498            }
499        }
500
501        // TODO: Refactor
502        private void printParameters(ExecutableElement e) {
503            List<? extends VariableElement> parameters = e.getParameters();
504            int size = parameters.size();
505
506            switch (size) {
507            case 0:
508                break;
509
510            case 1:
511                for(VariableElement parameter: parameters) {
512                    printModifiers(parameter);
513
514                    if (e.isVarArgs() ) {
515                        TypeMirror tm = parameter.asType();
516                        if (tm.getKind() != TypeKind.ARRAY)
517                            throw new AssertionError("Var-args parameter is not an array type: " + tm);
518                        writer.print((ArrayType.class.cast(tm)).getComponentType() );
519                        writer.print("...");
520                    } else
521                        writer.print(parameter.asType());
522                    writer.print(" " + parameter.getSimpleName());
523                }
524                break;
525
526            default:
527                {
528                    int i = 1;
529                    for(VariableElement parameter: parameters) {
530                        if (i == 2)
531                            indentation++;
532
533                        if (i > 1)
534                            indent();
535
536                        printModifiers(parameter);
537
538                        if (i == size && e.isVarArgs() ) {
539                            TypeMirror tm = parameter.asType();
540                            if (tm.getKind() != TypeKind.ARRAY)
541                                throw new AssertionError("Var-args parameter is not an array type: " + tm);
542                                    writer.print((ArrayType.class.cast(tm)).getComponentType() );
543
544                            writer.print("...");
545                        } else
546                            writer.print(parameter.asType());
547                        writer.print(" " + parameter.getSimpleName());
548
549                        if (i < size)
550                            writer.println(",");
551
552                        i++;
553                    }
554
555                    if (parameters.size() >= 2)
556                        indentation--;
557                }
558                break;
559            }
560        }
561
562        private void printInterfaces(TypeElement e) {
563            ElementKind kind = e.getKind();
564
565            if(kind != ANNOTATION_TYPE) {
566                List<? extends TypeMirror> interfaces = e.getInterfaces();
567                if (interfaces.size() > 0) {
568                    writer.print((kind.isClass() ? " implements" : " extends"));
569
570                    boolean first = true;
571                    for(TypeMirror interf: interfaces) {
572                        if (!first)
573                            writer.print(",");
574                        writer.print(" ");
575                        writer.print(interf.toString());
576                        first = false;
577                    }
578                }
579            }
580        }
581
582        private void printThrows(ExecutableElement e) {
583            List<? extends TypeMirror> thrownTypes = e.getThrownTypes();
584            final int size = thrownTypes.size();
585            if (size != 0) {
586                writer.print(" throws");
587
588                int i = 1;
589                for(TypeMirror thrownType: thrownTypes) {
590                    if (i == 1)
591                        writer.print(" ");
592
593                    if (i == 2)
594                        indentation++;
595
596                    if (i >= 2)
597                        indent();
598
599                    writer.print(thrownType);
600
601                    if (i != size)
602                        writer.println(", ");
603
604                    i++;
605                }
606
607                if (size >= 2)
608                    indentation--;
609            }
610        }
611
612        private static final String [] spaces = {
613            "",
614            "  ",
615            "    ",
616            "      ",
617            "        ",
618            "          ",
619            "            ",
620            "              ",
621            "                ",
622            "                  ",
623            "                    "
624        };
625
626        private void indent() {
627            int indentation = this.indentation;
628            if (indentation < 0)
629                return;
630            final int maxIndex = spaces.length - 1;
631
632            while (indentation > maxIndex) {
633                writer.print(spaces[maxIndex]);
634                indentation -= maxIndex;
635            }
636            writer.print(spaces[indentation]);
637        }
638
639    }
640}
641