• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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$
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      */
FieldGen(final int access_flags, final Type type, final String name, final ConstantPoolGen cp)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      */
FieldGen(final Field field, final ConstantPoolGen cp)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 
setValue( final int index )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      */
setInitValue( final String str )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 
setInitValue( final long l )127     public void setInitValue( final long l ) {
128         checkType(Type.LONG);
129         if (l != 0L) {
130             value = Long.valueOf(l);
131         }
132     }
133 
134 
setInitValue( final int i )135     public void setInitValue( final int i ) {
136         checkType(Type.INT);
137         if (i != 0) {
138             value = Integer.valueOf(i);
139         }
140     }
141 
142 
setInitValue( final short s )143     public void setInitValue( final short s ) {
144         checkType(Type.SHORT);
145         if (s != 0) {
146             value = Integer.valueOf(s);
147         }
148     }
149 
150 
setInitValue( final char c )151     public void setInitValue( final char c ) {
152         checkType(Type.CHAR);
153         if (c != 0) {
154             value = Integer.valueOf(c);
155         }
156     }
157 
158 
setInitValue( final byte b )159     public void setInitValue( final byte b ) {
160         checkType(Type.BYTE);
161         if (b != 0) {
162             value = Integer.valueOf(b);
163         }
164     }
165 
166 
setInitValue( final boolean b )167     public void setInitValue( final boolean b ) {
168         checkType(Type.BOOLEAN);
169         if (b) {
170             value = Integer.valueOf(1);
171         }
172     }
173 
174 
setInitValue( final float f )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 
setInitValue( final double d )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      */
cancelInitValue()193     public void cancelInitValue() {
194         value = null;
195     }
196 
197 
checkType( final Type atype )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      */
getField()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 
addAnnotationsAsAttribute(final ConstantPoolGen cp)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 
addConstant()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
getSignature()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      */
addObserver( final FieldObserver o )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      */
removeObserver( final FieldObserver o )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      */
update()291     public void update() {
292         if (observers != null) {
293             for (final FieldObserver observer : observers ) {
294                 observer.notify(this);
295             }
296         }
297     }
298 
299 
getInitValue()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
toString()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      */
copy( final ConstantPoolGen cp )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      */
getComparator()345     public static BCELComparator getComparator() {
346         return bcelComparator;
347     }
348 
349 
350     /**
351      * @param comparator Comparison strategy object
352      */
setComparator( final BCELComparator comparator )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
equals( final Object obj )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
hashCode()378     public int hashCode() {
379         return bcelComparator.hashCode(this);
380     }
381 }
382