1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 package org.exolab.castor.xml;
51
52
53 import java.lang.reflect.Constructor;
54 import java.lang.reflect.Method;
55 import java.lang.reflect.Modifier;
56 import java.util.ArrayList;
57 import java.util.List;
58
59 import org.apache.commons.logging.Log;
60 import org.apache.commons.logging.LogFactory;
61 import org.castor.mapping.BindingType;
62 import org.exolab.castor.mapping.AbstractFieldHandler;
63 import org.exolab.castor.mapping.ClassDescriptor;
64 import org.exolab.castor.mapping.CollectionHandler;
65 import org.exolab.castor.mapping.FieldDescriptor;
66 import org.exolab.castor.mapping.FieldHandler;
67 import org.exolab.castor.mapping.MapItem;
68 import org.exolab.castor.mapping.MappingException;
69 import org.exolab.castor.mapping.TypeConvertor;
70 import org.exolab.castor.mapping.loader.FieldDescriptorImpl;
71 import org.exolab.castor.mapping.loader.AbstractMappingLoader;
72 import org.exolab.castor.mapping.loader.CollectionHandlers;
73 import org.exolab.castor.mapping.loader.FieldHandlerImpl;
74 import org.exolab.castor.mapping.loader.TypeInfo;
75 import org.exolab.castor.mapping.loader.Types;
76 import org.exolab.castor.mapping.xml.BindXml;
77 import org.exolab.castor.mapping.xml.ClassMapping;
78 import org.exolab.castor.mapping.xml.FieldMapping;
79 import org.exolab.castor.mapping.xml.MapTo;
80 import org.exolab.castor.mapping.xml.MappingRoot;
81 import org.exolab.castor.mapping.xml.Property;
82 import org.exolab.castor.mapping.xml.types.BindXmlAutoNamingType;
83 import org.exolab.castor.mapping.xml.types.FieldMappingCollectionType;
84 import org.exolab.castor.xml.handlers.ContainerFieldHandler;
85 import org.exolab.castor.xml.handlers.ToStringFieldHandler;
86 import org.exolab.castor.xml.util.ContainerElement;
87 import org.exolab.castor.xml.util.XMLClassDescriptorAdapter;
88 import org.exolab.castor.xml.util.XMLClassDescriptorImpl;
89 import org.exolab.castor.xml.util.XMLContainerElementFieldDescriptor;
90 import org.exolab.castor.xml.util.XMLFieldDescriptorImpl;
91 import org.exolab.castor.xml.validators.IdRefValidator;
92 import org.exolab.castor.xml.validators.NameValidator;
93
94
95
96
97
98
99
100
101
102 public final class XMLMappingLoader extends AbstractMappingLoader {
103
104
105
106
107 private static final Log LOG = LogFactory.getLog(XMLMappingLoader.class);
108
109
110
111
112
113 private static final String XML_PREFIX = "xml:";
114
115
116 private static final Class[] EMPTY_ARGS = new Class[0];
117
118
119 private static final String NCNAME = "NCName";
120
121
122 private static final Class[] STRING_ARG = {String.class};
123
124
125
126 private static final String VALUE_OF = "valueOf";
127
128
129
130
131
132
133
134 public XMLMappingLoader(final ClassLoader loader) {
135 super(loader);
136 }
137
138
139
140
141 public BindingType getBindingType() {
142 return BindingType.XML;
143 }
144
145
146
147
148 public void loadMapping(final MappingRoot mapping, final Object param)
149 throws MappingException {
150 if (loadMapping()) {
151 createFieldHandlers(mapping);
152 createClassDescriptors(mapping);
153 }
154 }
155
156
157
158
159
160
161
162
163
164
165 protected ClassDescriptor createClassDescriptor(final ClassMapping classMapping)
166 throws MappingException {
167
168 if ((getInternalContext() == null)
169 || (getInternalContext().getXMLClassDescriptorResolver() == null)) {
170 String message = "Internal context or class descriptor resolver within are not valid";
171 LOG.warn(message);
172 throw new IllegalStateException(message);
173 }
174
175 XMLClassDescriptorAdapter xmlClassDesc = new XMLClassDescriptorAdapter();
176
177
178 getInternalContext().getXMLClassDescriptorResolver().setUseIntrospection(false);
179 getInternalContext().getXMLClassDescriptorResolver().setLoadPackageMappings(false);
180
181 try {
182 if (classMapping.getAutoComplete()) {
183 if ((classMapping.getMapTo() == null)
184 && ((classMapping.getClassChoice() == null)
185 || (classMapping.getClassChoice().getFieldMappingCount() == 0))
186 && (classMapping.getIdentityCount() == 0)) {
187
188 try {
189 ClassDescriptor clsDesc =
190 getInternalContext()
191 .getXMLClassDescriptorResolver().resolve(classMapping.getName());
192 if (clsDesc != null) {
193 return clsDesc;
194 }
195 } catch (ResolverException e) {
196 if (LOG.isDebugEnabled()) {
197 String message =
198 new StringBuffer().append("Ignoring exception: ").append(e)
199 .append(" at resolving: ").append(classMapping.getName()).toString();
200 LOG.debug(message);
201 }
202 }
203 }
204 }
205
206
207 Class javaClass = resolveType(classMapping.getName());
208 if (classMapping.getVerifyConstructable()) {
209 if (!Types.isConstructable(javaClass, true)) {
210 throw new MappingException(
211 "mapping.classNotConstructable", javaClass.getName());
212 }
213 }
214 xmlClassDesc.setJavaClass(javaClass);
215
216
217 String xmlName;
218 MapTo mapTo = classMapping.getMapTo();
219 if ((mapTo != null) && (mapTo.getXml() != null)) {
220 xmlName = mapTo.getXml();
221 } else {
222 String clsName = getInternalContext().getJavaNaming().getClassName(javaClass);
223 xmlName = getInternalContext().getXMLNaming().toXMLName(clsName);
224 }
225 xmlClassDesc.setXMLName(xmlName);
226
227
228
229 ClassDescriptor extDesc = getExtended(classMapping, javaClass);
230 xmlClassDesc.setExtends((XMLClassDescriptor) extDesc);
231
232
233 FieldDescriptorImpl[] allFields = createFieldDescriptors(classMapping, javaClass);
234
235
236 checkFieldNameDuplicates(allFields, javaClass);
237
238
239 List fieldList = new ArrayList(allFields.length);
240 List idList = new ArrayList();
241 if (extDesc == null) {
242
243 for (int i = 0; i < allFields.length; i++) {
244 if (!allFields[i].isIdentity()) {
245 fieldList.add(allFields[i]);
246 } else {
247 idList.add(allFields[i]);
248 }
249 }
250
251 if (idList.size() == 0) {
252
253
254 String[] idNames = classMapping.getIdentity();
255
256 FieldDescriptor identity;
257 for (int i = 0; i < idNames.length; i++) {
258 identity = findIdentityByName(fieldList, idNames[i], javaClass);
259 if (identity != null) {
260 idList.add(identity);
261 } else {
262 throw new MappingException("mapping.identityMissing",
263 idNames[i], javaClass.getName());
264 }
265 }
266 }
267 } else {
268
269 for (int i = 0; i < allFields.length; i++) { fieldList.add(allFields[i]); }
270
271
272 if (extDesc.getIdentity() != null) { idList.add(extDesc.getIdentity()); }
273
274
275 FieldDescriptor identity;
276 for (int i = 0; i < idList.size(); i++) {
277 String idname = ((FieldDescriptor) idList.get(i)).getFieldName();
278 identity = findIdentityByName(fieldList, idname, javaClass);
279 if (identity != null) { idList.set(i, identity); }
280 }
281 }
282
283 FieldDescriptor xmlId = null;
284 if (idList.size() != 0) { xmlId = (FieldDescriptor) idList.get(0); }
285
286 if (xmlId != null) { xmlClassDesc.setIdentity((XMLFieldDescriptorImpl) xmlId); }
287 for (int i = 0; i < fieldList.size(); i++) {
288 FieldDescriptor fieldDesc = (FieldDescriptor) fieldList.get(i);
289 if (fieldDesc != null) {
290 xmlClassDesc.addFieldDescriptor((XMLFieldDescriptorImpl) fieldDesc);
291 }
292 }
293
294 if (classMapping.getAutoComplete()) {
295
296 XMLClassDescriptor referenceDesc = null;
297
298 Class type = xmlClassDesc.getJavaClass();
299
300
301 if ((getInternalContext() == null)
302 || (getInternalContext().getXMLClassDescriptorResolver() == null)) {
303 String message = "Internal context or class descriptor resolver within are not valid";
304 LOG.warn(message);
305 throw new IllegalStateException(message);
306 }
307 try {
308 referenceDesc = (XMLClassDescriptor) getInternalContext().getXMLClassDescriptorResolver().resolve(type);
309 } catch (ResolverException rx) {
310 throw new MappingException(rx);
311 }
312
313 if (referenceDesc == null) {
314 Introspector introspector = getInternalContext().getIntrospector();
315 try {
316 referenceDesc = introspector.generateClassDescriptor(type);
317 if (classMapping.getExtends() != null) {
318
319
320 ((XMLClassDescriptorImpl) referenceDesc).setExtends(null);
321 }
322 } catch (MarshalException mx) {
323 String error = "unable to introspect class '" +
324 type.getName() + "' for auto-complete: ";
325 throw new MappingException(error + mx.getMessage());
326 }
327 }
328
329
330 String identity = "";
331 if (classMapping.getIdentityCount() > 0) {
332 identity = classMapping.getIdentity(0);
333 }
334
335
336 FieldDescriptor[] xmlFields2 = xmlClassDesc.getFields();
337
338
339 XMLFieldDescriptor[] introFields = referenceDesc.getAttributeDescriptors();
340 for (int i = 0; i < introFields.length; ++i) {
341 if (!isMatchFieldName(xmlFields2, introFields[i].getFieldName())) {
342
343 if (introFields[i].getFieldName().equals(identity)) {
344 xmlClassDesc.setIdentity(introFields[i]);
345 }
346 else {
347 xmlClassDesc.addFieldDescriptor(introFields[i]);
348 }
349 }
350 }
351
352
353 introFields = referenceDesc.getElementDescriptors();
354 for (int i = 0; i < introFields.length; ++i) {
355 if (!isMatchFieldName(xmlFields2, introFields[i].getFieldName())) {
356
357 if (introFields[i].getFieldName().equals(identity)) {
358 xmlClassDesc.setIdentity(introFields[i]);
359 }
360 else {
361 xmlClassDesc.addFieldDescriptor(introFields[i]);
362 }
363 }
364 }
365
366
367 XMLFieldDescriptor field = referenceDesc.getContentDescriptor();
368 if (field != null) {
369 if (!isMatchFieldName(xmlFields2, field.getFieldName())) {
370
371 xmlClassDesc.addFieldDescriptor(field);
372 }
373 }
374 }
375
376
377 if (mapTo != null) {
378 xmlClassDesc.setNameSpacePrefix(mapTo.getNsPrefix());
379 xmlClassDesc.setNameSpaceURI(mapTo.getNsUri());
380 xmlClassDesc.setElementDefinition(mapTo.getElementDefinition());
381 }
382 }
383 finally {
384 getInternalContext().getXMLClassDescriptorResolver().setUseIntrospection(true);
385 getInternalContext().getXMLClassDescriptorResolver().setLoadPackageMappings(true);
386 }
387
388 return xmlClassDesc;
389 }
390
391 protected final FieldDescriptor findIdentityByName(
392 final List fldList, final String idName, final Class javaClass) {
393 for (int i = 0; i < fldList.size(); i++) {
394 FieldDescriptor field = (FieldDescriptor) fldList.get(i);
395 if (idName.equals(field.getFieldName())) {
396 fldList.remove(i);
397 return field;
398 }
399 }
400 return null;
401 }
402
403 protected final void resolveRelations(ClassDescriptor clsDesc) {
404 FieldDescriptor[] fields;
405
406 fields = clsDesc.getFields();
407 for (int i = 0 ; i < fields.length ; ++i ) {
408 if (fields[i].getClassDescriptor() != null) continue;
409 ClassDescriptor relDesc;
410
411 Class fieldType = fields[i].getFieldType();
412 if (fieldType != null) {
413 relDesc = getDescriptor(fieldType.getName());
414 if (relDesc != null &&
415 relDesc instanceof XMLClassDescriptor &&
416 fields[ i ] instanceof XMLFieldDescriptorImpl) {
417 ((XMLFieldDescriptorImpl) fields[i]).setClassDescriptor(relDesc);
418 }
419 }
420 }
421 if ( clsDesc instanceof XMLClassDescriptorImpl )
422 ( (XMLClassDescriptorImpl) clsDesc ).sortDescriptors();
423 }
424
425
426
427
428
429
430 private boolean isMatchFieldName(FieldDescriptor[] fields, String fieldName) {
431 for (int i=0; i< fields.length; ++i)
432 if (fields[i].getFieldName().equals(fieldName))
433 return true;
434
435 return false;
436 }
437
438
439 protected FieldDescriptorImpl createFieldDesc( Class javaClass, FieldMapping fieldMap )
440 throws MappingException
441 {
442
443 FieldDescriptor fieldDesc;
444 FieldMappingCollectionType colType = fieldMap.getCollection();
445 String xmlName = null;
446 NodeType nodeType = null;
447 String match = null;
448 XMLFieldDescriptorImpl xmlDesc;
449 boolean isReference = false;
450 boolean isXMLTransient = false;
451
452
453 if ((fieldMap.getType() == null) && (colType != null)) {
454 if ((colType == FieldMappingCollectionType.HASHTABLE) ||
455 (colType == FieldMappingCollectionType.MAP) ||
456 (colType == FieldMappingCollectionType.SORTEDMAP))
457 {
458 fieldMap.setType(MapItem.class.getName());
459 }
460 }
461
462
463 fieldDesc = super.createFieldDesc( javaClass, fieldMap );
464
465 BindXml xml = fieldMap.getBindXml();
466
467 boolean deriveNameByClass = false;
468
469 if (xml != null) {
470
471 xmlName = xml.getName();
472
473
474 if ( xml.getNode() != null )
475 nodeType = NodeType.getNodeType( xml.getNode().toString() );
476
477
478 match = xml.getMatches();
479
480
481 isReference = xml.getReference();
482
483
484 isXMLTransient = xml.getTransient();
485
486
487 BindXmlAutoNamingType autoName = xml.getAutoNaming();
488 if (autoName != null) {
489 deriveNameByClass = (autoName == BindXmlAutoNamingType.DERIVEBYCLASS);
490 }
491
492 }
493
494
495
496
497 isXMLTransient = isXMLTransient || fieldDesc.isTransient();
498
499
500
501
502 String namespace = null;
503 if ((xmlName != null) && (xmlName.length() > 0)){
504 if (xmlName.charAt(0) == '{') {
505 int idx = xmlName.indexOf('}');
506 if (idx < 0) {
507 throw new MappingException("Invalid QName: " + xmlName);
508 }
509 namespace = xmlName.substring(1, idx);
510 xmlName = xmlName.substring(idx+1);
511 }
512 else if (xmlName.startsWith(XML_PREFIX)) {
513 namespace = Namespaces.XML_NAMESPACE;
514 xmlName = xmlName.substring(4);
515 }
516 }
517
518 if (nodeType == null) {
519 if (isPrimitive(javaClass))
520 nodeType = getInternalContext().getPrimitiveNodeType();
521 else
522 nodeType = NodeType.Element;
523 }
524
525
526
527
528
529 if ((!deriveNameByClass) && ((xmlName == null) && (match == null)))
530 {
531 xmlName = getInternalContext().getXMLNaming().toXMLName( fieldDesc.getFieldName() );
532 match = xmlName + ' ' + fieldDesc.getFieldName();
533 }
534
535 xmlDesc = new XMLFieldDescriptorImpl( fieldDesc, xmlName, nodeType, getInternalContext().getPrimitiveNodeType() );
536
537 if (xmlDesc.getHandler() != null && xmlDesc.getHandler() instanceof AbstractFieldHandler) {
538 AbstractFieldHandler handler = (AbstractFieldHandler) xmlDesc.getHandler();
539 handler.setFieldDescriptor(xmlDesc);
540 }
541
542
543 xmlDesc.setTransient(isXMLTransient);
544
545
546 xmlDesc.setValidator(new FieldValidator());
547
548
549 xmlDesc.setUseParentsNamespace(true);
550
551
552
553
554 if (deriveNameByClass) {
555 xmlDesc.setXMLName(null);
556 }
557
558
559 if (namespace != null) {
560 xmlDesc.setNameSpaceURI(namespace);
561 }
562
563
564 if (match != null) {
565 xmlDesc.setMatches(match);
566
567
568 if (xmlName == null) xmlDesc.setXMLName(null);
569 }
570
571
572 xmlDesc.setReference(isReference);
573 if (isReference) {
574 if (colType == null) {
575 FieldValidator fieldValidator = new FieldValidator();
576 fieldValidator.setValidator(new IdRefValidator());
577 xmlDesc.setValidator(fieldValidator);
578 } else {
579
580 }
581 }
582
583 xmlDesc.setContainer(fieldMap.getContainer());
584
585 xmlDesc.setNillable(fieldMap.isNillable());
586
587 if (xml != null) {
588
589
590 if (xml.getClassMapping() != null) {
591 ClassDescriptor cd = createClassDescriptor(xml.getClassMapping());
592 xmlDesc.setClassDescriptor(cd);
593 }
594
595
596 if (xml.getLocation() != null) {
597 xmlDesc.setLocationPath(xml.getLocation());
598 }
599
600
601 String xmlType = xml.getType();
602 xmlDesc.setSchemaType(xmlType);
603 xmlDesc.setQNamePrefix(xml.getQNamePrefix());
604 TypeValidator validator = null;
605 if (NCNAME.equals(xmlType)) {
606 validator = new NameValidator(XMLConstants.NAME_TYPE_NCNAME);
607 xmlDesc.setValidator(new FieldValidator(validator));
608 }
609
610
611 Property[] props = xml.getProperty();
612 if ((props != null) && (props.length > 0)) {
613 for (int pIdx = 0; pIdx < props.length; pIdx++) {
614 Property prop = props[pIdx];
615 xmlDesc.setXMLProperty(prop.getName(), prop.getValue());
616 }
617 }
618 }
619
620
621 if (colType == null) {
622
623
624 Class type = fieldDesc.getFieldType();
625 if (type != null && CollectionHandlers.hasHandler(type)) {
626 String typeName = CollectionHandlers.getCollectionName(type);
627 colType = FieldMappingCollectionType.valueOf(typeName);
628 }
629 }
630
631
632 if (colType != null) {
633 if ((colType == FieldMappingCollectionType.HASHTABLE) ||
634 (colType == FieldMappingCollectionType.MAP) ||
635 (colType == FieldMappingCollectionType.SORTEDMAP))
636 {
637
638
639 String methodName = fieldMap.getSetMethod();
640 if (methodName != null) {
641 if (!methodName.startsWith("add")) {
642 xmlDesc.setMapped(true);
643 }
644 }
645 else xmlDesc.setMapped(true);
646 }
647
648
649
650
651
652 if ((nodeType == NodeType.Namespace) || (xmlDesc.isMapped())) {
653 Object handler = xmlDesc.getHandler();
654 if (handler instanceof FieldHandlerImpl) {
655 FieldHandlerImpl handlerImpl = (FieldHandlerImpl)handler;
656 handlerImpl.setConvertFrom(new IdentityConvertor());
657 }
658 }
659
660 if (nodeType == NodeType.Element) {
661 if (fieldMap.hasContainer() && (!fieldMap.getContainer())) {
662 xmlDesc = wrapCollection(xmlDesc);
663 }
664 }
665 }
666
667
668
669
670
671 else if ((!isReference) && (!isXMLTransient)) {
672 Class fieldType = xmlDesc.getFieldType();
673 if (!isPrimitive(fieldType)) {
674
675 Constructor cons = null;
676 try {
677 cons = fieldType.getConstructor(EMPTY_ARGS);
678 if (!Modifier.isPublic(cons.getModifiers())) {
679 cons = null;
680 }
681 }
682 catch(NoSuchMethodException nsmx) {
683
684 }
685 try {
686 if (cons == null) {
687
688
689 Method method = fieldType.getMethod(VALUE_OF, STRING_ARG);
690 Class returnType = method.getReturnType();
691 if ((returnType != null) && fieldType.isAssignableFrom(returnType)) {
692 if (fieldMap.getHandler() == null) {
693
694
695
696
697
698
699
700 FieldHandler handler = new ToStringFieldHandler(fieldType, xmlDesc.getHandler());
701
702 xmlDesc.setHandler(handler);
703 xmlDesc.setImmutable(true);
704 }
705 }
706 }
707 } catch (NoSuchMethodException nsmx) {
708
709 }
710 }
711 }
712
713
714 String setter = fieldMap.getSetMethod();
715 if (setter != null && setter.startsWith("%")) {
716 String parameterNumberAsString = setter.substring(1).trim();
717 int index = Integer.parseInt(parameterNumberAsString);
718 if (index < 1) {
719 throw new MappingException("mapper.invalidParameterIndex", parameterNumberAsString);
720 }
721
722 xmlDesc.setConstructorArgumentIndex(--index);
723 }
724
725 return xmlDesc;
726 }
727
728
729
730
731
732
733
734
735 public void setLoadPackageMappings(final boolean loadPackageMappings) {
736 if ((getInternalContext() == null)
737 || (getInternalContext().getXMLClassDescriptorResolver() == null)) {
738 String message = "Internal context or class descriptor resolver within are not valid";
739 LOG.warn(message);
740 throw new IllegalStateException(message);
741 }
742 getInternalContext()
743 .getXMLClassDescriptorResolver()
744 .setLoadPackageMappings(loadPackageMappings);
745 }
746
747
748 protected TypeInfo getTypeInfo( Class fieldType, CollectionHandler colHandler, FieldMapping fieldMap )
749 throws MappingException {
750 return new TypeInfo(fieldType, null, null, fieldMap.getRequired(), null, colHandler, false);
751 }
752
753
754
755
756
757
758 private XMLFieldDescriptorImpl wrapCollection
759 (XMLFieldDescriptorImpl fieldDesc)
760 throws MappingException
761 {
762
763
764
765
766
767
768
769
770
771
772
773
774 Class type = ContainerElement.class;
775 XMLClassDescriptorImpl classDesc = new XMLClassDescriptorImpl(type);
776
777 XMLFieldDescriptorImpl newFieldDesc
778 = new XMLFieldDescriptorImpl(fieldDesc,
779 fieldDesc.getXMLName(),
780 fieldDesc.getNodeType(),
781 getInternalContext().getPrimitiveNodeType());
782
783
784
785 newFieldDesc.setXMLName(null);
786 newFieldDesc.setMatches("*");
787
788
789 classDesc.addFieldDescriptor(newFieldDesc);
790
791
792 fieldDesc.setClassDescriptor(classDesc);
793
794
795
796 FieldHandler handler = new ContainerFieldHandler(fieldDesc.getHandler());
797 newFieldDesc.setHandler(handler);
798 fieldDesc.setHandler(handler);
799
800
801
802 return new XMLContainerElementFieldDescriptor(fieldDesc, getInternalContext().getPrimitiveNodeType());
803 }
804
805
806
807
808
809
810 class IdentityConvertor implements TypeConvertor {
811 public Object convert(final Object object) {
812 return object;
813 }
814 }
815 }
816
817
818