package com.skaringa.javaxml.impl;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import com.skaringa.javaxml.DeserializerException;
import com.skaringa.javaxml.NoImplementationException;
import com.skaringa.javaxml.ObjectTransformer;
import com.skaringa.javaxml.SerializerException;
import com.skaringa.javaxml.handler.sax.AbstractXMLReader;
import com.skaringa.javaxml.handler.sax.DocumentInputHandler;
import com.skaringa.javaxml.handler.sax.ObjectInputSource;
import com.skaringa.javaxml.handler.sax.ObjectXMLReader;
import com.skaringa.javaxml.handler.sax.ObjectXSDReader;
import com.skaringa.javaxml.handler.sax.SAXInputHandler;
import com.skaringa.javaxml.serializers.ComponentSerializer;
import com.skaringa.javaxml.serializers.SerializerRegistry;
import com.skaringa.json.parser.JsonParser;
import com.skaringa.util.Log;
/**
* Implementation of ObjectTransformer. This implementation uses SAX events to
* parse and generate the XML documents.
*/
public final class ObjectTransformerImpl implements ObjectTransformer {
private SAXTransformerFactory _saxTransFact;
/**
* A pre-processed stylesheet for XML-preprocessing Creating new transformers
* from this object is fast because the stylesheet is already parsed and
* processed into the transformer's internal model.
*/
private Templates _preProcessorTemplate;
/**
* A pre-processed stylesheet for object transformation.
*/
private Templates _postProcessorTemplate;
private Map _properties = new HashMap();
private ClassLoader _classLoader = getClass().getClassLoader();
/**
* Construct a new Implementation of an ObjectTransformer
*
* @throws NoImplementationException
* If the underlying XML Transformer doesn't support SAXTransformer.
*/
public ObjectTransformerImpl() throws NoImplementationException {
TransformerFactory transFact = TransformerFactory.newInstance();
if (transFact.getFeature(SAXTransformerFactory.FEATURE)) {
_saxTransFact = (SAXTransformerFactory) transFact;
} else {
Log.error("SAXTransformerFactory is not supported.");
throw new NoImplementationException(
"SAXTransformerFactory is not supported.");
}
}
/**
* @see ObjectTransformer#serialize(Object, Result)
*/
public void serialize(Object obj, Result result) throws SerializerException {
Log.info("serialize - start "
+ (obj != null ? obj.getClass().getName() : "null"));
transform(getTransformerHandler(_postProcessorTemplate), obj,
new ObjectXMLReader(), result);
Log.info("serialize - end "
+ (obj != null ? obj.getClass().getName() : "null"));
}
/**
* @see ObjectTransformer#serializeToString(Object)
*/
public String serializeToString(Object obj) throws SerializerException {
StringWriter out = new StringWriter();
serialize(obj, new StreamResult(out));
return out.toString();
}
/**
* @see ObjectTransformer#deserialize(Source)
*/
public Object deserialize(Source src) throws DeserializerException {
Object obj;
Log.info("deserialize - start ...");
try {
Transformer transformer;
if (_preProcessorTemplate != null) {
// use a preprocessor if one is given
transformer = _preProcessorTemplate.newTransformer();
} else {
transformer = _saxTransFact.newTransformer();
}
DocumentInputHandler inputHandler = new DocumentInputHandler();
inputHandler.setProperties(_properties);
inputHandler.setClassLoader(_classLoader);
transformer.transform(src, newObjectResult(inputHandler));
obj = inputHandler.getObject();
} catch (TransformerConfigurationException e) {
Log.error(e);
throw new DeserializerException(e.toString());
} catch (TransformerException e) {
Log.error(e);
Throwable emb = e.getException();
if (emb instanceof DeserializerException) {
throw (DeserializerException) emb;
}
throw new DeserializerException(e.getMessage());
}
Log.info("deserialize - end "
+ (obj != null ? obj.getClass().getName() : "null"));
return obj;
}
/**
* @see ObjectTransformer#deserializeFromString(String)
*/
public Object deserializeFromString(String s) throws DeserializerException {
StringReader in = new StringReader(s);
Object obj = deserialize(new StreamSource(in));
return obj;
}
/**
* @see ObjectTransformer#writeXMLSchema(Class, Result)
*/
public void writeXMLSchema(Class type, Result result)
throws SerializerException {
Log.info("writeXMLSchema - start "
+ (type != null ? type.getName() : "null"));
transform(getCopyTransformerHandler(), type, new ObjectXSDReader(), result);
Log
.info("writeXMLSchema - end "
+ (type != null ? type.getName() : "null"));
}
/**
* @see ObjectTransformer#writeXMLSchema(Class, Collection, Result)
*/
public void writeXMLSchema(Class type, Collection componentTypes,
Result result) throws SerializerException {
Log.info("writeXMLSchema - start "
+ (type != null ? type.getName() : "null"));
ObjectXSDReader xsdReader = new ObjectXSDReader();
xsdReader.addUsedClasses(componentTypes);
transform(getCopyTransformerHandler(), type, xsdReader, result);
Log
.info("writeXMLSchema - end "
+ (type != null ? type.getName() : "null"));
}
/**
* @see ObjectTransformer#transform(Object, Source)
* @deprecated
*/
public Object transform(Object obj, Source transformationInstruction)
throws SerializerException, DeserializerException {
Log.info("transform - start "
+ (obj != null ? obj.getClass().getName() : "null"));
DocumentInputHandler inputHandler = new DocumentInputHandler();
inputHandler.setProperties(_properties);
inputHandler.setClassLoader(_classLoader);
transform(getTransformerHandler(transformationInstruction), obj,
new ObjectXMLReader(), newObjectResult(inputHandler));
Log.info("transform - end "
+ (obj != null ? obj.getClass().getName() : "null"));
return inputHandler.getObject();
}
/**
* @see ObjectTransformer#transform(Object)
*/
public Object transform(Object obj) throws SerializerException,
DeserializerException {
Log.info("transform - start "
+ (obj != null ? obj.getClass().getName() : "null"));
DocumentInputHandler inputHandler = new DocumentInputHandler();
inputHandler.setProperties(_properties);
inputHandler.setClassLoader(_classLoader);
transform(getTransformerHandler(_postProcessorTemplate), obj,
new ObjectXMLReader(), newObjectResult(inputHandler));
Log.info("transform - end "
+ (obj != null ? obj.getClass().getName() : "null"));
return inputHandler.getObject();
}
/**
* @see ObjectTransformer#transform(Object, Source, Result)
* @deprecated
*/
public void transform(Object obj, Source transformationInstruction,
Result result) throws SerializerException {
transform(getTransformerHandler(transformationInstruction), obj,
new ObjectXMLReader(), result);
}
/**
* @see ObjectTransformer#setProperty(String, Object)
*/
public void setProperty(String name, Object value) {
_properties.put(name, value);
}
/**
* @see com.skaringa.javaxml.ObjectTransformer#setClassLoader(java.lang.ClassLoader)
*/
public void setClassLoader(ClassLoader classLoader) {
_classLoader = classLoader;
}
/**
* Obtain a transformer handler for a source of transformation instructions.
*
* @param transformationInstruction
* The source of trafo instructions.
* @return The transformer handler.
* @throws SerializerException
* If the transformer can't be created.
*/
private TransformerHandler getTransformerHandler(
Source transformationInstruction) throws SerializerException {
TransformerHandler transHand;
try {
if (transformationInstruction != null) {
// if transformation instructions are given explicitely, then use them.
transHand = _saxTransFact
.newTransformerHandler(transformationInstruction);
} else {
// otherwise use an empty stylesheet (copy transformation)
transHand = _saxTransFact.newTransformerHandler();
}
return transHand;
} catch (TransformerConfigurationException e) {
Log.error(e);
throw new SerializerException(e.getMessage());
}
}
/**
* Obtain a transformer handler for a pre-parsed transformer template.
*
* @param template
* The pre-parsed transformer template.
* @return The transformer handler.
* @throws SerializerException
* If the transformer can't be created.
*/
private TransformerHandler getTransformerHandler(Templates template)
throws SerializerException {
TransformerHandler transHand;
try {
if (template != null) {
// use a template if one is given
transHand = _saxTransFact.newTransformerHandler(template);
} else {
// otherwise use an empty stylesheet (copy transformation)
transHand = _saxTransFact.newTransformerHandler();
}
return transHand;
} catch (TransformerConfigurationException e) {
Log.error(e);
throw new SerializerException(e.getMessage());
}
}
/**
* Obtain a transformer handler for a copy transformation (empty stylesheet).
*
* @return The transformer handler.
* @throws SerializerException
* If the transformer can't be created.
*/
private TransformerHandler getCopyTransformerHandler()
throws SerializerException {
try {
return _saxTransFact.newTransformerHandler();
} catch (TransformerConfigurationException e) {
Log.error(e);
throw new SerializerException(e.getMessage());
}
}
/**
* Transform a Java object into a XML Result.
*
* @param transHand
* The transformer handler.
* @param obj
* The object to transform.
* @param objReader
* The reader which is used to evaluate ("parse") the Java object.
* @param result
* The result of the transformation.
* @throws SerializerException
* If the transformation failes.
*/
private void transform(TransformerHandler transHand, Object obj,
AbstractXMLReader objReader, Result result) throws SerializerException {
try {
// set properties of the Transformer
Transformer transformer = transHand.getTransformer();
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
// set the destination for the XSLT transformation
transHand.setResult(result);
// pass the properties to the XSL transformer or to the XMLReader
setProperties(transformer, objReader);
// attach the XSLT processor to the XMLReader
objReader.setContentHandler(transHand);
// parse and transform
objReader.parse(new ObjectInputSource(obj));
} catch (IOException e) {
Log.error(e);
throw new SerializerException(e.getMessage());
} catch (org.xml.sax.SAXException e) {
Log.error(e);
Exception emb = e.getException();
if (emb instanceof SerializerException) {
throw (SerializerException) emb;
}
throw new SerializerException(e.getMessage());
}
}
/**
* Pass the properties to the XSL transformer or to the XML reader. All
* properties that are not valid for the XML reader are passed to the
* underlying XSL transformer.
*
* @param transformer
* The XSL transformer.
* @param objReader
* The XML reader.
* @throws org.xml.sax.SAXNotRecognizedException
* When the XMLReader does not recognize the property name.
* @throws org.xml.sax.SAXNotSupportedException
* When the XMLReader recognizes the property name but cannot set
* the requested value.
*/
private void setProperties(Transformer transformer,
AbstractXMLReader objReader)
throws org.xml.sax.SAXNotRecognizedException,
org.xml.sax.SAXNotSupportedException {
Iterator it = _properties.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String name = (String) entry.getKey();
Object value = entry.getValue();
if (PropertyHelper.isValidNameForObjectTransformer(name)) {
objReader.setProperty(name, value);
} else {
transformer.setOutputProperty(name, (String) value);
}
}
}
/**
* Create a new SAXResult which is assoziated with a DocumentInputHandler.
*
* @param inputHandler
* The document input handler which can construct a new Java object
* from SAX envents.
* @return A SAXResult that geta events from a SAX parser and forwards them to
* the inputHandler.
*/
private Result newObjectResult(DocumentInputHandler inputHandler) {
SAXInputHandler saxHandler = new SAXInputHandler(inputHandler);
return new SAXResult(saxHandler);
}
/**
* @see ObjectTransformer#setPreprocessorInstruction(Source)
*/
public void setPreprocessorInstruction(Source transformationInstruction)
throws NoImplementationException {
try {
_preProcessorTemplate = _saxTransFact
.newTemplates(transformationInstruction);
} catch (TransformerConfigurationException e) {
Log.error(e);
throw new NoImplementationException(e.getMessage());
}
}
/**
* @see ObjectTransformer#setPostprocessorInstruction(Source)
*/
public void setPostprocessorInstruction(Source transformationInstruction)
throws NoImplementationException {
try {
_postProcessorTemplate = _saxTransFact
.newTemplates(transformationInstruction);
} catch (TransformerConfigurationException e) {
Log.error(e);
throw new NoImplementationException(e.getMessage());
}
}
/**
* @see ObjectTransformer#serializeToJson(Object, OutputStream)
*/
public void serializeToJson(Object obj, OutputStream stream)
throws SerializerException {
Log.info("toJson - start "
+ (obj != null ? obj.getClass().getName() : "null"));
Class type = Object.class;
if (obj != null) {
type = obj.getClass();
}
ComponentSerializer ser = SerializerRegistry.getInstance().getSerializer(
type);
PrintStream output;
try {
output = new PrintStream(new BufferedOutputStream(stream), false, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e); // UTF-8 should always be supported
}
ser.toJson(obj, type, _properties, output);
output.flush();
Log.info("toJson - end "
+ (obj != null ? obj.getClass().getName() : "null"));
}
/**
* @see ObjectTransformer#serializeToJsonString(Object)
*/
public String serializeToJsonString(Object obj) throws SerializerException {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
serializeToJson(obj, stream);
try {
return stream.toString("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e); // UTF-8 should always be supported
}
}
/**
* @see com.skaringa.javaxml.ObjectTransformer#deserializeFromJson(InputStream, Class)
*/
public Object deserializeFromJson(InputStream stream, Class rootType)
throws DeserializerException {
try {
Reader reader = new InputStreamReader(new BufferedInputStream(stream), "UTF-8");
JsonParser parser = new JsonParser(reader, rootType, _properties, _classLoader);
parser.process();
return parser.getObject();
} catch (IOException e) {
Log.error(e);
throw new DeserializerException(e.getMessage());
}
}
/**
* @see com.skaringa.javaxml.ObjectTransformer#deserializeFromJsonString(java.lang.String,
* java.lang.Class)
*/
public Object deserializeFromJsonString(String s, Class rootType)
throws DeserializerException {
try {
Reader reader = new StringReader(s);
JsonParser parser = new JsonParser(reader, rootType, _properties, _classLoader);
parser.process();
return parser.getObject();
} catch (IOException e) {
Log.error(e);
throw new DeserializerException(e.getMessage());
}
}
/**
* @see com.skaringa.javaxml.ObjectTransformer#deserializeFromJson(java.io.InputStream)
*/
public Object deserializeFromJson(InputStream stream)
throws DeserializerException {
return deserializeFromJson(stream, null);
}
/**
* @see com.skaringa.javaxml.ObjectTransformer#deserializeFromJsonString(java.lang.String)
*/
public Object deserializeFromJsonString(String s)
throws DeserializerException {
return deserializeFromJsonString(s, null);
}
}