Merge branch 'release/3.8.0'
[cdmlib.git] / cdmlib-io / src / main / java / eu / etaxonomy / cdm / io / jaxb / CdmDocumentBuilder.java
index 40abc320a9b2d011cd2c6becf89bda610990fc05..c90cc9c84bc76a14bd59f6add71d648c375544c8 100644 (file)
@@ -1,11 +1,11 @@
 /**\r
-* Copyright (C) 2008 EDIT\r
-* European Distributed Institute of Taxonomy \r
-* http://www.e-taxonomy.eu\r
- * \r
+ * Copyright (C) 2008 EDIT\r
+ * European Distributed Institute of Taxonomy\r
+ * http://www.e-taxonomy.eu\r
+ *\r
  * The contents of this file are subject to the Mozilla Public License Version 1.1\r
  * See LICENSE.TXT at the top of this package for the full license terms.\r
-*/\r
+ */\r
 \r
 package eu.etaxonomy.cdm.io.jaxb;\r
 \r
@@ -17,191 +17,223 @@ import java.io.InputStreamReader;
 import java.io.Reader;\r
 import java.io.UnsupportedEncodingException;\r
 import java.io.Writer;\r
+import java.net.URI;\r
+import java.net.URISyntaxException;\r
 \r
-import javax.xml.XMLConstants;\r
-import javax.xml.bind.JAXBContext;\r
 import javax.xml.bind.JAXBException;\r
 import javax.xml.bind.Marshaller;\r
-import javax.xml.bind.Unmarshaller;\r
+import javax.xml.bind.PropertyException;\r
 import javax.xml.parsers.ParserConfigurationException;\r
 import javax.xml.parsers.SAXParser;\r
 import javax.xml.parsers.SAXParserFactory;\r
 import javax.xml.transform.Source;\r
 import javax.xml.transform.sax.SAXResult;\r
 import javax.xml.transform.sax.SAXSource;\r
-import javax.xml.transform.stream.StreamSource;\r
+import javax.xml.transform.stream.StreamResult;\r
 import javax.xml.validation.Schema;\r
 import javax.xml.validation.SchemaFactory;\r
 \r
 import org.apache.log4j.Logger;\r
 import org.apache.xml.resolver.tools.CatalogResolver;\r
+import org.springframework.core.io.ClassPathResource;\r
+import org.springframework.core.io.Resource;\r
+import org.springframework.oxm.UncategorizedMappingException;\r
+import org.springframework.oxm.XmlMappingException;\r
+import org.springframework.oxm.jaxb.Jaxb2Marshaller;\r
 import org.xml.sax.InputSource;\r
 import org.xml.sax.SAXException;\r
 import org.xml.sax.XMLReader;\r
+import org.xml.sax.helpers.XMLReaderFactory;\r
 \r
 import eu.etaxonomy.cdm.jaxb.CdmNamespacePrefixMapper;\r
 import eu.etaxonomy.cdm.jaxb.FormattedText;\r
 import eu.etaxonomy.cdm.jaxb.MultilanguageTextElement;\r
 \r
 /**\r
- * Initializes a JaxbContext with one class (eu.etaxonomy.cdm.model.DataSet). \r
- * \r
+ * Initializes a JaxbContext with one class (eu.etaxonomy.cdm.model.DataSet).\r
+ *\r
  * @author a.babadshanjan, ben.clark\r
  */\r
 //Binds it to XML schemas found in /src/main/resources/schema/cdm (cdm.xsd, common.xsd, name.xsd).\r
 //There is a bit of magic with a resource resolver in eu.etaxonomy.cdm.io.jaxb\r
 //which allows to package the schemas into a jar file.\r
-public class CdmDocumentBuilder {\r
-       \r
-       private static final Logger logger = Logger.getLogger(CdmDocumentBuilder.class);\r
-       \r
-       private JAXBContext jaxbContext;\r
-       private boolean formattedOutput = Boolean.TRUE;\r
-       private String encoding = "UTF-8";\r
-       private Schema schema;\r
-    private Marshaller marshaller;\r
-    private Unmarshaller unmarshaller;\r
-    private XMLReader xmlReader;\r
-       \r
-       public static String CDM_NAMESPACE = "eu.etaxonomy.cdm.model";\r
-       public static String[] CDM_SCHEMA_FILES = { "/schema/cdm/agent.xsd",\r
-                                                       "/schema/cdm/cdm.xsd",\r
-                                                       "/schema/cdm/common.xsd",\r
-                                                       "/schema/cdm/description.xsd",\r
-                                                       "/schema/cdm/location.xsd",\r
-                                                       "/schema/cdm/media.xsd",\r
-                                                       "/schema/cdm/molecular.xsd",\r
-                                                       "/schema/cdm/name.xsd",\r
-                                                       "/schema/cdm/occurrence.xsd",\r
-                                                       "/schema/cdm/reference.xsd",\r
-                                                       "/schema/cdm/taxon.xsd"};\r
-       public static Class[] CONTEXT_CLASSES = {DataSet.class,FormattedText.class,MultilanguageTextElement.class};\r
-       \r
-       protected String[] getSchemaFiles() {\r
-               return CDM_SCHEMA_FILES;\r
-       }\r
-       \r
-       protected Class[] getContextClasses() {\r
-               return CONTEXT_CLASSES;\r
-       }\r
-       \r
-       protected void constructSchema() throws IOException, SAXException {\r
-               SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);\r
-               schemaFactory.setResourceResolver(new CdmResourceResolver());\r
-               String[] schemaFiles = getSchemaFiles();\r
-               \r
-               Source[] sources = new Source[schemaFiles.length];\r
-               \r
-               for(int i = 0; i < schemaFiles.length; i++) {\r
-                       String schemaName = schemaFiles[i];\r
-                       sources[i] = new StreamSource(this.getClass().getResourceAsStream(schemaName));\r
-               }\r
-\r
-               schema = schemaFactory.newSchema(sources);\r
-       }\r
-       \r
-       protected void constructUnmarshaller() throws ParserConfigurationException, SAXException, JAXBException {\r
-               unmarshaller = jaxbContext.createUnmarshaller();\r
-        unmarshaller.setSchema(schema);\r
-           \r
+public class CdmDocumentBuilder extends Jaxb2Marshaller  {\r
+\r
+    private static final Logger logger = Logger.getLogger(CdmDocumentBuilder.class);\r
+    private boolean formattedOutput = Boolean.TRUE;\r
+    private String encoding = "UTF-8";\r
+\r
+    public static String CDM_NAMESPACE = "eu.etaxonomy.cdm.model";\r
+    public static String[] CDM_SCHEMA_FILES = { "/schema/cdm/agent.xsd",\r
+        "/schema/cdm/cdm.xsd",\r
+        "/schema/cdm/common.xsd",\r
+        "/schema/cdm/description.xsd",\r
+        "/schema/cdm/location.xsd",\r
+        "/schema/cdm/media.xsd",\r
+        "/schema/cdm/molecular.xsd",\r
+        "/schema/cdm/name.xsd",\r
+        "/schema/cdm/occurrence.xsd",\r
+        "/schema/cdm/reference.xsd",\r
+    "/schema/cdm/taxon.xsd"};\r
+    public static Class[] CONTEXT_CLASSES = {DataSet.class,FormattedText.class,MultilanguageTextElement.class};\r
+\r
+    private Resource schemas[];\r
+\r
+    protected String[] getSchemaFiles() {\r
+        return CDM_SCHEMA_FILES;\r
+    }\r
+\r
+    protected Class[] getContextClasses() {\r
+        return CONTEXT_CLASSES;\r
+    }\r
+\r
+    public CdmDocumentBuilder()        {\r
+        schemas = new Resource[CDM_SCHEMA_FILES.length];\r
+\r
+        for(int i = 0; i < CDM_SCHEMA_FILES.length; i++) {\r
+            schemas[i] = new ClassPathResource(CDM_SCHEMA_FILES[i]);\r
+        }\r
+\r
+        super.setSchemas(schemas);\r
+        super.setClassesToBeBound(CONTEXT_CLASSES);\r
+        super.setSchemaLanguage("http://www.w3.org/2001/XMLSchema");\r
+    }\r
+\r
+    public CdmDocumentBuilder(boolean formattedOutput, String encoding) {\r
+        this.formattedOutput = formattedOutput;\r
+        this.encoding = encoding;\r
+    }\r
+\r
+\r
+    @Override\r
+    protected void initJaxbMarshaller(Marshaller marshaller) throws JAXBException {\r
+        try {\r
+            marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new CdmNamespacePrefixMapper() );\r
+\r
+            // For test purposes insert newlines to make the XML output readable\r
+            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, formattedOutput);\r
+            //marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION,"http://etaxonomy.eu/cdm/model/1.0 schema/cdm/cdm.xsd");\r
+            //marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION,"http://etaxonomy.eu/cdm/model/1.0 cdm.xsd");\r
+            marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);\r
+\r
+            CdmMarshallerListener marshallerListener = new CdmMarshallerListener();\r
+            marshaller.setListener(marshallerListener);\r
+            marshaller.setEventHandler(new WarningTolerantValidationEventHandler());\r
+        } catch(PropertyException pe) {\r
+            throw new JAXBException(pe.getMessage(),pe);\r
+        }\r
+    }\r
+\r
+    protected <T> T unmarshal(Class<T> clazz, InputSource input) {\r
         SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();\r
-       \r
-        saxParserFactory.setNamespaceAware(true);\r
-        saxParserFactory.setXIncludeAware(true);\r
-        saxParserFactory.setValidating(true);\r
-        \r
-        SAXParser saxParser = saxParserFactory.newSAXParser();\r
-        saxParser.setProperty("http://java.sun.com/xml/jaxp/properties/schemaLanguage",\r
-                                     "http://www.w3.org/2001/XMLSchema");\r
-           xmlReader = saxParser.getXMLReader();\r
-           xmlReader.setEntityResolver(new CatalogResolver());\r
-           xmlReader.setErrorHandler(new DefaultErrorHandler());\r
-        unmarshaller.setEventHandler(new WarningTolerantValidationEventHandler());\r
-       }\r
-                                                       \r
-       public CdmDocumentBuilder() throws SAXException, JAXBException, IOException, ParserConfigurationException {\r
-               constructSchema();\r
-               \r
-           jaxbContext = JAXBContext.newInstance(getContextClasses());\r
-               \r
-        constructUnmarshaller();\r
-        constructMarshaller();\r
-       }\r
-       \r
-       protected void constructMarshaller() throws JAXBException {\r
-               marshaller = jaxbContext.createMarshaller();\r
-        marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new CdmNamespacePrefixMapper() );\r
-               marshaller.setSchema(schema);\r
-        \r
-               // For test purposes insert newlines to make the XML output readable\r
-               marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, formattedOutput);\r
-//             marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION,"http://etaxonomy.eu/cdm/model/1.0 schema/cdm/cdm.xsd");\r
-               marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION,"http://etaxonomy.eu/cdm/model/1.0 cdm.xsd");\r
-               marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);\r
-               \r
-               CdmMarshallerListener marshallerListener = new CdmMarshallerListener();\r
-               marshaller.setListener(marshallerListener);             \r
-               marshaller.setEventHandler(new WarningTolerantValidationEventHandler());\r
-               System.out.println(marshaller.toString());\r
-               \r
-       }\r
-\r
-       public CdmDocumentBuilder(boolean formattedOutput, String encoding) throws SAXException, JAXBException, IOException, ParserConfigurationException {\r
-               this.formattedOutput = formattedOutput;\r
-               this.encoding = encoding;\r
-        constructSchema();\r
-               \r
-           jaxbContext = JAXBContext.newInstance(getContextClasses());\r
-               \r
-        constructUnmarshaller();\r
-        constructMarshaller();\r
-       }\r
-       \r
-       public void marshal(DataSet dataSet, Writer writer) throws JAXBException {\r
-               \r
-               logger.info("Start marshalling");\r
-               marshaller.marshal(dataSet, writer);\r
-               \r
-       }\r
-       \r
-       public <T> T unmarshal(Class<T> clazz,Reader reader) throws JAXBException {\r
-               InputSource input = new InputSource(reader);\r
-               SAXSource saxSource = new SAXSource( xmlReader, input);\r
-               logger.info("Start unmarshalling");\r
-               T t = (T) unmarshaller.unmarshal(saxSource);\r
-               return t;\r
-       } \r
-       \r
-       public <T> T unmarshal(Class<T> clazz,Reader reader, String systemId) throws JAXBException {\r
-               InputSource input = new InputSource(reader);\r
-               input.setSystemId(systemId);\r
-               SAXSource saxSource = new SAXSource( xmlReader, input);\r
-               logger.info("Start unmarshalling");\r
-               T t = (T) unmarshaller.unmarshal(saxSource);\r
-               return t;\r
-       } \r
-\r
-       public <T> T unmarshal(Class<T> clazz, File file) throws JAXBException, UnsupportedEncodingException, FileNotFoundException {\r
-\r
-               InputSource input = new InputSource(new InputStreamReader(new FileInputStream(file),encoding));\r
-               input.setSystemId(file.toURI().toString());\r
-               SAXSource saxSource = new SAXSource( xmlReader, input);\r
-               logger.info("Start unmarshalling");\r
-               T t = (T) unmarshaller.unmarshal(saxSource);\r
-               return t;\r
-               \r
-       }\r
-\r
-       public void marshal(DataSet dataSet, SAXResult result) throws JAXBException {\r
-               logger.info("Start marshalling");\r
-               marshaller.marshal(dataSet, result);\r
-               \r
-       }\r
-\r
-//     public void marshal(DataSet dataSet, File file) throws JAXBException {\r
-//             marshaller.marshal(dataSet, file);\r
-//     }\r
+        Schema schema;\r
+        try {\r
+            schema = createSchema();\r
+            saxParserFactory.setNamespaceAware(true);\r
+            saxParserFactory.setXIncludeAware(true);\r
+            saxParserFactory.setValidating(true);\r
+            saxParserFactory.setSchema(schema);\r
+\r
+            SAXParser saxParser = saxParserFactory.newSAXParser();\r
+            XMLReader xmlReader = saxParser.getXMLReader();\r
+            xmlReader.setEntityResolver(new CatalogResolver());\r
+            xmlReader.setErrorHandler(new DefaultErrorHandler());\r
+            SAXSource saxSource = new SAXSource( xmlReader, input);\r
+            saxSource.setSystemId(input.getSystemId());\r
+\r
+            return (T)super.unmarshal(saxSource);\r
+        } catch (IOException e) {\r
+            throw new UncategorizedMappingException(e.getMessage(), e);\r
+        } catch (SAXException e) {\r
+            throw new UncategorizedMappingException(e.getMessage(), e);\r
+        } catch (ParserConfigurationException e) {\r
+            throw new UncategorizedMappingException(e.getMessage(), e);\r
+        }\r
+    }\r
+\r
+    private Schema createSchema() throws SAXException, IOException {\r
+        //method created to avoid dependency of spring-xml like in earlier versions\r
+        //old implementation was schema = SchemaLoaderUtils.loadSchema(schemas, "http://www.w3.org/2001/XMLSchema");\r
+        //maybe we can improve this loading in future\r
+\r
+        String schemaLanguage = "http://www.w3.org/2001/XMLSchema";\r
+        Source[] schemaSources = new Source[schemas.length];\r
+        XMLReader reader = XMLReaderFactory.createXMLReader();\r
+        reader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);\r
+        for (int i = 0; i < schemas.length; i++) {\r
+            schemaSources[i] = makeSchemaSource(reader, schemas[i]);//  new ResourceSource(reader, schemas[i]);\r
+        }\r
+        SchemaFactory schemaFactory = SchemaFactory.newInstance(schemaLanguage);\r
+        return schemaFactory.newSchema(schemaSources);\r
+    }\r
+\r
+    private Source makeSchemaSource(XMLReader reader, Resource resource) throws IOException {\r
+        Source result = new SAXSource(reader, createInputSource(resource));\r
+        return result;\r
+    }\r
+\r
+    private static InputSource createInputSource(Resource resource) throws IOException {\r
+        InputSource inputSource = new InputSource(resource.getInputStream());\r
+        inputSource.setSystemId(getSystemId(resource));\r
+        return inputSource;\r
+    }\r
+\r
+    /** Retrieves the URL from the given resource as System ID. Returns <code>null</code> if it cannot be opened. */\r
+    private static String getSystemId(Resource resource) {\r
+        try {\r
+            return new URI(resource.getURL().toExternalForm()).toString();\r
+        }\r
+        catch (IOException ex) {\r
+            logger.debug("Could not get System ID from [" + resource + "], ex");\r
+            return null;\r
+        }\r
+        catch (URISyntaxException e) {\r
+            logger.debug("Could not get System ID from [" + resource + "], ex");\r
+            return null;\r
+        }\r
+    }\r
+\r
+\r
+    public <T> T unmarshal(Class<T> clazz,Reader reader) throws XmlMappingException {\r
+        InputSource source = new InputSource(reader);\r
+        return unmarshal(clazz,source);\r
+    }\r
+\r
+    public <T> T unmarshal(Class<T> clazz,Reader reader, String systemId) throws XmlMappingException {\r
+        InputSource input = new InputSource(reader);\r
+        input.setSystemId(systemId);\r
+        return unmarshal(clazz,input);\r
+    }\r
+\r
+    public <T> T unmarshal(Class<T> clazz, File file) throws XmlMappingException {\r
+\r
+        InputSource input;\r
+        try {\r
+            input = new InputSource(new InputStreamReader(new FileInputStream(file),encoding));\r
+            return unmarshal(clazz,input);\r
+        } catch (UnsupportedEncodingException e) {\r
+            throw new UncategorizedMappingException(e.getMessage(), e);\r
+        } catch (FileNotFoundException e) {\r
+            throw new UncategorizedMappingException(e.getMessage(), e);\r
+        }\r
+\r
+    }\r
+\r
+    public void marshal(DataSet dataSet, Writer writer) throws XmlMappingException {\r
+        logger.info("Start marshalling");\r
+        super.marshal(dataSet, new StreamResult(writer));\r
+    }\r
+\r
+    public void marshal(DataSet dataSet, StreamResult result) throws XmlMappingException {\r
+        logger.info("Start marshalling");\r
+        super.marshal(dataSet, result);\r
+    }\r
+\r
+    public void marshal(DataSet dataSet, SAXResult result) throws XmlMappingException {\r
+        logger.info("Start marshalling");\r
+        super.marshal(dataSet, result);\r
+    }\r
 \r
 }\r
 \r
\r
+\r