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