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