1 /* 2 * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 package com.sun.org.apache.bcel.internal.generic; 21 22 import java.util.ArrayList; 23 import java.util.List; 24 import java.util.Objects; 25 26 import com.sun.org.apache.bcel.internal.Const; 27 import com.sun.org.apache.bcel.internal.classfile.AnnotationEntry; 28 import com.sun.org.apache.bcel.internal.classfile.Annotations; 29 import com.sun.org.apache.bcel.internal.classfile.Attribute; 30 import com.sun.org.apache.bcel.internal.classfile.Constant; 31 import com.sun.org.apache.bcel.internal.classfile.ConstantObject; 32 import com.sun.org.apache.bcel.internal.classfile.ConstantPool; 33 import com.sun.org.apache.bcel.internal.classfile.ConstantValue; 34 import com.sun.org.apache.bcel.internal.classfile.Field; 35 import com.sun.org.apache.bcel.internal.classfile.Utility; 36 import com.sun.org.apache.bcel.internal.util.BCELComparator; 37 38 /** 39 * Template class for building up a field. The only extraordinary thing 40 * one can do is to add a constant value attribute to a field (which must of 41 * course be compatible with to the declared type). 42 * 43 * @see Field 44 * @LastModified: Jan 2020 45 */ 46 public class FieldGen extends FieldGenOrMethodGen { 47 48 private Object value = null; 49 private static BCELComparator bcelComparator = new BCELComparator() { 50 51 @Override 52 public boolean equals( final Object o1, final Object o2 ) { 53 final FieldGen THIS = (FieldGen) o1; 54 final FieldGen THAT = (FieldGen) o2; 55 return Objects.equals(THIS.getName(), THAT.getName()) 56 && Objects.equals(THIS.getSignature(), THAT.getSignature()); 57 } 58 59 60 @Override 61 public int hashCode( final Object o ) { 62 final FieldGen THIS = (FieldGen) o; 63 return THIS.getSignature().hashCode() ^ THIS.getName().hashCode(); 64 } 65 }; 66 67 68 /** 69 * Declare a field. If it is static (isStatic() == true) and has a 70 * basic type like int or String it may have an initial value 71 * associated with it as defined by setInitValue(). 72 * 73 * @param access_flags access qualifiers 74 * @param type field type 75 * @param name field name 76 * @param cp constant pool 77 */ 78 public FieldGen(final int access_flags, final Type type, final String name, final ConstantPoolGen cp) { 79 super(access_flags); 80 setType(type); 81 setName(name); 82 setConstantPool(cp); 83 } 84 85 86 /** 87 * Instantiate from existing field. 88 * 89 * @param field Field object 90 * @param cp constant pool (must contain the same entries as the field's constant pool) 91 */ 92 public FieldGen(final Field field, final ConstantPoolGen cp) { 93 this(field.getAccessFlags(), Type.getType(field.getSignature()), field.getName(), cp); 94 final Attribute[] attrs = field.getAttributes(); 95 for (final Attribute attr : attrs) { 96 if (attr instanceof ConstantValue) { 97 setValue(((ConstantValue) attr).getConstantValueIndex()); 98 } else if (attr instanceof Annotations) { 99 final Annotations runtimeAnnotations = (Annotations)attr; 100 final AnnotationEntry[] annotationEntries = runtimeAnnotations.getAnnotationEntries(); 101 for (final AnnotationEntry element : annotationEntries) { 102 addAnnotationEntry(new AnnotationEntryGen(element,cp,false)); 103 } 104 } else { 105 addAttribute(attr); 106 } 107 } 108 } 109 110 111 private void setValue( final int index ) { 112 final ConstantPool cp = super.getConstantPool().getConstantPool(); 113 final Constant c = cp.getConstant(index); 114 value = ((ConstantObject) c).getConstantValue(cp); 115 } 116 117 118 /** 119 * Set (optional) initial value of field, otherwise it will be set to null/0/false 120 * by the JVM automatically. 121 */ 122 public void setInitValue( final String str ) { 123 checkType( ObjectType.getInstance("java.lang.String")); 124 if (str != null) { 125 value = str; 126 } 127 } 128 129 130 public void setInitValue( final long l ) { 131 checkType(Type.LONG); 132 if (l != 0L) { 133 value = Long.valueOf(l); 134 } 135 } 136 137 138 public void setInitValue( final int i ) { 139 checkType(Type.INT); 140 if (i != 0) { 141 value = Integer.valueOf(i); 142 } 143 } 144 145 146 public void setInitValue( final short s ) { 147 checkType(Type.SHORT); 148 if (s != 0) { 149 value = Integer.valueOf(s); 150 } 151 } 152 153 154 public void setInitValue( final char c ) { 155 checkType(Type.CHAR); 156 if (c != 0) { 157 value = Integer.valueOf(c); 158 } 159 } 160 161 162 public void setInitValue( final byte b ) { 163 checkType(Type.BYTE); 164 if (b != 0) { 165 value = Integer.valueOf(b); 166 } 167 } 168 169 170 public void setInitValue( final boolean b ) { 171 checkType(Type.BOOLEAN); 172 if (b) { 173 value = Integer.valueOf(1); 174 } 175 } 176 177 178 public void setInitValue( final float f ) { 179 checkType(Type.FLOAT); 180 if (f != 0.0) { 181 value = f; 182 } 183 } 184 185 186 public void setInitValue( final double d ) { 187 checkType(Type.DOUBLE); 188 if (d != 0.0) { 189 value = d; 190 } 191 } 192 193 194 /** Remove any initial value. 195 */ 196 public void cancelInitValue() { 197 value = null; 198 } 199 200 201 private void checkType( final Type atype ) { 202 final Type superType = super.getType(); 203 if (superType == null) { 204 throw new ClassGenException("You haven't defined the type of the field yet"); 205 } 206 if (!isFinal()) { 207 throw new ClassGenException("Only final fields may have an initial value!"); 208 } 209 if (!superType.equals(atype)) { 210 throw new ClassGenException("Types are not compatible: " + superType + " vs. " + atype); 211 } 212 } 213 214 215 /** 216 * Get field object after having set up all necessary values. 217 */ 218 public Field getField() { 219 final String signature = getSignature(); 220 final int name_index = super.getConstantPool().addUtf8(super.getName()); 221 final int signature_index = super.getConstantPool().addUtf8(signature); 222 if (value != null) { 223 checkType(super.getType()); 224 final int index = addConstant(); 225 addAttribute(new ConstantValue(super.getConstantPool().addUtf8("ConstantValue"), 2, index, 226 super.getConstantPool().getConstantPool())); // sic 227 } 228 addAnnotationsAsAttribute(super.getConstantPool()); 229 return new Field(super.getAccessFlags(), name_index, signature_index, getAttributes(), 230 super.getConstantPool().getConstantPool()); // sic 231 } 232 233 private void addAnnotationsAsAttribute(final ConstantPoolGen cp) { 234 final Attribute[] attrs = AnnotationEntryGen.getAnnotationAttributes(cp, super.getAnnotationEntries()); 235 for (final Attribute attr : attrs) { 236 addAttribute(attr); 237 } 238 } 239 240 241 private int addConstant() { 242 switch (super.getType().getType()) { // sic 243 case Const.T_INT: 244 case Const.T_CHAR: 245 case Const.T_BYTE: 246 case Const.T_BOOLEAN: 247 case Const.T_SHORT: 248 return super.getConstantPool().addInteger(((Integer) value)); 249 case Const.T_FLOAT: 250 return super.getConstantPool().addFloat(((Float) value)); 251 case Const.T_DOUBLE: 252 return super.getConstantPool().addDouble(((Double) value)); 253 case Const.T_LONG: 254 return super.getConstantPool().addLong(((Long) value)); 255 case Const.T_REFERENCE: 256 return super.getConstantPool().addString((String) value); 257 default: 258 throw new RuntimeException("Oops: Unhandled : " + super.getType().getType()); // sic 259 } 260 } 261 262 263 @Override 264 public String getSignature() { 265 return super.getType().getSignature(); 266 } 267 268 private List<FieldObserver> observers; 269 270 271 /** Add observer for this object. 272 */ 273 public void addObserver( final FieldObserver o ) { 274 if (observers == null) { 275 observers = new ArrayList<>(); 276 } 277 observers.add(o); 278 } 279 280 281 /** Remove observer for this object. 282 */ 283 public void removeObserver( final FieldObserver o ) { 284 if (observers != null) { 285 observers.remove(o); 286 } 287 } 288 289 290 /** Call notify() method on all observers. This method is not called 291 * automatically whenever the state has changed, but has to be 292 * called by the user after he has finished editing the object. 293 */ 294 public void update() { 295 if (observers != null) { 296 for (final FieldObserver observer : observers ) { 297 observer.notify(this); 298 } 299 } 300 } 301 302 303 public String getInitValue() { 304 if (value != null) { 305 return value.toString(); 306 } 307 return null; 308 } 309 310 311 /** 312 * Return string representation close to declaration format, 313 * `public static final short MAX = 100', e.g.. 314 * 315 * @return String representation of field 316 */ 317 @Override 318 public final String toString() { 319 String name; 320 String signature; 321 String access; // Short cuts to constant pool 322 access = Utility.accessToString(super.getAccessFlags()); 323 access = access.isEmpty() ? "" : (access + " "); 324 signature = super.getType().toString(); 325 name = getName(); 326 final StringBuilder buf = new StringBuilder(32); // CHECKSTYLE IGNORE MagicNumber 327 buf.append(access).append(signature).append(" ").append(name); 328 final String value = getInitValue(); 329 if (value != null) { 330 buf.append(" = ").append(value); 331 } 332 return buf.toString(); 333 } 334 335 336 /** @return deep copy of this field 337 */ 338 public FieldGen copy( final ConstantPoolGen cp ) { 339 final FieldGen fg = (FieldGen) clone(); 340 fg.setConstantPool(cp); 341 return fg; 342 } 343 344 345 /** 346 * @return Comparison strategy object 347 */ 348 public static BCELComparator getComparator() { 349 return bcelComparator; 350 } 351 352 353 /** 354 * @param comparator Comparison strategy object 355 */ 356 public static void setComparator( final BCELComparator comparator ) { 357 bcelComparator = comparator; 358 } 359 360 361 /** 362 * Return value as defined by given BCELComparator strategy. 363 * By default two FieldGen objects are said to be equal when 364 * their names and signatures are equal. 365 * 366 * @see java.lang.Object#equals(java.lang.Object) 367 */ 368 @Override 369 public boolean equals( final Object obj ) { 370 return bcelComparator.equals(this, obj); 371 } 372 373 374 /** 375 * Return value as defined by given BCELComparator strategy. 376 * By default return the hashcode of the field's name XOR signature. 377 * 378 * @see java.lang.Object#hashCode() 379 */ 380 @Override 381 public int hashCode() { 382 return bcelComparator.hashCode(this); 383 } 384 }