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                if (e.isOpen()) {
310                    writer.print("open ");
311                }
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            (new PrintDirective(writer)).visit(directive);
327            writer.println(";");
328        }
329
330        private static class PrintDirective implements ModuleElement.DirectiveVisitor<Void, Void> {
331            private final PrintWriter writer;
332
333            PrintDirective(PrintWriter writer) {
334                this.writer = writer;
335            }
336
337            @Override @DefinedBy(Api.LANGUAGE_MODEL)
338            public Void visitExports(ExportsDirective d, Void p) {
339                // "exports package-name [to module-name-list]"
340                writer.print("exports ");
341                writer.print(d.getPackage().getQualifiedName());
342                printModuleList(d.getTargetModules());
343                return null;
344            }
345
346            @Override @DefinedBy(Api.LANGUAGE_MODEL)
347            public Void visitOpens(OpensDirective d, Void p) {
348                // opens package-name [to module-name-list]
349                writer.print("opens ");
350                writer.print(d.getPackage().getQualifiedName());
351                printModuleList(d.getTargetModules());
352                return null;
353            }
354
355            @Override @DefinedBy(Api.LANGUAGE_MODEL)
356            public Void visitProvides(ProvidesDirective d, Void p) {
357                // provides service-name with implementation-name
358                writer.print("provides ");
359                writer.print(d.getService().getQualifiedName());
360                writer.print(" with ");
361                printNameableList(d.getImplementations());
362                return null;
363            }
364
365            @Override @DefinedBy(Api.LANGUAGE_MODEL)
366            public Void visitRequires(RequiresDirective d, Void p) {
367                // requires (static|transitive)* module-name
368                writer.print("requires ");
369                if (d.isStatic())
370                    writer.print("static ");
371                if (d.isTransitive())
372                    writer.print("transitive ");
373                writer.print(d.getDependency().getQualifiedName());
374                return null;
375            }
376
377            @Override @DefinedBy(Api.LANGUAGE_MODEL)
378            public Void visitUses(UsesDirective d, Void p) {
379                // uses service-name
380                writer.print("uses ");
381                writer.print(d.getService().getQualifiedName());
382                return null;
383            }
384
385            private void printModuleList(List<? extends ModuleElement> modules) {
386                if (modules != null) {
387                    writer.print(" to ");
388                    printNameableList(modules);
389                }
390            }
391
392            private void printNameableList(List<? extends QualifiedNameable> nameables) {
393                writer.print(nameables.stream().
394                             map(QualifiedNameable::getQualifiedName).
395                             collect(Collectors.joining(", ")));
396            }
397        }
398
399        public void flush() {
400            writer.flush();
401        }
402
403        private void printDocComment(Element e) {
404            String docComment = elementUtils.getDocComment(e);
405
406            if (docComment != null) {
407                // Break comment into lines
408                java.util.StringTokenizer st = new StringTokenizer(docComment,
409                                                                  "\n\r");
410                indent();
411                writer.println("/**");
412
413                while(st.hasMoreTokens()) {
414                    indent();
415                    writer.print(" *");
416                    writer.println(st.nextToken());
417                }
418
419                indent();
420                writer.println(" */");
421            }
422        }
423
424        private void printModifiers(Element e) {
425            ElementKind kind = e.getKind();
426            if (kind == PARAMETER) {
427                printAnnotationsInline(e);
428            } else {
429                printAnnotations(e);
430                indent();
431            }
432
433            if (kind == ENUM_CONSTANT)
434                return;
435
436            Set<Modifier> modifiers = new LinkedHashSet<>();
437            modifiers.addAll(e.getModifiers());
438
439            switch (kind) {
440            case ANNOTATION_TYPE:
441            case INTERFACE:
442                modifiers.remove(Modifier.ABSTRACT);
443                break;
444
445            case ENUM:
446                modifiers.remove(Modifier.FINAL);
447                modifiers.remove(Modifier.ABSTRACT);
448                break;
449
450            case METHOD:
451            case FIELD:
452                Element enclosingElement = e.getEnclosingElement();
453                if (enclosingElement != null &&
454                    enclosingElement.getKind().isInterface()) {
455                    modifiers.remove(Modifier.PUBLIC);
456                    modifiers.remove(Modifier.ABSTRACT); // only for methods
457                    modifiers.remove(Modifier.STATIC);   // only for fields
458                    modifiers.remove(Modifier.FINAL);    // only for fields
459                }
460                break;
461
462            }
463
464            for(Modifier m: modifiers) {
465                writer.print(m.toString() + " ");
466            }
467        }
468
469        private void printFormalTypeParameters(Parameterizable e,
470                                               boolean pad) {
471            List<? extends TypeParameterElement> typeParams = e.getTypeParameters();
472            if (typeParams.size() > 0) {
473                writer.print("<");
474
475                boolean first = true;
476                for(TypeParameterElement tpe: typeParams) {
477                    if (!first)
478                        writer.print(", ");
479                    printAnnotationsInline(tpe);
480                    writer.print(tpe.toString());
481                    first = false;
482                }
483
484                writer.print(">");
485                if (pad)
486                    writer.print(" ");
487            }
488        }
489
490        private void printAnnotationsInline(Element e) {
491            List<? extends AnnotationMirror> annots = e.getAnnotationMirrors();
492            for(AnnotationMirror annotationMirror : annots) {
493                writer.print(annotationMirror);
494                writer.print(" ");
495            }
496        }
497
498        private void printAnnotations(Element e) {
499            List<? extends AnnotationMirror> annots = e.getAnnotationMirrors();
500            for(AnnotationMirror annotationMirror : annots) {
501                indent();
502                writer.println(annotationMirror);
503            }
504        }
505
506        // TODO: Refactor
507        private void printParameters(ExecutableElement e) {
508            List<? extends VariableElement> parameters = e.getParameters();
509            int size = parameters.size();
510
511            switch (size) {
512            case 0:
513                break;
514
515            case 1:
516                for(VariableElement parameter: parameters) {
517                    printModifiers(parameter);
518
519                    if (e.isVarArgs() ) {
520                        TypeMirror tm = parameter.asType();
521                        if (tm.getKind() != TypeKind.ARRAY)
522                            throw new AssertionError("Var-args parameter is not an array type: " + tm);
523                        writer.print((ArrayType.class.cast(tm)).getComponentType() );
524                        writer.print("...");
525                    } else
526                        writer.print(parameter.asType());
527                    writer.print(" " + parameter.getSimpleName());
528                }
529                break;
530
531            default:
532                {
533                    int i = 1;
534                    for(VariableElement parameter: parameters) {
535                        if (i == 2)
536                            indentation++;
537
538                        if (i > 1)
539                            indent();
540
541                        printModifiers(parameter);
542
543                        if (i == size && e.isVarArgs() ) {
544                            TypeMirror tm = parameter.asType();
545                            if (tm.getKind() != TypeKind.ARRAY)
546                                throw new AssertionError("Var-args parameter is not an array type: " + tm);
547                                    writer.print((ArrayType.class.cast(tm)).getComponentType() );
548
549                            writer.print("...");
550                        } else
551                            writer.print(parameter.asType());
552                        writer.print(" " + parameter.getSimpleName());
553
554                        if (i < size)
555                            writer.println(",");
556
557                        i++;
558                    }
559
560                    if (parameters.size() >= 2)
561                        indentation--;
562                }
563                break;
564            }
565        }
566
567        private void printInterfaces(TypeElement e) {
568            ElementKind kind = e.getKind();
569
570            if(kind != ANNOTATION_TYPE) {
571                List<? extends TypeMirror> interfaces = e.getInterfaces();
572                if (interfaces.size() > 0) {
573                    writer.print((kind.isClass() ? " implements" : " extends"));
574
575                    boolean first = true;
576                    for(TypeMirror interf: interfaces) {
577                        if (!first)
578                            writer.print(",");
579                        writer.print(" ");
580                        writer.print(interf.toString());
581                        first = false;
582                    }
583                }
584            }
585        }
586
587        private void printThrows(ExecutableElement e) {
588            List<? extends TypeMirror> thrownTypes = e.getThrownTypes();
589            final int size = thrownTypes.size();
590            if (size != 0) {
591                writer.print(" throws");
592
593                int i = 1;
594                for(TypeMirror thrownType: thrownTypes) {
595                    if (i == 1)
596                        writer.print(" ");
597
598                    if (i == 2)
599                        indentation++;
600
601                    if (i >= 2)
602                        indent();
603
604                    writer.print(thrownType);
605
606                    if (i != size)
607                        writer.println(", ");
608
609                    i++;
610                }
611
612                if (size >= 2)
613                    indentation--;
614            }
615        }
616
617        private static final String [] spaces = {
618            "",
619            "  ",
620            "    ",
621            "      ",
622            "        ",
623            "          ",
624            "            ",
625            "              ",
626            "                ",
627            "                  ",
628            "                    "
629        };
630
631        private void indent() {
632            int indentation = this.indentation;
633            if (indentation < 0)
634                return;
635            final int maxIndex = spaces.length - 1;
636
637            while (indentation > maxIndex) {
638                writer.print(spaces[maxIndex]);
639                indentation -= maxIndex;
640            }
641            writer.print(spaces[indentation]);
642        }
643
644    }
645}
646