/**
* Redistribution and use of this software and associated documentation
* ("Software"), with or without modification, are permitted provided
* that the following conditions are met:
*
* 1. Redistributions of source code must retain copyright
* statements and notices. Redistributions must also contain a
* copy of this document.
*
* 2. Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. The name "Exolab" must not be used to endorse or promote
* products derived from this Software without prior written
* permission of Intalio, Inc. For written permission,
* please contact info@exolab.org.
*
* 4. Products derived from this Software may not be called "Exolab"
* nor may "Exolab" appear in their names without prior written
* permission of Intalio, Inc. Exolab is a registered
* trademark of Intalio, Inc.
*
* 5. Due credit should be given to the Exolab Project
* (http://www.exolab.org/).
*
* THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Copyright 1999-2003 (C) Intalio, Inc. All Rights Reserved.
*
* $Id$
*/
package org.exolab.castor.mapping.loader;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.InvocationTargetException;
import java.util.Enumeration;
import java.util.Iterator;
import org.castor.core.util.Messages;
import org.exolab.castor.core.exceptions.CastorIllegalStateException;
import org.exolab.castor.mapping.AbstractFieldHandler;
import org.exolab.castor.mapping.ExtendedFieldHandler;
import org.exolab.castor.mapping.FieldDescriptor;
import org.exolab.castor.mapping.FieldHandler;
import org.exolab.castor.mapping.GeneralizedFieldHandler;
import org.exolab.castor.mapping.TypeConvertor;
import org.exolab.castor.mapping.CollectionHandler;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.mapping.MappingRuntimeException;
import org.exolab.castor.util.IteratorEnumeration;
/**
* A field handler that knows how to get/set the values of a field
* directly or through the get/set methods. Uses reflection.
* <p>
* Note: the field Java type is obtained from {@link TypeInfo#getFieldType()},
* but if the field is a collection, the actual field/accessor type is
* obtained from {@link TypeInfo#getCollectionHandler} and the object to create
* (with {@link #newInstance(Object)}) is the former field type.
*
* @author <a href="arkin@intalio.com">Assaf Arkin</a>
* @version $Revision$ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $
*/
public final class FieldHandlerImpl
extends AbstractFieldHandler
{
/**
* The prefix for an "add" method
**/
private static final String ADD_PREFIX = "add";
/**
* The prefix for an "enum" method
*/
private static final String ENUM_PREFIX = "enum";
/**
* The prefix for an "iter" method
*/
private static final String ITER_PREFIX = "iter";
/**
* The underlying field handler used by this handler.
*/
private final FieldHandler _handler;
/**
* The Java field described and accessed through this descriptor.
*/
private final Field _field;
/**
* The sequence of methods used to obtain the nested field. May be null.
*/
private Method[] _getSequence;
/**
* The sequence of methods used to create the nested object. May be null.
*/
private Method[] _setSequence;
/**
* The method used to "incrementally" set the value of this field.
* This is only used if the field is a collection
*/
private Method _addMethod;
/**
* The method used to enumerate entries of a container.
*/
private Method _enumMethod;
/**
* The method used to iterate over a container.
*/
private Method _iterMethod;
/**
* The method used to obtain the value of this field. May be null.
*/
private Method _getMethod;
/**
* The method used to set the value of this field. May be null.
*/
private Method _setMethod;
/**
* The method used to check if the value of this field exists. May be null.
*/
private Method _hasMethod;
/**
* The method used to delete the value of this field. May be null.
*/
private Method _deleteMethod;
/**
* The method used to create a new instance of the field.
*/
private Method _createMethod;
/**
* The Java field name.
*/
private final String _fieldName;
/**
* The Java field type.
*/
private final Class _fieldType;
/**
* True if this field is an immutable type.
*/
private final boolean _immutable;
/**
* The default value for primitive fields. Will be set if the field is null.
*/
private final Object _default;
/**
* Convertor to apply when setting the value of the field. Converts from
* the value to the field type. Null if no convertor is required.
*/
private TypeConvertor _convertTo = null;
/**
* Convertor to apply when reading the value of the field. Converts from
* the field type to the return value. Null if no convertor is required.
*/
private TypeConvertor _convertFrom = null;
/**
* The collection handler for multi valued fields.
*/
private final CollectionHandler _colHandler;
/**
* Construct a new field handler for the specified field. The field must
* be public, and may not be static or transient. The field name is
* determined from the Java field, the type from the type information.
*
* @param handler
* @param typeInfo Type information
*/
public FieldHandlerImpl( FieldHandler handler, TypeInfo typeInfo )
{
_handler = handler;
_field = null;
_fieldName = handler.toString();
_fieldType = Types.typeFromPrimitive( typeInfo.getFieldType() );
_immutable = typeInfo.isImmutable();
_default = typeInfo.getDefaultValue();
_convertTo = typeInfo.getConvertorTo();
_convertFrom = typeInfo.getConvertorFrom();
_colHandler = typeInfo.getCollectionHandler();
}
/**
* Construct a new field handler for the specified field. The field must
* be public, and may not be static or transient. The field name is
* determined from the Java field, the type from the type information.
*
* @param field The field being described
* @param typeInfo Type information
* @throws MappingException If the field is not public, is static or
* transient
*/
public FieldHandlerImpl( Field field, TypeInfo typeInfo )
throws MappingException
{
if ( field.getModifiers() != Modifier.PUBLIC &&
field.getModifiers() != ( Modifier.PUBLIC | Modifier.VOLATILE ) )
throw new MappingException( "mapping.fieldNotAccessible", field.getName(),
field.getDeclaringClass().getName() );
_handler = null;
_field = field;
_fieldType = Types.typeFromPrimitive( typeInfo.getFieldType() );
_fieldName = field.getName() + "(" + field.getType().getName() + ")";
_immutable = typeInfo.isImmutable();
// If the field is of a primitive type or if it is required
// we use the default value
if ( _field.getType().isPrimitive() )
_default = typeInfo.getDefaultValue();
else
_default = null;
_convertTo = typeInfo.getConvertorTo();
_convertFrom = typeInfo.getConvertorFrom();
_colHandler = typeInfo.getCollectionHandler();
}
/**
* Construct a new field handler for the specified field that
* is accessed through the accessor methods (get/set). The accessor
* methods must be public and not static. The field name is
* required for descriptive purposes. The field type must match
* the return value of the get method and the single parameter of
* the set method. Either get or set methods are optional.
*
* @param fieldName The field being described
* @param getMethod The method used to retrieve the field value,
* must accept no parameters and have a return type castable to
* the field type
* @param setMethod The method used to set the field value, must
* accept a single parameter that is castable to the field type
* @param typeInfo Type information
* @throws MappingException If the get or set method are not public,
* are static, or do not specify the proper types
*
*/
public FieldHandlerImpl( String fieldName, Method[] getSequence, Method[] setSequence,
Method getMethod, Method setMethod, TypeInfo typeInfo )
throws MappingException
{
_handler = null;
_field = null;
if ( fieldName == null )
throw new IllegalArgumentException( "Argument 'fieldName' is null" );
// Originally commented out by Oleg....not sure why?
// if ( getMethod == null && setMethod == null )
// throw new IllegalArgumentException( "Both arguments 'getMethod' and 'setMethod' are null" );
_getSequence = getSequence;
_setSequence = setSequence;
if ( setMethod != null ) {
//-- might be an "add" method
if ( setMethod.getName().startsWith(ADD_PREFIX) ) {
Class pType = setMethod.getParameterTypes()[0];
if (pType != typeInfo.getFieldType() )
setAddMethod (setMethod);
else
setWriteMethod(setMethod);
}
// for(Iterator iter = setMethods.iterator(); iter.hasNext(); ) {
// Method method = (Method) iter.next();
//
// if (method.getName().startsWith(ADD_PREFIX)) {
// Class paraType = method.getParameterTypes()[0];
//
// if (paraType != typeInfo.getFieldType()) {
// addMethods.add(method);
// iter.remove();
// }
// }
// }
else setWriteMethod( setMethod );
}
if ( getMethod != null ) {
// getMethod might be an enumeration or iteration.
if(getMethod.getName().startsWith(ENUM_PREFIX)) {
Class rType = getMethod.getReturnType();
// Check if getMethod really returns an enumeration.
if (Enumeration.class.isAssignableFrom(rType))
setEnumMethod(getMethod);
else
// If getMethod does not return an enumeration, treat it as a normal getMethod.
setReadMethod(getMethod);
} else if(getMethod.getName().startsWith(ITER_PREFIX)) {
Class rType = getMethod.getReturnType();
// Check if getMethod really returns an iterator.
if (Iterator.class.isAssignableFrom(rType))
setIterMethod(getMethod);
else
// If getMethod does not return an iterator, treat it as a normal getMethod.
setReadMethod(getMethod);
} else
setReadMethod(getMethod);
}
_fieldType = Types.typeFromPrimitive( typeInfo.getFieldType() );
_fieldName = fieldName + "(" + _fieldType.getName() + ")";
_immutable = typeInfo.isImmutable();
// If the field is of a primitive type or if it is required
// we use the default value
if ( setMethod != null && setMethod.getParameterTypes()[0].isPrimitive() )
_default = typeInfo.getDefaultValue();
else
_default = null;
_convertTo = typeInfo.getConvertorTo();
_convertFrom = typeInfo.getConvertorFrom();
_colHandler = typeInfo.getCollectionHandler();
}
public TypeConvertor getConvertFrom() {
return _convertFrom;
}
public TypeConvertor getConvertTo() {
return _convertTo;
}
/**
* {@inheritDoc}
* @see org.exolab.castor.mapping.AbstractFieldHandler#getValue(java.lang.Object)
*/
public Object getValue(Object object) {
Object value;
try {
// If field is accessed directly, get its value. If not, we need to call
// its get method. It's possible to not have a way to access the field.
if (_handler != null) {
value = _handler.getValue(object);
} else if (_field != null) {
value = _field.get(object);
} else if (_enumMethod != null) {
// If there is an enumeration method supplied, return the enumeration.
value = _enumMethod.invoke(object, (Object[]) null);
} else if (_iterMethod != null ) {
// If there is an iterator method supplied, wrap it in an enumeration.
value = new IteratorEnumeration((Iterator) _iterMethod.invoke(object, (Object[]) null));
} else if (_getMethod != null) {
if (_getSequence != null) {
for (int i = 0; i < _getSequence.length; i++) {
object = _getSequence[i].invoke(object, (Object[]) null);
if (object == null) {
break;
}
}
}
// Some of the objects in the sequence might be null, then the value is null.
// If field has 'has' method, false means field is null and do not attempt to
// call getValue. Otherwise, ????
if (object == null || (_hasMethod != null
&& !((Boolean) _hasMethod.invoke(object, (Object[]) null)).booleanValue())) {
value = null;
} else {
value = _getMethod.invoke(object, (Object[]) null);
}
} else {
value = null;
}
} catch (IllegalAccessException except) {
throw new CastorIllegalStateException(
Messages.format("mapping.schemaChangeNoAccess", toString()),
except);
} catch (InvocationTargetException except) {
throw new CastorIllegalStateException(
Messages.format("mapping.schemaChangeInvocation", toString(), except),
except);
}
//-- If a collection, return an enumeration of it's values.
//-- Only use collection handler, if there is no convertor or enum method.
if (_colHandler != null && _enumMethod == null
&& _iterMethod == null && _convertFrom == null) {
if (value == null) {
return new CollectionHandlers.EmptyEnumerator();
}
return _colHandler.elements(value);
}
// If there is a convertor, apply it
if (_convertFrom == null || value == null) {
return value;
}
try {
return _convertFrom.convert(value);
} catch (ClassCastException except) {
String errorMessage = Messages.format("mapping.wrongConvertor",
value.getClass().getName());
throw new IllegalArgumentException(errorMessage, except);
}
}
/**
* {@inheritDoc}
* @see org.exolab.castor.mapping.AbstractFieldHandler#setValue(java.lang.Object, java.lang.Object)
*/
public void setValue(Object object, Object value) {
if (_colHandler == null || _addMethod != null) {
// If there is a convertor, apply conversion here.
if (value != null && _convertTo != null) {
try {
value = _convertTo.convert(value);
} catch (ClassCastException except) {
String errorMessage =
Messages.format("mapping.wrongConvertor", value.getClass().getName());
throw new IllegalArgumentException(errorMessage, except);
}
} else {
//-- unwrap MapItem if necessary
//if (_colHandler != null) {
// if ((value instanceof MapItem) && (_fieldType != MapItem.class))
// {
// value = ((MapItem)value).getValue();
// }
//}
}
try {
if (_handler != null) {
_handler.setValue(object, value);
} else if (_field != null) {
_field.set(object, value == null ? _default : value);
} else {
//-- either add or set
Method setter = selectWriteMethod(value);
if (setter != null) {
if (_getSequence != null) {
for (int i = 0; i < _getSequence.length; i++) {
Object last;
last = object;
object = _getSequence[i].invoke(object, (Object[]) null);
if (object == null) {
// if the value is not null, we must instantiate
// the object in the sequence
if (value == null || _setSequence[i] == null) {
break;
}
object = Types.newInstance(_getSequence[i].getReturnType());
_setSequence[ i ].invoke(last, new Object[] {object});
}
}
}
if (object != null) {
if (value == null && _deleteMethod != null) {
_deleteMethod.invoke(object, (Object[]) null);
} else {
setter.invoke(object,
new Object[] {value == null ? _default : value});
}
}
}
}
// If the field has no set method, ignore it.
// If this is a problem, identity it someplace else.
} catch (IllegalArgumentException except) {
// Graceful way of dealing with unwrapping exception
if (value == null) {
String errorMessage = Messages.format("mapping.typeConversionNull", toString());
throw new IllegalArgumentException(errorMessage);
}
String errorMessage = Messages.format("mapping.typeConversion",
toString(), value.getClass().getName());
throw new IllegalArgumentException(errorMessage, except);
} catch (IllegalAccessException except) {
// This should never happen
String errorMessage =
Messages.format("mapping.schemaChangeNoAccess", toString());
throw new CastorIllegalStateException(errorMessage, except);
} catch (InvocationTargetException except) {
// This should never happen
throw new MappingRuntimeException(except.getTargetException());
}
} else if ( value != null ) {
Object collect;
try {
// Get the field value (the collection), add the value to it,
// possibly yielding a new collection (in the case of an array),
// and set that collection back into the field.
if ( _handler != null ) {
collect = _handler.getValue( object );
collect = _colHandler.add( collect, value );
if ( collect != null )
_handler.setValue( object, collect );
} else if ( _field != null ) {
collect = _field.get( object );
if (collect == null) {
// The type of the collection.
Class type = _field.getType();
//-- Handle Arrays, we need to declare the array with
//-- the correct type. The other cases are handled in
//-- the J1CollectionHandler during the
//-- add(collect,value) call
if (type.isArray()) {
Class componentType = type.getComponentType();
Class valueType = value.getClass();
if (componentType.isPrimitive() ||
((!valueType.isArray()) && (valueType != componentType)))
{
try {
collect = Array.newInstance(componentType, 0);
}
catch (Exception e) {
String err = "Unable to instantiate an array of '" +
componentType + "' : " + e;
throw new CastorIllegalStateException(err, e);
}
}
}
}
collect = _colHandler.add( collect, value );
if ( collect != null )
_field.set( object, collect );
}
else if ( _getMethod != null ) {
if ( _getSequence != null )
for ( int i = 0; i < _getSequence.length; i++ )
object = _getSequence[ i ].invoke( object, (Object[]) null );
collect = _getMethod.invoke( object, (Object[]) null );
// If we deal with a collection who is an array of primitive
// and that has not been instantiated, we have to handle the
// instantiation here rather than in J1CollectionHandler,
// because we have acces to the Field object here.
boolean setCollection = false;
if (collect == null) {
// The return type of the get method should be the type of the collection.
Class type = _getMethod.getReturnType();
//-- Handle Arrays, we need to declare the array with
//-- the correct type. The other cases are handled in
//-- the J1CollectionHandler during the
//-- add(collect,value) call
if (type.isArray()) {
Class componentType = type.getComponentType();
Class valueType = value.getClass();
if (componentType.isPrimitive() ||
((!valueType.isArray()) && (valueType != componentType)))
{
try {
collect = Array.newInstance(componentType, 0);
}
catch (Exception e) {
String err = "Unable to instantiate an array of '" +
componentType + "' : " + e;
throw new CastorIllegalStateException(err, e);
}
}
}
setCollection = true;
}
else {
setCollection = collect.getClass().isArray();
}
Object tmp = _colHandler.add(collect, value);
//-- make sure we do not overwrite collect unless
//-- the new collection is not null
if (tmp != null) collect = tmp;
if ( setCollection && (_setMethod != null))
_setMethod.invoke( object, new Object[] { collect } );
}
} catch ( IllegalAccessException except ) {
// This should never happen
throw new IllegalStateException( Messages.format( "mapping.schemaChangeNoAccess", toString() ) );
} catch ( InvocationTargetException except ) {
// This should never happen
throw new MappingRuntimeException(except.getTargetException());
}
}
}
public void resetValue( Object object )
{
if ( _colHandler == null ) {
try {
if ( _handler != null )
_handler.resetValue( object );
else if ( _field != null )
_field.set( object, _default );
else if ( _setMethod != null ) {
if ( _getSequence != null )
for ( int i = 0; i < _getSequence.length; i++ ) {
object = _getSequence[ i ].invoke( object, (Object[]) null );
if ( object == null )
break;
}
if ( object != null ) {
if ( _deleteMethod != null )
_deleteMethod.invoke( object, (Object[]) null );
else
_setMethod.invoke( object, new Object[] { _default } );
}
}
// If the field has no set method, ignore it.
// If this is a problem, identity it someplace else.
} catch ( IllegalArgumentException except ) {
// Graceful way of dealing with unwrapping exception
throw new IllegalArgumentException( Messages.format( "mapping.typeConversionNull", toString() ) );
} catch ( IllegalAccessException except ) {
// This should never happen
throw new IllegalStateException( Messages.format( "mapping.schemaChangeNoAccess", toString() ) );
} catch ( InvocationTargetException except ) {
// This should never happen
throw new MappingRuntimeException(except.getTargetException());
}
} else {
Object collect;
try {
// Get the field value (the collection), add the value to it,
// possibly yielding a new collection (in the case of an array),
// and set that collection back into the field.
if ( _handler != null ) {
_handler.resetValue( object );
} else if ( _field != null ) {
collect = _field.get( object );
collect = _colHandler.clear( collect );
if ( collect != null )
_field.set( object, collect );
} else if ( _getMethod != null ) {
if ( _getSequence != null )
for ( int i = 0; i < _getSequence.length; i++ )
object = _getSequence[ i ].invoke( object, (Object[]) null );
collect = _getMethod.invoke( object, (Object[]) null );
collect = _colHandler.clear( collect );
if ( collect != null && _setMethod != null)
_setMethod.invoke( object, new Object[] { collect } );
}
} catch ( IllegalAccessException except ) {
// This should never happen
throw new IllegalStateException( Messages.format( "mapping.schemaChangeNoAccess", toString() ) );
} catch ( InvocationTargetException except ) {
// This should never happen
throw new MappingRuntimeException(except.getTargetException());
}
}
}
/**
* Creates a new instance of the object described by this field.
*
* @param parent The object for which the field is created
* @return A new instance of the field's value
* @throws IllegalStateException This field is a simple type and
* cannot be instantiated
*/
public Object newInstance( Object parent )
throws IllegalStateException
{
return newInstance( parent, null);
}
/**
* Creates a new instance of the object described by this field.
*
* @param parent The object for which the field is created
* @param args the set of constructor arguments
* @return A new instance of the field's value
* @throws IllegalStateException This field is a simple type and
* cannot be instantiated
*/
public Object newInstance( Object parent, Object[] args )
throws IllegalStateException
{
if (_fieldType.isInterface() && _createMethod == null)
return null;
if (( _immutable ) && ((args == null) || (args.length == 0)))
throw new IllegalStateException( Messages.format( "mapping.classNotConstructable", _fieldType ) );
if ( _handler != null ) {
if (_handler instanceof ExtendedFieldHandler)
return ((ExtendedFieldHandler)_handler).newInstance( parent, args );
return _handler.newInstance( parent );
}
// If we have a create method and parent object, call the create method.
if ( _createMethod != null && parent != null ) {
try {
return _createMethod.invoke( parent, args );
} catch ( IllegalAccessException except ) {
// This should never happen
throw new IllegalStateException( Messages.format( "mapping.schemaChangeNoAccess", toString() ) );
} catch ( InvocationTargetException except ) {
// This should never happen
throw new MappingRuntimeException(except.getTargetException());
}
}
return Types.newInstance( _fieldType, args );
} //-- newInstance
/**
* Mutator method used by {@link AbstractMappingLoader}.
*/
void setRequired(final boolean required) { }
/**
* Sets the TypeConvertor used during calls to getValue
*
* @param convertor the TypeConvertor to use during calls
* to getValue
**/
public void setConvertFrom(TypeConvertor convertor) {
_convertFrom = convertor;
} //-- setConvertFrom
/**
* Sets the TypeConvertor used during calls to setValue
*
* @param convertor the TypeConvertor to use during calls
* to setValue
**/
public void setConvertTo(TypeConvertor convertor) {
_convertTo = convertor;
} //-- setConvertTo
/**
* Mutator method used by {@link AbstractMappingLoader} and
* {@link org.exolab.castor.xml.Introspector}.
* Please understand how this method is used before you start
* playing with it! :-)
*/
public void setCreateMethod( Method method )
throws MappingException
{
if ( ( method.getModifiers() & Modifier.PUBLIC ) == 0 ||
( method.getModifiers() & Modifier.STATIC ) != 0 )
throw new MappingException( "mapping.accessorNotAccessible",
method, method.getDeclaringClass().getName() );
if ( method.getParameterTypes().length != 0 )
throw new MappingException( "mapping.createMethodNoParam",
method, method.getDeclaringClass().getName() );
_createMethod = method;
}
/**
* Mutator method used by {@link AbstractMappingLoader} and
* {@link org.exolab.castor.xml.Introspector}.
* Please understand how this method is used before you start
* playing with it! :-)
*/
public void setHasDeleteMethod( Method hasMethod, Method deleteMethod )
throws MappingException
{
if ( hasMethod != null ) {
if ( ( hasMethod.getModifiers() & Modifier.PUBLIC ) == 0 ||
( hasMethod.getModifiers() & Modifier.STATIC ) != 0 )
throw new MappingException( "mapping.accessorNotAccessible",
hasMethod, hasMethod.getDeclaringClass().getName() );
if ( hasMethod.getParameterTypes().length != 0 )
throw new MappingException( "mapping.createMethodNoParam",
hasMethod, hasMethod.getDeclaringClass().getName() );
_hasMethod = hasMethod;
}
if ( deleteMethod != null ) {
if ( ( deleteMethod.getModifiers() & Modifier.PUBLIC ) == 0 ||
( deleteMethod.getModifiers() & Modifier.STATIC ) != 0 )
throw new MappingException( "mapping.accessorNotAccessible",
deleteMethod, deleteMethod.getDeclaringClass().getName() );
if ( deleteMethod.getParameterTypes().length != 0 )
throw new MappingException( "mapping.createMethodNoParam",
deleteMethod, deleteMethod.getDeclaringClass().getName() );
_deleteMethod = deleteMethod;
}
}
/**
* Mutator method used by {@link org.exolab.castor.xml.Introspector}.
* Please understand how this method is used before you start
* playing with it! :-)
*/
public void setReadMethod( Method method )
throws MappingException
{
if ( ( method.getModifiers() & Modifier.PUBLIC ) == 0 ||
( method.getModifiers() & Modifier.STATIC ) != 0 )
throw new MappingException( "mapping.accessorNotAccessible",
method, method.getDeclaringClass().getName() );
if ( method.getParameterTypes().length != 0 )
throw new MappingException( "mapping.readMethodHasParam",
method, method.getDeclaringClass().getName() );
_getMethod = method;
}
/**
* Mutator method used by {@link org.exolab.castor.xml.Introspector}.
* Please understand how this method is used before you start
* playing with it! :-)
*/
public void setWriteMethod( Method method )
throws MappingException
{
if ( ( method.getModifiers() & Modifier.PUBLIC ) == 0 ||
( method.getModifiers() & Modifier.STATIC ) != 0 )
throw new MappingException( "mapping.accessorNotAccessible",
method, method.getDeclaringClass().getName() );
if ( method.getParameterTypes().length != 1 )
throw new MappingException( "mapping.writeMethodNoParam",
method, method.getDeclaringClass().getName() );
_setMethod = method;
}
/**
* Mutator method used by {@link org.exolab.castor.xml.Introspector}.
* Please understand how this method is used before you start
* playing with it! :-)
*/
public void setAddMethod( Method method )
throws MappingException
{
if ( ( method.getModifiers() & Modifier.PUBLIC ) == 0 ||
( method.getModifiers() & Modifier.STATIC ) != 0 )
throw new MappingException( "mapping.accessorNotAccessible",
method, method.getDeclaringClass().getName() );
if ( method.getParameterTypes().length != 1 )
throw new MappingException( "mapping.writeMethodNoParam",
method, method.getDeclaringClass().getName() );
_addMethod = method;
//-- make sure add method is not the same as the set method
if (_addMethod == _setMethod) _setMethod = null;
} //-- setAddMethod
/**
* Sets the enumeration method.
*/
public void setEnumMethod( Method method )
throws MappingException
{
if ( ( method.getModifiers() & Modifier.PUBLIC ) == 0 ||
( method.getModifiers() & Modifier.STATIC ) != 0 )
throw new MappingException( "mapping.accessorNotAccessible",
method, method.getDeclaringClass().getName() );
if ( method.getParameterTypes().length != 0 )
throw new MappingException( "mapping.readMethodHasParam",
method, method.getDeclaringClass().getName() );
_enumMethod = method;
}
/**
* Sets the iteration method.
*/
public void setIterMethod( Method method )
throws MappingException
{
if ( ( method.getModifiers() & Modifier.PUBLIC ) == 0 ||
( method.getModifiers() & Modifier.STATIC ) != 0 )
throw new MappingException( "mapping.accessorNotAccessible",
method, method.getDeclaringClass().getName() );
if ( method.getParameterTypes().length != 0 )
throw new MappingException( "mapping.readMethodHasParam",
method, method.getDeclaringClass().getName() );
_iterMethod = method;
}
/**
* Selects the appropriate "write" method based on the
* value. This is used when there is an "add" method
* and a "set" method.
*
* @return the selected write method
**/
private Method selectWriteMethod( Object value ) {
if (_setMethod != null) {
if (_addMethod == null) return _setMethod;
if (value == null) {
if (_default != null) value = _default;
else return _setMethod;
}
//-- check value's class type
Class paramType = _setMethod.getParameterTypes()[0];
if (paramType.isAssignableFrom(value.getClass()))
return _setMethod;
}
return _addMethod;
} //-- selectWriteMethod
/**
* Return true if the field is a collection.
*/
public boolean isCollection() {
return (_colHandler != null);
}
public String toString()
{
return _fieldName;
}
/**
* Sets the FieldDescriptor that this FieldHander is
* responsibile for. By setting the FieldDescriptor, it
* allows the implementation of the FieldHandler methods
* to obtain information about the field itself. This allows
* a particular implementation to become more generic and
* reusable.
*
* @param fieldDesc the FieldDescriptor to set
*/
public void setFieldDescriptor(FieldDescriptor fieldDesc) {
super.setFieldDescriptor(fieldDesc);
if (_handler != null) {
if (_handler instanceof GeneralizedFieldHandler) {
((GeneralizedFieldHandler) _handler).setFieldDescriptor(fieldDesc);
}
}
}
}