FieldUtils.java :  » Net » Terracotta » com » tc » util » Java Open Source

Java Open Source » Net » Terracotta 
Terracotta » com » tc » util » FieldUtils.java
/*
 * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
 * notice. All rights reserved.
 */
package com.tc.util;

import sun.reflect.FieldAccessor;

import com.tc.asm.Type;
import com.tc.exception.TCRuntimeException;
import com.tc.object.bytecode.ByteCodeUtil;
import com.tc.object.bytecode.Manageable;
import com.tc.object.bytecode.ManagerUtil;
import com.tc.object.bytecode.TransparentAccess;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class FieldUtils {
  public final static String CLASS       = "com/tc/util/FieldUtils";

  public final static String GET_DESC    = "(Ljava/lang/Object;Ljava/lang/reflect/Field;Lsun/reflect/FieldAccessor;)";

  private static ThreadLocal allowAccess = new ThreadLocal();

  private static boolean isTCField(Field field) {
    return field.getName().startsWith(ByteCodeUtil.TC_FIELD_PREFIX);
  }

  public static Object get(Object obj, Field field, FieldAccessor fieldAccessor) {
    if (isTCField(field)) {
      return null;
    } else if (!isStaticAndNonRootField(field)) {
      if (ManagerUtil.isRoot(field) || ManagerUtil.isPhysicallyInstrumented(field.getDeclaringClass())) {
        if ((obj instanceof TransparentAccess) && !isStaticField(field)) {
          return resolveReference((TransparentAccess) obj, field);
        } else {
          return resolveReference(obj, field);
        }
      }
    }
    // XXX: disallow field reads of shared logical objects?
    return fieldAccessor.get(obj);
  }

  private static void throwIllegalArgumentException(String type) {
    StringBuffer sb = new StringBuffer("The argument of type ");
    sb.append(type);
    sb.append(" is illegal.");
    throw new IllegalArgumentException(type);
  }

  public static boolean setBoolean(Object obj, boolean value, Field field) throws IllegalAccessException {
    if (Type.getType(field.getType()).getSort() != Type.BOOLEAN) {
      throwIllegalArgumentException(field.getType().getName());
    }

    return set(obj, new Boolean(value), field);
  }

  public static boolean setFloat(Object obj, float value, Field field) throws IllegalAccessException {
    Type fieldType = Type.getType(field.getType());

    switch (fieldType.getSort()) {
      case Type.FLOAT:
        return set(obj, new Float(value), field);
      default:
        return setDouble(obj, value, field);
    }
  }

  public static boolean setDouble(Object obj, double value, Field field) throws IllegalAccessException {
    if (Type.getType(field.getType()).getSort() != Type.DOUBLE) {
      throwIllegalArgumentException(field.getType().getName());
    }

    return set(obj, new Double(value), field);
  }

  public static boolean setChar(Object obj, char value, Field field) throws IllegalAccessException {
    Type fieldType = Type.getType(field.getType());

    switch (fieldType.getSort()) {
      case Type.CHAR:
        return set(obj, new Character(value), field);
      default:
        return setInt(obj, value, field);
    }
  }

  public static boolean setByte(Object obj, byte value, Field field) throws IllegalAccessException {
    Type fieldType = Type.getType(field.getType());

    switch (fieldType.getSort()) {
      case Type.BYTE:
        return set(obj, new Byte(value), field);
      default:
        return setShort(obj, value, field);
    }
  }

  public static boolean setShort(Object obj, short value, Field field) throws IllegalAccessException {
    Type fieldType = Type.getType(field.getType());

    switch (fieldType.getSort()) {
      case Type.SHORT:
        return set(obj, new Short(value), field);
      default:
        return setInt(obj, value, field);
    }
  }

  public static boolean setInt(Object obj, int value, Field field) throws IllegalAccessException {
    Type fieldType = Type.getType(field.getType());

    switch (fieldType.getSort()) {
      case Type.INT:
        return set(obj, new Integer(value), field);
      default:
        return setLong(obj, value, field);
    }
  }

  public static boolean setLong(Object obj, long value, Field field) throws IllegalAccessException {
    Type fieldType = Type.getType(field.getType());

    switch (fieldType.getSort()) {
      case Type.LONG:
        return set(obj, new Long(value), field);
      default:
        return setFloat(obj, value, field);
    }
  }

  /*
   * This method bypasses our check to set values to shared objects thru reflection. This is used from TC code base when
   * this is needed. (like subclass of TreeMap, LinkedHashMap cases)
   */
  public static void tcSet(Object target, Object value, Field field) throws IllegalArgumentException,
      IllegalAccessException {
    allowAccess.set(field);
    try {
      field.set(target, value);
    } finally {
      allowAccess.set(null);
    }

  }

  private static boolean accessAllowed(Field field) {
    return field == allowAccess.get();
  }

  public static boolean set(Object obj, Object value, Field field) throws IllegalAccessException {
    if (isTCField(field)) { return true; }

    if (accessAllowed(field)) {
      // returning false allows the orignial uninstrumented code to run.
      return false;
    }

    if (isStaticAndNonRootField(field)) { return false; }

    if (ManagerUtil.isRoot(field)) {
      if ((obj instanceof TransparentAccess) && !isStaticField(field)) {
        setValue((TransparentAccess) obj, field, value);
      } else {
        // This is an exception handling since we allow defining a field of an non-
        // instrumented class to be a root.
        setValue(obj, field, value);
      }
      return true;
    }

    if ((obj instanceof Manageable) && (((Manageable) obj).__tc_managed() != null)) {
      if (ManagerUtil.isLogical(obj)) {
        //
        throw new IllegalAccessException(
                                         "Field modification through reflection for non-physical shared object of type "
                                             + obj.getClass().getName() + " is not supported!");
      }

      if (!TransparentAccess.class.isAssignableFrom(field.getDeclaringClass())) {
        //
        throw new IllegalAccessException(
                                         "Field modification through reflection for fields of non-physically instrumented type "
                                             + obj.getClass().getName() + " is not supported!");
      }

      if (obj instanceof TransparentAccess) {
        // field of physically managed object
        setValue((TransparentAccess) obj, field, value);
        return true;
      }
    }

    return false;
  }

  private static boolean isStaticField(Field field) {
    return Modifier.isStatic(field.getModifiers());
  }

  private static boolean isStaticAndNonRootField(Field field) {
    return isStaticField(field) && !ManagerUtil.isRoot(field);
  }

  private static Object resolveReference(TransparentAccess obj, Field field) {
    // XXX: deal with statics
    return obj.__tc_getmanagedfield(fullFieldName(field));
  }

  private static Object resolveReference(Object obj, Field field) {
    String fieldGetterMethodName = fieldGetterMethod(field.getName());
    try {
      Method m = field.getDeclaringClass().getDeclaredMethod(fieldGetterMethodName, null);
      m.setAccessible(true);
      Object retValue = m.invoke(obj, null);
      return retValue;
    } catch (NoSuchMethodException e) {
      throw new TCRuntimeException(e);
    } catch (InvocationTargetException e) {
      throw new TCRuntimeException(e);
    } catch (IllegalArgumentException e) {
      throw new TCRuntimeException(e);
    } catch (IllegalAccessException e) {
      throw new TCRuntimeException(e);
    }
  }

  private static void setValue(Object obj, Field field, Object value) {
    String fieldSetterMethodName = fieldSetterMethod(field.getName());
    Class[] setterArgumentsTypes = new Class[] { field.getType() };
    try {
      Method m = field.getDeclaringClass().getDeclaredMethod(fieldSetterMethodName, setterArgumentsTypes);
      m.setAccessible(true);
      m.invoke(obj, new Object[] { value });
    } catch (NoSuchMethodException e) {
      throw new TCRuntimeException(e);
    } catch (InvocationTargetException e) {
      throw new TCRuntimeException(e);
    } catch (IllegalArgumentException e) {
      throw new TCRuntimeException(e);
    } catch (IllegalAccessException e) {
      throw new TCRuntimeException(e);
    }

  }

  private static void setValue(TransparentAccess obj, Field field, Object value) {
    // XXX: deal with statics
    obj.__tc_setmanagedfield(fullFieldName(field), value);
  }

  private static String fullFieldName(Field field) {
    return new StringBuffer(field.getDeclaringClass().getName()).append('.').append(field.getName()).toString();
  }

  /**
   * fieldGetterMethod and fieldSetterMethod methods are copied from ByteCodeUtil in order not to put ByteCodeUtil in
   * the boot jar.
   */
  private static String fieldGetterMethod(String fieldName) {
    return ByteCodeUtil.TC_METHOD_PREFIX + "get" + fieldName;
  }

  private static String fieldSetterMethod(String fieldName) {
    return ByteCodeUtil.TC_METHOD_PREFIX + "set" + fieldName;
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.