package com.tinesoft.droidlinguist.server.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.net.URL; import java.util.List; import javax.xml.XMLConstants; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.bind.ValidationEvent; import javax.xml.bind.ValidationEventHandler; import javax.xml.bind.ValidationEventLocator; import javax.xml.bind.helpers.DefaultValidationEventHandler; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import com.tinesoft.droidlinguist.server.tools.SourceStringsParser; /** * JAXB utility class. * * @author Tine Kondo * */ public final class JaxbUtil { private static final Logger LOG = LoggerFactory.getLogger(JaxbUtil.class); private static final String XMLNS_REGEXP = " xmlns:[^=]+=\"[^\"]*\""; /** * Utility class. No public constructor */ private JaxbUtil() { } /** * Creates the {@link JAXBContext} for the given class, if null * * @param clazz * @param context * @return */ public static <T> JAXBContext createContextIfNull(Class<T> clazz, JAXBContext context) { if (context != null) return context; try { return JAXBContext.newInstance(clazz.getPackage().getName()); } catch (JAXBException e) { LOG.error("Failed to create JaxbContext for {}", clazz.getPackage().getName(), e); return null; } } /** * Converts the given xml into the corresponding T object. * * @param xml * @param context * @param validationSchema * @param validationEventHandler * @return */ @SuppressWarnings("unchecked") public static <T> T convertTo(byte[] xml, JAXBContext context, Schema validationSchema, ValidationEventHandler validationEventHandler) { try (ByteArrayInputStream bis = new ByteArrayInputStream(xml);) { Unmarshaller unmarshaller = context.createUnmarshaller(); if (validationSchema != null) unmarshaller.setSchema(validationSchema); if (validationEventHandler != null) unmarshaller.setEventHandler(validationEventHandler); return (T) unmarshaller.unmarshal(bis); } catch (JAXBException | IOException e) { LOG.error("Failed to unmarshall ", e); return null; } } /** * Converts the given xml object into the corresponding a string * representation. * * @param xmlObject * @param context * @param validationSchema * @param validationEventHandler * @return */ public static <T> String convertToString(T xmlObject, JAXBContext context, Schema validationSchema, ValidationEventHandler validationEventHandler) { try (ByteArrayOutputStream baos = new ByteArrayOutputStream(1024)) { Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); if (validationSchema != null) marshaller.setSchema(validationSchema); if (validationEventHandler != null) marshaller.setEventHandler(validationEventHandler); marshaller.marshal(xmlObject, baos); return baos.toString().replaceAll(XMLNS_REGEXP, "");// FIXME remove // xmlns attr } catch (JAXBException | IOException e) { LOG.error("Failed to unmarshall ", e); return null; } } /** * Creates the {@link Schema} if null * * @param xsd * @param schema * @return */ public static Schema createSchemaIfNull(String xsd, Schema schema) { if (schema != null) return schema; try { SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); StreamSource xsdSource = new StreamSource(SourceStringsParser.class.getClassLoader().getResourceAsStream(xsd)); return sf.newSchema(xsdSource); } catch (SAXException e) { LOG.error("Failed to create schema at '{}' ", xsd, e); return null; } } /** * Calculates a location message for the event. * * {@link DefaultValidationEventHandler#getLocation(ValidationEvent)} */ public static String getErrorLocation(ValidationEvent event) { StringBuilder msg = new StringBuilder(); ValidationEventLocator locator = event.getLocator(); if (locator != null) { URL url = locator.getURL(); Object obj = locator.getObject(); Node node = locator.getNode(); int line = locator.getLineNumber(); if (url != null || line != -1) { msg.append("line " + line); if (url != null) msg.append(" of " + url); } else if (obj != null) { msg.append(" obj: " + obj.toString()); } else if (node != null) { msg.append(" node: " + node.toString()); } } else { msg.append("Cannot determinate the location of the error/warning"); } return msg.toString(); } public static String getXmlMixedContentAsString(List<Object> contents) { StringBuilder result = new StringBuilder(); for (Object c : contents) { if (c instanceof String) result.append(c); else if (c instanceof Element) { result.append(convertNodeToString((Element) c)); } } return result.toString(); } public static String convertNodeToString(Node node) { TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer; try { transformer = tf.newTransformer(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); StringWriter writer = new StringWriter(); transformer.transform(new DOMSource(node), new StreamResult(writer)); return writer.toString().replaceAll(XMLNS_REGEXP, "");// FIXME // remove // xmlns // attr } catch (TransformerConfigurationException e) { LOG.error("Error while creating new transformer for Node {}", node, e); } catch (TransformerException e) { LOG.error("Error while transforming Node {} to string", node, e); } return null; } public static Node convertStringToNode(String xmlStr) { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder; try { builder = factory.newDocumentBuilder(); Node node = builder.parse(new InputSource(new StringReader(xmlStr))).getDocumentElement(); return node; } catch (ParserConfigurationException e) { LOG.error("Error while creating the new document builder ", e); } catch (SAXException e) { LOG.error("Error while parsing the xml string '{}' ", xmlStr, e); } catch (IOException e) { LOG.error("IO Error while parsing the xml string '{}' ", xmlStr, e); } return null; } }