/** * XWeb project * Created by Hamed Abdollahpour * https://github.com/abdollahpour/xweb */ package ir.xweb.data; import ir.xweb.util.MimeType; import org.jdom.Document; import org.jdom.Element; import org.jdom.output.Format; import org.jdom.output.XMLOutputter; import java.io.IOException; import java.io.Writer; import java.util.Collection; import java.util.Map; /** * Format XWeb object to XML. */ public class XmlFormatter implements Formatter { /** * Content type for this formatter. */ private final String contentType = MimeType.get("text/xml"); /** * {@inheritDoc} */ @Override public final void write(final Writer writer, final Object object) throws IOException { if (object instanceof Map) { final Map<?, ?> map = (Map) object; if (map.size() > 0) { final Element root; if (map.size() == 1) { String key = map.keySet().iterator().next().toString(); root = new Element(fixName(key)); write(root, map.values().iterator().next()); } else { root = new Element("data"); write(root, map); } final Document document = new Document(root); final XMLOutputter xmlOutput = new XMLOutputter(); xmlOutput.setFormat(Format.getCompactFormat()); xmlOutput.output(document, writer); } } else if (object instanceof Collection) { final Element root = new Element("data"); write(root, object); final Document document = new Document(root); final XMLOutputter xmlOutput = new XMLOutputter(); xmlOutput.setFormat(Format.getCompactFormat()); xmlOutput.output(document, writer); } else { writer.write(object.toString()); } } /** * Write an object to parent. * @param parent parent element * @param object xweb object * @throws IOException Error to write object */ private void write(final Element parent, final Object object) throws IOException { if (object instanceof Map) { final Map<?, ?> map = (Map<?, ?>) object; for (Map.Entry<?, ?> e:map.entrySet()) { final Element element = new Element(fixName(e.getKey().toString())); write(element, e.getValue()); parent.addContent(element); } } else if (object instanceof Collection) { final Collection<?> list = (Collection<?>) object; for (Object o:list) { String key = null; if (o instanceof AnnotedMap) { key = ((AnnotedMap) o).name.toLowerCase(); } if (key == null || key.length() == 0) { if (o instanceof String) { key = "string"; } else if (o instanceof Integer) { key = "integer"; } else if (o instanceof Float) { key = "float"; } else if (o instanceof Long) { key = "long"; } else if (o instanceof Double) { key = "double"; } else { key = o.getClass().getSimpleName(); } } final Element e = new Element(fixName(key)); write(e, o); parent.addContent(e); } } else { parent.setText(object.toString()); } } /** * {@inheritDoc} */ @Override public final String getContentType() { return contentType; } /** * Fix XML tag name. We can just use English and numeric character for names. All the other * characters will replace by '_' character. Also if name start with 'XML' or numeric * character, '_' will add at the start of the name. * @param name Element name * @return Fixed name */ private static String fixName(final String name) { String s = name.replaceAll("[^a-zA-Z0-9-_]", "_"); if (s.matches("^((\\d)|(?i)(XML))+.*$")) { s = "_" + s; } return s; } }