001: /*
002: * The contents of this file are subject to the terms
003: * of the Common Development and Distribution License
004: * (the "License"). You may not use this file except
005: * in compliance with the License.
006: *
007: * You can obtain a copy of the license at
008: * https://jwsdp.dev.java.net/CDDLv1.0.html
009: * See the License for the specific language governing
010: * permissions and limitations under the License.
011: *
012: * When distributing Covered Code, include this CDDL
013: * HEADER in each file and include the License file at
014: * https://jwsdp.dev.java.net/CDDLv1.0.html If applicable,
015: * add the following below this CDDL HEADER, with the
016: * fields enclosed by brackets "[]" replaced with your
017: * own identifying information: Portions Copyright [yyyy]
018: * [name of copyright owner]
019: */
020:
021: package com.sun.xml.txw2.output;
022:
023: import org.w3c.dom.Document;
024: import org.w3c.dom.Element;
025: import org.w3c.dom.Node;
026: import org.w3c.dom.Text;
027: import org.xml.sax.Attributes;
028: import org.xml.sax.ContentHandler;
029: import org.xml.sax.Locator;
030: import org.xml.sax.SAXException;
031: import org.xml.sax.ext.LexicalHandler;
032:
033: import javax.xml.parsers.DocumentBuilder;
034: import javax.xml.parsers.DocumentBuilderFactory;
035: import javax.xml.parsers.ParserConfigurationException;
036: import javax.xml.transform.dom.DOMResult;
037: import java.util.ArrayList;
038: import java.util.Stack;
039:
040: import com.sun.xml.txw2.TxwException;
041:
042: /**
043: * {@link XmlSerializer} for {@link javax.xml.transform.dom.DOMResult} and {@link org.w3c.dom.Node}.
044: *
045: * @author Ryan.Shoemaker@Sun.COM
046: */
047: public class DomSerializer implements XmlSerializer {
048:
049: // delegate to SaxSerializer
050: private final SaxSerializer serializer;
051:
052: public DomSerializer(Node node) {
053: Dom2SaxAdapter adapter = new Dom2SaxAdapter(node);
054: serializer = new SaxSerializer(adapter, adapter, false);
055: }
056:
057: public DomSerializer(DOMResult domResult) {
058: Node node = domResult.getNode();
059:
060: if (node == null) {
061: try {
062: DocumentBuilderFactory dbf = DocumentBuilderFactory
063: .newInstance();
064: dbf.setNamespaceAware(true);
065: DocumentBuilder db = dbf.newDocumentBuilder();
066: Document doc = db.newDocument();
067: domResult.setNode(doc);
068: serializer = new SaxSerializer(new Dom2SaxAdapter(doc),
069: null, false);
070: } catch (ParserConfigurationException pce) {
071: throw new TxwException(pce);
072: }
073: } else {
074: serializer = new SaxSerializer(new Dom2SaxAdapter(node),
075: null, false);
076: }
077: }
078:
079: // XmlSerializer api's - delegate to SaxSerializer
080: public void startDocument() {
081: serializer.startDocument();
082: }
083:
084: public void beginStartTag(String uri, String localName,
085: String prefix) {
086: serializer.beginStartTag(uri, localName, prefix);
087: }
088:
089: public void writeAttribute(String uri, String localName,
090: String prefix, StringBuilder value) {
091: serializer.writeAttribute(uri, localName, prefix, value);
092: }
093:
094: public void writeXmlns(String prefix, String uri) {
095: serializer.writeXmlns(prefix, uri);
096: }
097:
098: public void endStartTag(String uri, String localName, String prefix) {
099: serializer.endStartTag(uri, localName, prefix);
100: }
101:
102: public void endTag() {
103: serializer.endTag();
104: }
105:
106: public void text(StringBuilder text) {
107: serializer.text(text);
108: }
109:
110: public void cdata(StringBuilder text) {
111: serializer.cdata(text);
112: }
113:
114: public void comment(StringBuilder comment) {
115: serializer.comment(comment);
116: }
117:
118: public void endDocument() {
119: serializer.endDocument();
120: }
121:
122: public void flush() {
123: // no flushing
124: }
125: }
126:
127: /**
128: * Builds a DOM tree from SAX2 events.
129: *
130: * @author Vivek Pandey
131: */
132: class Dom2SaxAdapter implements ContentHandler, LexicalHandler {
133:
134: private final Node _node;
135: private final Stack _nodeStk = new Stack();
136: private boolean inCDATA;
137:
138: public final Element getCurrentElement() {
139: return (Element) _nodeStk.peek();
140: }
141:
142: /**
143: * Document object that owns the specified node.
144: */
145: private final Document _document;
146:
147: /**
148: * @param node
149: * Nodes will be created and added under this object.
150: */
151: public Dom2SaxAdapter(Node node) {
152: _node = node;
153: _nodeStk.push(_node);
154:
155: if (node instanceof Document)
156: this ._document = (Document) node;
157: else
158: this ._document = node.getOwnerDocument();
159: }
160:
161: /**
162: * Creates a fresh empty DOM document and adds nodes under this document.
163: */
164: public Dom2SaxAdapter() throws ParserConfigurationException {
165: DocumentBuilderFactory factory = DocumentBuilderFactory
166: .newInstance();
167: factory.setNamespaceAware(true);
168: factory.setValidating(false);
169:
170: _document = factory.newDocumentBuilder().newDocument();
171: _node = _document;
172: _nodeStk.push(_document);
173: }
174:
175: public Node getDOM() {
176: return _node;
177: }
178:
179: public void startDocument() {
180: }
181:
182: public void endDocument() {
183: }
184:
185: public void startElement(String namespace, String localName,
186: String qName, Attributes attrs) {
187:
188: // some broken DOM implementatino (we confirmed it with SAXON)
189: // return null from this method.
190: Element element = _document.createElementNS(namespace, qName);
191:
192: if (element == null) {
193: // if so, report an user-friendly error message,
194: // rather than dying mysteriously with NPE.
195: throw new TxwException(
196: "Your DOM provider doesn't support the createElementNS method properly");
197: }
198:
199: // process namespace bindings
200: for (int i = 0; i < unprocessedNamespaces.size(); i += 2) {
201: String prefix = (String) unprocessedNamespaces.get(i + 0);
202: String uri = (String) unprocessedNamespaces.get(i + 1);
203:
204: String qname;
205: if ("".equals(prefix) || prefix == null)
206: qname = "xmlns";
207: else
208: qname = "xmlns:" + prefix;
209:
210: // older version of Xerces (I confirmed that the bug is gone with Xerces 2.4.0)
211: // have a problem of re-setting the same namespace attribute twice.
212: // work around this bug removing it first.
213: if (element.hasAttributeNS("http://www.w3.org/2000/xmlns/",
214: qname)) {
215: // further workaround for an old Crimson bug where the removeAttribtueNS
216: // method throws NPE when the element doesn't have any attribute.
217: // to be on the safe side, check the existence of attributes before
218: // attempting to remove it.
219: // for details about this bug, see org.apache.crimson.tree.ElementNode2
220: // line 540 or the following message:
221: // https://jaxb.dev.java.net/servlets/ReadMsg?list=users&msgNo=2767
222: element.removeAttributeNS(
223: "http://www.w3.org/2000/xmlns/", qname);
224: }
225: // workaround until here
226:
227: element.setAttributeNS("http://www.w3.org/2000/xmlns/",
228: qname, uri);
229: }
230: unprocessedNamespaces.clear();
231:
232: int length = attrs.getLength();
233: for (int i = 0; i < length; i++) {
234: String namespaceuri = attrs.getURI(i);
235: String value = attrs.getValue(i);
236: String qname = attrs.getQName(i);
237: element.setAttributeNS(namespaceuri, qname, value);
238: }
239: // append this new node onto current stack node
240: getParent().appendChild(element);
241: // push this node onto stack
242: _nodeStk.push(element);
243: }
244:
245: private final Node getParent() {
246: return (Node) _nodeStk.peek();
247: }
248:
249: public void endElement(String namespace, String localName,
250: String qName) {
251: _nodeStk.pop();
252: }
253:
254: public void characters(char[] ch, int start, int length) {
255: Node text;
256: if (inCDATA)
257: text = _document.createCDATASection(new String(ch, start,
258: length));
259: else
260: text = _document.createTextNode(new String(ch, start,
261: length));
262: getParent().appendChild(text);
263: }
264:
265: public void comment(char ch[], int start, int length)
266: throws SAXException {
267: getParent().appendChild(
268: _document.createComment(new String(ch, start, length)));
269: }
270:
271: public void ignorableWhitespace(char[] ch, int start, int length) {
272: }
273:
274: public void processingInstruction(String target, String data)
275: throws org.xml.sax.SAXException {
276: Node node = _document.createProcessingInstruction(target, data);
277: getParent().appendChild(node);
278: }
279:
280: public void setDocumentLocator(Locator locator) {
281: }
282:
283: public void skippedEntity(String name) {
284: }
285:
286: private ArrayList unprocessedNamespaces = new ArrayList();
287:
288: public void startPrefixMapping(String prefix, String uri) {
289: unprocessedNamespaces.add(prefix);
290: unprocessedNamespaces.add(uri);
291: }
292:
293: public void endPrefixMapping(String prefix) {
294: }
295:
296: public void startDTD(String name, String publicId, String systemId)
297: throws SAXException {
298: }
299:
300: public void endDTD() throws SAXException {
301: }
302:
303: public void startEntity(String name) throws SAXException {
304: }
305:
306: public void endEntity(String name) throws SAXException {
307: }
308:
309: public void startCDATA() throws SAXException {
310: inCDATA = true;
311: }
312:
313: public void endCDATA() throws SAXException {
314: inCDATA = false;
315: }
316: }
|