001: /**
002: * Redistribution and use of this software and associated documentation
003: * ("Software"), with or without modification, are permitted provided
004: * that the following conditions are met:
005: *
006: * 1. Redistributions of source code must retain copyright
007: * statements and notices. Redistributions must also contain a
008: * copy of this document.
009: *
010: * 2. Redistributions in binary form must reproduce the
011: * above copyright notice, this list of conditions and the
012: * following disclaimer in the documentation and/or other
013: * materials provided with the distribution.
014: *
015: * 3. The name "Exolab" must not be used to endorse or promote
016: * products derived from this Software without prior written
017: * permission of Intalio, Inc. For written permission,
018: * please contact info@exolab.org.
019: *
020: * 4. Products derived from this Software may not be called "Exolab"
021: * nor may "Exolab" appear in their names without prior written
022: * permission of Intalio, Inc. Exolab is a registered
023: * trademark of Intalio, Inc.
024: *
025: * 5. Due credit should be given to the Exolab Project
026: * (http://www.exolab.org/).
027: *
028: * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
029: * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
030: * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
031: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
032: * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
033: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
034: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
035: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
036: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
037: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
038: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
039: * OF THE POSSIBILITY OF SUCH DAMAGE.
040: *
041: * Copyright 1999-2003 (C) Intalio, Inc. All Rights Reserved.
042: *
043: * $Id: FieldHandlerImpl.java 6457 2006-11-29 08:21:59Z wguttmn $
044: */package org.exolab.castor.mapping.loader;
045:
046: import java.lang.reflect.Array;
047: import java.lang.reflect.Field;
048: import java.lang.reflect.Method;
049: import java.lang.reflect.Modifier;
050: import java.lang.reflect.InvocationTargetException;
051: import java.util.Enumeration;
052: import java.util.Iterator;
053:
054: import org.castor.util.Messages;
055: import org.exolab.castor.core.exceptions.CastorIllegalStateException;
056: import org.exolab.castor.mapping.AbstractFieldHandler;
057: import org.exolab.castor.mapping.ExtendedFieldHandler;
058: import org.exolab.castor.mapping.FieldHandler;
059: import org.exolab.castor.mapping.TypeConvertor;
060: import org.exolab.castor.mapping.CollectionHandler;
061: import org.exolab.castor.mapping.MappingException;
062: import org.exolab.castor.mapping.MappingRuntimeException;
063: import org.exolab.castor.util.IteratorEnumeration;
064:
065: /**
066: * A field handler that knows how to get/set the values of a field
067: * directly or through the get/set methods. Uses reflection.
068: * <p>
069: * Note: the field Java type is obtained from {@link TypeInfo#getFieldType()},
070: * but if the field is a collection, the actual field/accessor type is
071: * obtained from {@link TypeInfo#getCollectionHandler} and the object to create
072: * (with {@link #newInstance(Object)}) is the former field type.
073: *
074: * @author <a href="arkin@intalio.com">Assaf Arkin</a>
075: * @version $Revision: 6457 $ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $
076: */
077: public final class FieldHandlerImpl extends AbstractFieldHandler {
078: /**
079: * The prefix for an "add" method
080: **/
081: private static final String ADD_PREFIX = "add";
082:
083: /**
084: * The prefix for an "enum" method
085: */
086: private static final String ENUM_PREFIX = "enum";
087:
088: /**
089: * The prefix for an "iter" method
090: */
091: private static final String ITER_PREFIX = "iter";
092:
093: /**
094: * The underlying field handler used by this handler.
095: */
096: private final FieldHandler _handler;
097:
098: /**
099: * The Java field described and accessed through this descriptor.
100: */
101: private final Field _field;
102:
103: /**
104: * The sequence of methods used to obtain the nested field. May be null.
105: */
106: private Method[] _getSequence;
107:
108: /**
109: * The sequence of methods used to create the nested object. May be null.
110: */
111: private Method[] _setSequence;
112:
113: /**
114: * The method used to "incrementally" set the value of this field.
115: * This is only used if the field is a collection
116: */
117: private Method _addMethod;
118:
119: /**
120: * The method used to enumerate entries of a container.
121: */
122: private Method _enumMethod;
123:
124: /**
125: * The method used to iterate over a container.
126: */
127: private Method _iterMethod;
128:
129: /**
130: * The method used to obtain the value of this field. May be null.
131: */
132: private Method _getMethod;
133:
134: /**
135: * The method used to set the value of this field. May be null.
136: */
137: private Method _setMethod;
138:
139: /**
140: * The method used to check if the value of this field exists. May be null.
141: */
142: private Method _hasMethod;
143:
144: /**
145: * The method used to delete the value of this field. May be null.
146: */
147: private Method _deleteMethod;
148:
149: /**
150: * The method used to create a new instance of the field.
151: */
152: private Method _createMethod;
153:
154: /**
155: * The Java field name.
156: */
157: private final String _fieldName;
158:
159: /**
160: * The Java field type.
161: */
162: private final Class _fieldType;
163:
164: /**
165: * True if this field is an immutable type.
166: */
167: private final boolean _immutable;
168:
169: /**
170: * The default value for primitive fields. Will be set if the field is null.
171: */
172: private final Object _default;
173:
174: /**
175: * Convertor to apply when setting the value of the field. Converts from
176: * the value to the field type. Null if no convertor is required.
177: */
178: private TypeConvertor _convertTo = null;
179:
180: /**
181: * Convertor to apply when reading the value of the field. Converts from
182: * the field type to the return value. Null if no convertor is required.
183: */
184: private TypeConvertor _convertFrom = null;
185:
186: /**
187: * Parameter for the type convertor. Null if no convertor is required
188: * or on parameter is specified.
189: */
190: private final String _convertParam;
191:
192: /**
193: * The collection handler for multi valued fields.
194: */
195: private final CollectionHandler _colHandler;
196:
197: /**
198: * Construct a new field handler for the specified field. The field must
199: * be public, and may not be static or transient. The field name is
200: * determined from the Java field, the type from the type information.
201: *
202: * @param handler
203: * @param typeInfo Type information
204: */
205: public FieldHandlerImpl(FieldHandler handler, TypeInfo typeInfo) {
206: _handler = handler;
207: _field = null;
208: _fieldName = handler.toString();
209: _fieldType = Types.typeFromPrimitive(typeInfo.getFieldType());
210: _immutable = typeInfo.isImmutable();
211: _default = typeInfo.getDefaultValue();
212: _convertTo = typeInfo.getConvertorTo();
213: _convertFrom = typeInfo.getConvertorFrom();
214: _convertParam = typeInfo.getConvertorParam();
215: _colHandler = typeInfo.getCollectionHandler();
216: }
217:
218: /**
219: * Construct a new field handler for the specified field. The field must
220: * be public, and may not be static or transient. The field name is
221: * determined from the Java field, the type from the type information.
222: *
223: * @param field The field being described
224: * @param typeInfo Type information
225: * @throws MappingException If the field is not public, is static or
226: * transient
227: */
228: public FieldHandlerImpl(Field field, TypeInfo typeInfo)
229: throws MappingException {
230: if (field.getModifiers() != Modifier.PUBLIC
231: && field.getModifiers() != (Modifier.PUBLIC | Modifier.VOLATILE))
232: throw new MappingException("mapping.fieldNotAccessible",
233: field.getName(), field.getDeclaringClass()
234: .getName());
235: _handler = null;
236: _field = field;
237: _fieldType = Types.typeFromPrimitive(typeInfo.getFieldType());
238: _fieldName = field.getName() + "(" + field.getType().getName()
239: + ")";
240: _immutable = typeInfo.isImmutable();
241: // If the field is of a primitive type or if it is required
242: // we use the default value
243: if (_field.getType().isPrimitive())
244: _default = typeInfo.getDefaultValue();
245: else
246: _default = null;
247: _convertTo = typeInfo.getConvertorTo();
248: _convertFrom = typeInfo.getConvertorFrom();
249: _convertParam = typeInfo.getConvertorParam();
250: _colHandler = typeInfo.getCollectionHandler();
251: }
252:
253: /**
254: * Construct a new field handler for the specified field that
255: * is accessed through the accessor methods (get/set). The accessor
256: * methods must be public and not static. The field name is
257: * required for descriptive purposes. The field type must match
258: * the return value of the get method and the single parameter of
259: * the set method. Either get or set methods are optional.
260: *
261: * @param fieldName The field being described
262: * @param getMethod The method used to retrieve the field value,
263: * must accept no parameters and have a return type castable to
264: * the field type
265: * @param setMethod The method used to set the field value, must
266: * accept a single parameter that is castable to the field type
267: * @param typeInfo Type information
268: * @throws MappingException If the get or set method are not public,
269: * are static, or do not specify the proper types
270: *
271: */
272: public FieldHandlerImpl(String fieldName, Method[] getSequence,
273: Method[] setSequence, Method getMethod, Method setMethod,
274: TypeInfo typeInfo) throws MappingException {
275: _handler = null;
276: _field = null;
277: if (fieldName == null)
278: throw new IllegalArgumentException(
279: "Argument 'fieldName' is null");
280:
281: // Originally commented out by Oleg....not sure why?
282: // if ( getMethod == null && setMethod == null )
283: // throw new IllegalArgumentException( "Both arguments 'getMethod' and 'setMethod' are null" );
284:
285: _getSequence = getSequence;
286: _setSequence = setSequence;
287:
288: if (setMethod != null) {
289: //-- might be an "add" method
290: if (setMethod.getName().startsWith(ADD_PREFIX)) {
291: Class pType = setMethod.getParameterTypes()[0];
292: if (pType != typeInfo.getFieldType())
293: setAddMethod(setMethod);
294: else
295: setWriteMethod(setMethod);
296: }
297:
298: // for(Iterator iter = setMethods.iterator(); iter.hasNext(); ) {
299: // Method method = (Method) iter.next();
300: //
301: // if (method.getName().startsWith(ADD_PREFIX)) {
302: // Class paraType = method.getParameterTypes()[0];
303: //
304: // if (paraType != typeInfo.getFieldType()) {
305: // addMethods.add(method);
306: // iter.remove();
307: // }
308: // }
309: // }
310: else
311: setWriteMethod(setMethod);
312: }
313:
314: if (getMethod != null) {
315: // getMethod might be an enumeration or iteration.
316: if (getMethod.getName().startsWith(ENUM_PREFIX)) {
317: Class rType = getMethod.getReturnType();
318:
319: // Check if getMethod really returns an enumeration.
320: if (Enumeration.class.isAssignableFrom(rType))
321: setEnumMethod(getMethod);
322: else
323: // If getMethod does not return an enumeration, treat it as a normal getMethod.
324: setReadMethod(getMethod);
325: } else if (getMethod.getName().startsWith(ITER_PREFIX)) {
326: Class rType = getMethod.getReturnType();
327:
328: // Check if getMethod really returns an iterator.
329: if (Iterator.class.isAssignableFrom(rType))
330: setIterMethod(getMethod);
331: else
332: // If getMethod does not return an iterator, treat it as a normal getMethod.
333: setReadMethod(getMethod);
334: } else
335: setReadMethod(getMethod);
336: }
337:
338: _fieldType = Types.typeFromPrimitive(typeInfo.getFieldType());
339: _fieldName = fieldName + "(" + _fieldType.getName() + ")";
340: _immutable = typeInfo.isImmutable();
341:
342: // If the field is of a primitive type or if it is required
343: // we use the default value
344: if (setMethod != null
345: && setMethod.getParameterTypes()[0].isPrimitive())
346: _default = typeInfo.getDefaultValue();
347: else
348: _default = null;
349: _convertTo = typeInfo.getConvertorTo();
350: _convertFrom = typeInfo.getConvertorFrom();
351: _convertParam = typeInfo.getConvertorParam();
352: _colHandler = typeInfo.getCollectionHandler();
353: }
354:
355: public TypeConvertor getConvertFrom() {
356: return _convertFrom;
357: }
358:
359: public TypeConvertor getConvertTo() {
360: return _convertTo;
361: }
362:
363: public String getConvertParam() {
364: return _convertParam;
365: }
366:
367: public Object getValue(Object object) {
368: Object value;
369:
370: try {
371: // If field is accessed directly, get its value. If not, we need to call
372: // its get method. It's possible to not have a way to access the field.
373:
374: if (_handler != null) {
375: value = _handler.getValue(object);
376: } else if (_field != null) {
377: value = _field.get(object);
378: } else if (_enumMethod != null) {
379: // If there is an enumeration method supplied, return the enumeration.
380: value = _enumMethod.invoke(object, (Object[]) null);
381: } else if (_iterMethod != null) {
382: // If there is an iterator method supplied, wrap it in an enumeration.
383: value = new IteratorEnumeration((Iterator) _iterMethod
384: .invoke(object, (Object[]) null));
385: } else if (_getMethod != null) {
386: if (_getSequence != null) {
387: for (int i = 0; i < _getSequence.length; i++) {
388: object = _getSequence[i].invoke(object,
389: (Object[]) null);
390: if (object == null) {
391: break;
392: }
393: }
394: }
395:
396: // Some of the objects in the sequence might be null, then the value is null.
397: // If field has 'has' method, false means field is null and do not attempt to
398: // call getValue. Otherwise, ????
399: if (object == null
400: || (_hasMethod != null && !((Boolean) _hasMethod
401: .invoke(object, (Object[]) null))
402: .booleanValue())) {
403: value = null;
404: } else {
405: value = _getMethod.invoke(object, (Object[]) null);
406: }
407: } else {
408: value = null;
409: }
410: } catch (IllegalAccessException except) {
411: throw new CastorIllegalStateException(Messages.format(
412: "mapping.schemaChangeNoAccess", toString()), except);
413: } catch (InvocationTargetException except) {
414: throw new CastorIllegalStateException(Messages.format(
415: "mapping.schemaChangeInvocation", toString(),
416: except), except);
417: }
418:
419: //-- If a collection, return an enumeration of it's values.
420: //-- Only use collection handler, if there is no convertor or enum method.
421: if (_colHandler != null && _enumMethod == null
422: && _iterMethod == null && _convertFrom == null) {
423: if (value == null) {
424: return new CollectionHandlers.EmptyEnumerator();
425: }
426: return _colHandler.elements(value);
427: }
428:
429: // If there is a convertor, apply it
430: if (_convertFrom == null || value == null) {
431: return value;
432: }
433:
434: try {
435: return _convertFrom.convert(value, _convertParam);
436: } catch (ClassCastException except) {
437: throw new IllegalArgumentException(Messages.format(
438: "mapping.wrongConvertor", value.getClass()
439: .getName()));
440: }
441: }
442:
443: public void setValue(Object object, Object value) {
444: if (_colHandler == null || _addMethod != null) {
445:
446: // If there is a convertor, apply conversion here.
447: if (value != null && _convertTo != null) {
448: try {
449: value = _convertTo.convert(value, _convertParam);
450: } catch (ClassCastException except) {
451: throw new IllegalArgumentException(Messages.format(
452: "mapping.wrongConvertor", value.getClass()
453: .getName()));
454: }
455: } else {
456: //-- unwrap MapItem if necessary
457: //if (_colHandler != null) {
458: // if ((value instanceof MapItem) && (_fieldType != MapItem.class))
459: // {
460: // value = ((MapItem)value).getValue();
461: // }
462: //}
463: }
464:
465: try {
466: if (_handler != null)
467: _handler.setValue(object, value);
468: else if (_field != null)
469: _field
470: .set(object, value == null ? _default
471: : value);
472: else {
473:
474: //-- either add or set
475: Method setter = selectWriteMethod(value);
476:
477: if (setter != null) {
478: if (_getSequence != null)
479: for (int i = 0; i < _getSequence.length; i++) {
480: Object last;
481:
482: last = object;
483: object = _getSequence[i].invoke(object,
484: (Object[]) null);
485: if (object == null) {
486: // if the value is not null, we must instantiate
487: // the object in the sequence
488: if (value == null
489: || _setSequence[i] == null)
490: break;
491: object = Types
492: .newInstance(_getSequence[i]
493: .getReturnType());
494: _setSequence[i].invoke(last,
495: new Object[] { object });
496: }
497: }
498: if (object != null) {
499: if (value == null && _deleteMethod != null)
500: _deleteMethod.invoke(object,
501: (Object[]) null);
502: else
503: setter
504: .invoke(
505: object,
506: new Object[] { value == null ? _default
507: : value });
508: }
509: }
510: }
511: // If the field has no set method, ignore it.
512: // If this is a problem, identity it someplace else.
513: } catch (IllegalArgumentException except) {
514: // Graceful way of dealing with unwrapping exception
515: if (value == null)
516: throw new IllegalArgumentException(Messages.format(
517: "mapping.typeConversionNull", toString()));
518: throw new IllegalArgumentException(Messages.format(
519: "mapping.typeConversion", toString(), value
520: .getClass().getName()));
521: } catch (IllegalAccessException except) {
522: // This should never happen
523: throw new CastorIllegalStateException(Messages.format(
524: "mapping.schemaChangeNoAccess", toString()),
525: except);
526: } catch (InvocationTargetException except) {
527: // This should never happen
528: throw new MappingRuntimeException(except
529: .getTargetException());
530: }
531:
532: } else if (value != null) {
533: Object collect;
534: try {
535: // Get the field value (the collection), add the value to it,
536: // possibly yielding a new collection (in the case of an array),
537: // and set that collection back into the field.
538: if (_handler != null) {
539: collect = _handler.getValue(object);
540: collect = _colHandler.add(collect, value);
541: if (collect != null)
542: _handler.setValue(object, collect);
543: } else if (_field != null) {
544: collect = _field.get(object);
545: if (collect == null) {
546: // The type of the collection.
547: Class type = _field.getType();
548: //-- Handle Arrays, we need to declare the array with
549: //-- the correct type. The other cases are handled in
550: //-- the J1CollectionHandler during the
551: //-- add(collect,value) call
552: if (type.isArray()) {
553: Class componentType = type
554: .getComponentType();
555: Class valueType = value.getClass();
556: if (componentType.isPrimitive()
557: || ((!valueType.isArray()) && (valueType != componentType))) {
558: try {
559: collect = Array.newInstance(
560: componentType, 0);
561: } catch (Exception e) {
562: String err = "Unable to instantiate an array of '"
563: + componentType
564: + "' : "
565: + e;
566: throw new CastorIllegalStateException(
567: err, e);
568: }
569: }
570: }
571: }
572: collect = _colHandler.add(collect, value);
573: if (collect != null)
574: _field.set(object, collect);
575:
576: } else if (_getMethod != null) {
577: if (_getSequence != null)
578: for (int i = 0; i < _getSequence.length; i++)
579: object = _getSequence[i].invoke(object,
580: (Object[]) null);
581: collect = _getMethod
582: .invoke(object, (Object[]) null);
583:
584: // If we deal with a collection who is an array of primitive
585: // and that has not been instantiated, we have to handle the
586: // instantiation here rather than in J1CollectionHandler,
587: // because we have acces to the Field object here.
588: boolean setCollection = false;
589: if (collect == null) {
590: // The return type of the get method should be the type of the collection.
591: Class type = _getMethod.getReturnType();
592:
593: //-- Handle Arrays, we need to declare the array with
594: //-- the correct type. The other cases are handled in
595: //-- the J1CollectionHandler during the
596: //-- add(collect,value) call
597: if (type.isArray()) {
598: Class componentType = type
599: .getComponentType();
600: Class valueType = value.getClass();
601: if (componentType.isPrimitive()
602: || ((!valueType.isArray()) && (valueType != componentType))) {
603: try {
604: collect = Array.newInstance(
605: componentType, 0);
606: } catch (Exception e) {
607: String err = "Unable to instantiate an array of '"
608: + componentType
609: + "' : "
610: + e;
611: throw new CastorIllegalStateException(
612: err, e);
613: }
614: }
615: }
616: setCollection = true;
617: } else {
618: setCollection = collect.getClass().isArray();
619: }
620:
621: Object tmp = _colHandler.add(collect, value);
622:
623: //-- make sure we do not overwrite collect unless
624: //-- the new collection is not null
625: if (tmp != null)
626: collect = tmp;
627:
628: if (setCollection && (_setMethod != null))
629: _setMethod.invoke(object,
630: new Object[] { collect });
631: }
632: } catch (IllegalAccessException except) {
633: // This should never happen
634: throw new IllegalStateException(Messages.format(
635: "mapping.schemaChangeNoAccess", toString()));
636: } catch (InvocationTargetException except) {
637: // This should never happen
638: throw new MappingRuntimeException(except
639: .getTargetException());
640: }
641: }
642: }
643:
644: public void resetValue(Object object) {
645: if (_colHandler == null) {
646:
647: try {
648: if (_handler != null)
649: _handler.resetValue(object);
650: else if (_field != null)
651: _field.set(object, _default);
652: else if (_setMethod != null) {
653: if (_getSequence != null)
654: for (int i = 0; i < _getSequence.length; i++) {
655: object = _getSequence[i].invoke(object,
656: (Object[]) null);
657: if (object == null)
658: break;
659: }
660: if (object != null) {
661: if (_deleteMethod != null)
662: _deleteMethod.invoke(object,
663: (Object[]) null);
664: else
665: _setMethod.invoke(object,
666: new Object[] { _default });
667: }
668: }
669: // If the field has no set method, ignore it.
670: // If this is a problem, identity it someplace else.
671: } catch (IllegalArgumentException except) {
672: // Graceful way of dealing with unwrapping exception
673: throw new IllegalArgumentException(Messages.format(
674: "mapping.typeConversionNull", toString()));
675: } catch (IllegalAccessException except) {
676: // This should never happen
677: throw new IllegalStateException(Messages.format(
678: "mapping.schemaChangeNoAccess", toString()));
679: } catch (InvocationTargetException except) {
680: // This should never happen
681: throw new MappingRuntimeException(except
682: .getTargetException());
683: }
684:
685: } else {
686: Object collect;
687:
688: try {
689: // Get the field value (the collection), add the value to it,
690: // possibly yielding a new collection (in the case of an array),
691: // and set that collection back into the field.
692: if (_handler != null) {
693: _handler.resetValue(object);
694: } else if (_field != null) {
695: collect = _field.get(object);
696: collect = _colHandler.clear(collect);
697: if (collect != null)
698: _field.set(object, collect);
699: } else if (_getMethod != null) {
700: if (_getSequence != null)
701: for (int i = 0; i < _getSequence.length; i++)
702: object = _getSequence[i].invoke(object,
703: (Object[]) null);
704: collect = _getMethod
705: .invoke(object, (Object[]) null);
706: collect = _colHandler.clear(collect);
707: if (collect != null && _setMethod != null)
708: _setMethod.invoke(object,
709: new Object[] { collect });
710: }
711: } catch (IllegalAccessException except) {
712: // This should never happen
713: throw new IllegalStateException(Messages.format(
714: "mapping.schemaChangeNoAccess", toString()));
715: } catch (InvocationTargetException except) {
716: // This should never happen
717: throw new MappingRuntimeException(except
718: .getTargetException());
719: }
720:
721: }
722: }
723:
724: /**
725: * Creates a new instance of the object described by this field.
726: *
727: * @param parent The object for which the field is created
728: * @return A new instance of the field's value
729: * @throws IllegalStateException This field is a simple type and
730: * cannot be instantiated
731: */
732: public Object newInstance(Object parent)
733: throws IllegalStateException {
734: return newInstance(parent, null);
735: }
736:
737: /**
738: * Creates a new instance of the object described by this field.
739: *
740: * @param parent The object for which the field is created
741: * @param args the set of constructor arguments
742: * @return A new instance of the field's value
743: * @throws IllegalStateException This field is a simple type and
744: * cannot be instantiated
745: */
746: public Object newInstance(Object parent, Object[] args)
747: throws IllegalStateException {
748: if (_fieldType.isInterface() && _createMethod == null)
749: return null;
750:
751: if ((_immutable) && ((args == null) || (args.length == 0)))
752: throw new IllegalStateException(Messages.format(
753: "mapping.classNotConstructable", _fieldType));
754:
755: if (_handler != null) {
756: if (_handler instanceof ExtendedFieldHandler)
757: return ((ExtendedFieldHandler) _handler).newInstance(
758: parent, args);
759: return _handler.newInstance(parent);
760: }
761: // If we have a create method and parent object, call the create method.
762: if (_createMethod != null && parent != null) {
763: try {
764: return _createMethod.invoke(parent, args);
765: } catch (IllegalAccessException except) {
766: // This should never happen
767: throw new IllegalStateException(Messages.format(
768: "mapping.schemaChangeNoAccess", toString()));
769: } catch (InvocationTargetException except) {
770: // This should never happen
771: throw new MappingRuntimeException(except
772: .getTargetException());
773: }
774: }
775: return Types.newInstance(_fieldType, args);
776: } //-- newInstance
777:
778: /**
779: * Mutator method used by {@link AbstractMappingLoader}.
780: */
781: void setRequired(final boolean required) {
782: }
783:
784: /**
785: * Sets the TypeConvertor used during calls to getValue
786: *
787: * @param convertor the TypeConvertor to use during calls
788: * to getValue
789: **/
790: public void setConvertFrom(TypeConvertor convertor) {
791: _convertFrom = convertor;
792: } //-- setConvertFrom
793:
794: /**
795: * Sets the TypeConvertor used during calls to setValue
796: *
797: * @param convertor the TypeConvertor to use during calls
798: * to setValue
799: **/
800: public void setConvertTo(TypeConvertor convertor) {
801: _convertTo = convertor;
802: } //-- setConvertTo
803:
804: /**
805: * Mutator method used by {@link AbstractMappingLoader} and
806: * {@link org.exolab.castor.xml.Introspector}.
807: * Please understand how this method is used before you start
808: * playing with it! :-)
809: */
810: public void setCreateMethod(Method method) throws MappingException {
811: if ((method.getModifiers() & Modifier.PUBLIC) == 0
812: || (method.getModifiers() & Modifier.STATIC) != 0)
813: throw new MappingException("mapping.accessorNotAccessible",
814: method, method.getDeclaringClass().getName());
815: if (method.getParameterTypes().length != 0)
816: throw new MappingException("mapping.createMethodNoParam",
817: method, method.getDeclaringClass().getName());
818: _createMethod = method;
819: }
820:
821: /**
822: * Mutator method used by {@link AbstractMappingLoader} and
823: * {@link org.exolab.castor.xml.Introspector}.
824: * Please understand how this method is used before you start
825: * playing with it! :-)
826: */
827: public void setHasDeleteMethod(Method hasMethod, Method deleteMethod)
828: throws MappingException {
829: if (hasMethod != null) {
830: if ((hasMethod.getModifiers() & Modifier.PUBLIC) == 0
831: || (hasMethod.getModifiers() & Modifier.STATIC) != 0)
832: throw new MappingException(
833: "mapping.accessorNotAccessible", hasMethod,
834: hasMethod.getDeclaringClass().getName());
835: if (hasMethod.getParameterTypes().length != 0)
836: throw new MappingException(
837: "mapping.createMethodNoParam", hasMethod,
838: hasMethod.getDeclaringClass().getName());
839: _hasMethod = hasMethod;
840: }
841:
842: if (deleteMethod != null) {
843: if ((deleteMethod.getModifiers() & Modifier.PUBLIC) == 0
844: || (deleteMethod.getModifiers() & Modifier.STATIC) != 0)
845: throw new MappingException(
846: "mapping.accessorNotAccessible", deleteMethod,
847: deleteMethod.getDeclaringClass().getName());
848: if (deleteMethod.getParameterTypes().length != 0)
849: throw new MappingException(
850: "mapping.createMethodNoParam", deleteMethod,
851: deleteMethod.getDeclaringClass().getName());
852: _deleteMethod = deleteMethod;
853: }
854: }
855:
856: /**
857: * Mutator method used by {@link org.exolab.castor.xml.Introspector}.
858: * Please understand how this method is used before you start
859: * playing with it! :-)
860: */
861: public void setReadMethod(Method method) throws MappingException {
862: if ((method.getModifiers() & Modifier.PUBLIC) == 0
863: || (method.getModifiers() & Modifier.STATIC) != 0)
864: throw new MappingException("mapping.accessorNotAccessible",
865: method, method.getDeclaringClass().getName());
866: if (method.getParameterTypes().length != 0)
867: throw new MappingException("mapping.readMethodHasParam",
868: method, method.getDeclaringClass().getName());
869: _getMethod = method;
870: }
871:
872: /**
873: * Mutator method used by {@link org.exolab.castor.xml.Introspector}.
874: * Please understand how this method is used before you start
875: * playing with it! :-)
876: */
877: public void setWriteMethod(Method method) throws MappingException {
878: if ((method.getModifiers() & Modifier.PUBLIC) == 0
879: || (method.getModifiers() & Modifier.STATIC) != 0)
880: throw new MappingException("mapping.accessorNotAccessible",
881: method, method.getDeclaringClass().getName());
882: if (method.getParameterTypes().length != 1)
883: throw new MappingException("mapping.writeMethodNoParam",
884: method, method.getDeclaringClass().getName());
885: _setMethod = method;
886: }
887:
888: /**
889: * Mutator method used by {@link org.exolab.castor.xml.Introspector}.
890: * Please understand how this method is used before you start
891: * playing with it! :-)
892: */
893: public void setAddMethod(Method method) throws MappingException {
894: if ((method.getModifiers() & Modifier.PUBLIC) == 0
895: || (method.getModifiers() & Modifier.STATIC) != 0)
896: throw new MappingException("mapping.accessorNotAccessible",
897: method, method.getDeclaringClass().getName());
898: if (method.getParameterTypes().length != 1)
899: throw new MappingException("mapping.writeMethodNoParam",
900: method, method.getDeclaringClass().getName());
901: _addMethod = method;
902:
903: //-- make sure add method is not the same as the set method
904: if (_addMethod == _setMethod)
905: _setMethod = null;
906:
907: } //-- setAddMethod
908:
909: /**
910: * Sets the enumeration method.
911: */
912: public void setEnumMethod(Method method) throws MappingException {
913: if ((method.getModifiers() & Modifier.PUBLIC) == 0
914: || (method.getModifiers() & Modifier.STATIC) != 0)
915: throw new MappingException("mapping.accessorNotAccessible",
916: method, method.getDeclaringClass().getName());
917: if (method.getParameterTypes().length != 0)
918: throw new MappingException("mapping.readMethodHasParam",
919: method, method.getDeclaringClass().getName());
920:
921: _enumMethod = method;
922: }
923:
924: /**
925: * Sets the iteration method.
926: */
927: public void setIterMethod(Method method) throws MappingException {
928: if ((method.getModifiers() & Modifier.PUBLIC) == 0
929: || (method.getModifiers() & Modifier.STATIC) != 0)
930: throw new MappingException("mapping.accessorNotAccessible",
931: method, method.getDeclaringClass().getName());
932: if (method.getParameterTypes().length != 0)
933: throw new MappingException("mapping.readMethodHasParam",
934: method, method.getDeclaringClass().getName());
935:
936: _iterMethod = method;
937: }
938:
939: /**
940: * Selects the appropriate "write" method based on the
941: * value. This is used when there is an "add" method
942: * and a "set" method.
943: *
944: * @return the selected write method
945: **/
946: private Method selectWriteMethod(Object value) {
947: if (_setMethod != null) {
948:
949: if (_addMethod == null)
950: return _setMethod;
951:
952: if (value == null) {
953: if (_default != null)
954: value = _default;
955: else
956: return _setMethod;
957: }
958:
959: //-- check value's class type
960: Class paramType = _setMethod.getParameterTypes()[0];
961:
962: if (paramType.isAssignableFrom(value.getClass()))
963: return _setMethod;
964: }
965:
966: return _addMethod;
967:
968: } //-- selectWriteMethod
969:
970: /**
971: * Return true if the field is a collection.
972: */
973: public boolean isCollection() {
974: return (_colHandler != null);
975: }
976:
977: public String toString() {
978: return _fieldName;
979: }
980: }
|