1 /*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5 /*
6 * Copyright 2001-2004 The Apache Software Foundation.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20 /*
21 * $Id: ToXMLSAXHandler.java,v 1.3 2005/09/28 13:49:08 pvedula Exp $
22 */
23 package com.sun.org.apache.xml.internal.serializer;
24
25 import java.io.IOException;
26 import java.io.OutputStream;
27 import java.io.Writer;
28 import java.util.Properties;
29
30 import javax.xml.transform.Result;
31
32 import org.w3c.dom.Node;
33 import org.xml.sax.Attributes;
34 import org.xml.sax.ContentHandler;
35 import org.xml.sax.Locator;
36 import org.xml.sax.SAXException;
37 import org.xml.sax.ext.LexicalHandler;
38
39 /**
40 * This class receives notification of SAX-like events, and with gathered
41 * information over these calls it will invoke the equivalent SAX methods
42 * on a handler, the ultimate xsl:output method is known to be "xml".
43 *
44 * This class is not a public API, it is only public because it is used by Xalan.
45 * @xsl.usage internal
46 */
47 public final class ToXMLSAXHandler extends ToSAXHandler
48 {
49
50 /**
51 * Keeps track of whether output escaping is currently enabled
52 */
53 protected boolean m_escapeSetting = false;
54
55 public ToXMLSAXHandler()
56 {
57 // default constructor (need to set content handler ASAP !)
58 m_prefixMap = new NamespaceMappings();
59 initCDATA();
60 }
61
62 /**
63 * @see Serializer#getOutputFormat()
64 */
65 public Properties getOutputFormat()
66 {
67 return null;
68 }
69
70 /**
71 * @see Serializer#getOutputStream()
72 */
73 public OutputStream getOutputStream()
74 {
75 return null;
76 }
77
78 /**
79 * @see Serializer#getWriter()
80 */
81 public Writer getWriter()
82 {
83 return null;
84 }
85
86 /**
87 * Do nothing for SAX.
88 */
89 public void indent(int n) throws SAXException
90 {
91 }
92
93
94 /**
95 * @see DOMSerializer#serialize(Node)
96 */
97 public void serialize(Node node) throws IOException
98 {
99 }
100
101 /**
102 * @see SerializationHandler#setEscaping(boolean)
103 */
104 public boolean setEscaping(boolean escape) throws SAXException
105 {
106 boolean oldEscapeSetting = m_escapeSetting;
107 m_escapeSetting = escape;
108
109 if (escape) {
110 processingInstruction(Result.PI_ENABLE_OUTPUT_ESCAPING, "");
111 } else {
112 processingInstruction(Result.PI_DISABLE_OUTPUT_ESCAPING, "");
113 }
114
115 return oldEscapeSetting;
116 }
117
118 /**
119 * @see Serializer#setOutputFormat(Properties)
120 */
121 public void setOutputFormat(Properties format)
122 {
123 }
124
125 /**
126 * @see Serializer#setOutputStream(OutputStream)
127 */
128 public void setOutputStream(OutputStream output)
129 {
130 }
131
132 /**
133 * @see Serializer#setWriter(Writer)
134 */
135 public void setWriter(Writer writer)
136 {
137 }
138
139 /**
140 * @see org.xml.sax.ext.DeclHandler#attributeDecl(String, String, String, String, String)
141 */
142 public void attributeDecl(
143 String arg0,
144 String arg1,
145 String arg2,
146 String arg3,
147 String arg4)
148 throws SAXException
149 {
150 }
151
152 /**
153 * @see org.xml.sax.ext.DeclHandler#elementDecl(String, String)
154 */
155 public void elementDecl(String arg0, String arg1) throws SAXException
156 {
157 }
158
159 /**
160 * @see org.xml.sax.ext.DeclHandler#externalEntityDecl(String, String, String)
161 */
162 public void externalEntityDecl(String arg0, String arg1, String arg2)
163 throws SAXException
164 {
165 }
166
167 /**
168 * @see org.xml.sax.ext.DeclHandler#internalEntityDecl(String, String)
169 */
170 public void internalEntityDecl(String arg0, String arg1)
171 throws SAXException
172 {
173 }
174
175 /**
176 * Receives notification of the end of the document.
177 * @see org.xml.sax.ContentHandler#endDocument()
178 */
179 public void endDocument() throws SAXException
180 {
181
182 flushPending();
183
184 // Close output document
185 m_saxHandler.endDocument();
186
187 if (m_tracer != null)
188 super.fireEndDoc();
189 }
190
191 /**
192 * This method is called when all the data needed for a call to the
193 * SAX handler's startElement() method has been gathered.
194 */
195 protected void closeStartTag() throws SAXException
196 {
197
198 m_elemContext.m_startTagOpen = false;
199
200 final String localName = getLocalName(m_elemContext.m_elementName);
201 final String uri = getNamespaceURI(m_elemContext.m_elementName, true);
202
203 // Now is time to send the startElement event
204 if (m_needToCallStartDocument)
205 {
206 startDocumentInternal();
207 }
208 m_saxHandler.startElement(uri, localName, m_elemContext.m_elementName, m_attributes);
209 // we've sent the official SAX attributes on their way,
210 // now we don't need them anymore.
211 m_attributes.clear();
212
213 if(m_state != null)
214 m_state.setCurrentNode(null);
215 }
216
217 /**
218 * Closes ane open cdata tag, and
219 * unlike the this.endCDATA() method (from the LexicalHandler) interface,
220 * this "internal" method will send the endCDATA() call to the wrapped
221 * handler.
222 *
223 */
224 public void closeCDATA() throws SAXException
225 {
226
227 // Output closing bracket - "]]>"
228 if (m_lexHandler != null && m_cdataTagOpen) {
229 m_lexHandler.endCDATA();
230 }
231
232
233 // There are no longer any calls made to
234 // m_lexHandler.startCDATA() without a balancing call to
235 // m_lexHandler.endCDATA()
236 // so we set m_cdataTagOpen to false to remember this.
237 m_cdataTagOpen = false;
238 }
239
240 /**
241 * @see org.xml.sax.ContentHandler#endElement(String, String, String)
242 */
243 public void endElement(String namespaceURI, String localName, String qName)
244 throws SAXException
245 {
246 // Close any open elements etc.
247 flushPending();
248
249 if (namespaceURI == null)
250 {
251 if (m_elemContext.m_elementURI != null)
252 namespaceURI = m_elemContext.m_elementURI;
253 else
254 namespaceURI = getNamespaceURI(qName, true);
255 }
256
257 if (localName == null)
258 {
259 if (m_elemContext.m_elementLocalName != null)
260 localName = m_elemContext.m_elementLocalName;
261 else
262 localName = getLocalName(qName);
263 }
264
265 m_saxHandler.endElement(namespaceURI, localName, qName);
266
267 if (m_tracer != null)
268 super.fireEndElem(qName);
269
270 /* Pop all namespaces at the current element depth.
271 * We are not waiting for official endPrefixMapping() calls.
272 */
273 m_prefixMap.popNamespaces(m_elemContext.m_currentElemDepth,
274 m_saxHandler);
275 m_elemContext = m_elemContext.m_prev;
276 }
277
278 /**
279 * @see org.xml.sax.ContentHandler#endPrefixMapping(String)
280 */
281 public void endPrefixMapping(String prefix) throws SAXException
282 {
283 /* poping all prefix mappings should have been done
284 * in endElement() already
285 */
286 return;
287 }
288
289 /**
290 * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
291 */
292 public void ignorableWhitespace(char[] arg0, int arg1, int arg2)
293 throws SAXException
294 {
295 m_saxHandler.ignorableWhitespace(arg0,arg1,arg2);
296 }
297
298 /**
299 * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator)
300 */
301 public void setDocumentLocator(Locator arg0)
302 {
303 super.setDocumentLocator(arg0);
304 m_saxHandler.setDocumentLocator(arg0);
305 }
306
307 /**
308 * @see org.xml.sax.ContentHandler#skippedEntity(String)
309 */
310 public void skippedEntity(String arg0) throws SAXException
311 {
312 m_saxHandler.skippedEntity(arg0);
313 }
314
315 /**
316 * @see org.xml.sax.ContentHandler#startPrefixMapping(String, String)
317 * @param prefix The prefix that maps to the URI
318 * @param uri The URI for the namespace
319 */
320 public void startPrefixMapping(String prefix, String uri)
321 throws SAXException
322 {
323 startPrefixMapping(prefix, uri, true);
324 }
325
326 /**
327 * Remember the prefix/uri mapping at the current nested element depth.
328 *
329 * @see org.xml.sax.ContentHandler#startPrefixMapping(String, String)
330 * @param prefix The prefix that maps to the URI
331 * @param uri The URI for the namespace
332 * @param shouldFlush a flag indicating if the mapping applies to the
333 * current element or an up coming child (not used).
334 */
335
336 public boolean startPrefixMapping(
337 String prefix,
338 String uri,
339 boolean shouldFlush)
340 throws org.xml.sax.SAXException
341 {
342
343 /* Remember the mapping, and at what depth it was declared
344 * This is one greater than the current depth because these
345 * mappings will apply to the next depth. This is in
346 * consideration that startElement() will soon be called
347 */
348
349 boolean pushed;
350 int pushDepth;
351 if (shouldFlush)
352 {
353 flushPending();
354 // the prefix mapping applies to the child element (one deeper)
355 pushDepth = m_elemContext.m_currentElemDepth + 1;
356 }
357 else
358 {
359 // the prefix mapping applies to the current element
360 pushDepth = m_elemContext.m_currentElemDepth;
361 }
362 pushed = m_prefixMap.pushNamespace(prefix, uri, pushDepth);
363
364 if (pushed)
365 {
366 m_saxHandler.startPrefixMapping(prefix,uri);
367
368 if (getShouldOutputNSAttr())
369 {
370
371 /* Brian M.: don't know if we really needto do this. The
372 * callers of this object should have injected both
373 * startPrefixMapping and the attributes. We are
374 * just covering our butt here.
375 */
376 String name;
377 if (EMPTYSTRING.equals(prefix))
378 {
379 name = "xmlns";
380 addAttributeAlways(XMLNS_URI, name, name,"CDATA",uri, false);
381 }
382 else
383 {
384 if (!EMPTYSTRING.equals(uri)) // hack for XSLTC attribset16 test
385 { // that maps ns1 prefix to "" URI
386 name = "xmlns:" + prefix;
387
388 /* for something like xmlns:abc="w3.pretend.org"
389 * the uri is the value, that is why we pass it in the
390 * value, or 5th slot of addAttributeAlways()
391 */
392 addAttributeAlways(XMLNS_URI, prefix, name,"CDATA",uri, false );
393 }
394 }
395 }
396 }
397 return pushed;
398 }
399
400
401 /**
402 * @see org.xml.sax.ext.LexicalHandler#comment(char[], int, int)
403 */
404 public void comment(char[] arg0, int arg1, int arg2) throws SAXException
405 {
406 flushPending();
407 if (m_lexHandler != null)
408 m_lexHandler.comment(arg0, arg1, arg2);
409
410 if (m_tracer != null)
411 super.fireCommentEvent(arg0, arg1, arg2);
412 }
413
414 /**
415 * @see org.xml.sax.ext.LexicalHandler#endCDATA()
416 */
417 public void endCDATA() throws SAXException
418 {
419 /* Normally we would do somthing with this but we ignore it.
420 * The neccessary call to m_lexHandler.endCDATA() will be made
421 * in flushPending().
422 *
423 * This is so that if we get calls like these:
424 * this.startCDATA();
425 * this.characters(chars1, off1, len1);
426 * this.endCDATA();
427 * this.startCDATA();
428 * this.characters(chars2, off2, len2);
429 * this.endCDATA();
430 *
431 * that we will only make these calls to the wrapped handlers:
432 *
433 * m_lexHandler.startCDATA();
434 * m_saxHandler.characters(chars1, off1, len1);
435 * m_saxHandler.characters(chars1, off2, len2);
436 * m_lexHandler.endCDATA();
437 *
438 * We will merge adjacent CDATA blocks.
439 */
440 }
441
442 /**
443 * @see org.xml.sax.ext.LexicalHandler#endDTD()
444 */
445 public void endDTD() throws SAXException
446 {
447 if (m_lexHandler != null)
448 m_lexHandler.endDTD();
449 }
450
451 /**
452 * @see org.xml.sax.ext.LexicalHandler#startEntity(String)
453 */
454 public void startEntity(String arg0) throws SAXException
455 {
456 if (m_lexHandler != null)
457 m_lexHandler.startEntity(arg0);
458 }
459
460 /**
461 * @see ExtendedContentHandler#characters(String)
462 */
463 public void characters(String chars) throws SAXException
464 {
465 final int length = chars.length();
466 if (length > m_charsBuff.length)
467 {
468 m_charsBuff = new char[length*2 + 1];
469 }
470 chars.getChars(0, length, m_charsBuff, 0);
471 this.characters(m_charsBuff, 0, length);
472 }
473
474 /////////////////// from XSLTC //////////////
475 public ToXMLSAXHandler(ContentHandler handler, String encoding)
476 {
477 super(handler, encoding);
478
479 initCDATA();
480 // initNamespaces();
481 m_prefixMap = new NamespaceMappings();
482 }
483
484 public ToXMLSAXHandler(
485 ContentHandler handler,
486 LexicalHandler lex,
487 String encoding)
488 {
489 super(handler, lex, encoding);
490
491 initCDATA();
492 // initNamespaces();
493 m_prefixMap = new NamespaceMappings();
494 }
495
496 /**
497 * Start an element in the output document. This might be an XML element
498 * (<elem>data</elem> type) or a CDATA section.
499 */
500 public void startElement(
501 String elementNamespaceURI,
502 String elementLocalName,
503 String elementName) throws SAXException
504 {
505 startElement(
506 elementNamespaceURI,elementLocalName,elementName, null);
507
508
509 }
510 public void startElement(String elementName) throws SAXException
511 {
512 startElement(null, null, elementName, null);
513 }
514
515
516 public void characters(char[] ch, int off, int len) throws SAXException
517 {
518 // We do the first two things in flushPending() but we don't
519 // close any open CDATA calls.
520 if (m_needToCallStartDocument)
521 {
522 startDocumentInternal();
523 m_needToCallStartDocument = false;
524 }
525
526 if (m_elemContext.m_startTagOpen)
527 {
528 closeStartTag();
529 m_elemContext.m_startTagOpen = false;
530 }
531
532 if (m_elemContext.m_isCdataSection && !m_cdataTagOpen
533 && m_lexHandler != null)
534 {
535 m_lexHandler.startCDATA();
536 // We have made a call to m_lexHandler.startCDATA() with
537 // no balancing call to m_lexHandler.endCDATA()
538 // so we set m_cdataTagOpen true to remember this.
539 m_cdataTagOpen = true;
540 }
541
542 /* If there are any occurances of "]]>" in the character data
543 * let m_saxHandler worry about it, we've already warned them with
544 * the previous call of m_lexHandler.startCDATA();
545 */
546 m_saxHandler.characters(ch, off, len);
547
548 // time to generate characters event
549 if (m_tracer != null)
550 fireCharEvent(ch, off, len);
551 }
552
553
554 /**
555 * @see ExtendedContentHandler#endElement(String)
556 */
557 public void endElement(String elemName) throws SAXException
558 {
559 endElement(null, null, elemName);
560 }
561
562
563 /**
564 * Send a namespace declaration in the output document. The namespace
565 * declaration will not be include if the namespace is already in scope
566 * with the same prefix.
567 */
568 public void namespaceAfterStartElement(
569 final String prefix,
570 final String uri)
571 throws SAXException
572 {
573 startPrefixMapping(prefix,uri,false);
574 }
575
576 /**
577 *
578 * @see org.xml.sax.ContentHandler#processingInstruction(String, String)
579 * Send a processing instruction to the output document
580 */
581 public void processingInstruction(String target, String data)
582 throws SAXException
583 {
584 flushPending();
585
586 // Pass the processing instruction to the SAX handler
587 m_saxHandler.processingInstruction(target, data);
588
589 // we don't want to leave serializer to fire off this event,
590 // so do it here.
591 if (m_tracer != null)
592 super.fireEscapingEvent(target, data);
593 }
594
595 /**
596 * Undeclare the namespace that is currently pointed to by a given
597 * prefix. Inform SAX handler if prefix was previously mapped.
598 */
599 protected boolean popNamespace(String prefix)
600 {
601 try
602 {
603 if (m_prefixMap.popNamespace(prefix))
604 {
605 m_saxHandler.endPrefixMapping(prefix);
606 return true;
607 }
608 }
609 catch (SAXException e)
610 {
611 // falls through
612 }
613 return false;
614 }
615
616 public void startCDATA() throws SAXException
617 {
618 /* m_cdataTagOpen can only be true here if we have ignored the
619 * previous call to this.endCDATA() and the previous call
620 * this.startCDATA() before that is still "open". In this way
621 * we merge adjacent CDATA. If anything else happened after the
622 * ignored call to this.endCDATA() and this call then a call to
623 * flushPending() would have been made which would have
624 * closed the CDATA and set m_cdataTagOpen to false.
625 */
626 if (!m_cdataTagOpen )
627 {
628 flushPending();
629 if (m_lexHandler != null) {
630 m_lexHandler.startCDATA();
631
632 // We have made a call to m_lexHandler.startCDATA() with
633 // no balancing call to m_lexHandler.endCDATA()
634 // so we set m_cdataTagOpen true to remember this.
635 m_cdataTagOpen = true;
636 }
637 }
638 }
639
640 /**
641 * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes)
642 */
643 public void startElement(
644 String namespaceURI,
645 String localName,
646 String name,
647 Attributes atts)
648 throws SAXException
649 {
650 flushPending();
651 super.startElement(namespaceURI, localName, name, atts);
652
653 // Handle document type declaration (for first element only)
654 if (m_needToOutputDocTypeDecl)
655 {
656 String doctypeSystem = getDoctypeSystem();
657 if (doctypeSystem != null && m_lexHandler != null)
658 {
659 String doctypePublic = getDoctypePublic();
660 if (doctypeSystem != null)
661 m_lexHandler.startDTD(
662 name,
663 doctypePublic,
664 doctypeSystem);
665 }
666 m_needToOutputDocTypeDecl = false;
667 }
668 m_elemContext = m_elemContext.push(namespaceURI, localName, name);
669
670 // ensurePrefixIsDeclared depends on the current depth, so
671 // the previous increment is necessary where it is.
672 if (namespaceURI != null)
673 ensurePrefixIsDeclared(namespaceURI, name);
674
675 // add the attributes to the collected ones
676 if (atts != null)
677 addAttributes(atts);
678
679
680 // do we really need this CDATA section state?
681 m_elemContext.m_isCdataSection = isCdataSection();
682
683 }
684
685 private void ensurePrefixIsDeclared(String ns, String rawName)
686 throws org.xml.sax.SAXException
687 {
688
689 if (ns != null && ns.length() > 0)
690 {
691 int index;
692 final boolean no_prefix = ((index = rawName.indexOf(":")) < 0);
693 String prefix = (no_prefix) ? "" : rawName.substring(0, index);
694
695
696 if (null != prefix)
697 {
698 String foundURI = m_prefixMap.lookupNamespace(prefix);
699
700 if ((null == foundURI) || !foundURI.equals(ns))
701 {
702 this.startPrefixMapping(prefix, ns, false);
703
704 if (getShouldOutputNSAttr()) {
705 // Bugzilla1133: Generate attribute as well as namespace event.
706 // SAX does expect both.
707 this.addAttributeAlways(
708 "http://www.w3.org/2000/xmlns/",
709 no_prefix ? "xmlns" : prefix, // local name
710 no_prefix ? "xmlns" : ("xmlns:"+ prefix), // qname
711 "CDATA",
712 ns,
713 false);
714 }
715 }
716
717 }
718 }
719 }
720 /**
721 * Adds the given attribute to the set of attributes, and also makes sure
722 * that the needed prefix/uri mapping is declared, but only if there is a
723 * currently open element.
724 *
725 * @param uri the URI of the attribute
726 * @param localName the local name of the attribute
727 * @param rawName the qualified name of the attribute
728 * @param type the type of the attribute (probably CDATA)
729 * @param value the value of the attribute
730 * @param XSLAttribute true if this attribute is coming from an xsl:attribute element
731 * @see ExtendedContentHandler#addAttribute(String, String, String, String, String)
732 */
733 public void addAttribute(
734 String uri,
735 String localName,
736 String rawName,
737 String type,
738 String value,
739 boolean XSLAttribute)
740 throws SAXException
741 {
742 if (m_elemContext.m_startTagOpen)
743 {
744 ensurePrefixIsDeclared(uri, rawName);
745 addAttributeAlways(uri, localName, rawName, type, value, false);
746 }
747
748 }
749
750 /**
751 * Try's to reset the super class and reset this class for
752 * re-use, so that you don't need to create a new serializer
753 * (mostly for performance reasons).
754 *
755 * @return true if the class was successfuly reset.
756 * @see Serializer#reset()
757 */
758 public boolean reset()
759 {
760 boolean wasReset = false;
761 if (super.reset())
762 {
763 resetToXMLSAXHandler();
764 wasReset = true;
765 }
766 return wasReset;
767 }
768
769 /**
770 * Reset all of the fields owned by ToXMLSAXHandler class
771 *
772 */
773 private void resetToXMLSAXHandler()
774 {
775 this.m_escapeSetting = false;
776 }
777
778 }