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