1 /*
   2  * Copyright (c) 2017, 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 
  21 package com.sun.org.apache.bcel.internal.generic;
  22 
  23 
  24 import com.sun.org.apache.bcel.internal.Constants;
  25 import com.sun.org.apache.bcel.internal.classfile.*;
  26 import java.util.ArrayList;
  27 import java.util.Iterator;
  28 
  29 /**
  30  * Template class for building up a field.  The only extraordinary thing
  31  * one can do is to add a constant value attribute to a field (which must of
  32  * course be compatible with to the declared type).
  33  *
  34  * @author  <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
  35  * @see Field
  36  */
  37 public class FieldGen extends FieldGenOrMethodGen {
  38   private Object value = null;
  39 
  40   /**
  41    * Declare a field. If it is static (isStatic() == true) and has a
  42    * basic type like int or String it may have an initial value
  43    * associated with it as defined by setInitValue().
  44    *
  45    * @param access_flags access qualifiers
  46    * @param type  field type
  47    * @param name field name
  48    * @param cp constant pool
  49    */
  50   public FieldGen(int access_flags, Type type, String name, ConstantPoolGen cp) {
  51     setAccessFlags(access_flags);
  52     setType(type);
  53     setName(name);
  54     setConstantPool(cp);
  55   }
  56 
  57   /**
  58    * Instantiate from existing field.
  59    *
  60    * @param field Field object
  61    * @param cp constant pool (must contain the same entries as the field's constant pool)
  62    */
  63   public FieldGen(Field field, ConstantPoolGen cp) {
  64     this(field.getAccessFlags(), Type.getType(field.getSignature()), field.getName(), cp);
  65 
  66     Attribute[] attrs = field.getAttributes();
  67 
  68     for(int i=0; i < attrs.length; i++) {
  69       if(attrs[i] instanceof ConstantValue)
  70         setValue(((ConstantValue)attrs[i]).getConstantValueIndex());
  71       else
  72         addAttribute(attrs[i]);
  73     }
  74   }
  75 
  76   private void setValue(int index) {
  77     ConstantPool cp  = this.cp.getConstantPool();
  78     Constant     c   = cp.getConstant(index);
  79     value = ((ConstantObject)c).getConstantValue(cp);
  80   }
  81 
  82   /**
  83    * Set (optional) initial value of field, otherwise it will be set to null/0/false
  84    * by the JVM automatically.
  85    */
  86   public void setInitValue(String str) {
  87     checkType(new ObjectType("java.lang.String"));
  88 
  89     if(str != null)
  90       value = str;
  91   }
  92 
  93   public void setInitValue(long l) {
  94     checkType(Type.LONG);
  95 
  96     if(l != 0L)
  97       value = Long.valueOf(l);
  98   }
  99 
 100   public void setInitValue(int i) {
 101     checkType(Type.INT);
 102 
 103     if(i != 0)
 104       value = Integer.valueOf(i);
 105   }
 106 
 107   public void setInitValue(short s) {
 108     checkType(Type.SHORT);
 109 
 110     if(s != 0)
 111       value = Integer.valueOf(s);
 112   }
 113 
 114   public void setInitValue(char c) {
 115     checkType(Type.CHAR);
 116 
 117     if(c != 0)
 118       value = Integer.valueOf(c);
 119   }
 120 
 121   public void setInitValue(byte b) {
 122     checkType(Type.BYTE);
 123 
 124     if(b != 0)
 125       value = Integer.valueOf(b);
 126   }
 127 
 128   public void setInitValue(boolean b) {
 129     checkType(Type.BOOLEAN);
 130 
 131     if(b)
 132       value = Integer.valueOf(1);
 133   }
 134 
 135   public void setInitValue(float f) {
 136     checkType(Type.FLOAT);
 137 
 138     if(f != 0.0)
 139       value = Float.valueOf(f);
 140   }
 141 
 142   public void setInitValue(double d) {
 143     checkType(Type.DOUBLE);
 144 
 145     if(d != 0.0)
 146       value = Double.valueOf(d);
 147   }
 148 
 149   /** Remove any initial value.
 150    */
 151   public void cancelInitValue() {
 152     value = null;
 153   }
 154 
 155   private void checkType(Type atype) {
 156     if(type == null)
 157       throw new ClassGenException("You haven't defined the type of the field yet");
 158 
 159     if(!isFinal())
 160       throw new ClassGenException("Only final fields may have an initial value!");
 161 
 162     if(!type.equals(atype))
 163       throw new ClassGenException("Types are not compatible: " + type + " vs. " + atype);
 164   }
 165 
 166   /**
 167    * Get field object after having set up all necessary values.
 168    */
 169   public Field getField() {
 170     String      signature       = getSignature();
 171     int         name_index      = cp.addUtf8(name);
 172     int         signature_index = cp.addUtf8(signature);
 173 
 174     if(value != null) {
 175       checkType(type);
 176       int index = addConstant();
 177       addAttribute(new ConstantValue(cp.addUtf8("ConstantValue"),
 178                                      2, index, cp.getConstantPool()));
 179     }
 180 
 181     return new Field(access_flags, name_index, signature_index, getAttributes(),
 182                      cp.getConstantPool());
 183   }
 184 
 185   private int addConstant() {
 186     switch(type.getType()) {
 187     case Constants.T_INT: case Constants.T_CHAR: case Constants.T_BYTE:
 188     case Constants.T_BOOLEAN: case Constants.T_SHORT:
 189       return cp.addInteger(((Integer)value).intValue());
 190 
 191     case Constants.T_FLOAT:
 192       return cp.addFloat(((Float)value).floatValue());
 193 
 194     case Constants.T_DOUBLE:
 195       return cp.addDouble(((Double)value).doubleValue());
 196 
 197     case Constants.T_LONG:
 198       return cp.addLong(((Long)value).longValue());
 199 
 200     case Constants.T_REFERENCE:
 201       return cp.addString(((String)value));
 202 
 203     default:
 204       throw new RuntimeException("Oops: Unhandled : " + type.getType());
 205     }
 206   }
 207 
 208   public String  getSignature()  { return type.getSignature(); }
 209 
 210   private ArrayList observers;
 211 
 212   /** Add observer for this object.
 213    */
 214   public void addObserver(FieldObserver o) {
 215     if(observers == null)
 216       observers = new ArrayList();
 217 
 218     observers.add(o);
 219   }
 220 
 221   /** Remove observer for this object.
 222    */
 223   public void removeObserver(FieldObserver o) {
 224     if(observers != null)
 225       observers.remove(o);
 226   }
 227 
 228   /** Call notify() method on all observers. This method is not called
 229    * automatically whenever the state has changed, but has to be
 230    * called by the user after he has finished editing the object.
 231    */
 232   public void update() {
 233     if(observers != null)
 234       for(Iterator e = observers.iterator(); e.hasNext(); )
 235         ((FieldObserver)e.next()).notify(this);
 236   }
 237 
 238   public String getInitValue() {
 239     if(value != null) {
 240       return value.toString();
 241     } else
 242       return null;
 243   }
 244 
 245   /**
 246    * Return string representation close to declaration format,
 247    * `public static final short MAX = 100', e.g..
 248    *
 249    * @return String representation of field
 250    */
 251   public final String toString() {
 252     String name, signature, access; // Short cuts to constant pool
 253 
 254     access    = Utility.accessToString(access_flags);
 255     access    = access.equals("")? "" : (access + " ");
 256     signature = type.toString();
 257     name      = getName();
 258 
 259     StringBuffer buf = new StringBuffer(access + signature + " " + name);
 260     String value = getInitValue();
 261 
 262     if(value != null)
 263       buf.append(" = " + value);
 264 
 265     return buf.toString();
 266   }
 267 
 268   /** @return deep copy of this field
 269    */
 270   public FieldGen copy(ConstantPoolGen cp) {
 271     FieldGen fg = (FieldGen)clone();
 272 
 273     fg.setConstantPool(cp);
 274     return fg;
 275   }
 276 }