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; 022import java.util.Objects; 023 024import org.apache.bcel.Const; 025import org.apache.bcel.classfile.AnnotationEntry; 026import org.apache.bcel.classfile.Annotations; 027import org.apache.bcel.classfile.Attribute; 028import org.apache.bcel.classfile.Constant; 029import org.apache.bcel.classfile.ConstantObject; 030import org.apache.bcel.classfile.ConstantPool; 031import org.apache.bcel.classfile.ConstantValue; 032import org.apache.bcel.classfile.Field; 033import org.apache.bcel.classfile.Utility; 034import org.apache.bcel.util.BCELComparator; 035 036/** 037 * Template class for building up a field. The only extraordinary thing 038 * one can do is to add a constant value attribute to a field (which must of 039 * course be compatible with to the declared type). 040 * 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 Objects.equals(THIS.getName(), THAT.getName()) 053 && Objects.equals(THIS.getSignature(), 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}