package org.fastcatsearch.util;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.transform.*;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
/**
* @author lupfeliz
*
*/
public class XMLResponseWriter implements ResponseWriter {
protected static Logger logger = LoggerFactory.getLogger(XMLResponseWriter.class);
private static final String ARRAY_DEFAULT_ITEM_NAME = "item";
enum NODE_TYPE { OBJECT, ARRAY };
Document document;
Element root;
Element currentElement;
List<String> arrayName;
List<NODE_TYPE> types;
boolean beautify;
boolean isKeyLowercase;
Writer w;
public XMLResponseWriter(Writer w, String rootName) {
this(w, rootName, false, false);
}
public XMLResponseWriter(Writer w, String rootName, boolean beautify, boolean isKeyLowercase) {
this.w = w;
document = DocumentHelper.createDocument();
root = document.addElement(rootName);
currentElement = root;
this.beautify = beautify;
this.isKeyLowercase = isKeyLowercase;
types = new ArrayList<NODE_TYPE>();
arrayName = new ArrayList<String>();
}
@Override
public ResponseWriter object() throws ResultWriterException {
if(types.size()>0 && types.get(types.size()-1)==NODE_TYPE.ARRAY) {
types.add(NODE_TYPE.OBJECT);
currentElement = currentElement.addElement(arrayName.get(arrayName.size()-1));
} else {
}
return this;
}
@Override
public ResponseWriter endObject() throws ResultWriterException {
if(types.size()>1 && types.get(types.size()-2)==NODE_TYPE.ARRAY) {
types.remove(types.size()-1);
currentElement = currentElement.getParent();
} else {
currentElement = currentElement.getParent();
}
return this;
}
@Override
public ResponseWriter array() throws ResultWriterException {
return array(ARRAY_DEFAULT_ITEM_NAME);
}
@Override
public ResponseWriter array(String arrayName) throws ResultWriterException {
if(types.size()>0 && types.get(types.size()-1)==NODE_TYPE.ARRAY) {
currentElement = currentElement.addElement(this.arrayName.get(
this.arrayName.size()-1));
}
types.add(NODE_TYPE.ARRAY);
this.arrayName.add(arrayName);
return this;
}
@Override
public ResponseWriter endArray() throws ResultWriterException {
currentElement = currentElement.getParent();
types.remove(types.size()-1);
this.arrayName.remove(arrayName.size()-1);
return this;
}
@Override
public ResponseWriter key(String key) throws ResultWriterException {
if(isKeyLowercase) {
key = key.toLowerCase();
}
currentElement = currentElement.addElement(key);
return this;
}
@Override
public ResponseWriter value(Object obj) throws ResultWriterException {
if(types.size()!=0 && types.get(types.size()-1)==NODE_TYPE.ARRAY) {
currentElement = currentElement.addElement(arrayName.get(arrayName.size()-1));
} else {
}
if(obj == null){
currentElement = currentElement.addText("");
}else{
currentElement = currentElement.addText(obj.toString());
}
currentElement = currentElement.getParent();
return this;
}
@Override
public String toString() {
if(beautify) {
StringWriter sw = new StringWriter();
try {
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer();
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.transform
(new StreamSource(new StringReader(document.asXML())),
new StreamResult(sw));
return sw.toString();
} catch (TransformerConfigurationException e) {
} catch (TransformerException e) {
} finally {
}
} else {
return document.asXML();
}
return "";
}
///FIXME : done을 여러번 호출시 결과가 여러번 기록되는 버그가 존재한다.
@Override
public void done() {
if(w != null){
try {
w.write(toString());
} catch (IOException e) {
logger.error("write error", e);
}
try {
w.flush();
} catch (IOException e) {
logger.error("close error", e);
}
}
}
}