package cz.cuni.mff.peckam.java.origamist.jaxb;
import java.util.List;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.util.JAXBResult;
import javax.xml.parsers.SAXParser;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.sax.SAXResult;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import cz.cuni.mff.peckam.java.origamist.jaxb.AdditionalTransforms.AdditionalTransform;
/**
* An implementation of Bindings that supports use of one or more XML transforms to convert an input XML document to
* some other XML schema before unmarshalling.
* <p>
*
* This implementation uses a series of TransformerHandler classes to achieve the series of XML transforms; this was
* found to be a more straightforward and "accessible" (i.e., less hidden resetting of error handlers, entity resolvers,
* etc) than using XMLFilters as recommended by the Java Web Services tutorial.
* <p>
*
* This package is based on a XML Schema versioning system from http://www.funkypeople.biz/knowledge/JavaXml-v2.zip .
*
* @author Sean Barnett
* @author Martin Pecka
*
* @param T The type of the unmarhalled object.
*/
public class ParserTransformBindings<T> implements Bindings<T>
{
/** The definition of the transform(s) to use to get from the XML schema to the Java bindings schema. */
protected TransformInfo transform;
/** The unmarshaller used during the unmarshalling process. */
protected Unmarshaller unmarshaller;
/** This element will receive the final unmarshalled classes. */
protected JAXBResult unmarshallerResult;
/** The parser that parses the XML file. */
protected SAXParser unmarshalParser;
/** The array of content handlers joined in a line to convert the XML file. */
protected SAXOutputtingContentHandler[] contentHandlers;
/** The {@link BindingsManager} that controls this binding. */
protected final BindingsManager bindingsManager;
/**
* Simple constructor.
*
* @param bindingsManager A reference to the bindings manager.
* @param transform The definition of the transform(s) to use to get from the XML schema to the Java bindings
* schema.
* @param start List of transforms to be added before the classic ones.
* @param beforeUnmarshaller List of transforms to be added before the unmarshaller.
* @param end List of transforms to be added after the unmarshaller.
*
* @throws JAXBException
*/
public ParserTransformBindings(BindingsManager bindingsManager, TransformInfo transform,
List<AdditionalTransform> start, List<AdditionalTransform> beforeUnmarshaller, List<AdditionalTransform> end)
throws JAXBException
{
this.bindingsManager = bindingsManager;
this.transform = transform;
// create unmarshaller - not validating as a validating parser will be used
// to do validation of raw XML
unmarshaller = bindingsManager.createUnmarshaller(transform.getToSchema());
// last transformer handler writes to JAXB result
unmarshallerResult = new JAXBResult(unmarshaller);
try {
// create the unmarshal transforms, and couple each to the next in sequence
createHandlers(transform, start, beforeUnmarshaller, end);
// index of the handler after which the unmarshalling should proceed
int lastBeforeUnmarshallerIndex = start.size() + transform.getDepth() + beforeUnmarshaller.size() - 1;
// set last unmarshal transform to point to JAXBResult
contentHandlers[lastBeforeUnmarshallerIndex].setResult(unmarshallerResult);
// set the remaining "tail" to be processed by the unmarshaller result
if (end.size() > 0)
unmarshallerResult.setHandler(contentHandlers[lastBeforeUnmarshallerIndex + 1]);
} catch (TransformerConfigurationException e) {
throw new JAXBException(e.getMessage(), (e.getException() == null) ? e : e.getException());
} catch (SAXException e) {
throw new JAXBException(e.getMessage(), (e.getException() == null) ? e : e.getException());
}
}
@Override
public ContentHandler getContentHandler()
{
return contentHandlers[0];
}
@SuppressWarnings("unchecked")
@Override
public T getResult() throws JAXBException, IllegalStateException, ClassCastException
{
Object result = unmarshallerResult.getResult();
// the result can be wrapped inside a JAXBElement if the schema doesn't define a root element
if (result instanceof JAXBElement<?>)
result = ((JAXBElement<?>) result).getValue();
return (T) result;
}
/**
* Create the series of unmarshal transforms, and point each to the next in sequence as its target content handler.
*
* @param transform The definition of the last transform in sequence.
* @param start List of transforms to be added before the classic ones.
* @param beforeUnmarshaller List of transforms to be added before the unmarshaller.
* @param end List of transforms to be added after the unmarshaller.
*
* @throws SAXException
* @throws TransformerConfigurationException
*/
protected void createHandlers(TransformInfo transform, List<AdditionalTransform> start,
List<AdditionalTransform> beforeUnmarshaller, List<AdditionalTransform> end) throws SAXException,
TransformerConfigurationException
{
contentHandlers = new SAXOutputtingContentHandler[start.size() + transform.getDepth()
+ beforeUnmarshaller.size() + end.size()];
int i = -1;
for (AdditionalTransform tr : start) {
i++;
if (tr.getTransform().getTemplates() != null) {
contentHandlers[i] = bindingsManager.getTransformerFactory().newTransformerHandler(
tr.getTransform().getTemplates());
} else {
contentHandlers[i] = tr.getTransform().getContentHandler();
}
}
for (TransformInfo tr = transform; tr != null; tr = tr.getParent()) {
int trDepth = tr.getDepth();
if (tr.getTemplates() != null) {
contentHandlers[i + trDepth] = bindingsManager.getTransformerFactory().newTransformerHandler(
tr.getTemplates());
} else {
contentHandlers[i + trDepth] = tr.getContentHandler();
}
}
i += transform.getDepth();
for (AdditionalTransform tr : beforeUnmarshaller) {
i++;
if (tr.getTransform().getTemplates() != null) {
contentHandlers[i] = bindingsManager.getTransformerFactory().newTransformerHandler(
tr.getTransform().getTemplates());
} else {
contentHandlers[i] = tr.getTransform().getContentHandler();
}
}
for (AdditionalTransform tr : end) {
i++;
if (tr.getTransform().getTemplates() != null) {
contentHandlers[i] = bindingsManager.getTransformerFactory().newTransformerHandler(
tr.getTransform().getTemplates());
} else {
contentHandlers[i] = tr.getTransform().getContentHandler();
}
}
// interconnect the handlers with setResult()
for (i = 0; i < contentHandlers.length - 1; i++) {
contentHandlers[i].setResult(new SAXResult(contentHandlers[i + 1]));
}
}
}