001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 */
018 package org.apache.bcel.generic;
019
020 import java.util.ArrayList;
021 import java.util.List;
022 import org.apache.bcel.Constants;
023 import org.apache.bcel.classfile.AnnotationEntry;
024 import org.apache.bcel.classfile.Annotations;
025 import org.apache.bcel.classfile.Attribute;
026 import org.apache.bcel.classfile.Constant;
027 import org.apache.bcel.classfile.ConstantObject;
028 import org.apache.bcel.classfile.ConstantPool;
029 import org.apache.bcel.classfile.ConstantValue;
030 import org.apache.bcel.classfile.Field;
031 import org.apache.bcel.classfile.Utility;
032 import org.apache.bcel.util.BCELComparator;
033
034 /**
035 * Template class for building up a field. The only extraordinary thing
036 * one can do is to add a constant value attribute to a field (which must of
037 * course be compatible with to the declared type).
038 *
039 * @version $Id: FieldGen.java 1152077 2011-07-29 02:29:42Z dbrosius $
040 * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
041 * @see Field
042 */
043 public class FieldGen extends FieldGenOrMethodGen {
044
045 private static final long serialVersionUID = -6050448955000008261L;
046 private Object value = null;
047 private static BCELComparator _cmp = new BCELComparator() {
048
049 public boolean equals( Object o1, Object o2 ) {
050 FieldGen THIS = (FieldGen) o1;
051 FieldGen THAT = (FieldGen) o2;
052 return THIS.getName().equals(THAT.getName())
053 && THIS.getSignature().equals(THAT.getSignature());
054 }
055
056
057 public int hashCode( Object o ) {
058 FieldGen THIS = (FieldGen) o;
059 return THIS.getSignature().hashCode() ^ THIS.getName().hashCode();
060 }
061 };
062
063
064 /**
065 * Declare a field. If it is static (isStatic() == true) and has a
066 * basic type like int or String it may have an initial value
067 * associated with it as defined by setInitValue().
068 *
069 * @param access_flags access qualifiers
070 * @param type field type
071 * @param name field name
072 * @param cp constant pool
073 */
074 public FieldGen(int access_flags, Type type, String name, ConstantPoolGen cp) {
075 setAccessFlags(access_flags);
076 setType(type);
077 setName(name);
078 setConstantPool(cp);
079 }
080
081
082 /**
083 * Instantiate from existing field.
084 *
085 * @param field Field object
086 * @param cp constant pool (must contain the same entries as the field's constant pool)
087 */
088 public FieldGen(Field field, ConstantPoolGen cp) {
089 this(field.getAccessFlags(), Type.getType(field.getSignature()), field.getName(), cp);
090 Attribute[] attrs = field.getAttributes();
091 for (int i = 0; i < attrs.length; i++) {
092 if (attrs[i] instanceof ConstantValue) {
093 setValue(((ConstantValue) attrs[i]).getConstantValueIndex());
094 } else if (attrs[i] instanceof Annotations) {
095 Annotations runtimeAnnotations = (Annotations)attrs[i];
096 AnnotationEntry[] annotationEntries = runtimeAnnotations.getAnnotationEntries();
097 for (int j = 0; j < annotationEntries.length; j++) {
098 AnnotationEntry element = annotationEntries[j];
099 addAnnotationEntry(new AnnotationEntryGen(element,cp,false));
100 }
101 } else {
102 addAttribute(attrs[i]);
103 }
104 }
105 }
106
107
108 private void setValue( int index ) {
109 ConstantPool cp = this.cp.getConstantPool();
110 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( String str ) {
120 checkType(new ObjectType("java.lang.String"));
121 if (str != null) {
122 value = str;
123 }
124 }
125
126
127 public void setInitValue( long l ) {
128 checkType(Type.LONG);
129 if (l != 0L) {
130 value = new Long(l);
131 }
132 }
133
134
135 public void setInitValue( int i ) {
136 checkType(Type.INT);
137 if (i != 0) {
138 value = Integer.valueOf(i);
139 }
140 }
141
142
143 public void setInitValue( short s ) {
144 checkType(Type.SHORT);
145 if (s != 0) {
146 value = Integer.valueOf(s);
147 }
148 }
149
150
151 public void setInitValue( char c ) {
152 checkType(Type.CHAR);
153 if (c != 0) {
154 value = Integer.valueOf(c);
155 }
156 }
157
158
159 public void setInitValue( byte b ) {
160 checkType(Type.BYTE);
161 if (b != 0) {
162 value = Integer.valueOf(b);
163 }
164 }
165
166
167 public void setInitValue( boolean b ) {
168 checkType(Type.BOOLEAN);
169 if (b) {
170 value = Integer.valueOf(1);
171 }
172 }
173
174
175 public void setInitValue( float f ) {
176 checkType(Type.FLOAT);
177 if (f != 0.0) {
178 value = new Float(f);
179 }
180 }
181
182
183 public void setInitValue( 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( Type atype ) {
199 if (type == null) {
200 throw new ClassGenException("You haven't defined the type of the field yet");
201 }
202 if (!isFinal()) {
203 throw new ClassGenException("Only final fields may have an initial value!");
204 }
205 if (!type.equals(atype)) {
206 throw new ClassGenException("Types are not compatible: " + type + " vs. " + atype);
207 }
208 }
209
210
211 /**
212 * Get field object after having set up all necessary values.
213 */
214 public Field getField() {
215 String signature = getSignature();
216 int name_index = cp.addUtf8(name);
217 int signature_index = cp.addUtf8(signature);
218 if (value != null) {
219 checkType(type);
220 int index = addConstant();
221 addAttribute(new ConstantValue(cp.addUtf8("ConstantValue"), 2, index, cp
222 .getConstantPool()));
223 }
224 addAnnotationsAsAttribute(cp);
225 return new Field(access_flags, name_index, signature_index, getAttributes(), cp
226 .getConstantPool());
227 }
228
229 private void addAnnotationsAsAttribute(ConstantPoolGen cp) {
230 Attribute[] attrs = Utility.getAnnotationAttributes(cp,annotation_vec);
231 for (int i = 0; i < attrs.length; i++) {
232 addAttribute(attrs[i]);
233 }
234 }
235
236
237 private int addConstant() {
238 switch (type.getType()) {
239 case Constants.T_INT:
240 case Constants.T_CHAR:
241 case Constants.T_BYTE:
242 case Constants.T_BOOLEAN:
243 case Constants.T_SHORT:
244 return cp.addInteger(((Integer) value).intValue());
245 case Constants.T_FLOAT:
246 return cp.addFloat(((Float) value).floatValue());
247 case Constants.T_DOUBLE:
248 return cp.addDouble(((Double) value).doubleValue());
249 case Constants.T_LONG:
250 return cp.addLong(((Long) value).longValue());
251 case Constants.T_REFERENCE:
252 return cp.addString(((String) value));
253 default:
254 throw new RuntimeException("Oops: Unhandled : " + type.getType());
255 }
256 }
257
258
259 @Override
260 public String getSignature() {
261 return type.getSignature();
262 }
263
264 private List<FieldObserver> observers;
265
266
267 /** Add observer for this object.
268 */
269 public void addObserver( FieldObserver o ) {
270 if (observers == null) {
271 observers = new ArrayList<FieldObserver>();
272 }
273 observers.add(o);
274 }
275
276
277 /** Remove observer for this object.
278 */
279 public void removeObserver( FieldObserver o ) {
280 if (observers != null) {
281 observers.remove(o);
282 }
283 }
284
285
286 /** Call notify() method on all observers. This method is not called
287 * automatically whenever the state has changed, but has to be
288 * called by the user after he has finished editing the object.
289 */
290 public void update() {
291 if (observers != null) {
292 for (FieldObserver observer : observers ) {
293 observer.notify(this);
294 }
295 }
296 }
297
298
299 public String getInitValue() {
300 if (value != null) {
301 return value.toString();
302 } else {
303 return null;
304 }
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, signature, access; // Short cuts to constant pool
317 access = Utility.accessToString(access_flags);
318 access = access.equals("") ? "" : (access + " ");
319 signature = type.toString();
320 name = getName();
321 StringBuilder buf = new StringBuilder(32);
322 buf.append(access).append(signature).append(" ").append(name);
323 String value = getInitValue();
324 if (value != null) {
325 buf.append(" = ").append(value);
326 }
327 return buf.toString();
328 }
329
330
331 /** @return deep copy of this field
332 */
333 public FieldGen copy( ConstantPoolGen cp ) {
334 FieldGen fg = (FieldGen) clone();
335 fg.setConstantPool(cp);
336 return fg;
337 }
338
339
340 /**
341 * @return Comparison strategy object
342 */
343 public static BCELComparator getComparator() {
344 return _cmp;
345 }
346
347
348 /**
349 * @param comparator Comparison strategy object
350 */
351 public static void setComparator( BCELComparator comparator ) {
352 _cmp = comparator;
353 }
354
355
356 /**
357 * Return value as defined by given BCELComparator strategy.
358 * By default two FieldGen objects are said to be equal when
359 * their names and signatures are equal.
360 *
361 * @see java.lang.Object#equals(java.lang.Object)
362 */
363 @Override
364 public boolean equals( Object obj ) {
365 return _cmp.equals(this, obj);
366 }
367
368
369 /**
370 * Return value as defined by given BCELComparator strategy.
371 * By default return the hashcode of the field's name XOR signature.
372 *
373 * @see java.lang.Object#hashCode()
374 */
375 @Override
376 public int hashCode() {
377 return _cmp.hashCode(this);
378 }
379 }