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