InnerClassesHierarchyTest.java revision 2942:08092deced3f
1/*
2 * Copyright (c) 2014, 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 8042251
27 * @summary Test that inner classes have in its inner classes attribute enclosing classes and its immediate members.
28 * @library /tools/lib /tools/javac/lib ../lib
29 * @modules jdk.jdeps/com.sun.tools.classfile
30 *          jdk.compiler/com.sun.tools.javac.api
31 *          jdk.compiler/com.sun.tools.javac.file
32 *          jdk.compiler/com.sun.tools.javac.main
33 * @build TestResult TestBase InMemoryFileManager ToolBox
34 * @run main InnerClassesHierarchyTest
35 */
36
37import com.sun.tools.classfile.*;
38import com.sun.tools.classfile.InnerClasses_attribute.Info;
39
40import java.io.File;
41import java.io.FilenameFilter;
42import java.io.IOException;
43import java.lang.annotation.Annotation;
44import java.util.*;
45import java.util.stream.Collectors;
46
47public class InnerClassesHierarchyTest extends TestResult {
48
49    private final Map<String, Set<String>> innerClasses;
50    private final String outerClassName;
51
52    public InnerClassesHierarchyTest() throws IOException, ConstantPoolException {
53        innerClasses = new HashMap<>();
54        outerClassName = InnerClassesHierarchyTest.class.getSimpleName();
55        File classDir = getClassDir();
56        FilenameFilter filter =
57                (dir, name) -> name.matches(outerClassName + ".*\\.class");
58        for (File file : Arrays.asList(classDir.listFiles(filter))) {
59            ClassFile classFile = readClassFile(file);
60            String className = classFile.getName();
61            for (ConstantPool.CPInfo info : classFile.constant_pool.entries()) {
62                if (info instanceof ConstantPool.CONSTANT_Class_info) {
63                    ConstantPool.CONSTANT_Class_info classInfo =
64                            (ConstantPool.CONSTANT_Class_info) info;
65                    String cpClassName = classInfo.getBaseName();
66                    if (isInnerClass(cpClassName)) {
67                        get(className).add(cpClassName);
68                    }
69                }
70            }
71        }
72    }
73
74    private boolean isInnerClass(String cpClassName) {
75        return cpClassName.contains("$");
76    }
77
78    private Set<String> get(String className) {
79        if (!innerClasses.containsKey(className)) {
80            innerClasses.put(className, new HashSet<>());
81        }
82        return innerClasses.get(className);
83    }
84
85    public static void main(String[] args) throws IOException, ConstantPoolException, TestFailedException {
86        new InnerClassesHierarchyTest().test();
87    }
88
89    private void test() throws TestFailedException {
90        addTestCase("Source file is InnerClassesHierarchyTest.java");
91        try {
92            Queue<String> queue = new LinkedList<>();
93            Set<String> visitedClasses = new HashSet<>();
94            queue.add(outerClassName);
95            while (!queue.isEmpty()) {
96                String currentClassName = queue.poll();
97                if (!currentClassName.startsWith(outerClassName)) {
98                    continue;
99                }
100                ClassFile cf = readClassFile(currentClassName);
101                InnerClasses_attribute attr = (InnerClasses_attribute)
102                        cf.getAttribute(Attribute.InnerClasses);
103                checkNotNull(attr, "Class should not contain "
104                        + "inner classes attribute : " + currentClassName);
105                checkTrue(innerClasses.containsKey(currentClassName),
106                        "map contains class name : " + currentClassName);
107                Set<String> setClasses = innerClasses.get(currentClassName);
108                if (setClasses == null) {
109                    continue;
110                }
111                checkEquals(attr.number_of_classes,
112                        setClasses.size(),
113                        "Check number of inner classes : " + setClasses);
114                for (Info info : attr.classes) {
115                    String innerClassName = info
116                            .getInnerClassInfo(cf.constant_pool).getBaseName();
117                    checkTrue(setClasses.contains(innerClassName),
118                            currentClassName + " contains inner class : "
119                                    + innerClassName);
120                    if (visitedClasses.add(innerClassName)) {
121                        queue.add(innerClassName);
122                    }
123                }
124            }
125            Set<String> allClasses = innerClasses.entrySet().stream()
126                    .flatMap(entry -> entry.getValue().stream())
127                    .collect(Collectors.toSet());
128
129            Set<String> a_b = removeAll(visitedClasses, allClasses);
130            Set<String> b_a = removeAll(allClasses, visitedClasses);
131            checkEquals(visitedClasses, allClasses,
132                    "All classes are found\n"
133                            + "visited - all classes : " + a_b
134                            + "\nall classes - visited : " + b_a);
135        } catch (Exception e) {
136            addFailure(e);
137        } finally {
138            checkStatus();
139        }
140    }
141
142    private Set<String> removeAll(Set<String> set1, Set<String> set2) {
143        Set<String> set = new HashSet<>(set1);
144        set.removeAll(set2);
145        return set;
146    }
147
148    public static class A1 {
149
150        public class B1 {
151        }
152
153        public enum B2 {
154        }
155
156        public interface B3 {
157        }
158
159        public @interface B4 {
160        }
161
162        public void f() {
163            new B1() {
164            };
165            new B3() {
166            };
167            new B4() {
168                @Override
169                public Class<? extends Annotation> annotationType() {
170                    return null;
171                }
172            };
173            class B5 {
174            }
175        }
176
177        Runnable r = () -> {
178            new B1() {
179            };
180            new B3() {
181            };
182            new B4() {
183                @Override
184                public Class<? extends Annotation> annotationType() {
185                    return null;
186                }
187            };
188            class B5 {
189            }
190        };
191    }
192
193    public enum A2 {;
194
195        public class B1 {
196        }
197
198        public enum B2 {
199        }
200
201        public interface B3 {
202        }
203
204        public @interface B4 {
205        }
206
207        public void a2() {
208            new B1() {
209            };
210            new B3() {
211            };
212            new B4() {
213                @Override
214                public Class<? extends Annotation> annotationType() {
215                    return null;
216                }
217            };
218            class B5 {
219            }
220        }
221
222        Runnable r = () -> {
223            new B1() {
224            };
225            new B3() {
226            };
227            new B4() {
228                @Override
229                public Class<? extends Annotation> annotationType() {
230                    return null;
231                }
232            };
233            class B5 {
234            }
235        };
236    }
237
238    public interface A3 {
239
240        public class B1 {
241        }
242
243        public enum B2 {
244        }
245
246        public interface B3 {
247        }
248
249        public @interface B4 {
250        }
251
252        default void a1() {
253            new B1() {
254            };
255            new B3() {
256            };
257            new B4() {
258                @Override
259                public Class<? extends Annotation> annotationType() {
260                    return null;
261                }
262            };
263            class B5 {
264            }
265        }
266
267        static void a2() {
268            new B1() {
269            };
270            new B3() {
271            };
272            new B4() {
273                @Override
274                public Class<? extends Annotation> annotationType() {
275                    return null;
276                }
277            };
278            class B5 {
279            }
280        }
281    }
282
283    public @interface A4 {
284
285        public class B1 {
286        }
287
288        public enum B2 {
289        }
290
291        public interface B3 {
292        }
293
294        public @interface B4 {
295        }
296    }
297
298    {
299        new A1() {
300            class B1 {
301            }
302
303            public void a2() {
304                new B1() {
305                };
306                class B5 {
307                }
308            }
309        };
310        new A3() {
311            class B1 {
312            }
313
314            public void a3() {
315                new B1() {
316                };
317                class B5 {
318                }
319            }
320        };
321        new A4() {
322            @Override
323            public Class<? extends Annotation> annotationType() {
324                return null;
325            }
326
327            class B1 {
328            }
329
330            public void a4() {
331                new B1() {
332                };
333                class B5 {
334                }
335            }
336        };
337        Runnable r = () -> {
338            new A1() {
339            };
340            new A3() {
341            };
342            new A4() {
343                @Override
344                public Class<? extends Annotation> annotationType() {
345                    return null;
346                }
347            };
348            class B5 {
349            }
350        };
351    }
352
353    static {
354        new A1() {
355            class B1 {
356            }
357
358            public void a2() {
359                new B1() {
360                };
361                class B5 {
362                }
363            }
364        };
365        new A3() {
366            class B1 {
367            }
368
369            public void a3() {
370                new B1() {
371                };
372                class B5 {
373                }
374            }
375        };
376        new A4() {
377            @Override
378            public Class<? extends Annotation> annotationType() {
379                return null;
380            }
381
382            class B1 {
383            }
384
385            public void a4() {
386                new B1() {
387                };
388                class B5 {
389                }
390            }
391        };
392        Runnable r = () -> {
393            new A1() {
394            };
395            new A3() {
396            };
397            new A4() {
398                @Override
399                public Class<? extends Annotation> annotationType() {
400                    return null;
401                }
402            };
403            class B5 {
404            }
405        };
406    }
407
408    public void a5() {
409        class A5 {
410
411            class B1 {
412            }
413
414            public void a5() {
415                new B1() {
416                };
417
418                class B5 {
419                }
420            }
421        }
422        Runnable r = () -> {
423            new A1() {
424            };
425            new A3() {
426            };
427            new A4() {
428                @Override
429                public Class<? extends Annotation> annotationType() {
430                    return null;
431                }
432            };
433            class B5 {
434            }
435        };
436    }
437}
438