Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Able to read or write a particular field in a type. Knows nothing about the instance upon which the read/write may be getting
* done.
*
* @author Andy Clement
* @since 0.5.0
*/
public class FieldReaderWriter {
private static Logger log = Logger.getLogger(FieldReaderWriter.class.getName());
/**
* The type descriptor for the type that defines the field we want to access
*/
protected TypeDescriptor typeDescriptor;
protected FieldMember theField;
public FieldReaderWriter(FieldMember theField, TypeDescriptor typeDescriptor) {
this.theField = theField;
this.typeDescriptor = typeDescriptor;
assert theField.typename == typeDescriptor.typename;
}
protected FieldReaderWriter() {
}
/**
* Set the value of an instance field on the specified instance to the specified value. If a state manager is passed in things
* can be done in a more optimal way, otherwise the state manager has to be discovered from the instance.
*
* @param instance the object instance upon which to set the field
* @param newValue the new value for that field
* @param stateManager the optional state manager for this instance, which will be looked up (expensive) if not passed in
* @throws IllegalAccessException if the field value cannot be set
*/
public void setValue(Object instance, Object newValue, ISMgr stateManager) throws IllegalAccessException {
if (typeDescriptor.isReloadable()) {
if (stateManager == null) {
// Look it up using reflection
stateManager = findInstanceStateManager(instance);
}
String declaringTypeName = typeDescriptor.getName();
Map typeLevelValues = stateManager.getMap().get(declaringTypeName);
if (typeLevelValues == null) {
// first time we've accessed this type for an instance field
typeLevelValues = new HashMap();
stateManager.getMap().put(declaringTypeName, typeLevelValues);
}
typeLevelValues.put(theField.getName(), newValue);
} else { // the type is not reloadable, must use reflection to access the value
// TODO generate get/set in the topmost reloader for these kinds of field and use them?
if (typeDescriptor.isInterface()) {
// field resolution has left us with an interface field, those can't be set like this
throw new IncompatibleClassChangeError("Expected non-static field " + instance.getClass().getName() + "."
+ theField.getName());
} else {
findAndSetFieldValueInHierarchy(instance, newValue);
}
}
}
public void setStaticFieldValue(Class> clazz, Object newValue, SSMgr stateManager) throws IllegalAccessException {
if (clazz == null) {
throw new IllegalStateException();
}
// First decision - is the field part of a reloadable type or not? The typeDescriptor here is the actual owner
// of the field, at this point we *know* this class has this field.
if (typeDescriptor.isReloadable()) {
if (stateManager == null) {
// need to go and find it, there *will* be one but it will be slow to retrieve (reflection)
stateManager = findStaticStateManager(clazz);
}
String declaringTypeName = typeDescriptor.getName();
Map typeLevelValues = stateManager.getMap().get(declaringTypeName);
if (typeLevelValues == null) {
typeLevelValues = new HashMap();
stateManager.getMap().put(declaringTypeName, typeLevelValues);
}
typeLevelValues.put(theField.getName(), newValue);
} else { // the type is not reloadable, must use reflection to access the value
try {
Field f = locateFieldByReflection(clazz, typeDescriptor.getDottedName(), typeDescriptor.isInterface(),
theField.getName());
f.setAccessible(true);
f.set(null, newValue);
// cant cache result - we dont control the sets so won't know it is happening anyway
} catch (Exception e) {
throw new IllegalStateException("Unexpectedly unable to reflectively set the field " + theField.getName()
+ " on the type " + clazz.getName());
}
}
}
/**
* Return the value of the field for which is reader-writer exists. To improve performance a fieldAccessor can be supplied but
* if it is missing the code will go and discover it.
*
* @param instance the instance for which the field should be fetched
* @param stateManager an optional state manager containing the map of values (will be discovered if not supplied)
* @return the value of the field
* @throws IllegalAccessException if there is a problem accessing the field value
*/
public Object getValue(Object instance, ISMgr stateManager) throws IllegalAccessException, IllegalArgumentException {
Object result = null;
String fieldname = theField.getName();
if (typeDescriptor.isReloadable()) {
if (stateManager == null) {
// find it using reflection
stateManager = findInstanceStateManager(instance);
}
String declaringTypeName = typeDescriptor.getName();
Map typeLevelValues = stateManager.getMap().get(declaringTypeName);
boolean knownField = false;
if (typeLevelValues != null) {
knownField = typeLevelValues.containsKey(fieldname);
}
if (knownField) {
result = typeLevelValues.get(fieldname);
}
// If a field has been deleted it may 'reveal' a field in a supertype. The revealed field may be in a type
// not yet dealt with. In this case typeLevelValues may be null (type not seen before) or the typelevelValues
// may not have heard of our field name. In these cases we need to go and find the field and 'relocate' it
// into our map, where it will be processed from now on.
if (typeLevelValues == null || !knownField) {
FieldMember fieldOnOriginalType = typeDescriptor.getReloadableType().getTypeRegistry()
.getReloadableType(declaringTypeName).getTypeDescriptor().getField(fieldname);
if (fieldOnOriginalType != null) {
// Copy the field into the map - that is where it will live from now on
ReloadableType rt = typeDescriptor.getReloadableType();
try {
Field f = rt.getClazz().getDeclaredField(fieldname);
f.setAccessible(true);
result = f.get(instance);
if (typeLevelValues == null) {
typeLevelValues = new HashMap();
stateManager.getMap().put(declaringTypeName, typeLevelValues);
}
typeLevelValues.put(fieldname, result);
} catch (Exception e) {
throw new IllegalStateException("Unexpectedly unable to access field " + fieldname + " on class "
+ rt.getClazz(), e);
}
} else {
// The field was not on the original type. As not seen before, can default it
result = Utils.toResultCheckIfNull(null, theField.getDescriptor());
if (typeLevelValues == null) {
typeLevelValues = new HashMap();
stateManager.getMap().put(declaringTypeName, typeLevelValues);
}
typeLevelValues.put(fieldname, result);
return result;
}
}
if (result != null) {
result = Utils.checkCompatibility(typeDescriptor.getTypeRegistry(), result, theField.getDescriptor());
if (result == null) {
// Was not compatible, forget it
typeLevelValues.remove(fieldname);
}
}
result = Utils.toResultCheckIfNull(result, theField.getDescriptor());
} else {
// the type is not reloadable, must use reflection to access the value.
// TODO measure how often we hit the reflection path, should never happen unless reflection is already on the frame
if (typeDescriptor.isInterface()) { // cant be an instance field if it is found to be on an interface
throw new IncompatibleClassChangeError("Expected non-static field " + instance.getClass().getName() + "."
+ fieldname);
} else {
result = findAndGetFieldValueInHierarchy(instance);
}
}
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.FINER)) {
log.finer(" clazz, SSMgr stateManager) throws IllegalAccessException, IllegalArgumentException {
Object result = null;
if (clazz == null) {
throw new IllegalStateException();
}
// First decision - is the field part of a reloadable type or not? The typeDescriptor here is the actual owner
// of the field, at this point we *know* this class has this field.
if (typeDescriptor.isReloadable()) {
if (stateManager == null) {
// need to go and find it, there *will* be one but it will be slow to retrieve (reflection)
stateManager = findStaticStateManager(clazz);
if (stateManager == null) {
return Utils.toResultCheckIfNull(null, theField.descriptor);
}
}
String declaringTypeName = typeDescriptor.getName();
Map typeLevelValues = stateManager.getMap().get(declaringTypeName);
String fieldname = theField.getName();
boolean knownField = false;
if (typeLevelValues != null) {
knownField = typeLevelValues.containsKey(fieldname);
}
if (knownField) {
result = typeLevelValues.get(fieldname);
}
// If a field has been deleted it may 'reveal' a field in a supertype. The revealed field may be in a type
// not yet dealt with. In this case typeLevelValues may be null (type not seen before) or the typelevelValues
// may not have heard of our field name. In these cases we need to go and find the field and 'relocate' it
// into our map, where it will be processed from now on.
// These revealed fields are not necessarily in the original form of the type so cannot always be accessed via reflection
if (typeLevelValues == null || !knownField) {
FieldMember fieldOnOriginalType = typeDescriptor.getReloadableType().getTypeRegistry()
.getReloadableType(declaringTypeName).getTypeDescriptor().getField(fieldname);
if (fieldOnOriginalType != null) { // && fieldOnOriginalType.isStatic()
// can use reflection
ReloadableType rt = typeDescriptor.getReloadableType();
try {
Field f = rt.getClazz().getDeclaredField(theField.getName());
if (!Modifier.isStatic(f.getModifiers())) {
// need to default it anyway, cant see that original value
// TODO this is a dup of the code below, refactor
result = Utils.toResultCheckIfNull(null, theField.getDescriptor());
if (typeLevelValues == null) {
typeLevelValues = new HashMap();
stateManager.getMap().put(declaringTypeName, typeLevelValues);
}
typeLevelValues.put(fieldname, result);
return result;
}
f.setAccessible(true);
// TODO can fail on this next line if the field we've found is non-static
result = f.get(null);
if (typeLevelValues == null) {
typeLevelValues = new HashMap();
stateManager.getMap().put(declaringTypeName, typeLevelValues);
}
typeLevelValues.put(theField.getName(), result);
} catch (Exception e) {
throw new IllegalStateException("Unexpectedly unable to read field " + theField.getName() + " on type "
+ rt.getClazz(), e);
}
} else {
// The field was not on the original type. As not seen before, can default it
result = Utils.toResultCheckIfNull(null, theField.getDescriptor());
if (typeLevelValues == null) {
typeLevelValues = new HashMap();
stateManager.getMap().put(declaringTypeName, typeLevelValues);
}
typeLevelValues.put(fieldname, result);
return result;
}
}
// A problem that can occur is if a fields type is changed on a reload. If the field
// was previously written to we can then retrieve it and attempt to pass it back to the caller
// and they'll get something unexpected.
if (result != null) {
result = Utils.checkCompatibility(typeDescriptor.getTypeRegistry(), result, theField.getDescriptor());
if (result == null) {
typeLevelValues.remove(theField.getName());
}
}
result = Utils.toResultCheckIfNull(result, theField.getDescriptor());
} else { // the type is not reloadable, must use reflection to access the value
// TODO measure how often this code gets hit - ensure it does not in the common (non reflective) case
try {
Field f = locateFieldByReflection(clazz, typeDescriptor.getDottedName(), typeDescriptor.isInterface(),
theField.getName());
f.setAccessible(true);
result = f.get(null);
// cant cache result - we dont control the sets so won't know it is happening anyway
} catch (Exception e) {
throw new IllegalStateException("Unexpectedly unable to set static field " + theField.getName() + " on type "
+ typeDescriptor.getDottedName());
}
}
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.FINER)) {
log.finer(" clazz, String typeWanted, boolean isInterface, String name) {
if (clazz.getName().equals(typeWanted)) {
Field[] fs = clazz.getDeclaredFields();
if (fs != null) {
for (Field f : fs) {
if (f.getName().equals(name)) {
return f;
}
}
}
}
// Check interfaces
if (!isInterface) { // not worth looking!
Class>[] interfaces = clazz.getInterfaces();
if (interfaces != null) {
for (Class> intface : interfaces) {
Field f = locateFieldByReflection(intface, typeWanted, isInterface, name);
if (f != null) {
return f;
}
}
}
}
// Check superclass
Class> superclass = clazz.getSuperclass();
if (superclass == null) {
return null;
} else {
return locateFieldByReflection(superclass, typeWanted, isInterface, name);
}
}
/**
* Discover the instance state manager for the specific object instance. Will fail by exception rather than returning null.
*
* @param instance the object instance on which to look
* @return the discovered state manager
*/
private ISMgr findInstanceStateManager(Object instance) {
Class> clazz = typeDescriptor.getReloadableType().getClazz();
try {
Field fieldAccessorField = clazz.getField(Constants.fInstanceFieldsName);
if (fieldAccessorField == null) {
throw new IllegalStateException("Cant find field accessor for type " + clazz.getName());
}
ISMgr stateManager = (ISMgr) fieldAccessorField.get(instance);
if (stateManager == null) {
// Looks to not have been initialized yet, this can happen if a non standard ctor was used.
// We could push this step into the generated ctors...
ISMgr instanceStateManager = new ISMgr(instance, typeDescriptor.getReloadableType());
fieldAccessorField.set(instance,instanceStateManager);
stateManager = (ISMgr) fieldAccessorField.get(instance);
// For some reason it didn't stick!
if (stateManager == null) {
throw new IllegalStateException("The class '" + clazz.getName()
+ "' has a null instance state manager object, instance is " + instance);
}
}
return stateManager;
} catch (Exception e) {
throw new IllegalStateException("Unexpectedly unable to find instance state manager on class " + clazz.getName(), e);
}
}
/**
* Discover the static state manager on the specified class and return it. Will fail by exception rather than returning null.
*
* @param clazz the class on which to look
* @return the discovered state manager
*/
private SSMgr findStaticStateManager(Class> clazz) {
try {
Field stateManagerField = clazz.getField(Constants.fStaticFieldsName);
if (stateManagerField == null) {
throw new IllegalStateException("Cant find field accessor for type " + typeDescriptor.getReloadableType().getName());
}
SSMgr stateManager = (SSMgr) stateManagerField.get(null);
// Field should always have been initialized - it is done at the start of the top most reloadable type
if (stateManager == null) {
throw new IllegalStateException("Instance of this class has no state manager: " + clazz.getName());
}
return stateManager;
} catch (Exception e) {
throw new IllegalStateException("Unexpectedly unable to find static state manager on class " + clazz.getName(), e);
}
}
public boolean isStatic() {
return theField.isStatic();
}
/**
* Walk up the instance hierarchy looking for the field, and when it is found access it and return the result. Will exit via
* exception if it cannot find the field or something goes wrong when accessing it.
*
* @param instance the object instance upon which the field is being accessed
* @return the value of the field
*/
private Object findAndGetFieldValueInHierarchy(Object instance) {
Class> clazz = instance.getClass();
String fieldname = theField.getName();
String searchName = typeDescriptor.getName().replace('/', '.');
while (clazz != null && !clazz.getName().equals(searchName)) {
clazz = clazz.getSuperclass();
}
if (clazz == null) {
throw new IllegalStateException("Failed to find " + searchName + " in hierarchy of " + instance.getClass());
}
try {
Field f = clazz.getDeclaredField(fieldname);
f.setAccessible(true);
return f.get(instance);
} catch (Exception e) {
throw new IllegalStateException("Unexpectedly could not access field named " + fieldname + " on class "
+ clazz.getName());
}
}
/**
* Walk up the instance hierarchy looking for the field, and when it is found set it. Will exit via exception if it cannot find
* the field or something goes wrong when accessing it.
*
* @param instance the object instance upon which the field is being set
* @param newValue the new value for the field
*/
private void findAndSetFieldValueInHierarchy(Object instance, Object newValue) {
Class> clazz = instance.getClass();
String fieldname = theField.getName();
String searchName = typeDescriptor.getName().replace('/', '.');
while (clazz != null && !clazz.getName().equals(searchName)) {
clazz = clazz.getSuperclass();
}
if (clazz == null) {
throw new IllegalStateException("Failed to find " + searchName + " in hierarchy of " + instance.getClass());
}
try {
Field f = clazz.getDeclaredField(fieldname);
f.setAccessible(true);
f.set(instance, newValue);
} catch (Exception e) {
throw new IllegalStateException("Unexpectedly could not access field named " + fieldname + " on class "
+ clazz.getName());
}
}
}