View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.bcel.generic;
21  
22  import static org.junit.jupiter.api.Assertions.assertEquals;
23  import static org.junit.jupiter.api.Assertions.assertNotNull;
24  import static org.junit.jupiter.api.Assertions.assertNull;
25  import static org.junit.jupiter.api.Assertions.assertTrue;
26  
27  import java.io.File;
28  import java.io.IOException;
29  
30  import org.apache.bcel.AbstractTest;
31  import org.apache.bcel.Const;
32  import org.apache.bcel.classfile.AnnotationEntry;
33  import org.apache.bcel.classfile.ElementValuePair;
34  import org.apache.bcel.classfile.Field;
35  import org.apache.bcel.classfile.JavaClass;
36  import org.apache.bcel.util.SyntheticRepository;
37  import org.junit.jupiter.api.Test;
38  
39  class FieldAnnotationsTest extends AbstractTest {
40      // helper methods
41      public void checkAnnotatedField(final JavaClass clazz, final String fieldname, final String AnnotationEntryName, final String AnnotationEntryElementName,
42          final String AnnotationEntryElementValue) {
43          final Field[] fields = clazz.getFields();
44          for (final Field f : fields) {
45              final AnnotationEntry[] fieldAnnotationEntrys = f.getAnnotationEntries();
46              if (f.getName().equals(fieldname)) {
47                  checkAnnotationEntry(fieldAnnotationEntrys[0], AnnotationEntryName, AnnotationEntryElementName, AnnotationEntryElementValue);
48                  assertNotNull(f.getAttribute(Const.ATTR_RUNTIME_VISIBLE_ANNOTATIONS));
49                  assertNull(f.getAttribute(Const.ATTR_RUNTIME_INVISIBLE_ANNOTATIONS));
50              }
51          }
52      }
53  
54      private void checkAnnotationEntry(final AnnotationEntry a, final String name, final String elementname, final String elementvalue) {
55          assertEquals(name, a.getAnnotationType(), "Wrong AnnotationEntry name");
56          assertEquals(1, a.getElementValuePairs().length, "Wrong number of AnnotationEntry elements");
57          final ElementValuePair envp = a.getElementValuePairs()[0];
58          assertEquals(envp.getNameString(), elementname, "Wrong element name");
59          assertEquals(envp.getValue().stringifyValue(), elementvalue, "Wrong element value");
60      }
61  
62      /**
63       * Check field AnnotationEntrys are retrievable.
64       */
65      @Test
66      void testFieldAnnotationEntrys() throws ClassNotFoundException {
67          final JavaClass clazz = getTestJavaClass(PACKAGE_BASE_NAME + ".data.AnnotatedFields");
68          // TODO L...;?
69          checkAnnotatedField(clazz, "i", "L" + PACKAGE_BASE_SIG + "/data/SimpleAnnotation;", "id", "1");
70          checkAnnotatedField(clazz, "s", "L" + PACKAGE_BASE_SIG + "/data/SimpleAnnotation;", "id", "2");
71      }
72  
73      /**
74       * Check field AnnotationEntrys (de)serialize ok.
75       */
76      @Test
77      void testFieldAnnotationEntrysReadWrite() throws ClassNotFoundException, IOException {
78          final String name = PACKAGE_BASE_NAME + ".data.AnnotatedFields";
79          final JavaClass clazz = getTestJavaClass(name);
80          checkAnnotatedField(clazz, "i", "L" + PACKAGE_BASE_SIG + "/data/SimpleAnnotation;", "id", "1");
81          checkAnnotatedField(clazz, "s", "L" + PACKAGE_BASE_SIG + "/data/SimpleAnnotation;", "id", "2");
82          // Write it out
83          final File tfile = createTestdataFile("AnnotatedFields.class");
84          clazz.dump(tfile);
85          final SyntheticRepository repos2 = createRepos(".");
86          repos2.loadClass(name);
87          checkAnnotatedField(clazz, "i", "L" + PACKAGE_BASE_SIG + "/data/SimpleAnnotation;", "id", "1");
88          checkAnnotatedField(clazz, "s", "L" + PACKAGE_BASE_SIG + "/data/SimpleAnnotation;", "id", "2");
89          assertTrue(tfile.delete());
90      }
91  
92      /**
93       * Check we can load in a class, modify its field AnnotationEntrys, save it, reload it and everything is correct.
94       */
95      @Test
96      void testFieldAnnotationModification() throws ClassNotFoundException {
97          final boolean dbg = false;
98          final JavaClass clazz = getTestJavaClass(PACKAGE_BASE_NAME + ".data.AnnotatedFields");
99          final ClassGen clg = new ClassGen(clazz);
100         Field f = clg.getFields()[0];
101         if (dbg) {
102             System.err.println("Field in freshly constructed class is: " + f);
103         }
104         assertEquals("[@Lorg/apache/bcel/data/SimpleAnnotation;(id=1)]", dumpAnnotationEntries(f.getAnnotationEntries()));
105         if (dbg) {
106             System.err.println("AnnotationEntrys on field are: " + dumpAnnotationEntries(f.getAnnotationEntries()));
107         }
108         final AnnotationEntryGen fruitBasedAnnotationEntry = createFruitAnnotationEntry(clg.getConstantPool(), "Tomato", false);
109         final FieldGen fg = new FieldGen(f, clg.getConstantPool());
110         if (dbg) {
111             System.err.println("Adding AnnotationEntry to the field");
112         }
113         fg.addAnnotationEntry(fruitBasedAnnotationEntry);
114         if (dbg) {
115             System.err.println("FieldGen (mutable field) is " + fg);
116         }
117         if (dbg) {
118             System.err.println("with AnnotationEntrys: " + dumpAnnotationEntries(fg.getAnnotationEntries()));
119         }
120         if (dbg) {
121             System.err.println("Replacing original field with new field that has extra AnnotationEntry");
122         }
123         clg.removeField(f);
124         clg.addField(fg.getField());
125         f = clg.getFields()[1]; // there are two fields in the class, removing
126                                 // and readding has changed the order
127         // so this time index [1] is the 'int i' field
128         if (dbg) {
129             System.err.println("Field now looks like this: " + f);
130         }
131         if (dbg) {
132             System.err.println("With AnnotationEntrys: " + dumpAnnotationEntries(f.getAnnotationEntries()));
133         }
134         assertEquals(2, f.getAnnotationEntries().length, "Wrong number of AnnotationEntries");
135     }
136 }