001: /*
002: * $Id: ElementSerializer.java,v 1.33 2007/09/18 11:21:02 agoubard Exp $
003: *
004: * Copyright 2003-2007 Orange Nederland Breedband B.V.
005: * See the COPYRIGHT file for redistribution and use restrictions.
006: */
007: package org.xins.common.xml;
008:
009: import java.io.IOException;
010: import java.io.StringWriter;
011: import java.io.UnsupportedEncodingException;
012: import java.io.Writer;
013: import java.util.HashMap;
014: import java.util.Iterator;
015: import java.util.List;
016: import java.util.Map;
017:
018: import org.xins.common.MandatoryArgumentChecker;
019: import org.xins.common.Utils;
020: import org.znerd.xmlenc.XMLOutputter;
021:
022: /**
023: * Serializer that takes an <code>Element</code> and converts it to an XML
024: * string.
025: *
026: * <p>This class is not thread-safe. It should only be used on one thread at a
027: * time.
028: *
029: * @version $Revision: 1.33 $ $Date: 2007/09/18 11:21:02 $
030: * @author <a href="mailto:anthony.goubard@japplis.com">Anthony Goubard</a>
031: * @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
032: *
033: * @since XINS 1.1.0
034: */
035: public final class ElementSerializer {
036:
037: /**
038: * Lock object that is synchronized on when reading or writing
039: * <code>_inUse</code>.
040: */
041: private final Object _lock;
042:
043: /**
044: * Flag that indicates whether this serializer is currently in use. It may
045: * only be used by one thread at a time.
046: */
047: private boolean _inUse;
048:
049: /**
050: * Constructs a new <code>ElementSerializer</code>.
051: */
052: public ElementSerializer() {
053: _lock = new Object();
054: }
055:
056: /**
057: * Serializes the element to XML. This method is not reentrant. Hence, it
058: * should only be used from a single thread.
059: *
060: * @param element
061: * the element to serialize, cannot be <code>null</code>.
062: *
063: * @return
064: * an XML document that represents <code>element</code>, never
065: * <code>null</code>.
066: *
067: * @throws IllegalArgumentException
068: * if <code>element == null</code>.
069: */
070: public String serialize(Element element)
071: throws IllegalArgumentException {
072:
073: synchronized (_lock) {
074:
075: // Make sure this serializer is not yet in use
076: if (_inUse) {
077: String detail = "ElementSerializer instance already in use.";
078: throw Utils.logProgrammingError(detail);
079: }
080:
081: // Lock this serializer
082: _inUse = true;
083: }
084:
085: // Check argument
086: MandatoryArgumentChecker.check("element", element);
087:
088: // Create an XMLOutputter
089: Writer fsw = new StringWriter(512);
090: XMLOutputter out;
091: final String ENCODING = "UTF-8";
092: try {
093: out = new XMLOutputter(fsw, ENCODING);
094: } catch (UnsupportedEncodingException uee) {
095: String message = "Expected XMLOutputter to support encoding \""
096: + ENCODING + "\".";
097: throw Utils.logProgrammingError(message, uee);
098: }
099:
100: // XXX: Allow output of declaration to be configured?
101:
102: // Output the XML that represents the Element
103: try {
104: output(out, element);
105:
106: // I/O errors should not happen on a StringWriter
107: } catch (IOException exception) {
108: throw Utils.logProgrammingError(exception);
109:
110: } finally {
111: _inUse = false;
112: }
113:
114: String xml = fsw.toString();
115:
116: return xml;
117: }
118:
119: /**
120: * Generates XML for the specified <code>Element</code>.
121: *
122: * @param out
123: * the {@link XMLOutputter} to use, cannot be <code>null</code>.
124: *
125: * @param element
126: * the {@link Element} object to convert to XML, cannot be
127: * <code>null</code>.
128: *
129: * @throws NullPointerException
130: * if <code>out == null || element == null</code>.
131: *
132: * @throws IOException
133: * if there is an I/O error.
134: */
135: public void output(XMLOutputter out, Element element)
136: throws NullPointerException, IOException {
137:
138: String namespacePrefix = element.getNamespacePrefix();
139: String namespaceURI = element.getNamespaceURI();
140: String localName = element.getLocalName();
141: Map namespaces = new HashMap();
142:
143: // Write an element with namespace
144: if (namespacePrefix != null) {
145: out.startTag(namespacePrefix + ':' + localName);
146:
147: // Write an element without namespace
148: } else {
149: out.startTag(localName);
150: }
151:
152: if (namespaceURI != null) {
153:
154: // Associate the namespace with the prefix in the result XML
155: if (namespacePrefix == null) {
156: out.attribute("xmlns", namespaceURI);
157: namespaces.put("", namespaceURI);
158: } else {
159: out.attribute("xmlns:" + namespacePrefix, namespaceURI);
160: namespaces.put(namespacePrefix, namespaceURI);
161: }
162: }
163:
164: // Loop through all attributes
165: Map attributes = element.getAttributeMap();
166: Iterator entries = attributes.entrySet().iterator();
167: while (entries.hasNext()) {
168:
169: // Get the next Map.Entry from the iterator
170: Map.Entry entry = (Map.Entry) entries.next();
171:
172: // Get the namespace, local name and value
173: Element.QualifiedName qn = (Element.QualifiedName) entry
174: .getKey();
175: String attrNamespaceURI = qn.getNamespaceURI();
176: String attrLocalName = qn.getLocalName();
177: String attrNamespacePrefix = qn.getNamespacePrefix();
178: String attrValue = (String) entry.getValue();
179:
180: // Do not write the attribute if no value or it is the namespace URI.
181: if (attrValue != null
182: && (!"xmlns".equals(attrNamespacePrefix) || !attrLocalName
183: .equals(namespacePrefix))) {
184:
185: // Write the attribute with prefix
186: if (attrNamespacePrefix != null) {
187: out.attribute(attrNamespacePrefix + ':'
188: + attrLocalName, attrValue);
189:
190: // Write an attribute without prefix
191: } else {
192: out.attribute(attrLocalName, attrValue);
193: }
194:
195: // Write the attribute namespace
196: if (attrNamespaceURI != null) {
197:
198: // Associate the namespace with the prefix in the result XML
199: if (attrNamespacePrefix == null
200: && !namespaces.containsKey("")) {
201: out.attribute("xmlns", attrNamespaceURI);
202: namespaces.put("", namespaceURI);
203: } else if (!namespaces
204: .containsKey(attrNamespacePrefix)) {
205: out.attribute("xmlns:" + attrNamespacePrefix,
206: attrNamespaceURI);
207: namespaces.put(attrNamespacePrefix,
208: namespaceURI);
209: }
210: }
211: }
212: }
213:
214: // Process all contained elements
215: List content = element.getChildElements();
216: int count = content.size();
217: for (int i = 0; i < count; i++) {
218: Object o = content.get(i);
219: output(out, (Element) o);
220: }
221:
222: // Output contained PCDATA
223: if (element.getText() != null) {
224: out.pcdata(element.getText());
225: }
226:
227: // End the tag
228: out.endTag();
229: }
230: }
|