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 * @version $Id$ 042 * @see Field 043 */ 044public class FieldGen extends FieldGenOrMethodGen { 045 046 private Object value = null; 047 private static BCELComparator bcelComparator = new BCELComparator() { 048 049 @Override 050 public boolean equals( final Object o1, final Object o2 ) { 051 final FieldGen THIS = (FieldGen) o1; 052 final FieldGen THAT = (FieldGen) o2; 053 return Objects.equals(THIS.getName(), THAT.getName()) 054 && Objects.equals(THIS.getSignature(), THAT.getSignature()); 055 } 056 057 058 @Override 059 public int hashCode( final Object o ) { 060 final FieldGen THIS = (FieldGen) o; 061 return THIS.getSignature().hashCode() ^ THIS.getName().hashCode(); 062 } 063 }; 064 065 066 /** 067 * Declare a field. If it is static (isStatic() == true) and has a 068 * basic type like int or String it may have an initial value 069 * associated with it as defined by setInitValue(). 070 * 071 * @param access_flags access qualifiers 072 * @param type field type 073 * @param name field name 074 * @param cp constant pool 075 */ 076 public FieldGen(final int access_flags, final Type type, final String name, final ConstantPoolGen cp) { 077 super(access_flags); 078 setType(type); 079 setName(name); 080 setConstantPool(cp); 081 } 082 083 084 /** 085 * Instantiate from existing field. 086 * 087 * @param field Field object 088 * @param cp constant pool (must contain the same entries as the field's constant pool) 089 */ 090 public FieldGen(final Field field, final ConstantPoolGen cp) { 091 this(field.getAccessFlags(), Type.getType(field.getSignature()), field.getName(), cp); 092 final Attribute[] attrs = field.getAttributes(); 093 for (final Attribute attr : attrs) { 094 if (attr instanceof ConstantValue) { 095 setValue(((ConstantValue) attr).getConstantValueIndex()); 096 } else if (attr instanceof Annotations) { 097 final Annotations runtimeAnnotations = (Annotations)attr; 098 final AnnotationEntry[] annotationEntries = runtimeAnnotations.getAnnotationEntries(); 099 for (final AnnotationEntry element : annotationEntries) { 100 addAnnotationEntry(new AnnotationEntryGen(element,cp,false)); 101 } 102 } else { 103 addAttribute(attr); 104 } 105 } 106 } 107 108 109 private void setValue( final int index ) { 110 final ConstantPool cp = super.getConstantPool().getConstantPool(); 111 final Constant c = cp.getConstant(index); 112 value = ((ConstantObject) c).getConstantValue(cp); 113 } 114 115 116 /** 117 * Set (optional) initial value of field, otherwise it will be set to null/0/false 118 * by the JVM automatically. 119 */ 120 public void setInitValue( final String str ) { 121 checkType( ObjectType.getInstance("java.lang.String")); 122 if (str != null) { 123 value = str; 124 } 125 } 126 127 128 public void setInitValue( final long l ) { 129 checkType(Type.LONG); 130 if (l != 0L) { 131 value = Long.valueOf(l); 132 } 133 } 134 135 136 public void setInitValue( final int i ) { 137 checkType(Type.INT); 138 if (i != 0) { 139 value = Integer.valueOf(i); 140 } 141 } 142 143 144 public void setInitValue( final short s ) { 145 checkType(Type.SHORT); 146 if (s != 0) { 147 value = Integer.valueOf(s); 148 } 149 } 150 151 152 public void setInitValue( final char c ) { 153 checkType(Type.CHAR); 154 if (c != 0) { 155 value = Integer.valueOf(c); 156 } 157 } 158 159 160 public void setInitValue( final byte b ) { 161 checkType(Type.BYTE); 162 if (b != 0) { 163 value = Integer.valueOf(b); 164 } 165 } 166 167 168 public void setInitValue( final boolean b ) { 169 checkType(Type.BOOLEAN); 170 if (b) { 171 value = Integer.valueOf(1); 172 } 173 } 174 175 176 public void setInitValue( final float f ) { 177 checkType(Type.FLOAT); 178 if (f != 0.0) { 179 value = new Float(f); 180 } 181 } 182 183 184 public void setInitValue( final double d ) { 185 checkType(Type.DOUBLE); 186 if (d != 0.0) { 187 value = new Double(d); 188 } 189 } 190 191 192 /** Remove any initial value. 193 */ 194 public void cancelInitValue() { 195 value = null; 196 } 197 198 199 private void checkType( final Type atype ) { 200 final Type superType = super.getType(); 201 if (superType == null) { 202 throw new ClassGenException("You haven't defined the type of the field yet"); 203 } 204 if (!isFinal()) { 205 throw new ClassGenException("Only final fields may have an initial value!"); 206 } 207 if (!superType.equals(atype)) { 208 throw new ClassGenException("Types are not compatible: " + superType + " vs. " + atype); 209 } 210 } 211 212 213 /** 214 * Get field object after having set up all necessary values. 215 */ 216 public Field getField() { 217 final String signature = getSignature(); 218 final int name_index = super.getConstantPool().addUtf8(super.getName()); 219 final int signature_index = super.getConstantPool().addUtf8(signature); 220 if (value != null) { 221 checkType(super.getType()); 222 final int index = addConstant(); 223 addAttribute(new ConstantValue(super.getConstantPool().addUtf8("ConstantValue"), 2, index, 224 super.getConstantPool().getConstantPool())); // sic 225 } 226 addAnnotationsAsAttribute(super.getConstantPool()); 227 return new Field(super.getAccessFlags(), name_index, signature_index, getAttributes(), 228 super.getConstantPool().getConstantPool()); // sic 229 } 230 231 private void addAnnotationsAsAttribute(final ConstantPoolGen cp) { 232 final Attribute[] attrs = AnnotationEntryGen.getAnnotationAttributes(cp, super.getAnnotationEntries()); 233 for (final Attribute attr : attrs) { 234 addAttribute(attr); 235 } 236 } 237 238 239 private int addConstant() { 240 switch (super.getType().getType()) { // sic 241 case Const.T_INT: 242 case Const.T_CHAR: 243 case Const.T_BYTE: 244 case Const.T_BOOLEAN: 245 case Const.T_SHORT: 246 return super.getConstantPool().addInteger(((Integer) value).intValue()); 247 case Const.T_FLOAT: 248 return super.getConstantPool().addFloat(((Float) value).floatValue()); 249 case Const.T_DOUBLE: 250 return super.getConstantPool().addDouble(((Double) value).doubleValue()); 251 case Const.T_LONG: 252 return super.getConstantPool().addLong(((Long) value).longValue()); 253 case Const.T_REFERENCE: 254 return super.getConstantPool().addString((String) value); 255 default: 256 throw new RuntimeException("Oops: Unhandled : " + super.getType().getType()); // sic 257 } 258 } 259 260 261 @Override 262 public String getSignature() { 263 return super.getType().getSignature(); 264 } 265 266 private List<FieldObserver> observers; 267 268 269 /** Add observer for this object. 270 */ 271 public void addObserver( final FieldObserver o ) { 272 if (observers == null) { 273 observers = new ArrayList<>(); 274 } 275 observers.add(o); 276 } 277 278 279 /** Remove observer for this object. 280 */ 281 public void removeObserver( final FieldObserver o ) { 282 if (observers != null) { 283 observers.remove(o); 284 } 285 } 286 287 288 /** Call notify() method on all observers. This method is not called 289 * automatically whenever the state has changed, but has to be 290 * called by the user after he has finished editing the object. 291 */ 292 public void update() { 293 if (observers != null) { 294 for (final FieldObserver observer : observers ) { 295 observer.notify(this); 296 } 297 } 298 } 299 300 301 public String getInitValue() { 302 if (value != null) { 303 return value.toString(); 304 } 305 return null; 306 } 307 308 309 /** 310 * Return string representation close to declaration format, 311 * `public static final short MAX = 100', e.g.. 312 * 313 * @return String representation of field 314 */ 315 @Override 316 public final String toString() { 317 String name; 318 String signature; 319 String access; // Short cuts to constant pool 320 access = Utility.accessToString(super.getAccessFlags()); 321 access = access.isEmpty() ? "" : (access + " "); 322 signature = super.getType().toString(); 323 name = getName(); 324 final StringBuilder buf = new StringBuilder(32); // CHECKSTYLE IGNORE MagicNumber 325 buf.append(access).append(signature).append(" ").append(name); 326 final String value = getInitValue(); 327 if (value != null) { 328 buf.append(" = ").append(value); 329 } 330 return buf.toString(); 331 } 332 333 334 /** @return deep copy of this field 335 */ 336 public FieldGen copy( final ConstantPoolGen cp ) { 337 final FieldGen fg = (FieldGen) clone(); 338 fg.setConstantPool(cp); 339 return fg; 340 } 341 342 343 /** 344 * @return Comparison strategy object 345 */ 346 public static BCELComparator getComparator() { 347 return bcelComparator; 348 } 349 350 351 /** 352 * @param comparator Comparison strategy object 353 */ 354 public static void setComparator( final BCELComparator comparator ) { 355 bcelComparator = comparator; 356 } 357 358 359 /** 360 * Return value as defined by given BCELComparator strategy. 361 * By default two FieldGen objects are said to be equal when 362 * their names and signatures are equal. 363 * 364 * @see java.lang.Object#equals(java.lang.Object) 365 */ 366 @Override 367 public boolean equals( final Object obj ) { 368 return bcelComparator.equals(this, obj); 369 } 370 371 372 /** 373 * Return value as defined by given BCELComparator strategy. 374 * By default return the hashcode of the field's name XOR signature. 375 * 376 * @see java.lang.Object#hashCode() 377 */ 378 @Override 379 public int hashCode() { 380 return bcelComparator.hashCode(this); 381 } 382}