/*******************************************************************************
* Copyright 2016
* Ubiquitous Knowledge Processing (UKP) Lab
* Technische Universität Darmstadt
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package de.tudarmstadt.ukp.lmf.writer.xml;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import de.tudarmstadt.ukp.lmf.model.interfaces.IHasID;
import de.tudarmstadt.ukp.lmf.model.miscellaneous.AccessType;
import de.tudarmstadt.ukp.lmf.model.miscellaneous.EAccessType;
import de.tudarmstadt.ukp.lmf.model.miscellaneous.EVarType;
import de.tudarmstadt.ukp.lmf.model.miscellaneous.VarType;
import de.tudarmstadt.ukp.lmf.writer.LMFWriter;
import de.tudarmstadt.ukp.lmf.writer.LMFWriterException;
/**
* This class writes a LMF classes to XML Output Stream.
* @deprecated THIS CLASS WILL BE REMOVED SOON. USE MODULE persistence.transform INSTEAD!
*/
@Deprecated
class LMFXmlWriter extends LMFWriter{
/*
* Private members
*/
private String dtdPath; // path of the dtd File
private TransformerHandler th; // Transform Handler
private String outputPath;
private static Log logger = LogFactory.getLog(LMFXmlWriter.class);
/**
* Constructs a LMFXmlWriter, XML will be saved to file in outputPath
* @param outputPath
* @param dtdPath Path of the dtd-File
* @return LMFXmlWriter
* @throws FileNotFoundException if the writer can not to the specified outputPath
*/
public LMFXmlWriter(String outputPath, String dtdPath) throws FileNotFoundException {
this(new FileOutputStream(outputPath), dtdPath);
this.outputPath = outputPath;
}
/**
* Constructs a LMFXmlWriter, XML will be saved to OutputStream out
* @param out
* @param dtdPath
*/
public LMFXmlWriter(OutputStream out, String dtdPath) {
this.dtdPath = dtdPath;
th = getXMLTransformerHandler(out);
}
/**
* Writes lmfObject to XML without writing closing Tag for this Element
* @param lmfObject
* @throws LMFWriterException
*/
@Override
public void writeStartElement(Object lmfObject) throws LMFWriterException{
try{
doTransform(lmfObject, false);
}catch(Exception ex){
throw new LMFWriterException(ex);
}
}
/**
* Ends the element associated with lmfObject
* @param lmfObject
* @throws LMFWriterException
*/
@Override
public void writeEndElement(Object lmfObject) throws LMFWriterException{
try{
String elementName = lmfObject.getClass().getSimpleName();
th.endElement("", "", elementName);
}catch(Exception ex){
throw new LMFWriterException(ex);
}
}
/**
* Ends the Document
* @throws LMFWriterException
*/
@Override
public void writeEndDocument() throws LMFWriterException{
try{
th.endDocument();
}catch(SAXException ex){
throw new LMFWriterException(ex);
}
}
/**
* Writes lmfObject to XML with closing tags
* @param lmfObject
* @throws LMFWriterException
*/
@Override
public void writeElement(Object lmfObject) throws LMFWriterException{
//System.out.print("Transforimng to XML...");
try{
doTransform(lmfObject, true);
}catch(Exception ex){
throw new LMFWriterException(ex);
}
//System.out.println("done");
}
/**
* This method consumes a LMF Object and transforms it to XML. The method
* iterates over all fields of a class and searches for the {@link AccessType} annotations.
* Depending on the value of the annotation, the method reads the values of the objects
* fields by invoking a getter or by directly accessing the field.
*
* @param lmfObject An LMF Object for which an Element should be created
* @param writeEndElement If TRUE the closing Tag for the XML-Element will be created
*
* @throws IllegalAccessException when a direct access to a field of the class is for some reason not possible
* @throws IllegalArgumentException when a direct access to a field of the class is for some reason not possible
* @throws SAXException if writing to XML-file is for some reason not possible
*/
@SuppressWarnings("unchecked")
private void doTransform(Object lmfObject, boolean writeEndElement) throws IllegalArgumentException, IllegalAccessException, SAXException{
Class<?> something = lmfObject.getClass();
String elementName = something.getSimpleName();
int hibernateSuffixIdx = elementName.indexOf("_$$");
if(hibernateSuffixIdx > 0)
elementName = elementName.substring(0, hibernateSuffixIdx);
AttributesImpl atts = new AttributesImpl();
List<Object> children = new ArrayList<Object>();
// find all field, also the inherited ones
ArrayList<Field> fields = new ArrayList<Field>();
fields.addAll(Arrays.asList(something.getDeclaredFields()));
Class<?> superClass = something.getSuperclass();
while(superClass != null){
fields.addAll(Arrays.asList(superClass.getDeclaredFields()));
superClass = superClass.getSuperclass();
}
// Iterating over all fields
for(Field field : fields){
String fieldName = field.getName().replace("_", "");
Class<?> fieldClass = field.getType();
VarType varType = field.getAnnotation(VarType.class);
// No VarType-Annotation found for the field, then don't save to XML
if(varType == null)
continue;
EVarType type = varType.type();
// VarType is NONE, don't save to XML
if(type.equals(EVarType.NONE))
continue;
Object retObj = null;
/*
* Determine how to access the variable
*/
AccessType accessType = field.getAnnotation(AccessType.class);
if(accessType == null || accessType.equals(EAccessType.GETTER)){
// access using a canonical getter
String setFieldName = fieldName;
if(fieldName.startsWith("is")) // E.g. isHead --> setHead
setFieldName = setFieldName.replaceFirst("is", "");
// Get-Method for the field
String getFuncName = setFieldName.substring(0,1).toUpperCase() + setFieldName.substring(1);
if(fieldClass.equals(Boolean.class)){
getFuncName = "is"+getFuncName;
}
else getFuncName = "get"+getFuncName;
Method getMethod = null;
try {
getMethod = something.getMethod(getFuncName);
retObj = getMethod.invoke(lmfObject); // Run the Get-Method
} catch (Exception e) {
logger.warn("There was an error on accessing the method " + getFuncName + " in "+ elementName +" class. Falling back to field access");
field.setAccessible(true);
retObj = field.get(lmfObject);
}
}
else{
// Directly read the value of the field
field.setAccessible(true);
retObj = field.get(lmfObject);
}
if(retObj != null){
if(type.equals(EVarType.ATTRIBUTE)){ // Save Attribute to the new element
atts.addAttribute("", "", fieldName, "CDATA", retObj.toString());
}else if(type.equals(EVarType.IDREF)){ // Save IDREFs as attribute of the new element
atts.addAttribute("", "", fieldName, "CDATA", ((IHasID)retObj).getId());
}else if(type.equals(EVarType.CHILD)){ // Transform children of the new element to XML
children.add(retObj);
}else if(type.equals(EVarType.CHILDREN) && writeEndElement){
for(Object obj : (Iterable<Object>)retObj){
children.add(obj);
}
}else if(type.equals(EVarType.ATTRIBUTE_OPTIONAL)){
atts.addAttribute("", "", fieldName, "CDATA", retObj.toString());
}else if(type.equals(EVarType.IDREFS)){
String attrValue = "";
for(Object obj : (Iterable<Object>)retObj){
attrValue += ((IHasID)obj).getId() + " ";
}
if(!attrValue.isEmpty())
atts.addAttribute("", "", fieldName, "CDATA", attrValue.substring(0, attrValue.length()-1));
}
}else { // Element is null, save only if it is a non-optional Attribute or IDREF
if(type.equals(EVarType.ATTRIBUTE) || type.equals(EVarType.IDREF)){ // Save Attribute to the new element
//atts.addAttribute("", "", fieldName, "CDATA", "NULL");
}
}
}
// Save the current element and its children
th.startElement("", "",elementName, atts);
for(Object child : children){
//System.out.println("CHILD: "+child.getClass().getSimpleName());
doTransform(child, true);
}
if(writeEndElement)
th.endElement("", "", elementName);
}
/**
* Creates XML TransformerHandler
* @param xmlOutPath
* @param dtdPath
* @return
* @throws IOException
* @throws TransformerException
*/
public TransformerHandler getXMLTransformerHandler(OutputStream out) {
StreamResult streamResult = new StreamResult(out);
SAXTransformerFactory tf = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
TransformerHandler th = null;
try {
th = tf.newTransformerHandler();
} catch (TransformerConfigurationException e) {
logger.error("Error on initiating TransformerHandler");
e.printStackTrace();
}
Transformer serializer = th.getTransformer();
serializer.setOutputProperty(OutputKeys.METHOD, "xml");
serializer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
if(dtdPath != null)
serializer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, dtdPath);
serializer.setOutputProperty(OutputKeys.INDENT, "yes");
serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
th.setResult(streamResult);
return th;
}
/**
* Returns the {@link String} representing the output path of the XML file
* which will be created/overwritten by this {@link LMFXmlWriter} instance.
*
* @return the output path configured for this {@link LMFXmlWriter}.
*
* @since UBY 0.2.0
*/
public String getOutputPath(){
return this.outputPath;
}
}