1/*
2 * Copyright (c) 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
24import com.sun.tools.classfile.*;
25
26import java.io.IOException;
27import java.lang.annotation.RetentionPolicy;
28import java.util.*;
29import java.util.function.Supplier;
30
31import javax.tools.JavaFileObject;
32
33public abstract class RuntimeAnnotationsTestBase extends AnnotationsTestBase {
34
35    @Override
36    public void test(TestCase testCase, Map<String, ? extends JavaFileObject> classes)
37            throws IOException, ConstantPoolException, Descriptor.InvalidDescriptor {
38        for (Map.Entry<String, ? extends JavaFileObject> entry : classes.entrySet()) {
39            String className = entry.getKey();
40            TestCase.TestClassInfo clazz = testCase.getTestClassInfo(className);
41            echo("Testing class : " + className);
42            ClassFile classFile = readClassFile(entry.getValue());
43
44            testAttributes(clazz, classFile, () -> classFile.attributes);
45
46            testMethods(clazz, classFile);
47
48            testFields(clazz, classFile);
49        }
50    }
51
52    private void testMethods(TestCase.TestClassInfo clazz, ClassFile classFile)
53            throws ConstantPoolException, Descriptor.InvalidDescriptor {
54        String className = clazz.getName();
55        Set<String> foundMethods = new HashSet<>();
56        for (Method method : classFile.methods) {
57            String methodName = method.getName(classFile.constant_pool) +
58                    method.descriptor.getParameterTypes(classFile.constant_pool);
59            if (methodName.startsWith("<init>")) {
60                String constructorName = className.replaceAll(".*\\$", "");
61                methodName = methodName.replace("<init>", constructorName);
62            }
63            echo("Testing method : " + methodName);
64
65            TestCase.TestMethodInfo testMethod = clazz.getTestMethodInfo(methodName);
66            foundMethods.add(methodName);
67            if (testMethod == null) {
68                continue;
69            }
70            testAttributes(testMethod, classFile, () -> method.attributes);
71        }
72        checkContains(foundMethods, clazz.methods.keySet(), "Methods in class : " + className);
73    }
74
75    private void testFields(TestCase.TestClassInfo clazz, ClassFile classFile)
76            throws ConstantPoolException {
77        Set<String> foundFields = new HashSet<>();
78        for (Field field : classFile.fields) {
79            String fieldName = field.getName(classFile.constant_pool);
80            echo("Testing field : " + fieldName);
81
82            TestCase.TestFieldInfo testField = clazz.getTestFieldInfo(fieldName);
83            foundFields.add(fieldName);
84            if (testField == null) {
85                continue;
86            }
87            testAttributes(testField, classFile, () -> field.attributes);
88        }
89        checkContains(foundFields, clazz.fields.keySet(), "Fields in class : " + clazz.getName());
90    }
91
92    private void testAttributes(
93            TestCase.TestMemberInfo member,
94            ClassFile classFile,
95            Supplier<Attributes> attributes)
96            throws ConstantPoolException {
97        Map<String, Annotation> actualInvisible = collectAnnotations(
98                classFile,
99                member,
100                attributes.get(),
101                Attribute.RuntimeInvisibleAnnotations);
102        Map<String, Annotation> actualVisible = collectAnnotations(
103                classFile,
104                member,
105                attributes.get(),
106                Attribute.RuntimeVisibleAnnotations);
107
108        checkEquals(actualInvisible.keySet(),
109                member.getRuntimeInvisibleAnnotations(), "RuntimeInvisibleAnnotations");
110        checkEquals(actualVisible.keySet(),
111                member.getRuntimeVisibleAnnotations(), "RuntimeVisibleAnnotations");
112
113        for (TestAnnotationInfo expectedAnnotation : member.annotations.values()) {
114            RetentionPolicy policy = getRetentionPolicy(expectedAnnotation.annotationName);
115            if (policy == RetentionPolicy.SOURCE) {
116                continue;
117            }
118            printf("Testing: isVisible: %s %s%n", policy.toString(), expectedAnnotation.annotationName);
119            Annotation actualAnnotation =
120                    (policy == RetentionPolicy.RUNTIME ? actualVisible : actualInvisible)
121                            .get(expectedAnnotation.annotationName);
122            if (checkNotNull(actualAnnotation, "Annotation is found : "
123                    + expectedAnnotation.annotationName)) {
124                expectedAnnotation.testAnnotation(this, classFile, actualAnnotation);
125            }
126        }
127    }
128
129    private Map<String, Annotation> collectAnnotations(
130            ClassFile classFile,
131            TestCase.TestMemberInfo member,
132            Attributes attributes,
133            String attribute) throws ConstantPoolException {
134
135        RuntimeAnnotations_attribute attr = (RuntimeAnnotations_attribute) attributes.get(attribute);
136        Map<String, Annotation> actualAnnotations = new HashMap<>();
137        RetentionPolicy policy = getRetentionPolicy(attribute);
138        if (member.isAnnotated(policy)) {
139            if (!checkNotNull(attr, String.format("%s should be not null value", attribute))) {
140                // test case failed, stop checking
141                return actualAnnotations;
142            }
143            for (Annotation ann : attr.annotations) {
144                String name = classFile.constant_pool.getUTF8Value(ann.type_index);
145                actualAnnotations.put(name.substring(1, name.length() - 1), ann);
146            }
147            checkEquals(countNumberOfAttributes(attributes.attrs,
148                    getRetentionPolicy(attribute) == RetentionPolicy.RUNTIME
149                            ? RuntimeVisibleAnnotations_attribute.class
150                            : RuntimeInvisibleAnnotations_attribute.class),
151                    1l,
152                    String.format("Number of %s", attribute));
153        } else {
154            checkNull(attr, String.format("%s should be null", attribute));
155        }
156        return actualAnnotations;
157    }
158}
159