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-2004(C) Intalio, Inc. All Rights Reserved.
042: *
043: * This file was originally developed by Keith Visco during the
044: * course of employment at Intalio Inc.
045: * All portions of this file developed by Keith Visco after Jan 19 2005 are
046: * Copyright (C) 2005 Keith Visco. All Rights Reserved.
047: *
048: * $Id: XMLMappingLoader.java 6910 2007-03-31 15:14:38Z wguttmn $
049: */package org.exolab.castor.xml;
050:
051: import org.castor.mapping.BindingType;
052: import org.exolab.castor.mapping.ClassDescriptor;
053: import org.exolab.castor.mapping.FieldDescriptor;
054: import org.exolab.castor.mapping.FieldHandler;
055: import org.exolab.castor.mapping.MapItem;
056: import org.exolab.castor.mapping.MappingException;
057: import org.exolab.castor.mapping.TypeConvertor;
058: import org.exolab.castor.mapping.CollectionHandler;
059: import org.exolab.castor.mapping.loader.AbstractFieldDescriptor;
060: import org.exolab.castor.mapping.loader.CollectionHandlers;
061: import org.exolab.castor.mapping.loader.AbstractMappingLoader;
062: import org.exolab.castor.mapping.loader.FieldHandlerImpl;
063: import org.exolab.castor.mapping.loader.TypeInfo;
064: import org.exolab.castor.mapping.loader.Types;
065: import org.exolab.castor.mapping.xml.ClassMapping;
066: import org.exolab.castor.mapping.xml.FieldMapping;
067: import org.exolab.castor.mapping.xml.BindXml;
068: import org.exolab.castor.mapping.xml.MapTo;
069: import org.exolab.castor.mapping.xml.MappingRoot;
070: import org.exolab.castor.mapping.xml.Property;
071: import org.exolab.castor.mapping.xml.types.BindXmlAutoNamingType;
072: import org.exolab.castor.mapping.xml.types.FieldMappingCollectionType;
073: import org.exolab.castor.util.LocalConfiguration;
074:
075: import org.exolab.castor.xml.handlers.ContainerFieldHandler;
076: import org.exolab.castor.xml.handlers.ToStringFieldHandler;
077: import org.exolab.castor.xml.util.XMLClassDescriptorResolverImpl;
078: import org.exolab.castor.xml.util.ContainerElement;
079: import org.exolab.castor.xml.util.XMLClassDescriptorImpl;
080: import org.exolab.castor.xml.util.XMLClassDescriptorAdapter;
081: import org.exolab.castor.xml.util.XMLContainerElementFieldDescriptor;
082: import org.exolab.castor.xml.util.XMLFieldDescriptorImpl;
083: import org.exolab.castor.xml.validators.IdRefValidator;
084: import org.exolab.castor.xml.validators.NameValidator;
085:
086: import java.lang.reflect.Constructor;
087: import java.lang.reflect.Method;
088: import java.lang.reflect.Modifier;
089: import java.util.ArrayList;
090: import java.util.List;
091:
092: /**
093: * An XML implementation of mapping helper. Creates XML class
094: * descriptors from the mapping file.
095: *
096: * @author <a href="keith AT kvisco DOT com">Keith Visco</a>
097: * @author <a href="arkin@intalio.com">Assaf Arkin</a>
098: * @version $Revision: 6910 $ $Date: 2006-02-23 01:37:50 -0700 (Thu, 23 Feb 2006) $
099: */
100: public final class XMLMappingLoader extends AbstractMappingLoader {
101: //-----------------------------------------------------------------------------------
102:
103: /** The default xml prefix used on certain attributes such as xml:lang, xml:base,
104: * etc. */
105: private static final String XML_PREFIX = "xml:";
106:
107: /** Empty array of class types used for reflection. */
108: private static final Class[] EMPTY_ARGS = new Class[0];
109:
110: /** The NCName Schema type. */
111: private static final String NCNAME = "NCName";
112:
113: /** The string argument for the valueOf method, used for introspection. */
114: private static final Class[] STRING_ARG = { String.class };
115:
116: /** Factory method name for type-safe enumerations. This is primarily for allowing
117: * users to map classes that were created by Castor's SourceGenerator. */
118: private static final String VALUE_OF = "valueOf";
119:
120: //-----------------------------------------------------------------------------------
121:
122: /** A reference to the internal ClassDescriptorResolver. */
123: private XMLClassDescriptorResolverImpl _cdResolver;
124:
125: /** The default NodeType for primitives. */
126: private NodeType _primitiveNodeType = null;
127:
128: /** Naming conventions. */
129: private XMLNaming _naming = null;
130:
131: //-----------------------------------------------------------------------------------
132:
133: /**
134: * Creates a new XMLMappingLoader
135: */
136: public XMLMappingLoader(ClassLoader loader) {
137: super (loader);
138:
139: LocalConfiguration config = LocalConfiguration.getInstance();
140: _primitiveNodeType = config.getPrimitiveNodeType();
141: _naming = config.getXMLNaming();
142: }
143:
144: //-----------------------------------------------------------------------------------
145:
146: /**
147: * {@inheritDoc}
148: */
149: public BindingType getBindingType() {
150: return BindingType.XML;
151: }
152:
153: //-----------------------------------------------------------------------------------
154:
155: /**
156: * {@inheritDoc}
157: */
158: public void loadMapping(final MappingRoot mapping,
159: final Object param) throws MappingException {
160: if (loadMapping()) {
161: createClassDescriptors(mapping);
162: }
163: }
164:
165: //-----------------------------------------------------------------------------------
166:
167: protected final ClassDescriptor createClassDescriptor(
168: final ClassMapping clsMap) throws MappingException {
169: if (clsMap.getAutoComplete()) {
170: if ((clsMap.getMapTo() == null)
171: && ((clsMap.getClassChoice() == null) || (clsMap
172: .getClassChoice().getFieldMappingCount() == 0))
173: && (clsMap.getIdentityCount() == 0)) {
174: // If we make it here we simply try to load a compiled mapping
175: if (_cdResolver == null) {
176: createResolver();
177: }
178: try {
179: ClassDescriptor clsDesc = _cdResolver
180: .resolve(clsMap.getName());
181: if (clsDesc != null) {
182: return clsDesc;
183: }
184: } catch (ResolverException rx) {
185:
186: }
187: }
188: }
189:
190: // Create the class descriptor.
191: XMLClassDescriptorAdapter xmlClassDesc = new XMLClassDescriptorAdapter();
192:
193: // Obtain the Java class.
194: Class javaClass = resolveType(clsMap.getName());
195: if (clsMap.getVerifyConstructable()) {
196: if (!Types.isConstructable(javaClass, true)) {
197: throw new MappingException(
198: "mapping.classNotConstructable", javaClass
199: .getName());
200: }
201: }
202: xmlClassDesc.setJavaClass(javaClass);
203:
204: // Obtain XML name.
205: String xmlName;
206: MapTo mapTo = clsMap.getMapTo();
207: if ((mapTo != null) && (mapTo.getXml() != null)) {
208: xmlName = mapTo.getXml();
209: } else {
210: String clsName = javaClass.getName();
211: int idx = clsName.lastIndexOf('.');
212: if (idx >= 0) {
213: clsName = clsName.substring(idx + 1);
214: }
215: xmlName = _naming.toXMLName(clsName);
216: }
217: xmlClassDesc.setXMLName(xmlName);
218:
219: // If this class extends another class, we need to obtain the extended
220: // class and make sure this class indeed extends it.
221: ClassDescriptor extDesc = getExtended(clsMap, javaClass);
222: xmlClassDesc.setExtends((XMLClassDescriptor) extDesc);
223:
224: // Create all field descriptors.
225: AbstractFieldDescriptor[] allFields = createFieldDescriptors(
226: clsMap, javaClass);
227:
228: // Make sure there are no two fields with the same name.
229: checkFieldNameDuplicates(allFields, javaClass);
230:
231: // Identify identity and normal fields. Note that order must be preserved.
232: List fieldList = new ArrayList(allFields.length);
233: List idList = new ArrayList();
234: if (extDesc == null) {
235: // Sort fields into 2 lists based on identity definition of field.
236: for (int i = 0; i < allFields.length; i++) {
237: if (!allFields[i].isIdentity()) {
238: fieldList.add(allFields[i]);
239: } else {
240: idList.add(allFields[i]);
241: }
242: }
243:
244: if (idList.size() == 0) {
245: // Found no identities based on identity definition of field.
246: // Try to find identities based on identity definition on class.
247: String[] idNames = clsMap.getIdentity();
248:
249: FieldDescriptor identity;
250: for (int i = 0; i < idNames.length; i++) {
251: identity = findIdentityByName(fieldList,
252: idNames[i], javaClass);
253: if (identity != null) {
254: idList.add(identity);
255: } else {
256: throw new MappingException(
257: "mapping.identityMissing", idNames[i],
258: javaClass.getName());
259: }
260: }
261: }
262: } else {
263: // Add all fields of extending class to field list.
264: for (int i = 0; i < allFields.length; i++) {
265: fieldList.add(allFields[i]);
266: }
267:
268: // Add identity of extended class to identity list.
269: if (extDesc.getIdentity() != null) {
270: idList.add(extDesc.getIdentity());
271: }
272:
273: // Search redefined identities in extending class.
274: FieldDescriptor identity;
275: for (int i = 0; i < idList.size(); i++) {
276: String idname = ((FieldDescriptor) idList.get(i))
277: .getFieldName();
278: identity = findIdentityByName(fieldList, idname,
279: javaClass);
280: if (identity != null) {
281: idList.set(i, identity);
282: }
283: }
284: }
285:
286: FieldDescriptor xmlId = null;
287: if (idList.size() != 0) {
288: xmlId = (FieldDescriptor) idList.get(0);
289: }
290:
291: if (xmlId != null) {
292: xmlClassDesc.setIdentity((XMLFieldDescriptorImpl) xmlId);
293: }
294: for (int i = 0; i < fieldList.size(); i++) {
295: FieldDescriptor fieldDesc = (FieldDescriptor) fieldList
296: .get(i);
297: if (fieldDesc != null) {
298: xmlClassDesc
299: .addFieldDescriptor((XMLFieldDescriptorImpl) fieldDesc);
300: }
301: }
302:
303: if (clsMap.getAutoComplete()) {
304:
305: XMLClassDescriptor referenceDesc = null;
306:
307: Class type = xmlClassDesc.getJavaClass();
308:
309: //-- check compiled descriptors
310: if (_cdResolver == null) {
311: createResolver();
312: }
313: try {
314: referenceDesc = (XMLClassDescriptor) _cdResolver
315: .resolve(type);
316: } catch (ResolverException rx) {
317: throw new MappingException(rx);
318: }
319:
320: if (referenceDesc == null) {
321: Introspector introspector = new Introspector();
322: try {
323: referenceDesc = introspector
324: .generateClassDescriptor(type);
325: if (clsMap.getExtends() != null) {
326: //-- clear parent from introspected descriptor since
327: //-- a mapping was provided in the mapping file
328: ((XMLClassDescriptorImpl) referenceDesc)
329: .setExtends(null);
330: }
331: } catch (MarshalException mx) {
332: String error = "unable to introspect class '"
333: + type.getName() + "' for auto-complete: ";
334: throw new MappingException(error + mx.getMessage());
335: }
336: }
337:
338: //-- check for identity
339: String identity = "";
340: if (clsMap.getIdentityCount() > 0)
341: identity = clsMap.getIdentity(0);
342:
343: FieldDescriptor[] xmlFields2 = xmlClassDesc.getFields();
344:
345: // Attributes
346: XMLFieldDescriptor[] introFields = referenceDesc
347: .getAttributeDescriptors();
348: for (int i = 0; i < introFields.length; ++i) {
349: if (!isMatchFieldName(xmlFields2, introFields[i]
350: .getFieldName())) {
351: // If there is no field with this name, we can add it
352: if (introFields[i].getFieldName().equals(identity)) {
353: xmlClassDesc.setIdentity(introFields[i]);
354: } else {
355: xmlClassDesc.addFieldDescriptor(introFields[i]);
356: }
357: }
358: }
359:
360: // Elements
361: introFields = referenceDesc.getElementDescriptors();
362: for (int i = 0; i < introFields.length; ++i) {
363: if (!isMatchFieldName(xmlFields2, introFields[i]
364: .getFieldName())) {
365: // If there is no field with this name, we can add it
366: if (introFields[i].getFieldName().equals(identity)) {
367: xmlClassDesc.setIdentity(introFields[i]);
368: } else {
369: xmlClassDesc.addFieldDescriptor(introFields[i]);
370: }
371: }
372: }
373:
374: // Content
375: XMLFieldDescriptor field = referenceDesc
376: .getContentDescriptor();
377: if (field != null)
378: if (!isMatchFieldName(xmlFields2, field.getFieldName()))
379: // If there is no field with this name, we can add
380: xmlClassDesc.addFieldDescriptor(field);
381:
382: }
383:
384: // Copy ns-uri + ns-prefix + element-definition
385: if (mapTo != null) {
386: xmlClassDesc.setNameSpacePrefix(mapTo.getNsPrefix());
387: xmlClassDesc.setNameSpaceURI(mapTo.getNsUri());
388: xmlClassDesc.setElementDefinition(mapTo
389: .getElementDefinition());
390: }
391:
392: return xmlClassDesc;
393: }
394:
395: protected final FieldDescriptor findIdentityByName(
396: final List fldList, final String idName,
397: final Class javaClass) {
398: for (int i = 0; i < fldList.size(); i++) {
399: FieldDescriptor field = (FieldDescriptor) fldList.get(i);
400: if (idName.equals(field.getFieldName())) {
401: fldList.remove(i);
402: return field;
403: }
404: }
405: return null;
406: }
407:
408: protected final void resolveRelations(ClassDescriptor clsDesc) {
409: FieldDescriptor[] fields;
410:
411: fields = clsDesc.getFields();
412: for (int i = 0; i < fields.length; ++i) {
413: if (fields[i].getClassDescriptor() != null)
414: continue;
415: ClassDescriptor relDesc;
416:
417: Class fieldType = fields[i].getFieldType();
418: if (fieldType != null) {
419: relDesc = getDescriptor(fieldType.getName());
420: if (relDesc != null
421: && relDesc instanceof XMLClassDescriptor
422: && fields[i] instanceof XMLFieldDescriptorImpl) {
423: ((XMLFieldDescriptorImpl) fields[i])
424: .setClassDescriptor(relDesc);
425: }
426: }
427: }
428: if (clsDesc instanceof XMLClassDescriptorImpl)
429: ((XMLClassDescriptorImpl) clsDesc).sortDescriptors();
430: }
431:
432: //-----------------------------------------------------------------------------------
433:
434: private void createResolver() {
435: _cdResolver = (XMLClassDescriptorResolverImpl) ClassDescriptorResolverFactory
436: .createClassDescriptorResolver(BindingType.XML);
437: _cdResolver.setIntrospection(false);
438: _cdResolver.setLoadPackageMappings(false);
439: }
440:
441: /**
442: * Match if a field named <code>fieldName</code> is in fields
443: */
444: private boolean isMatchFieldName(FieldDescriptor[] fields,
445: String fieldName) {
446: for (int i = 0; i < fields.length; ++i)
447: if (fields[i].getFieldName().equals(fieldName))
448: return true;
449:
450: return false;
451: } //-- method: isMatchFieldName
452:
453: protected AbstractFieldDescriptor createFieldDesc(Class javaClass,
454: FieldMapping fieldMap) throws MappingException {
455:
456: FieldDescriptor fieldDesc;
457: FieldMappingCollectionType colType = fieldMap.getCollection();
458: String xmlName = null;
459: NodeType nodeType = null;
460: String match = null;
461: XMLFieldDescriptorImpl xmlDesc;
462: boolean isReference = false;
463: boolean isXMLTransient = false;
464:
465: //-- handle special case for HashMap/Hashtable
466: if ((fieldMap.getType() == null) && (colType != null)) {
467: if ((colType == FieldMappingCollectionType.HASHTABLE)
468: || (colType == FieldMappingCollectionType.MAP)
469: || (colType == FieldMappingCollectionType.SORTEDMAP)) {
470: fieldMap.setType(MapItem.class.getName());
471: }
472: }
473:
474: // Create an XML field descriptor
475: fieldDesc = super .createFieldDesc(javaClass, fieldMap);
476:
477: BindXml xml = fieldMap.getBindXml();
478:
479: boolean deriveNameByClass = false;
480:
481: if (xml != null) {
482: //-- xml name
483: xmlName = xml.getName();
484:
485: //-- node type
486: if (xml.getNode() != null)
487: nodeType = NodeType.getNodeType(xml.getNode()
488: .toString());
489:
490: //-- matches
491: match = xml.getMatches();
492:
493: //-- reference
494: isReference = xml.getReference();
495:
496: //-- XML transient
497: isXMLTransient = xml.getTransient();
498:
499: //-- autonaming
500: BindXmlAutoNamingType autoName = xml.getAutoNaming();
501: if (autoName != null) {
502: deriveNameByClass = (autoName == BindXmlAutoNamingType.DERIVEBYCLASS);
503: }
504:
505: }
506:
507: //-- transient
508: //-- XXXX -> if it's transient we probably shouldn't do all
509: //-- XXXX -> the unecessary work
510: isXMLTransient = isXMLTransient || fieldDesc.isTransient();
511:
512: //--
513:
514: //-- handle QName for xmlName
515: String namespace = null;
516: if ((xmlName != null) && (xmlName.length() > 0)) {
517: if (xmlName.charAt(0) == '{') {
518: int idx = xmlName.indexOf('}');
519: if (idx < 0) {
520: throw new MappingException("Invalid QName: "
521: + xmlName);
522: }
523: namespace = xmlName.substring(1, idx);
524: xmlName = xmlName.substring(idx + 1);
525: } else if (xmlName.startsWith(XML_PREFIX)) {
526: namespace = Namespaces.XML_NAMESPACE;
527: xmlName = xmlName.substring(4);
528: }
529: }
530:
531: if (nodeType == null) {
532: if (isPrimitive(javaClass))
533: nodeType = _primitiveNodeType;
534: else
535: nodeType = NodeType.Element;
536: }
537:
538: //-- Create XML name if necessary. Note if name is to be derived
539: //-- by class..we just make sure we set the name to null...
540: //-- the Marshaller does this during runtime. This allows
541: //-- Collections to be handled properly.
542: if ((!deriveNameByClass)
543: && ((xmlName == null) && (match == null))) {
544: xmlName = _naming.toXMLName(fieldDesc.getFieldName());
545: match = xmlName + ' ' + fieldDesc.getFieldName();
546: }
547:
548: xmlDesc = new XMLFieldDescriptorImpl(fieldDesc, xmlName,
549: nodeType, _primitiveNodeType);
550:
551: //-- transient?
552: xmlDesc.setTransient(isXMLTransient);
553:
554: //--set a default fieldValidator
555: xmlDesc.setValidator(new FieldValidator());
556:
557: //-- enable use parent namespace if explicit one doesn't exist
558: xmlDesc.setUseParentsNamespace(true);
559:
560: //-- If deriveNameByClass we need to reset the name to
561: //-- null because XMLFieldDescriptorImpl tries to be smart
562: //-- and automatically creates the name.
563: if (deriveNameByClass) {
564: xmlDesc.setXMLName(null);
565: }
566:
567: //-- namespace
568: if (namespace != null) {
569: xmlDesc.setNameSpaceURI(namespace);
570: }
571:
572: //-- matches
573: if (match != null) {
574: xmlDesc.setMatches(match);
575: //-- special fix for xml-name since XMLFieldDescriptorImpl
576: //-- will create a default name based off the field name
577: if (xmlName == null)
578: xmlDesc.setXMLName(null);
579: }
580:
581: //-- reference
582: xmlDesc.setReference(isReference);
583: if (isReference) {
584: if (colType == null) {
585: FieldValidator fieldValidator = new FieldValidator();
586: fieldValidator.setValidator(new IdRefValidator());
587: xmlDesc.setValidator(fieldValidator);
588: } else {
589: // TODO handle other cases
590: }
591: }
592:
593: xmlDesc.setContainer(fieldMap.getContainer());
594:
595: if (xml != null) {
596:
597: //-- has class descriptor for type specified
598: if (xml.getClassMapping() != null) {
599: ClassDescriptor cd = createClassDescriptor(xml
600: .getClassMapping());
601: xmlDesc.setClassDescriptor(cd);
602: }
603:
604: //-- has location path?
605: if (xml.getLocation() != null) {
606: xmlDesc.setLocationPath(xml.getLocation());
607: }
608: //is the value type needs specific handling
609: //such as QName or NCName support?
610: String xmlType = xml.getType();
611: xmlDesc.setSchemaType(xmlType);
612: xmlDesc.setQNamePrefix(xml.getQNamePrefix());
613: TypeValidator validator = null;
614: if (NCNAME.equals(xmlType)) {
615: validator = new NameValidator(
616: XMLConstants.NAME_TYPE_NCNAME);
617: xmlDesc.setValidator(new FieldValidator(validator));
618: }
619:
620: //-- special properties?
621: Property[] props = xml.getProperty();
622: if ((props != null) && (props.length > 0)) {
623: for (int pIdx = 0; pIdx < props.length; pIdx++) {
624: Property prop = props[pIdx];
625: xmlDesc
626: .setProperty(prop.getName(), prop
627: .getValue());
628: }
629: }
630: }
631:
632: //-- Get collection type
633: if (colType == null) {
634: //-- just in case user forgot to use collection="..."
635: //-- in the mapping file
636: Class type = fieldDesc.getFieldType();
637: if (type != null && CollectionHandlers.hasHandler(type)) {
638: String typeName = CollectionHandlers
639: .getCollectionName(type);
640: colType = FieldMappingCollectionType.valueOf(typeName);
641: }
642: }
643:
644: //-- isMapped item
645: if (colType != null) {
646: if ((colType == FieldMappingCollectionType.HASHTABLE)
647: || (colType == FieldMappingCollectionType.MAP)
648: || (colType == FieldMappingCollectionType.SORTEDMAP)) {
649: //-- Make sure user is not using an addMethod
650: //-- before setting the mapped field to true.
651: String methodName = fieldMap.getSetMethod();
652: if (methodName != null) {
653: if (!methodName.startsWith("add")) {
654: xmlDesc.setMapped(true);
655: }
656: } else
657: xmlDesc.setMapped(true);
658: }
659:
660: //-- special NodeType.Namespace handling
661: //-- prevent FieldHandlerImpl from using CollectionHandler
662: //-- during calls to #getValue
663: if ((nodeType == NodeType.Namespace)
664: || (xmlDesc.isMapped())) {
665: Object handler = xmlDesc.getHandler();
666: if (handler instanceof FieldHandlerImpl) {
667: FieldHandlerImpl handlerImpl = (FieldHandlerImpl) handler;
668: handlerImpl.setConvertFrom(new IdentityConvertor());
669: }
670: }
671: //-- wrap collection in element?
672: if (nodeType == NodeType.Element) {
673: if (fieldMap.hasContainer()
674: && (!fieldMap.getContainer())) {
675: xmlDesc = wrapCollection(xmlDesc);
676: }
677: }
678: }
679:
680: //-- is Type-Safe Enumeration?
681: //-- This is not very clean, we should have a way
682: //-- to specify something is a type-safe enumeration
683: //-- without having to guess.
684: else if ((!isReference) && (!isXMLTransient)) {
685: Class fieldType = xmlDesc.getFieldType();
686: if (!isPrimitive(fieldType)) {
687: //-- make sure no default constructor
688: Constructor cons = null;
689: try {
690: cons = fieldType.getConstructor(EMPTY_ARGS);
691: if (!Modifier.isPublic(cons.getModifiers())) {
692: cons = null;
693: }
694: } catch (NoSuchMethodException nsmx) {
695: //-- Do nothing
696: }
697: try {
698: if (cons == null) {
699: //-- make sure a valueOf factory method
700: //-- exists and no user specified handler exists
701: Method method = fieldType.getMethod(VALUE_OF,
702: STRING_ARG);
703: Class returnType = method.getReturnType();
704: if ((returnType != null)
705: && fieldType
706: .isAssignableFrom(returnType)) {
707: if (fieldMap.getHandler() == null) {
708: //-- Use EnumFieldHandler
709: //-- mapping loader now supports a basic EnumFieldHandler
710: //-- for xml we simply need to make sure the toString()
711: //-- method is called during getValue()
712: //FieldHandler handler = xmlDesc.getHandler();
713: //handler = new EnumFieldHandler(fieldType, handler);
714:
715: FieldHandler handler = new ToStringFieldHandler(
716: fieldType, xmlDesc.getHandler());
717:
718: xmlDesc.setHandler(handler);
719: xmlDesc.setImmutable(true);
720: }
721: }
722: }
723: } catch (NoSuchMethodException nsmx) {
724: //-- Do nothing
725: }
726: }
727: }
728:
729: //-- constructor argument?
730: String setter = fieldMap.getSetMethod();
731: if (setter != null) {
732: if (setter.startsWith("%")) {
733: int index = 0;
734: setter = setter.substring(1);
735: index = Integer.parseInt(setter);
736: if ((index < 1) || (index > 9)) {
737: throw new MappingException(
738: "mapper.invalidParameterIndex", setter);
739: }
740: //-- adjust index to base zero
741: --index;
742: xmlDesc.setConstructorArgumentIndex(index);
743: }
744: }
745:
746: return xmlDesc;
747: }
748:
749: /**
750: * Sets whether or not to look for and load package specific
751: * mapping files (".castor.xml" files).
752: *
753: * @param loadPackageMappings a boolean that enables or
754: * disables the loading of package specific mapping files
755: */
756: public void setLoadPackageMappings(boolean loadPackageMappings) {
757: if (_cdResolver == null) {
758: createResolver();
759: }
760: _cdResolver.setLoadPackageMappings(loadPackageMappings);
761: } //-- setLoadPackageMappings
762:
763: protected TypeInfo getTypeInfo(Class fieldType,
764: CollectionHandler colHandler, FieldMapping fieldMap)
765: throws MappingException {
766: return new TypeInfo(fieldType, null, null, null, fieldMap
767: .getRequired(), null, colHandler, false);
768: }
769:
770: /**
771: * This method allows a collection to be treated as a first class
772: * object (and not a container) so that it has an element representation
773: * in the marshalled XML.
774: */
775: private XMLFieldDescriptorImpl wrapCollection(
776: XMLFieldDescriptorImpl fieldDesc) throws MappingException {
777: //-- If we have a field 'c' that is a collection and
778: //-- we want to wrap that field in an element <e>, we
779: //-- need to create a field descriptor for
780: //-- an object that represents the element <e> and
781: //-- acts as a go-between from the parent of 'c'
782: //-- denoted as P(c) and 'c' itself
783: //
784: // object model: P(c) -> c
785: // xml : <p><e><c></e><p>
786:
787: //-- Make new class descriptor for the field that
788: //-- will represent the container element <e>
789: Class type = ContainerElement.class;
790: XMLClassDescriptorImpl classDesc = new XMLClassDescriptorImpl(
791: type);
792: //-- make copy of fieldDesc and add it to our new class descriptor
793: XMLFieldDescriptorImpl newFieldDesc = new XMLFieldDescriptorImpl(
794: fieldDesc, fieldDesc.getXMLName(), fieldDesc
795: .getNodeType(), _primitiveNodeType);
796: //-- nullify xmlName so that auto-naming will be enabled,
797: //-- we can't do this in the constructor because
798: //-- XMLFieldDescriptorImpl will create a default one.
799: newFieldDesc.setXMLName(null);
800: newFieldDesc.setMatches("*");
801:
802: //-- add the field descriptor to our new class descriptor
803: classDesc.addFieldDescriptor(newFieldDesc);
804: //-- reassociate the orignal class descriptor (for 'c')
805: // of fieldDesc with our new classDesc
806: fieldDesc.setClassDescriptor(classDesc);
807:
808: //-- wrap the field handler in a special container field
809: //-- handler that will actually do the delgation work
810: FieldHandler handler = new ContainerFieldHandler(fieldDesc
811: .getHandler());
812: newFieldDesc.setHandler(handler);
813: fieldDesc.setHandler(handler);
814:
815: //-- Change fieldType of original field descriptor and
816: //-- return new descriptor
817: return new XMLContainerElementFieldDescriptor(fieldDesc,
818: _primitiveNodeType);
819: } //-- createWrapperDescriptor
820:
821: /**
822: * A special TypeConvertor that simply returns the object
823: * given. This is used for preventing the FieldHandlerImpl
824: * from using a CollectionHandler when getValue is called.
825: **/
826: class IdentityConvertor implements TypeConvertor {
827: public Object convert(Object object, String param)
828: throws ClassCastException {
829: return object;
830: }
831: } //-- class: IdentityConvertor
832: } //-- class: XMLMappingLoader
|