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