/* Copyright 1996-2008 Ariba, Inc. 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. $Id: //ariba/platform/util/core/ariba/util/fieldvalue/FieldValue.java#6 $ */ package ariba.util.fieldvalue; import ariba.util.core.ClassExtension; import ariba.util.core.ClassExtensionRegistry; /** The FieldValue class defines a {@link ClassExtension} for JavaBean-like property access on target objects. Static methods {@link #getFieldValue(Object, String)} and {@link #setFieldValue(Object, String, Object)} are provided for convenience. Higher performance access can be achieved by caching {@link FieldPath} instances for particular key paths. Not only does that avoid per-access accessor lookup, but the field path caches {@link FieldValueSetter} and {@link FieldValueGetter} instances from its most recent lookup and can reuse them if the next access targets the same class. <p> The default implementation of FieldValue is {@link FieldValue_Object} -- it resolves property access using reflection to get/set using accessor methods or direct field access. (note that, unlike JavaBeans, FieldValue_Object will resolve a get on key "foo" by checking for foo(), getFoo, and the fields named foo and _foo). <p> Other specialized implementation of FieldValue exist for Maps, XML Nodes, etc. */ abstract public class FieldValue extends ClassExtension { public static final int Setter = 0; public static final int Getter = Setter + 1; protected static final ClassExtensionRegistry FieldValueClassExtensionRegistry = new ClassExtensionRegistry(); /** Force the loading/initialization of the FieldValue_Object. */ static { Class dummyClass = FieldValue_Object.class; registerClassExtension(Object.class, new FieldValue_Object()); registerClassExtension(Class.class, new FieldValue_Class()); registerClassExtension(java.util.Map.class, new FieldValue_JavaHashtable()); registerClassExtension(Extensible.class, new FieldValue_Extensible()); } /** Put a ClassExtension implementation of the FieldValue interface into the cache of categories which is used to dispatch the FieldValue cover methods in this class. Note that the FieldValue_Object will be cloned before caching, so you cannot depend upon getting back the exact same instance later with get(...). @param targetObjectClass the root class for which the classExtension applies @param fieldValueClassExtension the classExtension implementation of the FieldValue interface */ public static void registerClassExtension ( Class targetObjectClass, FieldValue fieldValueClassExtension) { FieldValueClassExtensionRegistry.registerClassExtension(targetObjectClass, fieldValueClassExtension); } /** Retrieve a ClassExtension registered by registerClassExtension(...). Note that this will clone the ClassExtension objects which are registered so that each subclass will have its own classExtension implementation. See ClassExtensionRegistry for details on this. @param targetClass the class for which a classExtension applies @return the classExtension which applies for the target */ public static FieldValue get (Class targetClass) { return (FieldValue)FieldValueClassExtensionRegistry.get(targetClass); } public static FieldValue get (Object target) { return (FieldValue)FieldValueClassExtensionRegistry.get(target.getClass()); } /** Converts fieldPathString into a FieldPath (from a pool of shared FieldPaths) and calls setFieldValue(FieldPath, Object). @param target see the FieldPath version of this method @param fieldPathString the dotted fieldPath String from which a shared FieldPath instance will be obtained @param value see the FieldPath version of this method */ public static void setFieldValue (Object target, String fieldPathString, Object value) { FieldPath fieldPath = FieldPath.sharedFieldPath(fieldPathString); FieldValue.get(target).setFieldValue(target, fieldPath, value); } /** Converts fieldPathString into a FieldPath (from a pool of shared FieldPaths) and calls getFieldValue(FieldPath). @param target see the FieldPath version of this method @param fieldPathString the dotted fieldPath String from which a shared FieldPath instance will be obtained @return see the FieldPath version of this method */ public static Object getFieldValue (Object target, String fieldPathString) { FieldPath fieldPath = FieldPath.sharedFieldPath(fieldPathString); return FieldValue.get(target).getFieldValue(target, fieldPath); } /** Creates and returns a new FieldValueAccessor (by default, a ReflectionFieldValueAccessor) for the given target and fieldName. No caching is done by this method. This method is designed to be overridden by subclasses of FieldValue_Object which want to define their own specialized accessors. Note that the target is passed rather than its class so that accessors can be created at a finer granularity than class. Certain meta-data driven classes require this flexibility. @param target the object for which the accessor will be created @param fieldName the name of the field for which the accessor will be created @return a new FieldValueAccessor (ReflectionFieldValueAccessor by default) */ abstract public FieldValueAccessor createAccessor (Object target, String fieldName, int type); /** Maintains a cache of FieldValueAccessor's for the instance by fieldName. In general, all instances of a given class will share the same FieldValueAccessor for a given fieldName. However, certain meta-data driven classes may need to introspect upon the instance before returning the proper accessor. @param target the object for which the accessor will be looked up @param fieldName the name of the field for the accessor @return the cached FieldValueAccessor (ReflectionFieldValueAccessor by default) */ abstract public FieldValueAccessor getAccessor (Object target, String fieldName, int type); /** Sets the value on the reveiver using the fieldName indicated by fieldPath -- only the first node of the fieldPath is considered if it is a multi-node path. @param target the object on which the value will be set for the field identiifed by fieldPath @param fieldPath the fieldPath node (which contains a candidate accessor) to be used to set the value on target. @param value the value to set on the target */ abstract public void setFieldValuePrimitive (Object target, FieldPath fieldPath, Object value); /** Gets the value from the reveiver using the fieldName indicated by fieldPath -- only the first node of the fieldPath is considered if it is a multi-node path. @param target the object from which to get the value of the field identified by fieldPath @param fieldPath the fieldPath node which identifes the field to get. @return the value obtained from the target using the fieldPath */ abstract public Object getFieldValuePrimitive (Object target, FieldPath fieldPath); /** Recursively calls getFieldValuePrimitive() with the head of the fieldPath list up to the last fieldPath node and then calls setFieldValuePrimitive(). Each time the recursion iterates, the receiver is the value of the previous getFieldValuePrimitive(). @param target the object on which to start the recursion for setting the value using the fieldPath @param fieldPath the linked list of fieldPath nodes used to navigate from target to the final object in the chain, upon which the value is set @param value the value which is set on the final object in the chain */ abstract public void setFieldValue (Object target, FieldPath fieldPath, Object value); /** Recursively calls getFieldValuePrimitive() with the head of the fieldPath list. Each time the recursion iterates, the receiver is the value of the previous getFieldValuePrimitive(). @param target the first object from which to start the recursion for getting the value identified by the fieldPath. @param fieldPath the linked list of fieldPath nodes that identifes the value to get @return the value obtained from the last object in the chain */ abstract public Object getFieldValue (Object target, FieldPath fieldPath); /** Called by FieldInfo.fieldInfoForClass() to populate FieldInfo.Collection with information on available fields. Implementing FieldValue extensions should callback with FieldInfo.Collection.updateFieldInfo() to provide information for all usable fields. Fields should be populated in a first-declaration-first manner -- i.e. super class before class, fields in java declaraion order where possible -- registration order is used to determine the "rank" recorded on the field. @param targetClass class for which info should be provided. @param collection repository to populate */ public void populateFieldInfo (Class targetClass, FieldInfo.Collection collection) {} }