1/*
2 * Copyright (c) 2014, 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.Annotation;
25import com.sun.tools.classfile.AnnotationDefault_attribute;
26import com.sun.tools.classfile.ClassFile;
27import com.sun.tools.classfile.ConstantPool;
28
29import java.util.Arrays;
30import java.util.HashMap;
31import java.util.Map;
32
33public class AnnotationDefaultVerifier {
34
35    private final Map<Integer, TestElementValue> verifiers;
36
37    public AnnotationDefaultVerifier() {
38        this.verifiers = new HashMap<>();
39        verifiers.put((int) 'B', new TestIntegerElementValue());
40        verifiers.put((int) 'C', new TestIntegerElementValue());
41        verifiers.put((int) 'D', new TestDoubleElementValue());
42        verifiers.put((int) 'F', new TestFloatElementValue());
43        verifiers.put((int) 'I', new TestIntegerElementValue());
44        verifiers.put((int) 'J', new TestLongElementValue());
45        verifiers.put((int) 'S', new TestIntegerElementValue());
46        verifiers.put((int) 'Z', new TestIntegerElementValue());
47        verifiers.put((int) 's', new TestStringElementValue());
48        verifiers.put((int) 'e', new TestEnumElementValue());
49        verifiers.put((int) 'c', new TestClassElementValue());
50        verifiers.put((int) '[', new TestArrayElementValue());
51        verifiers.put((int) '@', new TestAnnotationElementValue());
52    }
53
54    public void testLength(int tag, TestResult testResult, AnnotationDefault_attribute attr) {
55        verifiers.get(tag).testLength(testResult, attr);
56    }
57
58    public void testElementValue(int tag, TestResult testResult, ClassFile classFile,
59                                 Annotation.element_value element_value, String[] values)
60            throws ConstantPool.UnexpectedEntry, ConstantPool.InvalidIndex {
61        get(tag).testElementValue(testResult, classFile, element_value, values);
62    }
63
64    private TestElementValue get(int tag) {
65        TestElementValue ev = verifiers.get(tag);
66        if (ev == null) {
67            throw new IllegalArgumentException("Unknown tag : " + (char) tag);
68        }
69        return ev;
70    }
71
72    private abstract class TestElementValue {
73        public void testLength(TestResult testCase, AnnotationDefault_attribute attr) {
74            testCase.checkEquals(attr.attribute_length, 1 + attr.default_value.length(),
75                    "attribute_length");
76        }
77
78        public String[] getValues(String[] values, int index, int length) {
79            return Arrays.copyOfRange(values, index, index + length);
80        }
81
82        public int getLength() {
83            return 1;
84        }
85
86        public abstract void testElementValue(
87                TestResult testCase,
88                ClassFile classFile,
89                Annotation.element_value element_value,
90                String[] values)
91                throws ConstantPool.InvalidIndex, ConstantPool.UnexpectedEntry;
92    }
93
94    private class TestIntegerElementValue extends TestElementValue {
95
96        @Override
97        public void testElementValue(
98                TestResult testCase,
99                ClassFile classFile,
100                Annotation.element_value element_value,
101                String[] values) throws ConstantPool.InvalidIndex {
102            Annotation.Primitive_element_value ev =
103                    (Annotation.Primitive_element_value) element_value;
104            ConstantPool.CONSTANT_Integer_info info =
105                    (ConstantPool.CONSTANT_Integer_info)
106                            classFile.constant_pool.get(ev.const_value_index);
107            testCase.checkEquals(info.value, Integer.parseInt(values[0]), "const_value_index");
108        }
109    }
110
111    private class TestLongElementValue extends TestElementValue {
112        @Override
113        public void testElementValue(
114                TestResult testCase,
115                ClassFile classFile,
116                Annotation.element_value element_value,
117                String[] values) throws ConstantPool.InvalidIndex {
118            Annotation.Primitive_element_value ev =
119                    (Annotation.Primitive_element_value) element_value;
120            ConstantPool.CONSTANT_Long_info info =
121                    (ConstantPool.CONSTANT_Long_info)
122                            classFile.constant_pool.get(ev.const_value_index);
123            testCase.checkEquals(info.value, Long.parseLong(values[0]), "const_value_index");
124        }
125    }
126
127    private class TestFloatElementValue extends TestElementValue {
128        @Override
129        public void testElementValue(
130                TestResult testCase,
131                ClassFile classFile,
132                Annotation.element_value element_value,
133                String[] values) throws ConstantPool.InvalidIndex {
134            Annotation.Primitive_element_value ev =
135                    (Annotation.Primitive_element_value) element_value;
136            ConstantPool.CONSTANT_Float_info info =
137                    (ConstantPool.CONSTANT_Float_info)
138                            classFile.constant_pool.get(ev.const_value_index);
139            testCase.checkEquals(info.value, Float.parseFloat(values[0]), "const_value_index");
140        }
141    }
142
143    private class TestDoubleElementValue extends TestElementValue {
144        @Override
145        public void testElementValue(
146                TestResult testCase,
147                ClassFile classFile,
148                Annotation.element_value element_value,
149                String[] values) throws ConstantPool.InvalidIndex {
150            Annotation.Primitive_element_value ev =
151                    (Annotation.Primitive_element_value) element_value;
152            ConstantPool.CONSTANT_Double_info info =
153                    (ConstantPool.CONSTANT_Double_info)
154                            classFile.constant_pool.get(ev.const_value_index);
155            testCase.checkEquals(info.value, Double.parseDouble(values[0]), "const_value_index");
156        }
157    }
158
159    private class TestStringElementValue extends TestElementValue {
160        @Override
161        public void testElementValue(
162                TestResult testCase,
163                ClassFile classFile,
164                Annotation.element_value element_value,
165                String[] values) throws ConstantPool.InvalidIndex {
166            Annotation.Primitive_element_value ev =
167                    (Annotation.Primitive_element_value) element_value;
168            ConstantPool.CONSTANT_Utf8_info info =
169                    (ConstantPool.CONSTANT_Utf8_info)
170                            classFile.constant_pool.get(ev.const_value_index);
171            testCase.checkEquals(info.value, values[0], "const_value_index");
172        }
173    }
174
175    private class TestEnumElementValue extends TestElementValue {
176
177        @Override
178        public int getLength() {
179            return 2;
180        }
181
182        @Override
183        public void testElementValue(
184                TestResult testCase,
185                ClassFile classFile,
186                Annotation.element_value element_value,
187                String[] values)
188                throws ConstantPool.InvalidIndex, ConstantPool.UnexpectedEntry {
189            Annotation.Enum_element_value ev = (Annotation.Enum_element_value) element_value;
190            testCase.checkEquals(classFile.constant_pool.getUTF8Info(ev.type_name_index).value,
191                    values[0], "type_name_index");
192            testCase.checkEquals(classFile.constant_pool.getUTF8Info(ev.const_name_index).value,
193                    values[1], "const_name_index");
194        }
195    }
196
197    private class TestClassElementValue extends TestElementValue {
198        @Override
199        public void testElementValue(
200                TestResult testCase,
201                ClassFile classFile,
202                Annotation.element_value element_value,
203                String[] values)
204                throws ConstantPool.InvalidIndex, ConstantPool.UnexpectedEntry {
205            Annotation.Class_element_value ev = (Annotation.Class_element_value) element_value;
206            testCase.checkEquals(
207                    classFile.constant_pool.getUTF8Info(ev.class_info_index).value,
208                    values[0], "class_info_index");
209        }
210    }
211
212    private class TestAnnotationElementValue extends TestElementValue {
213        @Override
214        public void testLength(TestResult testCase, AnnotationDefault_attribute attr) {
215            // Suppress, since it is hard to test the length of this kind of element values.
216        }
217
218        @Override
219        public int getLength() {
220            // Expected that the test uses DefaultAnnotation
221            // tag (1 byte) + annotation_value (2 bytes) which contains const_value
222            return 3;
223        }
224
225        @Override
226        public void testElementValue(
227                TestResult testCase,
228                ClassFile classFile,
229                Annotation.element_value element_value,
230                String[] values)
231                throws ConstantPool.InvalidIndex, ConstantPool.UnexpectedEntry {
232            Annotation ev = ((Annotation.Annotation_element_value) element_value)
233                    .annotation_value;
234            testCase.checkEquals(
235                    classFile.constant_pool.getUTF8Info(ev.type_index).value,
236                    values[0],
237                    "type_index");
238            for (int i = 0; i < ev.num_element_value_pairs; ++i) {
239                Annotation.element_value_pair pair = ev.element_value_pairs[i];
240                testCase.checkEquals(
241                        classFile.constant_pool.getUTF8Info(pair.element_name_index).value,
242                        values[2 * i + 1],
243                        "element_name_index");
244                TestElementValue testElementValue = verifiers.get(pair.value.tag);
245                testElementValue.testElementValue(
246                        testCase,
247                        classFile,
248                        pair.value,
249                        new String[]{values[2 * i + 2]});
250            }
251        }
252    }
253
254    private class TestArrayElementValue extends TestElementValue {
255        @Override
256        public void testLength(TestResult testCase, AnnotationDefault_attribute attr) {
257            Annotation.Array_element_value ev =
258                    (Annotation.Array_element_value) attr.default_value;
259            int sizeOfTag = ev.values[0].tag == 'e' ? 0 : 1;
260            // tag (1 byte) + array header (2 byte) + length of entries
261            testCase.checkEquals(attr.attribute_length, 1 + 2 +
262                    (sizeOfTag + ev.length() / ev.num_values) * ev.num_values, "attribute_length");
263        }
264
265        @Override
266        public void testElementValue(
267                TestResult testCase,
268                ClassFile classFile,
269                Annotation.element_value element_value,
270                String[] values)
271                throws ConstantPool.InvalidIndex, ConstantPool.UnexpectedEntry {
272            Annotation.Array_element_value ev =
273                    (Annotation.Array_element_value) element_value;
274            int index = 0;
275            for (int i = 0; i < ev.num_values; ++i) {
276                TestElementValue testElementValue = verifiers.get(ev.values[i].tag);
277                int length = testElementValue.getLength();
278                testElementValue.testElementValue(
279                        testCase,
280                        classFile,
281                        ev.values[i],
282                        testElementValue.getValues(values, index, length));
283                index += length;
284            }
285        }
286    }
287}
288