001    /*
002     * Copyright 2005-2008 Sun Microsystems, Inc.  All Rights Reserved.
003     * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004     *
005     * This code is free software; you can redistribute it and/or modify it
006     * under the terms of the GNU General Public License version 2 only, as
007     * published by the Free Software Foundation.  Sun designates this
008     * particular file as subject to the "Classpath" exception as provided
009     * by Sun in the LICENSE file that accompanied this code.
010     *
011     * This code is distributed in the hope that it will be useful, but WITHOUT
012     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013     * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014     * version 2 for more details (a copy is included in the LICENSE file that
015     * accompanied this code).
016     *
017     * You should have received a copy of the GNU General Public License version
018     * 2 along with this work; if not, write to the Free Software Foundation,
019     * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020     *
021     * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022     * CA 95054 USA or visit www.sun.com if you need additional information or
023     * have any questions.
024     */
025    
026    package com.sun.tools.javac.processing;
027    
028    import javax.annotation.processing.*;
029    import javax.lang.model.*;
030    import javax.lang.model.element.*;
031    import static javax.lang.model.element.ElementKind.*;
032    import static javax.lang.model.element.NestingKind.*;
033    import javax.lang.model.type.*;
034    import javax.lang.model.util.*;
035    
036    import java.io.PrintWriter;
037    import java.io.Writer;
038    import java.util.*;
039    
040    /**
041     * A processor which prints out elements.  Used to implement the
042     * -Xprint option; the included visitor class is used to implement
043     * Elements.printElements.
044     *
045     * <p><b>This is NOT part of any API supported by Sun Microsystems.
046     * If you write code that depends on this, you do so at your own risk.
047     * This code and its internal interfaces are subject to change or
048     * deletion without notice.</b>
049     */
050    @SupportedAnnotationTypes("*")
051    @SupportedSourceVersion(SourceVersion.RELEASE_6)
052    public class PrintingProcessor extends AbstractProcessor {
053        PrintWriter writer;
054    
055        public PrintingProcessor() {
056            super();
057            writer = new PrintWriter(System.out);
058        }
059    
060        public void setWriter(Writer w) {
061            writer = new PrintWriter(w);
062        }
063    
064        @Override
065        public boolean process(Set<? extends TypeElement> tes,
066                               RoundEnvironment renv) {
067    
068            for(Element element : renv.getRootElements()) {
069                print(element);
070            }
071    
072            // Just print the elements, nothing more to do.
073            return true;
074        }
075    
076        void print(Element element) {
077            new PrintingElementVisitor(writer, processingEnv.getElementUtils()).
078                visit(element).flush();
079        }
080    
081        /**
082         * Used for the -Xprint option and called by Elements.printElements
083         */
084        public static class PrintingElementVisitor
085            extends SimpleElementVisitor6<PrintingElementVisitor, Boolean> {
086            int indentation; // Indentation level;
087            final PrintWriter writer;
088            final Elements elementUtils;
089    
090            public PrintingElementVisitor(Writer w, Elements elementUtils) {
091                super();
092                this.writer = new PrintWriter(w);
093                this.elementUtils = elementUtils;
094                indentation = 0;
095            }
096    
097            @Override
098            protected PrintingElementVisitor defaultAction(Element e, Boolean newLine) {
099                if (newLine != null && newLine)
100                    writer.println();
101                printDocComment(e);
102                printModifiers(e);
103                return this;
104            }
105    
106            @Override
107            public PrintingElementVisitor visitExecutable(ExecutableElement e, Boolean p) {
108                ElementKind kind = e.getKind();
109    
110                if (kind != STATIC_INIT &&
111                    kind != INSTANCE_INIT) {
112                    Element enclosing = e.getEnclosingElement();
113    
114                    // Don't print out the constructor of an anonymous class
115                    if (kind == CONSTRUCTOR &&
116                        enclosing != null &&
117                        NestingKind.ANONYMOUS ==
118                        // Use an anonymous class to determine anonymity!
119                        (new SimpleElementVisitor6<NestingKind, Void>() {
120                            @Override
121                            public NestingKind visitType(TypeElement e, Void p) {
122                                return e.getNestingKind();
123                            }
124                        }).visit(enclosing))
125                        return this;
126    
127                    defaultAction(e, true);
128                    printFormalTypeParameters(e);
129    
130                    switch(kind) {
131                        case CONSTRUCTOR:
132                        // Print out simple name of the class
133                        writer.print(e.getEnclosingElement().getSimpleName());
134                        break;
135    
136                        case METHOD:
137                        writer.print(e.getReturnType().toString());
138                        writer.print(" ");
139                        writer.print(e.getSimpleName().toString());
140                        break;
141                    }
142    
143                    writer.print("(");
144                    printParameters(e);
145                    writer.print(")");
146                    AnnotationValue defaultValue = e.getDefaultValue();
147                    if (defaultValue != null)
148                        writer.print(" default " + defaultValue);
149    
150                    printThrows(e);
151                    writer.println(";");
152                }
153                return this;
154            }
155    
156    
157            @Override
158            public PrintingElementVisitor visitType(TypeElement e, Boolean p) {
159                ElementKind kind = e.getKind();
160                NestingKind nestingKind = e.getNestingKind();
161    
162                if (NestingKind.ANONYMOUS == nestingKind) {
163                    // Print out an anonymous class in the style of a
164                    // class instance creation expression rather than a
165                    // class declaration.
166                    writer.print("new ");
167    
168                    // If the anonymous class implements an interface
169                    // print that name, otherwise print the superclass.
170                    List<? extends TypeMirror> interfaces = e.getInterfaces();
171                    if (!interfaces.isEmpty())
172                        writer.print(interfaces.get(0));
173                    else
174                        writer.print(e.getSuperclass());
175    
176                    writer.print("(");
177                    // Anonymous classes that implement an interface can't
178                    // have any constructor arguments.
179                    if (interfaces.isEmpty()) {
180                        // Print out the parameter list from the sole
181                        // constructor.  For now, don't try to elide any
182                        // synthetic parameters by determining if the
183                        // anonymous class is in a static context, etc.
184                        List<? extends ExecutableElement> constructors =
185                            ElementFilter.constructorsIn(e.getEnclosedElements());
186    
187                        if (!constructors.isEmpty())
188                            printParameters(constructors.get(0));
189                    }
190                    writer.print(")");
191                } else {
192                    if (nestingKind == TOP_LEVEL) {
193                        PackageElement pkg = elementUtils.getPackageOf(e);
194                        if (!pkg.isUnnamed())
195                            writer.print("package " + pkg.getQualifiedName() + ";\n");
196                    }
197    
198                    defaultAction(e, true);
199    
200                    switch(kind) {
201                    case ANNOTATION_TYPE:
202                        writer.print("@interface");
203                        break;
204                    default:
205                        writer.print(kind.toString().toLowerCase());
206                    }
207                    writer.print(" ");
208                    writer.print(e.getSimpleName());
209    
210                    printFormalTypeParameters(e);
211    
212                    // Print superclass information if informative
213                    if (kind == CLASS) {
214                        TypeMirror supertype = e.getSuperclass();
215                        if (supertype.getKind() != TypeKind.NONE) {
216                            TypeElement e2 = (TypeElement)
217                                ((DeclaredType) supertype).asElement();
218                            if (e2.getSuperclass().getKind() != TypeKind.NONE)
219                                writer.print(" extends " + supertype);
220                        }
221                    }
222    
223                    printInterfaces(e);
224                }
225                writer.println(" {");
226                indentation++;
227    
228                if (kind == ENUM) {
229                    List<Element> enclosedElements =
230                        new ArrayList<Element>(e.getEnclosedElements());
231                    List<Element> enumConstants = new ArrayList<Element>();
232                    for(Element element : enclosedElements) {
233                        if (element.getKind() == ENUM_CONSTANT)
234                            enumConstants.add(element);
235                    }
236    
237                    int i;
238                    for(i = 0; i < enumConstants.size()-1; i++) {
239                        this.visit(enumConstants.get(i), true);
240                        writer.print(",");
241                    }
242                    if (i >= 0 ) {
243                        this.visit(enumConstants.get(i), true);
244                        writer.print(";");
245                    }
246    
247                    enclosedElements.removeAll(enumConstants);
248                    for(Element element : enclosedElements)
249                        this.visit(element);
250                } else {
251                    for(Element element : e.getEnclosedElements())
252                        this.visit(element);
253                }
254    
255                indentation--;
256                indent();
257                writer.println("}");
258                return this;
259            }
260    
261            @Override
262            public PrintingElementVisitor visitVariable(VariableElement e, Boolean newLine) {
263                ElementKind kind = e.getKind();
264                defaultAction(e, newLine);
265    
266                if (kind == ENUM_CONSTANT)
267                    writer.print(e.getSimpleName());
268                else {
269                    writer.print(e.asType().toString() + " " + e.getSimpleName() );
270                    Object constantValue  = e.getConstantValue();
271                    if (constantValue != null) {
272                        writer.print(" = ");
273                        writer.print(elementUtils.getConstantExpression(constantValue));
274                    }
275                    writer.println(";");
276                }
277                return this;
278            }
279    
280            @Override
281            public PrintingElementVisitor visitTypeParameter(TypeParameterElement e, Boolean p) {
282                writer.print(e.getSimpleName());
283                return this;
284            }
285    
286            // Should we do more here?
287            @Override
288            public PrintingElementVisitor visitPackage(PackageElement e, Boolean p) {
289                defaultAction(e, false);
290                if (!e.isUnnamed())
291                    writer.println("package " + e.getQualifiedName() + ";");
292                else
293                    writer.println("// Unnamed package");
294                return this;
295            }
296    
297            public void flush() {
298                writer.flush();
299            }
300    
301            private void printDocComment(Element e) {
302                String docComment = elementUtils.getDocComment(e);
303    
304                if (docComment != null) {
305                    // Break comment into lines
306                    java.util.StringTokenizer st = new StringTokenizer(docComment,
307                                                                      "\n\r");
308                    indent();
309                    writer.println("/**");
310    
311                    while(st.hasMoreTokens()) {
312                        indent();
313                        writer.print(" *");
314                        writer.println(st.nextToken());
315                    }
316    
317                    indent();
318                    writer.println(" */");
319                }
320            }
321    
322            private void printModifiers(Element e) {
323                ElementKind kind = e.getKind();
324                if (kind == PARAMETER) {
325                    printAnnotationsInline(e);
326                } else {
327                    printAnnotations(e);
328                    indent();
329                }
330    
331                if (kind == ENUM_CONSTANT)
332                    return;
333    
334                Set<Modifier> modifiers = new LinkedHashSet<Modifier>();
335                modifiers.addAll(e.getModifiers());
336    
337                switch (kind) {
338                case ANNOTATION_TYPE:
339                case INTERFACE:
340                    modifiers.remove(Modifier.ABSTRACT);
341                    break;
342    
343                case ENUM:
344                    modifiers.remove(Modifier.FINAL);
345                    modifiers.remove(Modifier.ABSTRACT);
346                    break;
347    
348                case METHOD:
349                case FIELD:
350                    Element enclosingElement = e.getEnclosingElement();
351                    if (enclosingElement != null &&
352                        enclosingElement.getKind().isInterface()) {
353                        modifiers.remove(Modifier.PUBLIC);
354                        modifiers.remove(Modifier.ABSTRACT); // only for methods
355                        modifiers.remove(Modifier.STATIC);   // only for fields
356                        modifiers.remove(Modifier.FINAL);    // only for fields
357                    }
358                    break;
359    
360                }
361    
362                for(Modifier m: modifiers) {
363                    writer.print(m.toString() + " ");
364                }
365            }
366    
367            private void printFormalTypeParameters(ExecutableElement executable) {
368                printFormalTypeParameters(executable.getTypeParameters(), true);
369            }
370    
371            private void printFormalTypeParameters(TypeElement type) {
372                printFormalTypeParameters(type.getTypeParameters(), false);
373            }
374    
375            private void printFormalTypeParameters(List<? extends TypeParameterElement> typeParams,
376                                                   boolean pad) {
377                if (typeParams.size() > 0) {
378                    writer.print("<");
379    
380                    boolean first = true;
381                    for(TypeParameterElement tpe: typeParams) {
382                        if (!first)
383                            writer.print(", ");
384                        writer.print(tpe.toString());
385                        first = false;
386                    }
387    
388                    writer.print(">");
389                    if (pad)
390                        writer.print(" ");
391                }
392            }
393    
394            private void printAnnotationsInline(Element e) {
395                List<? extends AnnotationMirror> annots = e.getAnnotationMirrors();
396                for(AnnotationMirror annotationMirror : annots) {
397                    writer.print(annotationMirror);
398                    writer.print(" ");
399                }
400            }
401    
402            private void printAnnotations(Element e) {
403                List<? extends AnnotationMirror> annots = e.getAnnotationMirrors();
404                for(AnnotationMirror annotationMirror : annots) {
405                    indent();
406                    writer.println(annotationMirror);
407                }
408            }
409    
410            // TODO: Refactor
411            private void printParameters(ExecutableElement e) {
412                List<? extends VariableElement> parameters = e.getParameters();
413                int size = parameters.size();
414    
415                switch (size) {
416                case 0:
417                    break;
418    
419                case 1:
420                    for(VariableElement parameter: parameters) {
421                        printModifiers(parameter);
422    
423                        if (e.isVarArgs() ) {
424                            TypeMirror tm = parameter.asType();
425                            if (tm.getKind() != TypeKind.ARRAY)
426                                throw new AssertionError("Var-args parameter is not an array type: " + tm);
427                            writer.print((ArrayType.class.cast(tm)).getComponentType() );
428                            writer.print("...");
429                        } else
430                            writer.print(parameter.asType());
431                        writer.print(" " + parameter.getSimpleName());
432                    }
433                    break;
434    
435                default:
436                    {
437                        int i = 1;
438                        for(VariableElement parameter: parameters) {
439                            if (i == 2)
440                                indentation++;
441    
442                            if (i > 1)
443                                indent();
444    
445                            printModifiers(parameter);
446    
447                            if (i == size && e.isVarArgs() ) {
448                                TypeMirror tm = parameter.asType();
449                                if (tm.getKind() != TypeKind.ARRAY)
450                                    throw new AssertionError("Var-args parameter is not an array type: " + tm);
451                                        writer.print((ArrayType.class.cast(tm)).getComponentType() );
452    
453                                writer.print("...");
454                            } else
455                                writer.print(parameter.asType());
456                            writer.print(" " + parameter.getSimpleName());
457    
458                            if (i < size)
459                                writer.println(",");
460    
461                            i++;
462                        }
463    
464                        if (parameters.size() >= 2)
465                            indentation--;
466                    }
467                    break;
468                }
469            }
470    
471            private void printInterfaces(TypeElement e) {
472                ElementKind kind = e.getKind();
473    
474                if(kind != ANNOTATION_TYPE) {
475                    List<? extends TypeMirror> interfaces = e.getInterfaces();
476                    if (interfaces.size() > 0) {
477                        writer.print((kind.isClass() ? " implements" : " extends"));
478    
479                        boolean first = true;
480                        for(TypeMirror interf: interfaces) {
481                            if (!first)
482                                writer.print(",");
483                            writer.print(" ");
484                            writer.print(interf.toString());
485                            first = false;
486                        }
487                    }
488                }
489            }
490    
491            private void printThrows(ExecutableElement e) {
492                List<? extends TypeMirror> thrownTypes = e.getThrownTypes();
493                final int size = thrownTypes.size();
494                if (size != 0) {
495                    writer.print(" throws");
496    
497                    int i = 1;
498                    for(TypeMirror thrownType: thrownTypes) {
499                        if (i == 1)
500                            writer.print(" ");
501    
502                        if (i == 2)
503                            indentation++;
504    
505                        if (i >= 2)
506                            indent();
507    
508                        writer.print(thrownType);
509    
510                        if (i != size)
511                            writer.println(", ");
512    
513                        i++;
514                    }
515    
516                    if (size >= 2)
517                        indentation--;
518                }
519            }
520    
521            private static final String [] spaces = {
522                "",
523                "  ",
524                "    ",
525                "      ",
526                "        ",
527                "          ",
528                "            ",
529                "              ",
530                "                ",
531                "                  ",
532                "                    "
533            };
534    
535            private void indent() {
536                int indentation = this.indentation;
537                if (indentation < 0)
538                    return;
539                final int maxIndex = spaces.length - 1;
540    
541                while (indentation > maxIndex) {
542                    writer.print(spaces[maxIndex]);
543                    indentation -= maxIndex;
544                }
545                writer.print(spaces[indentation]);
546            }
547    
548        }
549    }