View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   *
17   */
18  package org.apache.bcel.generic;
19  
20  import java.util.ArrayList;
21  import java.util.List;
22  
23  import org.apache.bcel.Const;
24  import org.apache.bcel.classfile.AnnotationEntry;
25  import org.apache.bcel.classfile.Annotations;
26  import org.apache.bcel.classfile.Attribute;
27  import org.apache.bcel.classfile.Constant;
28  import org.apache.bcel.classfile.ConstantObject;
29  import org.apache.bcel.classfile.ConstantPool;
30  import org.apache.bcel.classfile.ConstantValue;
31  import org.apache.bcel.classfile.Field;
32  import org.apache.bcel.classfile.Utility;
33  import org.apache.bcel.util.BCELComparator;
34  
35  /**
36   * Template class for building up a field.  The only extraordinary thing
37   * one can do is to add a constant value attribute to a field (which must of
38   * course be compatible with to the declared type).
39   *
40   * @version $Id: FieldGen.html 1018313 2017-09-18 09:03:04Z britter $
41   * @see Field
42   */
43  public class FieldGen extends FieldGenOrMethodGen {
44  
45      private Object value = null;
46      private static BCELComparator bcelComparator = new BCELComparator() {
47  
48          @Override
49          public boolean equals( final Object o1, final Object o2 ) {
50              final FieldGen THIS = (FieldGen) o1;
51              final FieldGen THAT = (FieldGen) o2;
52              return THIS.getName().equals(THAT.getName())
53                      && THIS.getSignature().equals(THAT.getSignature());
54          }
55  
56  
57          @Override
58          public int hashCode( final Object o ) {
59              final FieldGen THIS = (FieldGen) o;
60              return THIS.getSignature().hashCode() ^ THIS.getName().hashCode();
61          }
62      };
63  
64  
65      /**
66       * Declare a field. If it is static (isStatic() == true) and has a
67       * basic type like int or String it may have an initial value
68       * associated with it as defined by setInitValue().
69       *
70       * @param access_flags access qualifiers
71       * @param type  field type
72       * @param name field name
73       * @param cp constant pool
74       */
75      public FieldGen(final int access_flags, final Type type, final String name, final ConstantPoolGen cp) {
76          super(access_flags);
77          setType(type);
78          setName(name);
79          setConstantPool(cp);
80      }
81  
82  
83      /**
84       * Instantiate from existing field.
85       *
86       * @param field Field object
87       * @param cp constant pool (must contain the same entries as the field's constant pool)
88       */
89      public FieldGen(final Field field, final ConstantPoolGen cp) {
90          this(field.getAccessFlags(), Type.getType(field.getSignature()), field.getName(), cp);
91          final Attribute[] attrs = field.getAttributes();
92          for (final Attribute attr : attrs) {
93              if (attr instanceof ConstantValue) {
94                  setValue(((ConstantValue) attr).getConstantValueIndex());
95              } else if (attr instanceof Annotations) {
96                  final Annotations runtimeAnnotations = (Annotations)attr;
97                  final AnnotationEntry[] annotationEntries = runtimeAnnotations.getAnnotationEntries();
98                  for (final AnnotationEntry element : annotationEntries) {
99                      addAnnotationEntry(new AnnotationEntryGen(element,cp,false));
100                 }
101             } else {
102                 addAttribute(attr);
103             }
104         }
105     }
106 
107 
108     private void setValue( final int index ) {
109         final ConstantPool cp = super.getConstantPool().getConstantPool();
110         final Constant c = cp.getConstant(index);
111         value = ((ConstantObject) c).getConstantValue(cp);
112     }
113 
114 
115     /**
116      * Set (optional) initial value of field, otherwise it will be set to null/0/false
117      * by the JVM automatically.
118      */
119     public void setInitValue( final String str ) {
120         checkType(  ObjectType.getInstance("java.lang.String"));
121         if (str != null) {
122             value = str;
123         }
124     }
125 
126 
127     public void setInitValue( final long l ) {
128         checkType(Type.LONG);
129         if (l != 0L) {
130             value = Long.valueOf(l);
131         }
132     }
133 
134 
135     public void setInitValue( final int i ) {
136         checkType(Type.INT);
137         if (i != 0) {
138             value = Integer.valueOf(i);
139         }
140     }
141 
142 
143     public void setInitValue( final short s ) {
144         checkType(Type.SHORT);
145         if (s != 0) {
146             value = Integer.valueOf(s);
147         }
148     }
149 
150 
151     public void setInitValue( final char c ) {
152         checkType(Type.CHAR);
153         if (c != 0) {
154             value = Integer.valueOf(c);
155         }
156     }
157 
158 
159     public void setInitValue( final byte b ) {
160         checkType(Type.BYTE);
161         if (b != 0) {
162             value = Integer.valueOf(b);
163         }
164     }
165 
166 
167     public void setInitValue( final boolean b ) {
168         checkType(Type.BOOLEAN);
169         if (b) {
170             value = Integer.valueOf(1);
171         }
172     }
173 
174 
175     public void setInitValue( final float f ) {
176         checkType(Type.FLOAT);
177         if (f != 0.0) {
178             value = new Float(f);
179         }
180     }
181 
182 
183     public void setInitValue( final double d ) {
184         checkType(Type.DOUBLE);
185         if (d != 0.0) {
186             value = new Double(d);
187         }
188     }
189 
190 
191     /** Remove any initial value.
192      */
193     public void cancelInitValue() {
194         value = null;
195     }
196 
197 
198     private void checkType( final Type atype ) {
199         final Type superType = super.getType();
200         if (superType == null) {
201             throw new ClassGenException("You haven't defined the type of the field yet");
202         }
203         if (!isFinal()) {
204             throw new ClassGenException("Only final fields may have an initial value!");
205         }
206         if (!superType.equals(atype)) {
207             throw new ClassGenException("Types are not compatible: " + superType + " vs. " + atype);
208         }
209     }
210 
211 
212     /**
213      * Get field object after having set up all necessary values.
214      */
215     public Field getField() {
216         final String signature = getSignature();
217         final int name_index = super.getConstantPool().addUtf8(super.getName());
218         final int signature_index = super.getConstantPool().addUtf8(signature);
219         if (value != null) {
220             checkType(super.getType());
221             final int index = addConstant();
222             addAttribute(new ConstantValue(super.getConstantPool().addUtf8("ConstantValue"), 2, index,
223                     super.getConstantPool().getConstantPool())); // sic
224         }
225         addAnnotationsAsAttribute(super.getConstantPool());
226         return new Field(super.getAccessFlags(), name_index, signature_index, getAttributes(),
227                 super.getConstantPool().getConstantPool()); // sic
228     }
229 
230     private void addAnnotationsAsAttribute(final ConstantPoolGen cp) {
231           final Attribute[] attrs = AnnotationEntryGen.getAnnotationAttributes(cp, super.getAnnotationEntries());
232         for (final Attribute attr : attrs) {
233             addAttribute(attr);
234         }
235       }
236 
237 
238     private int addConstant() {
239         switch (super.getType().getType()) { // sic
240             case Const.T_INT:
241             case Const.T_CHAR:
242             case Const.T_BYTE:
243             case Const.T_BOOLEAN:
244             case Const.T_SHORT:
245                 return super.getConstantPool().addInteger(((Integer) value).intValue());
246             case Const.T_FLOAT:
247                 return super.getConstantPool().addFloat(((Float) value).floatValue());
248             case Const.T_DOUBLE:
249                 return super.getConstantPool().addDouble(((Double) value).doubleValue());
250             case Const.T_LONG:
251                 return super.getConstantPool().addLong(((Long) value).longValue());
252             case Const.T_REFERENCE:
253                 return super.getConstantPool().addString((String) value);
254             default:
255                 throw new RuntimeException("Oops: Unhandled : " + super.getType().getType()); // sic
256         }
257     }
258 
259 
260     @Override
261     public String getSignature() {
262         return super.getType().getSignature();
263     }
264 
265     private List<FieldObserver> observers;
266 
267 
268     /** Add observer for this object.
269      */
270     public void addObserver( final FieldObserver o ) {
271         if (observers == null) {
272             observers = new ArrayList<>();
273         }
274         observers.add(o);
275     }
276 
277 
278     /** Remove observer for this object.
279      */
280     public void removeObserver( final FieldObserver o ) {
281         if (observers != null) {
282             observers.remove(o);
283         }
284     }
285 
286 
287     /** Call notify() method on all observers. This method is not called
288      * automatically whenever the state has changed, but has to be
289      * called by the user after he has finished editing the object.
290      */
291     public void update() {
292         if (observers != null) {
293             for (final FieldObserver observer : observers ) {
294                 observer.notify(this);
295             }
296         }
297     }
298 
299 
300     public String getInitValue() {
301         if (value != null) {
302             return value.toString();
303         }
304         return null;
305     }
306 
307 
308     /**
309      * Return string representation close to declaration format,
310      * `public static final short MAX = 100', e.g..
311      *
312      * @return String representation of field
313      */
314     @Override
315     public final String toString() {
316         String name;
317         String signature;
318         String access; // Short cuts to constant pool
319         access = Utility.accessToString(super.getAccessFlags());
320         access = access.isEmpty() ? "" : (access + " ");
321         signature = super.getType().toString();
322         name = getName();
323         final StringBuilder buf = new StringBuilder(32); // CHECKSTYLE IGNORE MagicNumber
324         buf.append(access).append(signature).append(" ").append(name);
325         final String value = getInitValue();
326         if (value != null) {
327             buf.append(" = ").append(value);
328         }
329         return buf.toString();
330     }
331 
332 
333     /** @return deep copy of this field
334      */
335     public FieldGen copy( final ConstantPoolGen cp ) {
336         final FieldGen fg = (FieldGen) clone();
337         fg.setConstantPool(cp);
338         return fg;
339     }
340 
341 
342     /**
343      * @return Comparison strategy object
344      */
345     public static BCELComparator getComparator() {
346         return bcelComparator;
347     }
348 
349 
350     /**
351      * @param comparator Comparison strategy object
352      */
353     public static void setComparator( final BCELComparator comparator ) {
354         bcelComparator = comparator;
355     }
356 
357 
358     /**
359      * Return value as defined by given BCELComparator strategy.
360      * By default two FieldGen objects are said to be equal when
361      * their names and signatures are equal.
362      *
363      * @see java.lang.Object#equals(java.lang.Object)
364      */
365     @Override
366     public boolean equals( final Object obj ) {
367         return bcelComparator.equals(this, obj);
368     }
369 
370 
371     /**
372      * Return value as defined by given BCELComparator strategy.
373      * By default return the hashcode of the field's name XOR signature.
374      *
375      * @see java.lang.Object#hashCode()
376      */
377     @Override
378     public int hashCode() {
379         return bcelComparator.hashCode(this);
380     }
381 }