001/*
002 * Copyright 2002-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.oxm.castor;
018
019import java.io.IOException;
020import java.io.InputStream;
021import java.io.OutputStream;
022import java.io.OutputStreamWriter;
023import java.io.Reader;
024import java.io.Writer;
025import java.util.Map;
026import javax.xml.stream.XMLEventReader;
027import javax.xml.stream.XMLEventWriter;
028import javax.xml.stream.XMLStreamReader;
029import javax.xml.stream.XMLStreamWriter;
030
031import org.exolab.castor.mapping.Mapping;
032import org.exolab.castor.mapping.MappingException;
033import org.exolab.castor.util.ObjectFactory;
034import org.exolab.castor.xml.IDResolver;
035import org.exolab.castor.xml.MarshalException;
036import org.exolab.castor.xml.Marshaller;
037import org.exolab.castor.xml.ResolverException;
038import org.exolab.castor.xml.UnmarshalHandler;
039import org.exolab.castor.xml.Unmarshaller;
040import org.exolab.castor.xml.ValidationException;
041import org.exolab.castor.xml.XMLClassDescriptorResolver;
042import org.exolab.castor.xml.XMLContext;
043import org.exolab.castor.xml.XMLException;
044import org.w3c.dom.Node;
045import org.xml.sax.ContentHandler;
046import org.xml.sax.EntityResolver;
047import org.xml.sax.InputSource;
048import org.xml.sax.SAXException;
049import org.xml.sax.XMLReader;
050import org.xml.sax.ext.LexicalHandler;
051
052import org.springframework.beans.factory.BeanClassLoaderAware;
053import org.springframework.beans.factory.InitializingBean;
054import org.springframework.core.io.Resource;
055import org.springframework.oxm.MarshallingFailureException;
056import org.springframework.oxm.UncategorizedMappingException;
057import org.springframework.oxm.UnmarshallingFailureException;
058import org.springframework.oxm.ValidationFailureException;
059import org.springframework.oxm.XmlMappingException;
060import org.springframework.oxm.support.AbstractMarshaller;
061import org.springframework.oxm.support.SaxResourceUtils;
062import org.springframework.util.ObjectUtils;
063import org.springframework.util.xml.DomUtils;
064import org.springframework.util.xml.StaxUtils;
065
066/**
067 * Implementation of the {@code Marshaller} interface for Castor. By default, Castor does
068 * not require any further configuration, though setting target classes, target packages or
069 * providing a mapping file can be used to have more control over the behavior of Castor.
070 *
071 * <p>If a target class is specified using {@code setTargetClass}, the {@code CastorMarshaller}
072 * can only be used to unmarshal XML that represents that specific class. If you want to unmarshal
073 * multiple classes, you have to provide a mapping file using {@code setMappingLocations}.
074 *
075 * <p>Due to limitations of Castor's API, it is required to set the encoding used for writing
076 * to output streams. It defaults to {@code UTF-8}.
077 *
078 * @author Arjen Poutsma
079 * @author Jakub Narloch
080 * @author Juergen Hoeller
081 * @since 3.0
082 * @see #setEncoding(String)
083 * @see #setTargetClass(Class)
084 * @see #setTargetPackages(String[])
085 * @see #setMappingLocation(Resource)
086 * @see #setMappingLocations(Resource[])
087 * @deprecated as of Spring Framework 4.3.13, due to the lack of activity on the Castor project
088 */
089@Deprecated
090public class CastorMarshaller extends AbstractMarshaller implements InitializingBean, BeanClassLoaderAware {
091
092        /**
093         * The default encoding used for stream access: UTF-8.
094         */
095        public static final String DEFAULT_ENCODING = "UTF-8";
096
097
098        private Resource[] mappingLocations;
099
100        private String encoding = DEFAULT_ENCODING;
101
102        private Class<?>[] targetClasses;
103
104        private String[] targetPackages;
105
106        private boolean validating = false;
107
108        private boolean suppressNamespaces = false;
109
110        private boolean suppressXsiType = false;
111
112        private boolean marshalAsDocument = true;
113
114        private boolean marshalExtendedType = true;
115
116        private String rootElement;
117
118        private String noNamespaceSchemaLocation;
119
120        private String schemaLocation;
121
122        private boolean useXSITypeAtRoot = false;
123
124        private boolean whitespacePreserve = false;
125
126        private boolean ignoreExtraAttributes = true;
127
128        private boolean ignoreExtraElements = false;
129
130        private Object rootObject;
131
132        private boolean reuseObjects = false;
133
134        private boolean clearCollections = false;
135
136        private Map<String, String> castorProperties;
137
138        private Map<String, String> doctypes;
139
140        private Map<String, String> processingInstructions;
141
142        private Map<String, String> namespaceMappings;
143
144        private Map<String, String> namespaceToPackageMapping;
145
146        private EntityResolver entityResolver;
147
148        private XMLClassDescriptorResolver classDescriptorResolver;
149
150        private IDResolver idResolver;
151
152        private ObjectFactory objectFactory;
153
154        private ClassLoader beanClassLoader;
155
156        private XMLContext xmlContext;
157
158
159        /**
160         * Set the encoding to be used for stream access.
161         * @see #DEFAULT_ENCODING
162         */
163        public void setEncoding(String encoding) {
164                this.encoding = encoding;
165        }
166
167        @Override
168        protected String getDefaultEncoding() {
169                return this.encoding;
170        }
171
172        /**
173         * Set the locations of the Castor XML mapping files.
174         */
175        public void setMappingLocation(Resource mappingLocation) {
176                this.mappingLocations = new Resource[]{mappingLocation};
177        }
178
179        /**
180         * Set the locations of the Castor XML mapping files.
181         */
182        public void setMappingLocations(Resource... mappingLocations) {
183                this.mappingLocations = mappingLocations;
184        }
185
186        /**
187         * Set the Castor target class.
188         * @see #setTargetPackage
189         * @see #setMappingLocation
190         */
191        public void setTargetClass(Class<?> targetClass) {
192                this.targetClasses = new Class<?>[] {targetClass};
193        }
194
195        /**
196         * Set the Castor target classes.
197         * @see #setTargetPackages
198         * @see #setMappingLocations
199         */
200        public void setTargetClasses(Class<?>... targetClasses) {
201                this.targetClasses = targetClasses;
202        }
203
204        /**
205         * Set the name of a package with the Castor descriptor classes.
206         */
207        public void setTargetPackage(String targetPackage) {
208                this.targetPackages = new String[] {targetPackage};
209        }
210
211        /**
212         * Set the names of packages with the Castor descriptor classes.
213         */
214        public void setTargetPackages(String... targetPackages) {
215                this.targetPackages = targetPackages;
216        }
217
218        /**
219         * Set whether this marshaller should validate in- and outgoing documents.
220         * <p>Default is {@code false}.
221         * @see Marshaller#setValidation(boolean)
222         */
223        public void setValidating(boolean validating) {
224                this.validating = validating;
225        }
226
227        /**
228         * Sets whether this marshaller should output namespaces.
229         * <p>The default is {@code false}, i.e. namespaces are written.
230         * @see org.exolab.castor.xml.Marshaller#setSuppressNamespaces(boolean)
231         */
232        public void setSuppressNamespaces(boolean suppressNamespaces) {
233                this.suppressNamespaces = suppressNamespaces;
234        }
235
236        /**
237         * Set whether this marshaller should output the {@code xsi:type} attribute.
238         * <p>The default is {@code false}, i.e. the {@code xsi:type} is written.
239         * @see org.exolab.castor.xml.Marshaller#setSuppressXSIType(boolean)
240         */
241        public void setSuppressXsiType(boolean suppressXsiType) {
242                this.suppressXsiType = suppressXsiType;
243        }
244
245        /**
246         * Set whether this marshaller should output the xml declaration.
247         * <p>The default is {@code true}, the XML declaration will be written.
248         * @see org.exolab.castor.xml.Marshaller#setMarshalAsDocument(boolean)
249         */
250        public void setMarshalAsDocument(boolean marshalAsDocument) {
251                this.marshalAsDocument = marshalAsDocument;
252        }
253
254        /**
255         * Set whether this marshaller should output for given type the {@code xsi:type} attribute.
256         * <p>The default is {@code true}, the {@code xsi:type} attribute will be written.
257         * @see org.exolab.castor.xml.Marshaller#setMarshalExtendedType(boolean)
258         */
259        public void setMarshalExtendedType(boolean marshalExtendedType) {
260                this.marshalExtendedType = marshalExtendedType;
261        }
262
263        /**
264         * Set the name of the root element.
265         * @see org.exolab.castor.xml.Marshaller#setRootElement(String)
266         */
267        public void setRootElement(String rootElement) {
268                this.rootElement = rootElement;
269        }
270
271        /**
272         * Set the value of {@code xsi:noNamespaceSchemaLocation} attribute. When set, the
273         * {@code xsi:noNamespaceSchemaLocation} attribute will be written for the root element.
274         * @see org.exolab.castor.xml.Marshaller#setNoNamespaceSchemaLocation(String)
275         */
276        public void setNoNamespaceSchemaLocation(String noNamespaceSchemaLocation) {
277                this.noNamespaceSchemaLocation = noNamespaceSchemaLocation;
278        }
279
280        /**
281         * Set the value of {@code xsi:schemaLocation} attribute. When set, the
282         * {@code xsi:schemaLocation} attribute will be written for the root element.
283         * @see org.exolab.castor.xml.Marshaller#setSchemaLocation(String)
284         */
285        public void setSchemaLocation(String schemaLocation) {
286                this.schemaLocation = schemaLocation;
287        }
288
289        /**
290         * Sets whether this marshaller should output the {@code xsi:type} attribute for the root element.
291         * This can be useful when the type of the element can not be simply determined from the element name.
292         * <p>The default is {@code false}: The {@code xsi:type} attribute for the root element won't be written.
293         * @see org.exolab.castor.xml.Marshaller#setUseXSITypeAtRoot(boolean)
294         */
295        public void setUseXSITypeAtRoot(boolean useXSITypeAtRoot) {
296                this.useXSITypeAtRoot = useXSITypeAtRoot;
297        }
298
299        /**
300         * Set whether the Castor {@link Unmarshaller} should preserve "ignorable" whitespace.
301         * <p>Default is {@code false}.
302         * @see org.exolab.castor.xml.Unmarshaller#setWhitespacePreserve(boolean)
303         */
304        public void setWhitespacePreserve(boolean whitespacePreserve) {
305                this.whitespacePreserve = whitespacePreserve;
306        }
307
308        /**
309         * Set whether the Castor {@link Unmarshaller} should ignore attributes that do not match a specific field.
310         * <p>Default is {@code true}: Extra attributes are ignored.
311         * @see org.exolab.castor.xml.Unmarshaller#setIgnoreExtraAttributes(boolean)
312         */
313        public void setIgnoreExtraAttributes(boolean ignoreExtraAttributes) {
314                this.ignoreExtraAttributes = ignoreExtraAttributes;
315        }
316
317        /**
318         * Set whether the Castor {@link Unmarshaller} should ignore elements that do not match a specific field.
319         * <p>Default is {@code false}: Extra elements are flagged as an error.
320         * @see org.exolab.castor.xml.Unmarshaller#setIgnoreExtraElements(boolean)
321         */
322        public void setIgnoreExtraElements(boolean ignoreExtraElements) {
323                this.ignoreExtraElements = ignoreExtraElements;
324        }
325
326        /**
327         * Set the expected root object for the unmarshaller, into which the source will be unmarshalled.
328         * @see org.exolab.castor.xml.Unmarshaller#setObject(Object)
329         */
330        public void setRootObject(Object root) {
331                this.rootObject = root;
332        }
333
334        /**
335         * Set whether this unmarshaller should re-use objects.
336         * This will be only used when unmarshalling to an existing object.
337         * <p>The default is {@code false}, which means that the objects won't be re-used.
338         * @see org.exolab.castor.xml.Unmarshaller#setReuseObjects(boolean)
339         */
340        public void setReuseObjects(boolean reuseObjects) {
341                this.reuseObjects = reuseObjects;
342        }
343
344        /**
345         * Sets whether this unmarshaller should clear collections upon the first use.
346         * <p>The default is {@code false} which means that marshaller won't clear collections.
347         * @see org.exolab.castor.xml.Unmarshaller#setClearCollections(boolean)
348         */
349        public void setClearCollections(boolean clearCollections) {
350                this.clearCollections = clearCollections;
351        }
352
353        /**
354         * Set Castor-specific properties for marshalling and unmarshalling.
355         * Each entry key is considered the property name and each value the property value.
356         * @see org.exolab.castor.xml.Marshaller#setProperty(String, String)
357         * @see org.exolab.castor.xml.Unmarshaller#setProperty(String, String)
358         */
359        public void setCastorProperties(Map<String, String> castorProperties) {
360                this.castorProperties = castorProperties;
361        }
362
363        /**
364         * Set the map containing document type definition for the marshaller.
365         * Each entry has system id as key and public id as value.
366         * @see org.exolab.castor.xml.Marshaller#setDoctype(String, String)
367         */
368        public void setDoctypes(Map<String, String> doctypes) {
369                this.doctypes = doctypes;
370        }
371
372        /**
373         * Sets the processing instructions that will be used by during marshalling.
374         * Keys are the processing targets and values contain the processing data.
375         * @see org.exolab.castor.xml.Marshaller#addProcessingInstruction(String, String)
376         */
377        public void setProcessingInstructions(Map<String, String> processingInstructions) {
378                this.processingInstructions = processingInstructions;
379        }
380
381        /**
382         * Set the namespace mappings.
383         * Property names are interpreted as namespace prefixes; values are namespace URIs.
384         * @see org.exolab.castor.xml.Marshaller#setNamespaceMapping(String, String)
385         */
386        public void setNamespaceMappings(Map<String, String> namespaceMappings) {
387                this.namespaceMappings = namespaceMappings;
388        }
389
390        /**
391         * Set the namespace to package mappings. Property names are represents the namespaces URI, values are packages.
392         * @see org.exolab.castor.xml.Marshaller#setNamespaceMapping(String, String)
393         */
394        public void setNamespaceToPackageMapping(Map<String, String> namespaceToPackageMapping) {
395                this.namespaceToPackageMapping = namespaceToPackageMapping;
396        }
397
398        /**
399         * Set the {@link EntityResolver} to be used during unmarshalling.
400         * This resolver will used to resolve system and public ids.
401         * @see org.exolab.castor.xml.Unmarshaller#setEntityResolver(EntityResolver)
402         */
403        public void setEntityResolver(EntityResolver entityResolver) {
404                this.entityResolver = entityResolver;
405        }
406
407        /**
408         * Set the {@link XMLClassDescriptorResolver} to be used during unmarshalling.
409         * This resolver will used to resolve class descriptors.
410         * @see org.exolab.castor.xml.Unmarshaller#setResolver(XMLClassDescriptorResolver)
411         */
412        public void setClassDescriptorResolver(XMLClassDescriptorResolver classDescriptorResolver) {
413                this.classDescriptorResolver = classDescriptorResolver;
414        }
415
416        /**
417         * Set the Castor {@link IDResolver} to be used during unmarshalling.
418         * @see org.exolab.castor.xml.Unmarshaller#setIDResolver(IDResolver)
419         */
420        public void setIdResolver(IDResolver idResolver) {
421                this.idResolver = idResolver;
422        }
423
424        /**
425         * Set the Castor {@link ObjectFactory} to be used during unmarshalling.
426         * @see org.exolab.castor.xml.Unmarshaller#setObjectFactory(ObjectFactory)
427         */
428        public void setObjectFactory(ObjectFactory objectFactory) {
429                this.objectFactory = objectFactory;
430        }
431
432        @Override
433        public void setBeanClassLoader(ClassLoader classLoader) {
434                this.beanClassLoader = classLoader;
435        }
436
437
438        @Override
439        public void afterPropertiesSet() throws CastorMappingException, IOException {
440                try {
441                        this.xmlContext = createXMLContext(this.mappingLocations, this.targetClasses, this.targetPackages);
442                }
443                catch (MappingException ex) {
444                        throw new CastorMappingException("Could not load Castor mapping", ex);
445                }
446                catch (ResolverException ex) {
447                        throw new CastorMappingException("Could not resolve Castor mapping", ex);
448                }
449        }
450
451        /**
452         * Create the Castor {@code XMLContext}. Subclasses can override this to create a custom context.
453         * <p>The default implementation loads mapping files if defined, or the target class or packages if defined.
454         * @return the created resolver
455         * @throws MappingException when the mapping file cannot be loaded
456         * @throws IOException in case of I/O errors
457         * @see XMLContext#addMapping(org.exolab.castor.mapping.Mapping)
458         * @see XMLContext#addClass(Class)
459         */
460        protected XMLContext createXMLContext(Resource[] mappingLocations, Class<?>[] targetClasses,
461                        String[] targetPackages) throws MappingException, ResolverException, IOException {
462
463                XMLContext context = new XMLContext();
464                if (!ObjectUtils.isEmpty(mappingLocations)) {
465                        Mapping mapping = new Mapping();
466                        for (Resource mappingLocation : mappingLocations) {
467                                mapping.loadMapping(SaxResourceUtils.createInputSource(mappingLocation));
468                        }
469                        context.addMapping(mapping);
470                }
471                if (!ObjectUtils.isEmpty(targetClasses)) {
472                        context.addClasses(targetClasses);
473                }
474                if (!ObjectUtils.isEmpty(targetPackages)) {
475                        context.addPackages(targetPackages);
476                }
477                if (this.castorProperties != null) {
478                        for (Map.Entry<String, String> property : this.castorProperties.entrySet()) {
479                                context.setProperty(property.getKey(), property.getValue());
480                        }
481                }
482                return context;
483        }
484
485
486        /**
487         * Returns {@code true} for all classes, i.e. Castor supports arbitrary classes.
488         */
489        @Override
490        public boolean supports(Class<?> clazz) {
491                return true;
492        }
493
494
495        // Marshalling
496
497        @Override
498        protected void marshalDomNode(Object graph, Node node) throws XmlMappingException {
499                marshalSaxHandlers(graph, DomUtils.createContentHandler(node), null);
500        }
501
502        @Override
503        protected void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) throws XmlMappingException {
504                ContentHandler contentHandler = StaxUtils.createContentHandler(eventWriter);
505                LexicalHandler lexicalHandler = null;
506                if (contentHandler instanceof LexicalHandler) {
507                        lexicalHandler = (LexicalHandler) contentHandler;
508                }
509                marshalSaxHandlers(graph, contentHandler, lexicalHandler);
510        }
511
512        @Override
513        protected void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter) throws XmlMappingException {
514                ContentHandler contentHandler = StaxUtils.createContentHandler(streamWriter);
515                LexicalHandler lexicalHandler = null;
516                if (contentHandler instanceof LexicalHandler) {
517                        lexicalHandler = (LexicalHandler) contentHandler;
518                }
519                marshalSaxHandlers(graph, StaxUtils.createContentHandler(streamWriter), lexicalHandler);
520        }
521
522        @Override
523        protected void marshalSaxHandlers(Object graph, ContentHandler contentHandler, LexicalHandler lexicalHandler)
524                        throws XmlMappingException {
525
526                Marshaller marshaller = xmlContext.createMarshaller();
527                marshaller.setContentHandler(contentHandler);
528                doMarshal(graph, marshaller);
529        }
530
531        @Override
532        protected void marshalOutputStream(Object graph, OutputStream outputStream) throws XmlMappingException, IOException {
533                marshalWriter(graph, new OutputStreamWriter(outputStream, encoding));
534        }
535
536        @Override
537        protected void marshalWriter(Object graph, Writer writer) throws XmlMappingException, IOException {
538                Marshaller marshaller = xmlContext.createMarshaller();
539                marshaller.setWriter(writer);
540                doMarshal(graph, marshaller);
541        }
542
543        private void doMarshal(Object graph, Marshaller marshaller) {
544                try {
545                        customizeMarshaller(marshaller);
546                        marshaller.marshal(graph);
547                }
548                catch (XMLException ex) {
549                        throw convertCastorException(ex, true);
550                }
551        }
552
553        /**
554         * Template method that allows for customizing of the given Castor {@link Marshaller}.
555         */
556        protected void customizeMarshaller(Marshaller marshaller) {
557                marshaller.setValidation(this.validating);
558                marshaller.setSuppressNamespaces(this.suppressNamespaces);
559                marshaller.setSuppressXSIType(this.suppressXsiType);
560                marshaller.setMarshalAsDocument(this.marshalAsDocument);
561                marshaller.setMarshalExtendedType(this.marshalExtendedType);
562                marshaller.setRootElement(this.rootElement);
563                marshaller.setNoNamespaceSchemaLocation(this.noNamespaceSchemaLocation);
564                marshaller.setSchemaLocation(this.schemaLocation);
565                marshaller.setUseXSITypeAtRoot(this.useXSITypeAtRoot);
566                if (this.doctypes != null) {
567                        for (Map.Entry<String, String> doctype : this.doctypes.entrySet()) {
568                                marshaller.setDoctype(doctype.getKey(), doctype.getValue());
569                        }
570                }
571                if (this.processingInstructions != null) {
572                        for (Map.Entry<String, String> processingInstruction : this.processingInstructions.entrySet()) {
573                                marshaller.addProcessingInstruction(processingInstruction.getKey(), processingInstruction.getValue());
574                        }
575                }
576                if (this.namespaceMappings != null) {
577                        for (Map.Entry<String, String> entry : this.namespaceMappings.entrySet()) {
578                                marshaller.setNamespaceMapping(entry.getKey(), entry.getValue());
579                        }
580                }
581        }
582
583
584        // Unmarshalling
585
586        @Override
587        protected Object unmarshalDomNode(Node node) throws XmlMappingException {
588                try {
589                        return createUnmarshaller().unmarshal(node);
590                }
591                catch (XMLException ex) {
592                        throw convertCastorException(ex, false);
593                }
594        }
595
596        @Override
597        protected Object unmarshalXmlEventReader(XMLEventReader eventReader) {
598                try {
599                        return createUnmarshaller().unmarshal(eventReader);
600                }
601                catch (XMLException ex) {
602                        throw convertCastorException(ex, false);
603                }
604        }
605
606        @Override
607        protected Object unmarshalXmlStreamReader(XMLStreamReader streamReader) {
608                try {
609                        return createUnmarshaller().unmarshal(streamReader);
610                }
611                catch (XMLException ex) {
612                        throw convertCastorException(ex, false);
613                }
614        }
615
616        @Override
617        protected Object unmarshalSaxReader(XMLReader xmlReader, InputSource inputSource)
618                        throws XmlMappingException, IOException {
619
620                UnmarshalHandler unmarshalHandler = createUnmarshaller().createHandler();
621                try {
622                        ContentHandler contentHandler = Unmarshaller.getContentHandler(unmarshalHandler);
623                        xmlReader.setContentHandler(contentHandler);
624                        xmlReader.parse(inputSource);
625                        return unmarshalHandler.getObject();
626                }
627                catch (SAXException ex) {
628                        throw new UnmarshallingFailureException("SAX reader exception", ex);
629                }
630        }
631
632        @Override
633        protected Object unmarshalInputStream(InputStream inputStream) throws XmlMappingException, IOException {
634                try {
635                        return createUnmarshaller().unmarshal(new InputSource(inputStream));
636                }
637                catch (XMLException ex) {
638                        throw convertCastorException(ex, false);
639                }
640        }
641
642        @Override
643        protected Object unmarshalReader(Reader reader) throws XmlMappingException, IOException {
644                try {
645                        return createUnmarshaller().unmarshal(new InputSource(reader));
646                }
647                catch (XMLException ex) {
648                        throw convertCastorException(ex, false);
649                }
650        }
651
652        private Unmarshaller createUnmarshaller() {
653                Unmarshaller unmarshaller = this.xmlContext.createUnmarshaller();
654                customizeUnmarshaller(unmarshaller);
655                return unmarshaller;
656        }
657
658        /**
659         * Template method that allows for customizing of the given Castor {@link Unmarshaller}.
660         */
661        protected void customizeUnmarshaller(Unmarshaller unmarshaller) {
662                unmarshaller.setValidation(this.validating);
663                unmarshaller.setWhitespacePreserve(this.whitespacePreserve);
664                unmarshaller.setIgnoreExtraAttributes(this.ignoreExtraAttributes);
665                unmarshaller.setIgnoreExtraElements(this.ignoreExtraElements);
666                unmarshaller.setObject(this.rootObject);
667                unmarshaller.setReuseObjects(this.reuseObjects);
668                unmarshaller.setClearCollections(this.clearCollections);
669                if (this.namespaceToPackageMapping != null) {
670                        for (Map.Entry<String, String> mapping : this.namespaceToPackageMapping.entrySet()) {
671                                unmarshaller.addNamespaceToPackageMapping(mapping.getKey(), mapping.getValue());
672                        }
673                }
674                if (this.entityResolver != null) {
675                        unmarshaller.setEntityResolver(this.entityResolver);
676                }
677                if (this.classDescriptorResolver != null) {
678                        unmarshaller.setResolver(this.classDescriptorResolver);
679                }
680                if (this.idResolver != null) {
681                        unmarshaller.setIDResolver(this.idResolver);
682                }
683                if (this.objectFactory != null) {
684                        unmarshaller.setObjectFactory(this.objectFactory);
685                }
686                if (this.beanClassLoader != null) {
687                        unmarshaller.setClassLoader(this.beanClassLoader);
688                }
689        }
690
691
692        /**
693         * Convert the given {@code XMLException} to an appropriate exception from the
694         * {@code org.springframework.oxm} hierarchy.
695         * <p>A boolean flag is used to indicate whether this exception occurs during marshalling or
696         * unmarshalling, since Castor itself does not make this distinction in its exception hierarchy.
697         * @param ex Castor {@code XMLException} that occurred
698         * @param marshalling indicates whether the exception occurs during marshalling ({@code true}),
699         * or unmarshalling ({@code false})
700         * @return the corresponding {@code XmlMappingException}
701         */
702        protected XmlMappingException convertCastorException(XMLException ex, boolean marshalling) {
703                if (ex instanceof ValidationException) {
704                        return new ValidationFailureException("Castor validation exception", ex);
705                }
706                else if (ex instanceof MarshalException) {
707                        if (marshalling) {
708                                return new MarshallingFailureException("Castor marshalling exception", ex);
709                        }
710                        else {
711                                return new UnmarshallingFailureException("Castor unmarshalling exception", ex);
712                        }
713                }
714                else {
715                        // fallback
716                        return new UncategorizedMappingException("Unknown Castor exception", ex);
717                }
718        }
719
720}