    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 */
    018package org.apache.bcel.generic;
    019
    020import java.util.ArrayList;
    021import java.util.List;
    022
    023import org.apache.bcel.Const;
    024import org.apache.bcel.classfile.AnnotationEntry;
    025import org.apache.bcel.classfile.Annotations;
    026import org.apache.bcel.classfile.Attribute;
    027import org.apache.bcel.classfile.Constant;
    028import org.apache.bcel.classfile.ConstantObject;
    029import org.apache.bcel.classfile.ConstantPool;
    030import org.apache.bcel.classfile.ConstantValue;
    031import org.apache.bcel.classfile.Field;
    032import org.apache.bcel.classfile.Utility;
    033import org.apache.bcel.util.BCELComparator;
    034
    035/**
    036 * Template class for building up a field.  The only extraordinary thing
    037 * one can do is to add a constant value attribute to a field (which must of
    038 * course be compatible with to the declared type).
    039 *
    040 * @version $Id: FieldGen.java 1806200 2017-08-25 16:33:06Z ggregory $
    041 * @see Field
    042 */
    043public class FieldGen extends FieldGenOrMethodGen {
    044
    045    private Object value = null;
    046    private static BCELComparator bcelComparator = new BCELComparator() {
    047
    048        @Override
    049        public boolean equals( final Object o1, final Object o2 ) {
    050            final FieldGen THIS = (FieldGen) o1;
    051            final FieldGen THAT = (FieldGen) o2;
    052            return THIS.getName().equals(THAT.getName())
    053                    && THIS.getSignature().equals(THAT.getSignature());
    054        }
    055
    056
    057        @Override
    058        public int hashCode( final Object o ) {
    059            final FieldGen THIS = (FieldGen) o;
    060            return THIS.getSignature().hashCode() ^ THIS.getName().hashCode();
    061        }
    062    };
    063
    064
    065    /**
    066     * Declare a field. If it is static (isStatic() == true) and has a
    067     * basic type like int or String it may have an initial value
    068     * associated with it as defined by setInitValue().
    069     *
    070     * @param access_flags access qualifiers
    071     * @param type  field type
    072     * @param name field name
    073     * @param cp constant pool
    074     */
    075    public FieldGen(final int access_flags, final Type type, final String name, final ConstantPoolGen cp) {
    076        super(access_flags);
    077        setType(type);
    078        setName(name);
    079        setConstantPool(cp);
    080    }
    081
    082
    083    /**
    084     * Instantiate from existing field.
    085     *
    086     * @param field Field object
    087     * @param cp constant pool (must contain the same entries as the field's constant pool)
    088     */
    089    public FieldGen(final Field field, final ConstantPoolGen cp) {
    090        this(field.getAccessFlags(), Type.getType(field.getSignature()), field.getName(), cp);
    091        final Attribute[] attrs = field.getAttributes();
    092        for (final Attribute attr : attrs) {
    093            if (attr instanceof ConstantValue) {
    094                setValue(((ConstantValue) attr).getConstantValueIndex());
    095            } else if (attr instanceof Annotations) {
    096                final Annotations runtimeAnnotations = (Annotations)attr;
    097                final AnnotationEntry[] annotationEntries = runtimeAnnotations.getAnnotationEntries();
    098                for (final AnnotationEntry element : annotationEntries) {
    099                    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}