/* (c) 2017 Open Source Geospatial Foundation - all rights reserved * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.rest.catalog; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Properties; import org.geoserver.rest.converters.BaseMessageConverter; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.jdom.output.Format; import org.jdom.output.XMLOutputter; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.stereotype.Component; @Component public class MapXMLConverter extends BaseMessageConverter<Map<?, ?>> { public MapXMLConverter() { super(MediaType.TEXT_XML, MediaType.APPLICATION_XML); } @Override protected boolean supports(Class<?> clazz) { return Map.class.isAssignableFrom(clazz) && !Properties.class.isAssignableFrom(clazz); } // // reading // @Override public Map<?, ?> readInternal(Class<? extends Map<?, ?>> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { Object result; SAXBuilder builder = new SAXBuilder(); Document doc; try { doc = builder.build(inputMessage.getBody()); } catch (JDOMException e) { throw new IOException("Error building document", e); } Element elem = doc.getRootElement(); result = convert(elem); return (Map<?, ?>) result; } // // writing // @Override public void writeInternal(Map<?, ?> map, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { Element root = new Element(getMapName(map)); final Document doc = new Document(root); insert(root, map); XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat()); outputter.output(doc, outputMessage.getBody()); } protected String getMapName(Map<?, ?> map) { if (map instanceof NamedMap) { return ((NamedMap<?, ?>) map).getName(); } else { return "root"; } } /** * Interpret XML and convert it back to a Java collection. * * @param elem a JDOM element * @return the Object produced by interpreting the XML */ protected 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<Object> l = new ArrayList<>(); for (Object o : elem.getChildren("entry")) { Element curr = (Element) o; l.add(convert(curr)); } return l; } else { Map<String, Object> m = new NamedMap<>(child.getName()); for (Object aChildren : children) { Element curr = (Element) aChildren; m.put(curr.getName(), convert(curr)); } return m; } } throw new RuntimeException("Unable to parse XML"); } /** * 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 object the Object to be converted */ protected void insert(Element elem, Object object) { if (object instanceof Map) { Map<?, ?> map = (Map<?, ?>) object; for (Map.Entry<?, ?> entry : map.entrySet()) { Element newElem = new Element(entry.getKey().toString()); insert(newElem, entry.getValue()); elem.addContent(newElem); } } else if (object instanceof Collection) { Collection<?> collection = (Collection<?>) object; for (Object entry : collection) { Element newElem = new Element("entry"); insert(newElem, entry); elem.addContent(newElem); } } else { elem.addContent(object == null ? "" : object.toString()); } } }