001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: DOM2TO.java 468653 2006-10-28 07:07:05Z minchau $
020 */
021
022 package org.apache.xalan.xsltc.trax;
023
024 import java.io.IOException;
025
026 import org.w3c.dom.NamedNodeMap;
027 import org.w3c.dom.Node;
028 import org.apache.xml.serializer.SerializationHandler;
029 import org.xml.sax.ContentHandler;
030 import org.xml.sax.DTDHandler;
031 import org.xml.sax.EntityResolver;
032 import org.xml.sax.ErrorHandler;
033 import org.xml.sax.InputSource;
034 import org.xml.sax.Locator;
035 import org.xml.sax.SAXException;
036 import org.xml.sax.SAXNotRecognizedException;
037 import org.xml.sax.SAXNotSupportedException;
038 import org.xml.sax.XMLReader;
039 import org.apache.xml.serializer.NamespaceMappings;
040
041 /**
042 * @author Santiago Pericas-Geertsen
043 */
044 public class DOM2TO implements XMLReader, Locator {
045
046 private final static String EMPTYSTRING = "";
047 private static final String XMLNS_PREFIX = "xmlns";
048
049 /**
050 * A reference to the DOM to be traversed.
051 */
052 private Node _dom;
053
054 /**
055 * A reference to the output handler receiving the events.
056 */
057 private SerializationHandler _handler;
058
059 public DOM2TO(Node root, SerializationHandler handler) {
060 _dom = root;
061 _handler = handler;
062 }
063
064 public ContentHandler getContentHandler() {
065 return null;
066 }
067
068 public void setContentHandler(ContentHandler handler) {
069 // Empty
070 }
071
072 public void parse(InputSource unused) throws IOException, SAXException {
073 parse(_dom);
074 }
075
076 public void parse() throws IOException, SAXException {
077 if (_dom != null) {
078 boolean isIncomplete =
079 (_dom.getNodeType() != org.w3c.dom.Node.DOCUMENT_NODE);
080
081 if (isIncomplete) {
082 _handler.startDocument();
083 parse(_dom);
084 _handler.endDocument();
085 }
086 else {
087 parse(_dom);
088 }
089 }
090 }
091
092 /**
093 * Traverse the DOM and generate TO events for a handler. Notice that
094 * we need to handle implicit namespace declarations too.
095 */
096 private void parse(Node node)
097 throws IOException, SAXException
098 {
099 if (node == null) return;
100
101 switch (node.getNodeType()) {
102 case Node.ATTRIBUTE_NODE: // handled by ELEMENT_NODE
103 case Node.DOCUMENT_TYPE_NODE :
104 case Node.ENTITY_NODE :
105 case Node.ENTITY_REFERENCE_NODE:
106 case Node.NOTATION_NODE :
107 // These node types are ignored!!!
108 break;
109 case Node.CDATA_SECTION_NODE:
110 _handler.startCDATA();
111 _handler.characters(node.getNodeValue());
112 _handler.endCDATA();
113 break;
114
115 case Node.COMMENT_NODE: // should be handled!!!
116 _handler.comment(node.getNodeValue());
117 break;
118
119 case Node.DOCUMENT_NODE:
120 _handler.startDocument();
121 Node next = node.getFirstChild();
122 while (next != null) {
123 parse(next);
124 next = next.getNextSibling();
125 }
126 _handler.endDocument();
127 break;
128
129 case Node.DOCUMENT_FRAGMENT_NODE:
130 next = node.getFirstChild();
131 while (next != null) {
132 parse(next);
133 next = next.getNextSibling();
134 }
135 break;
136
137 case Node.ELEMENT_NODE:
138 // Generate SAX event to start element
139 final String qname = node.getNodeName();
140 _handler.startElement(null, null, qname);
141
142 int colon;
143 String prefix;
144 final NamedNodeMap map = node.getAttributes();
145 final int length = map.getLength();
146
147 // Process all namespace attributes first
148 for (int i = 0; i < length; i++) {
149 final Node attr = map.item(i);
150 final String qnameAttr = attr.getNodeName();
151
152 // Is this a namespace declaration?
153 if (qnameAttr.startsWith(XMLNS_PREFIX)) {
154 final String uriAttr = attr.getNodeValue();
155 colon = qnameAttr.lastIndexOf(':');
156 prefix = (colon > 0) ? qnameAttr.substring(colon + 1)
157 : EMPTYSTRING;
158 _handler.namespaceAfterStartElement(prefix, uriAttr);
159 }
160 }
161
162 // Process all non-namespace attributes next
163 NamespaceMappings nm = new NamespaceMappings();
164 for (int i = 0; i < length; i++) {
165 final Node attr = map.item(i);
166 final String qnameAttr = attr.getNodeName();
167
168 // Is this a regular attribute?
169 if (!qnameAttr.startsWith(XMLNS_PREFIX)) {
170 final String uriAttr = attr.getNamespaceURI();
171 // Uri may be implicitly declared
172 if (uriAttr != null && !uriAttr.equals(EMPTYSTRING) ) {
173 colon = qnameAttr.lastIndexOf(':');
174
175 // Fix for bug 26319
176 // For attributes not given an prefix explictly
177 // but having a namespace uri we need
178 // to explicitly generate the prefix
179 String newPrefix = nm.lookupPrefix(uriAttr);
180 if (newPrefix == null)
181 newPrefix = nm.generateNextPrefix();
182 prefix = (colon > 0) ? qnameAttr.substring(0, colon)
183 : newPrefix;
184 _handler.namespaceAfterStartElement(prefix, uriAttr);
185 _handler.addAttribute((prefix + ":" + qnameAttr),
186 attr.getNodeValue());
187 } else {
188 _handler.addAttribute(qnameAttr, attr.getNodeValue());
189 }
190 }
191 }
192
193 // Now element namespace and children
194 final String uri = node.getNamespaceURI();
195 final String localName = node.getLocalName();
196
197 // Uri may be implicitly declared
198 if (uri != null) {
199 colon = qname.lastIndexOf(':');
200 prefix = (colon > 0) ? qname.substring(0, colon) : EMPTYSTRING;
201 _handler.namespaceAfterStartElement(prefix, uri);
202 }else {
203 // Fix for bug 26319
204 // If an element foo is created using
205 // createElementNS(null,locName)
206 // then the element should be serialized
207 // <foo xmlns=" "/>
208 if (uri == null && localName != null) {
209 prefix = EMPTYSTRING;
210 _handler.namespaceAfterStartElement(prefix, EMPTYSTRING);
211 }
212 }
213
214 // Traverse all child nodes of the element (if any)
215 next = node.getFirstChild();
216 while (next != null) {
217 parse(next);
218 next = next.getNextSibling();
219 }
220
221 // Generate SAX event to close element
222 _handler.endElement(qname);
223 break;
224
225 case Node.PROCESSING_INSTRUCTION_NODE:
226 _handler.processingInstruction(node.getNodeName(),
227 node.getNodeValue());
228 break;
229
230 case Node.TEXT_NODE:
231 _handler.characters(node.getNodeValue());
232 break;
233 }
234 }
235
236 /**
237 * This class is only used internally so this method should never
238 * be called.
239 */
240 public DTDHandler getDTDHandler() {
241 return null;
242 }
243
244 /**
245 * This class is only used internally so this method should never
246 * be called.
247 */
248 public ErrorHandler getErrorHandler() {
249 return null;
250 }
251
252 /**
253 * This class is only used internally so this method should never
254 * be called.
255 */
256 public boolean getFeature(String name) throws SAXNotRecognizedException,
257 SAXNotSupportedException
258 {
259 return false;
260 }
261
262 /**
263 * This class is only used internally so this method should never
264 * be called.
265 */
266 public void setFeature(String name, boolean value) throws
267 SAXNotRecognizedException, SAXNotSupportedException
268 {
269 }
270
271 /**
272 * This class is only used internally so this method should never
273 * be called.
274 */
275 public void parse(String sysId) throws IOException, SAXException {
276 throw new IOException("This method is not yet implemented.");
277 }
278
279 /**
280 * This class is only used internally so this method should never
281 * be called.
282 */
283 public void setDTDHandler(DTDHandler handler) throws NullPointerException {
284 }
285
286 /**
287 * This class is only used internally so this method should never
288 * be called.
289 */
290 public void setEntityResolver(EntityResolver resolver) throws
291 NullPointerException
292 {
293 }
294
295 /**
296 * This class is only used internally so this method should never
297 * be called.
298 */
299 public EntityResolver getEntityResolver() {
300 return null;
301 }
302
303 /**
304 * This class is only used internally so this method should never
305 * be called.
306 */
307 public void setErrorHandler(ErrorHandler handler) throws
308 NullPointerException
309 {
310 }
311
312 /**
313 * This class is only used internally so this method should never
314 * be called.
315 */
316 public void setProperty(String name, Object value) throws
317 SAXNotRecognizedException, SAXNotSupportedException {
318 }
319
320 /**
321 * This class is only used internally so this method should never
322 * be called.
323 */
324 public Object getProperty(String name) throws SAXNotRecognizedException,
325 SAXNotSupportedException
326 {
327 return null;
328 }
329
330 /**
331 * This class is only used internally so this method should never
332 * be called.
333 */
334 public int getColumnNumber() {
335 return 0;
336 }
337
338 /**
339 * This class is only used internally so this method should never
340 * be called.
341 */
342 public int getLineNumber() {
343 return 0;
344 }
345
346 /**
347 * This class is only used internally so this method should never
348 * be called.
349 */
350 public String getPublicId() {
351 return null;
352 }
353
354 /**
355 * This class is only used internally so this method should never
356 * be called.
357 */
358 public String getSystemId() {
359 return null;
360 }
361
362 // Debugging
363 private String getNodeTypeFromCode(short code) {
364 String retval = null;
365 switch (code) {
366 case Node.ATTRIBUTE_NODE :
367 retval = "ATTRIBUTE_NODE"; break;
368 case Node.CDATA_SECTION_NODE :
369 retval = "CDATA_SECTION_NODE"; break;
370 case Node.COMMENT_NODE :
371 retval = "COMMENT_NODE"; break;
372 case Node.DOCUMENT_FRAGMENT_NODE :
373 retval = "DOCUMENT_FRAGMENT_NODE"; break;
374 case Node.DOCUMENT_NODE :
375 retval = "DOCUMENT_NODE"; break;
376 case Node.DOCUMENT_TYPE_NODE :
377 retval = "DOCUMENT_TYPE_NODE"; break;
378 case Node.ELEMENT_NODE :
379 retval = "ELEMENT_NODE"; break;
380 case Node.ENTITY_NODE :
381 retval = "ENTITY_NODE"; break;
382 case Node.ENTITY_REFERENCE_NODE :
383 retval = "ENTITY_REFERENCE_NODE"; break;
384 case Node.NOTATION_NODE :
385 retval = "NOTATION_NODE"; break;
386 case Node.PROCESSING_INSTRUCTION_NODE :
387 retval = "PROCESSING_INSTRUCTION_NODE"; break;
388 case Node.TEXT_NODE:
389 retval = "TEXT_NODE"; break;
390 }
391 return retval;
392 }
393 }