package de.disy.preludio2.xml.bind; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import javax.xml.bind.JAXBException; import javax.xml.bind.UnmarshalException; import javax.xml.bind.Unmarshaller; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.sax.SAXSource; import org.apache.commons.lang.Validate; import org.jvnet.jaxbcommons.runtime.DefaultJAXBContextImpl; import org.jvnet.jaxbcommons.runtime.SAXUnmarshallerHandlerImpl; import org.jvnet.jaxbcommons.runtime.UnmarshallerImpl; import org.jvnet.jaxbcommons.runtime.UnmarshallingContext; import org.jvnet.jaxbcommons.runtime.UnmarshallingEventHandler; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLFilterImpl; public class JAXBUtils { private JAXBUtils() { } private static final SAXParserFactory SAX_PARSER_FACTORY; static { SAX_PARSER_FACTORY = SAXParserFactory.newInstance(); SAX_PARSER_FACTORY.setNamespaceAware(true); SAX_PARSER_FACTORY.setValidating(false); } private static XMLReader createXMLReader() throws SAXException { try { return SAX_PARSER_FACTORY.newSAXParser().getXMLReader(); } catch (ParserConfigurationException pcex) { throw new SAXException(pcex); } } public static Class getImplClass(Class refClass) throws IllegalArgumentException { Validate.notNull(refClass); final String packageName = refClass.getPackage().getName(); final String unqualifiedClassName = refClass.getName().substring( packageName.length() + 1); final String implClassName = packageName + ".impl." + unqualifiedClassName + "Impl"; try { final Class implClass = Class.forName(implClassName, true, refClass .getClassLoader()); Validate.isTrue(refClass.isAssignableFrom(implClass), "Implementation class [" + implClass.getName() + "] must be an instance of reference interface [" + refClass.getName() + "]."); return (Class) implClass; } catch (ClassNotFoundException ex) { throw new IllegalArgumentException("Implementation class [" + implClassName + "] could not be found.", ex); } } public static T unmarshall(DefaultJAXBContextImpl context, InputSource source, Class refClass) throws IllegalArgumentException, JAXBException, SAXException, IOException { final Class implClass = JAXBUtils.getImplClass(refClass); final T target = createTargetObject(implClass); return unmarshall(context, source, refClass, target); } private static T unmarshall(DefaultJAXBContextImpl context, InputSource source, Class refClass, final T target) throws JAXBException, SAXException, IOException, UnmarshalException { final UnmarshallerImpl unmarshaller = (UnmarshallerImpl) context .createUnmarshaller(); final SAXUnmarshallerHandlerImpl unmarshallingContext = new SAXUnmarshallerHandlerImpl( unmarshaller, context.getGrammarInfo()); final XMLReader reader = createXMLReader(); XMLFilterImpl filter = new XMLFilterImpl(reader) { @Override public void startDocument() throws SAXException { super.startDocument(); try { final Method createUnmarshaller = target.getClass() .getDeclaredMethod("createUnmarshaller", UnmarshallingContext.class); final UnmarshallingEventHandler rootHandler = (UnmarshallingEventHandler) createUnmarshaller .invoke(target, unmarshallingContext); final Field field = unmarshallingContext.getClass() .getDeclaredField("result"); field.setAccessible(true); field.set(unmarshallingContext, target); unmarshallingContext.pushContentHandler(rootHandler, 0); } catch (Exception ex) { throw new SAXException(ex); } } int depth = 0; @Override public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { depth++; if (depth == 1) { unmarshallingContext.pushAttributes(atts, true); } else { super.startElement(uri, localName, qName, atts); } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { depth--; if (depth != 0) { super.endElement(uri, localName, qName); } else { unmarshallingContext.popAttributes(); } } }; filter.setContentHandler(unmarshallingContext); reader.setContentHandler(filter); reader.parse(source); return (T) unmarshallingContext.getResult(); } private static T createTargetObject(final Class implClass) throws JAXBException { try { return implClass.newInstance(); } catch (Exception ex) { throw new JAXBException("Could not create target object.", ex); } } /** * XMLReader that will be used to parse a document. */ private static XMLReader reader = null; /** * Obtains a configured XMLReader. * * This method is used when the client-specified {@link SAXSource} object * doesn't have XMLReader. * * {@link Unmarshaller} is not re-entrant, so we will only use one instance * of XMLReader. */ protected static XMLReader getXMLReader() throws JAXBException { if (reader == null) { try { SAXParserFactory parserFactory; parserFactory = SAXParserFactory.newInstance(); parserFactory.setNamespaceAware(true); // there is no point in asking a validation because // there is no guarantee that the document will come with // a proper schemaLocation. parserFactory.setValidating(false); reader = parserFactory.newSAXParser().getXMLReader(); } catch (ParserConfigurationException e) { throw new JAXBException(e); } catch (SAXException e) { throw new JAXBException(e); } } return reader; } }