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    }