/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, availible at the root
* application directory.
*/
package org.geoserver.rest;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.restlet.data.MediaType;
import org.restlet.resource.OutputRepresentation;
import org.restlet.resource.Representation;
/**
* The AutoXMLFormat class automatically converts a map into an XML document
* with a pretty direct mapping between XML and in-memory structure. Map keys
* are used as tag names, with the contents being either:
* <ul>
* <li> A single string </li>
* <li> Another map </li>
* <li> A list represented by objects in <entry> tags </li>
* <ul>
*
* This format can automatically reconstruct the map from the XML. Note that,
* since the in-memory structure is deduced from the contents of each tag, strange
* things may happen if you try to use "entry" as a map key.
*
* @author David Winslow <dwinslow@openplans.org>
*/
public class AutoXMLFormat implements DataFormat {
String myRootName;
/**
* Create an AutoXMLFormat that will wrap the entire document in an element called 'root'.
*/
public AutoXMLFormat(){
this("root");
}
/**
* Create an AutoXMLFormat that will wrap the entire document in an element named with the
* string given.
*
* @param s the name of the root element for XML generated by this AutoXMLFormat
*/
public AutoXMLFormat(String s){
myRootName = s;
}
public Representation makeRepresentation(Object context) {
Element root = new Element(myRootName);
final Document doc = new Document(root);
insert(root, context);
return new OutputRepresentation(MediaType.APPLICATION_XML){
public void write(OutputStream outputStream){
try{
XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
outputter.output(doc, outputStream);
} catch(IOException ioe){
ioe.printStackTrace();
}
}
};
}
/**
* Generate the JDOM element needed to represent an object and insert it into the parent element given.
* @todo This method is recursive and could cause stack overflow errors for large input maps.
*
* @param elem the parent Element into which to insert the created JDOM element
* @param o the Object to be converted
*/
private final void insert(Element elem, Object o){
if (o instanceof Map){
Iterator it = ((Map)o).entrySet().iterator();
while (it.hasNext()){
Map.Entry entry = (Map.Entry)it.next();
Element newElem = new Element(entry.getKey().toString());
insert(newElem, entry.getValue());
elem.addContent(newElem);
}
} else if (o instanceof Collection) {
Iterator it = ((Collection)o).iterator();
while (it.hasNext()){
Element newElem = new Element("entry");
Object entry = it.next();
insert(newElem, entry);
elem.addContent(newElem);
}
} else {
elem.addContent(o == null ? "" : o.toString());
}
}
public Object readRepresentation(Representation rep) {
Object result = null;
try {
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(rep.getStream());
Element elem = doc.getRootElement();
result = convert(elem);
} catch (Exception e){
}
return result;
}
/**
* Interpret XML and convert it back to a Java collection.
*
* @param elem a JDOM element
* @return the Object produced by interpreting the XML
*/
private Object convert(Element elem){
List children = elem.getChildren();
if (children.size() == 0){
if (elem.getContent().size() == 0){
return null;
} else {
return elem.getText();
}
} else if (children.get(0) instanceof Element){
Element child = (Element)children.get(0);
if (child.getName().equals("entry")){
List l = new ArrayList();
Iterator it = elem.getChildren("entry").iterator();
while(it.hasNext()){
Element curr = (Element)it.next();
l.add(convert(curr));
}
return l;
} else {
Map m = new HashMap();
Iterator it = children.iterator();
while (it.hasNext()){
Element curr = (Element)it.next();
m.put(curr.getName(), convert(curr));
}
return m;
}
}
throw new RuntimeException("Hm, there was a problem parsing XML");
}
}