package org.odata4j.stax2.domimpl; import java.io.IOException; import java.io.Writer; import java.util.Stack; import org.odata4j.core.ODataConstants; import org.odata4j.core.ODataConstants.Charsets; import org.odata4j.core.Throwables; import org.odata4j.stax2.QName2; import org.odata4j.stax2.XMLWriter2; public class ManualXMLWriter2 implements XMLWriter2 { private final Writer writer; private boolean isStartElementOpen; private final Stack<QName2> elements = new Stack<QName2>(); public ManualXMLWriter2(Writer writer) { this.writer = writer; } @Override public void endDocument() { while (!elements.isEmpty()) endElement(elements.peek().getLocalPart()); try { writer.flush(); } catch (IOException e) { throw Throwables.propagate(e); } } @Override public void endElement(String localName) { QName2 startElementName = elements.pop(); if (!startElementName.getLocalPart().equals(localName)) throw new IllegalArgumentException(); if (isStartElementOpen) { write("/"); write(">"); isStartElementOpen = false; return; } write("</"); if (startElementName.getPrefix() != null) { write(startElementName.getPrefix()); write(":"); } write(localName); write(">"); } @Override public void startDocument() { write("<?xml version=\"1.0\" encoding=\"" + Charsets.Lower.UTF_8 + "\" standalone=\"yes\" ?>"); } @Override public void startElement(String name) { startElement(new QName2(name)); } @Override public void startElement(QName2 qname) { startElement(qname, null); } @Override public void startElement(QName2 qname, String xmlns) { ensureStartElementClosed(); write("<"); if (qname.getPrefix() != null) { write(qname.getPrefix()); write(":"); } write(qname.getLocalPart()); if (xmlns != null) { write(" xmlns=\"" + xmlns + "\""); } isStartElementOpen = true; elements.push(qname); } @Override public void writeAttribute(String localName, String value) { writeAttribute(new QName2(localName), value); } @Override public void writeAttribute(QName2 qname, String value) { if (!isStartElementOpen) throw new IllegalStateException(); write(" "); if (qname.getPrefix() != null) { write(qname.getPrefix()); write(":"); } write(qname.getLocalPart()); write("=\""); write(encodeAttributeValue(value)); write("\""); } @Override public void writeNamespace(String prefix, String namespaceUri) { if (!isStartElementOpen) throw new IllegalStateException(); write(" xmlns:" + prefix + "=\"" + namespaceUri + "\""); } @Override public void writeText(String content) { ensureStartElementClosed(); write(encodeElementValue(content)); } private void ensureStartElementClosed() { if (isStartElementOpen) { write(">"); isStartElementOpen = false; } } private void write(String value) { try { writer.write(value); } catch (IOException e) { // don't throw an exception; instead write a message as part of data and continue. this.writeErrorMessageAsCData(e.getMessage()); } } /** * In case we encounter any invalid characters in the XML, then print error message as CDATA so that it is highlighted at client side.<br><br> * * If we still have issue, we will throw the exception. * * @param content */ @Override public void writeErrorMessageAsCData(String message) { StringBuilder errorMessageBuilder = new StringBuilder(); StringBuilder errorMessage = new StringBuilder(ODataConstants.ERROR_TEXT); if (message != null) { errorMessage.append(message); } errorMessageBuilder.append(ODataConstants.CDATA_TAG_START).append(errorMessageBuilder.toString()).append(ODataConstants.CDATA_TAG_END); try { writer.write(errorMessageBuilder.toString()); } catch (IOException e) { throw Throwables.propagate(e); } } private String encodeElementValue(String value) { return encodeAttributeValue(value); // TODO } private String encodeAttributeValue(String value) { if (value == null) return null; int len = value.length(); if (len == 0) return value; StringBuffer encoded = new StringBuffer(); for (int i = 0; i < len; i++) { char c = value.charAt(i); if (c == '<') encoded.append("<"); else if (c == '\"') encoded.append("""); else if (c == '>') encoded.append(">"); else if (c == '\'') encoded.append("'"); else if (c == '&') encoded.append("&"); else encoded.append(c); } return encoded.toString(); } }