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