1/*
2 * Copyright (c) 2010, 2015, 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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @bug 6449781 6930508
27 * @summary Test that reported names of anonymous classes are non-null.
28 * @author  Joseph D. Darcy
29 * @library /tools/javac/lib
30 * @modules jdk.compiler
31 * @build   JavacTestingAbstractProcessor TestAnonSourceNames
32 * @compile -processor TestAnonSourceNames TestAnonClassNames.java
33 * @run main TestAnonClassNames
34 */
35
36/*
37 * This test operates in phases to test retrieving the qualified name
38 * of anonymous classes from type elements modeling the anonymous
39 * class.  The type elements are generated using both source files and
40 * class files as the basis of constructing the elements.
41 *
42 * Source files will be tested by the @compile line which runs
43 * TestAnonSourceNames as an annotation processor over this file.
44 *
45 * Class files are tested by the @run command on this type.  This
46 * class gets the names of classes with different nesting kinds,
47 * including anonymous classes, and then invokes the compiler with an
48 * annotation processor having the class files names as inputs.  The
49 * compiler is invoked via the javax.tools mechanism.
50 */
51
52import java.lang.annotation.*;
53import javax.lang.model.element.*;
54import javax.annotation.processing.*;
55import javax.lang.model.SourceVersion;
56import javax.lang.model.element.*;
57import javax.lang.model.util.*;
58import javax.tools.*;
59import java.util.*;
60
61import static java.lang.annotation.RetentionPolicy.*;
62import static javax.lang.model.element.NestingKind.*;
63import static javax.lang.model.util.ElementFilter.*;
64import static javax.tools.Diagnostic.Kind.*;
65import static javax.tools.StandardLocation.*;
66
67@Nesting(TOP_LEVEL)
68public class TestAnonClassNames {
69    @Nesting(MEMBER)
70    static class MemberClass1{}
71
72    @Nesting(MEMBER)
73    class MemberClass2{}
74
75    @Nesting(MEMBER)
76    class Win$$AtVegas { } // Class with funny name.
77
78    public static void main(String... argv) {
79        @Nesting(LOCAL)
80        class LocalClass{};
81
82        Object o =  new @Nesting(ANONYMOUS) Object() { // An anonymous annotated class
83                public String toString() {
84                    return "I have no name!";
85                }
86            };
87
88        Class<?>[] classes = {
89            MemberClass1.class,
90            MemberClass2.class,
91            LocalClass.class,
92            Win$$AtVegas.class,
93            o.getClass(),
94            TestAnonClassNames.class,
95        };
96
97        List<String> names = new ArrayList<String>();
98        for(Class<?> clazz : classes) {
99            String name = clazz.getName();
100            Nesting annotation = clazz.getAnnotation(Nesting.class);
101            NestingKind expected = annotation == null ?
102                NestingKind.ANONYMOUS : annotation.value();
103            System.out.format("%s is %s%n", name, expected);
104            testClassName(name);
105            names.add(name);
106        }
107
108        // test all names together
109        testClassNames(names);
110
111        if (errors > 0)
112            throw new RuntimeException(errors + " errors occurred");
113    }
114
115    /**
116     * Perform annotation processing on the class file name and verify
117     * the existence of different flavors of class names when the
118     * input classes are modeled as elements.
119     */
120    static void testClassName(String className) {
121        testClassNames(Arrays.asList(className));
122    }
123
124    /**
125     * Perform annotation processing on a list of class file names and verify
126     * the existence of different flavors of class names when the
127     * input classes are modeled as elements.
128     */
129    static void testClassNames(List<String> classNames) {
130        System.out.println("test: " + classNames);
131
132        List<String> options = new ArrayList<String>();
133        options.add("-proc:only");
134        options.add("-classpath");
135        options.add(System.getProperty("test.classes"));
136
137        JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
138        JavaCompiler.CompilationTask compileTask =
139            javaCompiler.getTask(null, // Output
140                                 null, // File manager
141                                 null, // Diagnostics
142                                 options,
143                                 classNames,
144                                 null); // Sources
145        List<Processor> processors = new ArrayList<Processor>();
146        processors.add(new ClassNameProber());
147        compileTask.setProcessors(processors);
148        Boolean goodResult = compileTask.call();
149        if (!goodResult) {
150            error("Errors found during compile.");
151        }
152    }
153
154    static int errors = 0;
155
156    static void error(String msg) {
157        System.out.println("Error: " + msg);
158        errors++;
159    }
160}
161
162@Target({ElementType.TYPE, ElementType.TYPE_USE})
163@Retention(RUNTIME)
164@interface Nesting {
165    NestingKind value();
166}
167
168/**
169 * Probe at the various kinds of names of a type element.
170 */
171class ClassNameProber extends JavacTestingAbstractProcessor {
172    public ClassNameProber(){super();}
173
174    private boolean classesFound=false;
175
176    public boolean process(Set<? extends TypeElement> annotations,
177                           RoundEnvironment roundEnv) {
178        if (!roundEnv.processingOver()) {
179            for(TypeElement typeElt : typesIn(roundEnv.getRootElements())) {
180                classesFound = true;
181
182                // Verify different names are non-null; an NPE will
183                // result in failed compile status being reported.
184                NestingKind nestingKind = typeElt.getNestingKind();
185                System.out.printf("\tSimple name: ''%s''\tQualified Name: ''%s''\tKind ''%s''\tNesting ''%s''%n",
186                                  typeElt.getSimpleName().toString(),
187                                  typeElt.getQualifiedName().toString(),
188                                  typeElt.getKind().toString(),
189                                  nestingKind.toString());
190
191                Nesting annotation = typeElt.getAnnotation(Nesting.class);
192                NestingKind expected = annotation == null ?
193                    NestingKind.ANONYMOUS : annotation.value();
194
195                if (expected != nestingKind) {
196                    throw new RuntimeException("Mismatch of expected and reported nesting kind.");
197                }
198            }
199
200        }
201
202        if (!classesFound) {
203            throw new RuntimeException("Error: no classes processed.");
204        }
205        return true;
206    }
207}
208