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: ToStream.java,v 1.4 2005/11/10 06:43:26 suresh_emailid 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.UnsupportedEncodingException;
28 import java.io.Writer;
29 import java.util.Properties;
30 import java.util.StringTokenizer;
31 import java.util.Vector;
32
33 import javax.xml.transform.ErrorListener;
34 import javax.xml.transform.OutputKeys;
35 import javax.xml.transform.Transformer;
36 import javax.xml.transform.TransformerException;
37
38 import com.sun.org.apache.xml.internal.serializer.utils.MsgKey;
39 import com.sun.org.apache.xml.internal.serializer.utils.Utils;
40 import com.sun.org.apache.xml.internal.serializer.utils.WrappedRuntimeException;
41 import org.w3c.dom.Node;
42 import org.xml.sax.Attributes;
43 import org.xml.sax.ContentHandler;
44 import org.xml.sax.SAXException;
45
46 //import com.sun.media.sound.IESecurity;
47
48 /**
49 * This abstract class is a base class for other stream
50 * serializers (xml, html, text ...) that write output to a stream.
51 *
52 * @xsl.usage internal
53 */
54 abstract public class ToStream extends SerializerBase
55 {
56
57 private static final String COMMENT_BEGIN = "<!--";
58 private static final String COMMENT_END = "-->";
59
60 /** Stack to keep track of disabling output escaping. */
61 protected BoolStack m_disableOutputEscapingStates = new BoolStack();
62
63
64 /**
65 * The encoding information associated with this serializer.
66 * Although initially there is no encoding,
67 * there is a dummy EncodingInfo object that will say
68 * that every character is in the encoding. This is useful
69 * for a serializer that is in temporary output state and has
70 * no associated encoding. A serializer in final output state
71 * will have an encoding, and will worry about whether
72 * single chars or surrogate pairs of high/low chars form
73 * characters in the output encoding.
74 */
75 EncodingInfo m_encodingInfo = new EncodingInfo(null,null);
76
77 /**
78 * Method reference to the sun.io.CharToByteConverter#canConvert method
79 * for this encoding. Invalid if m_charToByteConverter is null.
80 */
81 java.lang.reflect.Method m_canConvertMeth;
82
83
84
85 /**
86 * Boolean that tells if we already tried to get the converter.
87 */
88 boolean m_triedToGetConverter = false;
89
90
91 /**
92 * Opaque reference to the sun.io.CharToByteConverter for this
93 * encoding.
94 */
95 Object m_charToByteConverter = null;
96
97
98 /**
99 * Stack to keep track of whether or not we need to
100 * preserve whitespace.
101 *
102 * Used to push/pop values used for the field m_ispreserve, but
103 * m_ispreserve is only relevant if m_doIndent is true.
104 * If m_doIndent is false this field has no impact.
105 *
106 */
107 protected BoolStack m_preserves = new BoolStack();
108
109 /**
110 * State flag to tell if preservation of whitespace
111 * is important.
112 *
113 * Used only in shouldIndent() but only if m_doIndent is true.
114 * If m_doIndent is false this flag has no impact.
115 *
116 */
117 protected boolean m_ispreserve = false;
118
119 /**
120 * State flag that tells if the previous node processed
121 * was text, so we can tell if we should preserve whitespace.
122 *
123 * Used in endDocument() and shouldIndent() but
124 * only if m_doIndent is true.
125 * If m_doIndent is false this flag has no impact.
126 */
127 protected boolean m_isprevtext = false;
128
129 /**
130 * The maximum character size before we have to resort
131 * to escaping.
132 */
133 protected int m_maxCharacter = Encodings.getLastPrintable();
134
135
136 /**
137 * The system line separator for writing out line breaks.
138 * The default value is from the system property,
139 * but this value can be set through the xsl:output
140 * extension attribute xalan:line-separator.
141 */
142 protected char[] m_lineSep =
143 System.getProperty("line.separator").toCharArray();
144
145 /**
146 * True if the the system line separator is to be used.
147 */
148 protected boolean m_lineSepUse = true;
149
150 /**
151 * The length of the line seperator, since the write is done
152 * one character at a time.
153 */
154 protected int m_lineSepLen = m_lineSep.length;
155
156 /**
157 * Map that tells which characters should have special treatment, and it
158 * provides character to entity name lookup.
159 */
160 protected CharInfo m_charInfo;
161
162 /** True if we control the buffer, and we should flush the output on endDocument. */
163 boolean m_shouldFlush = true;
164
165 /**
166 * Add space before '/>' for XHTML.
167 */
168 protected boolean m_spaceBeforeClose = false;
169
170 /**
171 * Flag to signal that a newline should be added.
172 *
173 * Used only in indent() which is called only if m_doIndent is true.
174 * If m_doIndent is false this flag has no impact.
175 */
176 boolean m_startNewLine;
177
178 /**
179 * Tells if we're in an internal document type subset.
180 */
181 protected boolean m_inDoctype = false;
182
183 /**
184 * Flag to quickly tell if the encoding is UTF8.
185 */
186 boolean m_isUTF8 = false;
187
188 /** The xsl:output properties. */
189 protected Properties m_format;
190
191 /**
192 * remembers if we are in between the startCDATA() and endCDATA() callbacks
193 */
194 protected boolean m_cdataStartCalled = false;
195
196 /**
197 * If this flag is true DTD entity references are not left as-is,
198 * which is exiting older behavior.
199 */
200 private boolean m_expandDTDEntities = true;
201
202
203 /**
204 * Default constructor
205 */
206 public ToStream()
207 {
208 }
209
210 /**
211 * This helper method to writes out "]]>" when closing a CDATA section.
212 *
213 * @throws org.xml.sax.SAXException
214 */
215 protected void closeCDATA() throws org.xml.sax.SAXException
216 {
217 try
218 {
219 m_writer.write(CDATA_DELIMITER_CLOSE);
220 // write out a CDATA section closing "]]>"
221 m_cdataTagOpen = false; // Remember that we have done so.
222 }
223 catch (IOException e)
224 {
225 throw new SAXException(e);
226 }
227 }
228
229 /**
230 * Serializes the DOM node. Throws an exception only if an I/O
231 * exception occured while serializing.
232 *
233 * @param node Node to serialize.
234 * @throws IOException An I/O exception occured while serializing
235 */
236 public void serialize(Node node) throws IOException
237 {
238
239 try
240 {
241 TreeWalker walker =
242 new TreeWalker(this);
243
244 walker.traverse(node);
245 }
246 catch (org.xml.sax.SAXException se)
247 {
248 throw new WrappedRuntimeException(se);
249 }
250 }
251
252 /**
253 * Return true if the character is the high member of a surrogate pair.
254 *
255 * NEEDSDOC @param c
256 *
257 * NEEDSDOC ($objectName$) @return
258 */
259 static final boolean isUTF16Surrogate(char c)
260 {
261 return (c & 0xFC00) == 0xD800;
262 }
263
264 /**
265 * Taken from XSLTC
266 */
267 private boolean m_escaping = true;
268
269 /**
270 * Flush the formatter's result stream.
271 *
272 * @throws org.xml.sax.SAXException
273 */
274 protected final void flushWriter() throws org.xml.sax.SAXException
275 {
276 final java.io.Writer writer = m_writer;
277 if (null != writer)
278 {
279 try
280 {
281 if (writer instanceof WriterToUTF8Buffered)
282 {
283 if (m_shouldFlush)
284 ((WriterToUTF8Buffered) writer).flush();
285 else
286 ((WriterToUTF8Buffered) writer).flushBuffer();
287 }
288 if (writer instanceof WriterToASCI)
289 {
290 if (m_shouldFlush)
291 writer.flush();
292 }
293 else
294 {
295 // Flush always.
296 // Not a great thing if the writer was created
297 // by this class, but don't have a choice.
298 writer.flush();
299 }
300 }
301 catch (IOException ioe)
302 {
303 throw new org.xml.sax.SAXException(ioe);
304 }
305 }
306 }
307
308 /**
309 * Get the output stream where the events will be serialized to.
310 *
311 * @return reference to the result stream, or null of only a writer was
312 * set.
313 */
314 public OutputStream getOutputStream()
315 {
316
317 if (m_writer instanceof WriterToUTF8Buffered)
318 return ((WriterToUTF8Buffered) m_writer).getOutputStream();
319 if (m_writer instanceof WriterToASCI)
320 return ((WriterToASCI) m_writer).getOutputStream();
321 else
322 return null;
323 }
324
325 // Implement DeclHandler
326
327 /**
328 * Report an element type declaration.
329 *
330 * <p>The content model will consist of the string "EMPTY", the
331 * string "ANY", or a parenthesised group, optionally followed
332 * by an occurrence indicator. The model will be normalized so
333 * that all whitespace is removed,and will include the enclosing
334 * parentheses.</p>
335 *
336 * @param name The element type name.
337 * @param model The content model as a normalized string.
338 * @exception SAXException The application may raise an exception.
339 */
340 public void elementDecl(String name, String model) throws SAXException
341 {
342 // Do not inline external DTD
343 if (m_inExternalDTD)
344 return;
345 try
346 {
347 final java.io.Writer writer = m_writer;
348 DTDprolog();
349
350 writer.write("<!ELEMENT ");
351 writer.write(name);
352 writer.write(' ');
353 writer.write(model);
354 writer.write('>');
355 writer.write(m_lineSep, 0, m_lineSepLen);
356 }
357 catch (IOException e)
358 {
359 throw new SAXException(e);
360 }
361
362 }
363
364 /**
365 * Report an internal entity declaration.
366 *
367 * <p>Only the effective (first) declaration for each entity
368 * will be reported.</p>
369 *
370 * @param name The name of the entity. If it is a parameter
371 * entity, the name will begin with '%'.
372 * @param value The replacement text of the entity.
373 * @exception SAXException The application may raise an exception.
374 * @see #externalEntityDecl
375 * @see org.xml.sax.DTDHandler#unparsedEntityDecl
376 */
377 public void internalEntityDecl(String name, String value)
378 throws SAXException
379 {
380 // Do not inline external DTD
381 if (m_inExternalDTD)
382 return;
383 try
384 {
385 DTDprolog();
386 outputEntityDecl(name, value);
387 }
388 catch (IOException e)
389 {
390 throw new SAXException(e);
391 }
392
393 }
394
395 /**
396 * Output the doc type declaration.
397 *
398 * @param name non-null reference to document type name.
399 * NEEDSDOC @param value
400 *
401 * @throws org.xml.sax.SAXException
402 */
403 void outputEntityDecl(String name, String value) throws IOException
404 {
405 final java.io.Writer writer = m_writer;
406 writer.write("<!ENTITY ");
407 writer.write(name);
408 writer.write(" \"");
409 writer.write(value);
410 writer.write("\">");
411 writer.write(m_lineSep, 0, m_lineSepLen);
412 }
413
414 /**
415 * Output a system-dependent line break.
416 *
417 * @throws org.xml.sax.SAXException
418 */
419 protected final void outputLineSep() throws IOException
420 {
421
422 m_writer.write(m_lineSep, 0, m_lineSepLen);
423 }
424
425 /**
426 * Specifies an output format for this serializer. It the
427 * serializer has already been associated with an output format,
428 * it will switch to the new format. This method should not be
429 * called while the serializer is in the process of serializing
430 * a document.
431 *
432 * @param format The output format to use
433 */
434 public void setOutputFormat(Properties format)
435 {
436
437 boolean shouldFlush = m_shouldFlush;
438
439 init(m_writer, format, false, false);
440
441 m_shouldFlush = shouldFlush;
442 }
443
444 /**
445 * Initialize the serializer with the specified writer and output format.
446 * Must be called before calling any of the serialize methods.
447 * This method can be called multiple times and the xsl:output properties
448 * passed in the 'format' parameter are accumulated across calls.
449 *
450 * @param writer The writer to use
451 * @param format The output format
452 * @param shouldFlush True if the writer should be flushed at EndDocument.
453 */
454 private synchronized void init(
455 Writer writer,
456 Properties format,
457 boolean defaultProperties,
458 boolean shouldFlush)
459 {
460
461 m_shouldFlush = shouldFlush;
462
463
464 // if we are tracing events we need to trace what
465 // characters are written to the output writer.
466 if (m_tracer != null
467 && !(writer instanceof SerializerTraceWriter) )
468 m_writer = new SerializerTraceWriter(writer, m_tracer);
469 else
470 m_writer = writer;
471
472
473 m_format = format;
474 // m_cdataSectionNames =
475 // OutputProperties.getQNameProperties(
476 // OutputKeys.CDATA_SECTION_ELEMENTS,
477 // format);
478 setCdataSectionElements(OutputKeys.CDATA_SECTION_ELEMENTS, format);
479
480 setIndentAmount(
481 OutputPropertyUtils.getIntProperty(
482 OutputPropertiesFactory.S_KEY_INDENT_AMOUNT,
483 format));
484 setIndent(
485 OutputPropertyUtils.getBooleanProperty(OutputKeys.INDENT, format));
486
487 {
488 String sep =
489 format.getProperty(OutputPropertiesFactory.S_KEY_LINE_SEPARATOR);
490 if (sep != null) {
491 m_lineSep = sep.toCharArray();
492 m_lineSepLen = sep.length();
493 }
494 }
495
496 boolean shouldNotWriteXMLHeader =
497 OutputPropertyUtils.getBooleanProperty(
498 OutputKeys.OMIT_XML_DECLARATION,
499 format);
500 setOmitXMLDeclaration(shouldNotWriteXMLHeader);
501 setDoctypeSystem(format.getProperty(OutputKeys.DOCTYPE_SYSTEM));
502 String doctypePublic = format.getProperty(OutputKeys.DOCTYPE_PUBLIC);
503 setDoctypePublic(doctypePublic);
504
505 // if standalone was explicitly specified
506 if (format.get(OutputKeys.STANDALONE) != null)
507 {
508 String val = format.getProperty(OutputKeys.STANDALONE);
509 if (defaultProperties)
510 setStandaloneInternal(val);
511 else
512 setStandalone(val);
513 }
514
515 setMediaType(format.getProperty(OutputKeys.MEDIA_TYPE));
516
517 if (null != doctypePublic)
518 {
519 if (doctypePublic.startsWith("-//W3C//DTD XHTML"))
520 m_spaceBeforeClose = true;
521 }
522
523 /*
524 * This code is added for XML 1.1 Version output.
525 */
526 String version = getVersion();
527 if (null == version)
528 {
529 version = format.getProperty(OutputKeys.VERSION);
530 setVersion(version);
531 }
532
533 // initCharsMap();
534 String encoding = getEncoding();
535 if (null == encoding)
536 {
537 encoding =
538 Encodings.getMimeEncoding(
539 format.getProperty(OutputKeys.ENCODING));
540 setEncoding(encoding);
541 }
542
543 m_isUTF8 = encoding.equals(Encodings.DEFAULT_MIME_ENCODING);
544
545 // Access this only from the Hashtable level... we don't want to
546 // get default properties.
547 String entitiesFileName =
548 (String) format.get(OutputPropertiesFactory.S_KEY_ENTITIES);
549
550 if (null != entitiesFileName)
551 {
552
553 String method =
554 (String) format.get(OutputKeys.METHOD);
555
556 m_charInfo = CharInfo.getCharInfo(entitiesFileName, method);
557 }
558
559 }
560
561 /**
562 * Initialize the serializer with the specified writer and output format.
563 * Must be called before calling any of the serialize methods.
564 *
565 * @param writer The writer to use
566 * @param format The output format
567 */
568 private synchronized void init(Writer writer, Properties format)
569 {
570 init(writer, format, false, false);
571 }
572 /**
573 * Initialize the serializer with the specified output stream and output
574 * format. Must be called before calling any of the serialize methods.
575 *
576 * @param output The output stream to use
577 * @param format The output format
578 * @param defaultProperties true if the properties are the default
579 * properties
580 *
581 * @throws UnsupportedEncodingException The encoding specified in the
582 * output format is not supported
583 */
584 protected synchronized void init(
585 OutputStream output,
586 Properties format,
587 boolean defaultProperties)
588 throws UnsupportedEncodingException
589 {
590
591 String encoding = getEncoding();
592 if (encoding == null)
593 {
594 // if not already set then get it from the properties
595 encoding =
596 Encodings.getMimeEncoding(
597 format.getProperty(OutputKeys.ENCODING));
598 setEncoding(encoding);
599 }
600
601 if (encoding.equalsIgnoreCase("UTF-8"))
602 {
603 m_isUTF8 = true;
604 // if (output instanceof java.io.BufferedOutputStream)
605 // {
606 // init(new WriterToUTF8(output), format, defaultProperties, true);
607 // }
608 // else if (output instanceof java.io.FileOutputStream)
609 // {
610 // init(new WriterToUTF8Buffered(output), format, defaultProperties, true);
611 // }
612 // else
613 // {
614 // // Not sure what to do in this case. I'm going to be conservative
615 // // and not buffer.
616 // init(new WriterToUTF8(output), format, defaultProperties, true);
617 // }
618
619
620 init(
621 new WriterToUTF8Buffered(output),
622 format,
623 defaultProperties,
624 true);
625
626
627 }
628 else if (
629 encoding.equals("WINDOWS-1250")
630 || encoding.equals("US-ASCII")
631 || encoding.equals("ASCII"))
632 {
633 init(new WriterToASCI(output), format, defaultProperties, true);
634 }
635 else
636 {
637 Writer osw;
638
639 try
640 {
641 osw = Encodings.getWriter(output, encoding);
642 }
643 catch (UnsupportedEncodingException uee)
644 {
645 System.out.println(
646 "Warning: encoding \""
647 + encoding
648 + "\" not supported"
649 + ", using "
650 + Encodings.DEFAULT_MIME_ENCODING);
651
652 encoding = Encodings.DEFAULT_MIME_ENCODING;
653 setEncoding(encoding);
654 osw = Encodings.getWriter(output, encoding);
655 }
656
657 init(osw, format, defaultProperties, true);
658 }
659
660 }
661
662 /**
663 * Returns the output format for this serializer.
664 *
665 * @return The output format in use
666 */
667 public Properties getOutputFormat()
668 {
669 return m_format;
670 }
671
672 /**
673 * Specifies a writer to which the document should be serialized.
674 * This method should not be called while the serializer is in
675 * the process of serializing a document.
676 *
677 * @param writer The output writer stream
678 */
679 public void setWriter(Writer writer)
680 {
681 // if we are tracing events we need to trace what
682 // characters are written to the output writer.
683 if (m_tracer != null
684 && !(writer instanceof SerializerTraceWriter) )
685 m_writer = new SerializerTraceWriter(writer, m_tracer);
686 else
687 m_writer = writer;
688 }
689
690 /**
691 * Set if the operating systems end-of-line line separator should
692 * be used when serializing. If set false NL character
693 * (decimal 10) is left alone, otherwise the new-line will be replaced on
694 * output with the systems line separator. For example on UNIX this is
695 * NL, while on Windows it is two characters, CR NL, where CR is the
696 * carriage-return (decimal 13).
697 *
698 * @param use_sytem_line_break True if an input NL is replaced with the
699 * operating systems end-of-line separator.
700 * @return The previously set value of the serializer.
701 */
702 public boolean setLineSepUse(boolean use_sytem_line_break)
703 {
704 boolean oldValue = m_lineSepUse;
705 m_lineSepUse = use_sytem_line_break;
706 return oldValue;
707 }
708
709 /**
710 * Specifies an output stream to which the document should be
711 * serialized. This method should not be called while the
712 * serializer is in the process of serializing a document.
713 * <p>
714 * The encoding specified in the output properties is used, or
715 * if no encoding was specified, the default for the selected
716 * output method.
717 *
718 * @param output The output stream
719 */
720 public void setOutputStream(OutputStream output)
721 {
722
723 try
724 {
725 Properties format;
726 if (null == m_format)
727 format =
728 OutputPropertiesFactory.getDefaultMethodProperties(
729 Method.XML);
730 else
731 format = m_format;
732 init(output, format, true);
733 }
734 catch (UnsupportedEncodingException uee)
735 {
736
737 // Should have been warned in init, I guess...
738 }
739 }
740
741 /**
742 * @see SerializationHandler#setEscaping(boolean)
743 */
744 public boolean setEscaping(boolean escape)
745 {
746 final boolean temp = m_escaping;
747 m_escaping = escape;
748 return temp;
749
750 }
751
752
753 /**
754 * Might print a newline character and the indentation amount
755 * of the given depth.
756 *
757 * @param depth the indentation depth (element nesting depth)
758 *
759 * @throws org.xml.sax.SAXException if an error occurs during writing.
760 */
761 protected void indent(int depth) throws IOException
762 {
763
764 if (m_startNewLine)
765 outputLineSep();
766 /* For m_indentAmount > 0 this extra test might be slower
767 * but Xalan's default value is 0, so this extra test
768 * will run faster in that situation.
769 */
770 if (m_indentAmount > 0)
771 printSpace(depth * m_indentAmount);
772
773 }
774
775 /**
776 * Indent at the current element nesting depth.
777 * @throws IOException
778 */
779 protected void indent() throws IOException
780 {
781 indent(m_elemContext.m_currentElemDepth);
782 }
783 /**
784 * Prints <var>n</var> spaces.
785 * @param n Number of spaces to print.
786 *
787 * @throws org.xml.sax.SAXException if an error occurs when writing.
788 */
789 private void printSpace(int n) throws IOException
790 {
791 final java.io.Writer writer = m_writer;
792 for (int i = 0; i < n; i++)
793 {
794 writer.write(' ');
795 }
796
797 }
798
799 /**
800 * Report an attribute type declaration.
801 *
802 * <p>Only the effective (first) declaration for an attribute will
803 * be reported. The type will be one of the strings "CDATA",
804 * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY",
805 * "ENTITIES", or "NOTATION", or a parenthesized token group with
806 * the separator "|" and all whitespace removed.</p>
807 *
808 * @param eName The name of the associated element.
809 * @param aName The name of the attribute.
810 * @param type A string representing the attribute type.
811 * @param valueDefault A string representing the attribute default
812 * ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if
813 * none of these applies.
814 * @param value A string representing the attribute's default value,
815 * or null if there is none.
816 * @exception SAXException The application may raise an exception.
817 */
818 public void attributeDecl(
819 String eName,
820 String aName,
821 String type,
822 String valueDefault,
823 String value)
824 throws SAXException
825 {
826 // Do not inline external DTD
827 if (m_inExternalDTD)
828 return;
829 try
830 {
831 final java.io.Writer writer = m_writer;
832 DTDprolog();
833
834 writer.write("<!ATTLIST ");
835 writer.write(eName);
836 writer.write(' ');
837
838 writer.write(aName);
839 writer.write(' ');
840 writer.write(type);
841 if (valueDefault != null)
842 {
843 writer.write(' ');
844 writer.write(valueDefault);
845 }
846
847 //writer.write(" ");
848 //writer.write(value);
849 writer.write('>');
850 writer.write(m_lineSep, 0, m_lineSepLen);
851 }
852 catch (IOException e)
853 {
854 throw new SAXException(e);
855 }
856 }
857
858 /**
859 * Get the character stream where the events will be serialized to.
860 *
861 * @return Reference to the result Writer, or null.
862 */
863 public Writer getWriter()
864 {
865 return m_writer;
866 }
867
868 /**
869 * Report a parsed external entity declaration.
870 *
871 * <p>Only the effective (first) declaration for each entity
872 * will be reported.</p>
873 *
874 * @param name The name of the entity. If it is a parameter
875 * entity, the name will begin with '%'.
876 * @param publicId The declared public identifier of the entity, or
877 * null if none was declared.
878 * @param systemId The declared system identifier of the entity.
879 * @exception SAXException The application may raise an exception.
880 * @see #internalEntityDecl
881 * @see org.xml.sax.DTDHandler#unparsedEntityDecl
882 */
883 public void externalEntityDecl(
884 String name,
885 String publicId,
886 String systemId)
887 throws SAXException
888 {
889 try {
890 DTDprolog();
891
892 m_writer.write("<!ENTITY ");
893 m_writer.write(name);
894 if (publicId != null) {
895 m_writer.write(" PUBLIC \"");
896 m_writer.write(publicId);
897
898 }
899 else {
900 m_writer.write(" SYSTEM \"");
901 m_writer.write(systemId);
902 }
903 m_writer.write("\" >");
904 m_writer.write(m_lineSep, 0, m_lineSepLen);
905 } catch (IOException e) {
906 // TODO Auto-generated catch block
907 e.printStackTrace();
908 }
909
910 }
911
912 /**
913 * Tell if this character can be written without escaping.
914 */
915 protected boolean escapingNotNeeded(char ch)
916 {
917 final boolean ret;
918 if (ch < 127)
919 {
920 // This is the old/fast code here, but is this
921 // correct for all encodings?
922 if (ch >= 0x20 || (0x0A == ch || 0x0D == ch || 0x09 == ch))
923 ret= true;
924 else
925 ret = false;
926 }
927 else {
928 ret = m_encodingInfo.isInEncoding(ch);
929 }
930 return ret;
931 }
932
933 /**
934 * Once a surrogate has been detected, write out the pair of
935 * characters if it is in the encoding, or if there is no
936 * encoding, otherwise write out an entity reference
937 * of the value of the unicode code point of the character
938 * represented by the high/low surrogate pair.
939 * <p>
940 * An exception is thrown if there is no low surrogate in the pair,
941 * because the array ends unexpectely, or if the low char is there
942 * but its value is such that it is not a low surrogate.
943 *
944 * @param c the first (high) part of the surrogate, which
945 * must be confirmed before calling this method.
946 * @param ch Character array.
947 * @param i position Where the surrogate was detected.
948 * @param end The end index of the significant characters.
949 * @return 0 if the pair of characters was written out as-is,
950 * the unicode code point of the character represented by
951 * the surrogate pair if an entity reference with that value
952 * was written out.
953 *
954 * @throws IOException
955 * @throws org.xml.sax.SAXException if invalid UTF-16 surrogate detected.
956 */
957 protected int writeUTF16Surrogate(char c, char ch[], int i, int end)
958 throws IOException
959 {
960 int codePoint = 0;
961 if (i + 1 >= end)
962 {
963 throw new IOException(
964 Utils.messages.createMessage(
965 MsgKey.ER_INVALID_UTF16_SURROGATE,
966 new Object[] { Integer.toHexString((int) c)}));
967 }
968
969 final char high = c;
970 final char low = ch[i+1];
971 if (!Encodings.isLowUTF16Surrogate(low)) {
972 throw new IOException(
973 Utils.messages.createMessage(
974 MsgKey.ER_INVALID_UTF16_SURROGATE,
975 new Object[] {
976 Integer.toHexString((int) c)
977 + " "
978 + Integer.toHexString(low)}));
979 }
980
981 final java.io.Writer writer = m_writer;
982
983 // If we make it to here we have a valid high, low surrogate pair
984 if (m_encodingInfo.isInEncoding(c,low)) {
985 // If the character formed by the surrogate pair
986 // is in the encoding, so just write it out
987 writer.write(ch,i,2);
988 }
989 else {
990 // Don't know what to do with this char, it is
991 // not in the encoding and not a high char in
992 // a surrogate pair, so write out as an entity ref
993 final String encoding = getEncoding();
994 if (encoding != null) {
995 /* The output encoding is known,
996 * so somthing is wrong.
997 */
998 codePoint = Encodings.toCodePoint(high, low);
999 // not in the encoding, so write out a character reference
1000 writer.write('&');
1001 writer.write('#');
1002 writer.write(Integer.toString(codePoint));
1003 writer.write(';');
1004 } else {
1005 /* The output encoding is not known,
1006 * so just write it out as-is.
1007 */
1008 writer.write(ch, i, 2);
1009 }
1010 }
1011 // non-zero only if character reference was written out.
1012 return codePoint;
1013 }
1014
1015 /**
1016 * Handle one of the default entities, return false if it
1017 * is not a default entity.
1018 *
1019 * @param ch character to be escaped.
1020 * @param i index into character array.
1021 * @param chars non-null reference to character array.
1022 * @param len length of chars.
1023 * @param fromTextNode true if the characters being processed
1024 * are from a text node, false if they are from an attribute value
1025 * @param escLF true if the linefeed should be escaped.
1026 *
1027 * @return i+1 if the character was written, else i.
1028 *
1029 * @throws java.io.IOException
1030 */
1031 protected int accumDefaultEntity(
1032 java.io.Writer writer,
1033 char ch,
1034 int i,
1035 char[] chars,
1036 int len,
1037 boolean fromTextNode,
1038 boolean escLF)
1039 throws IOException
1040 {
1041
1042 if (!escLF && CharInfo.S_LINEFEED == ch)
1043 {
1044 writer.write(m_lineSep, 0, m_lineSepLen);
1045 }
1046 else
1047 {
1048 // if this is text node character and a special one of those,
1049 // or if this is a character from attribute value and a special one of those
1050 if ((fromTextNode && m_charInfo.isSpecialTextChar(ch)) || (!fromTextNode && m_charInfo.isSpecialAttrChar(ch)))
1051 {
1052 String outputStringForChar = m_charInfo.getOutputStringForChar(ch);
1053
1054 if (null != outputStringForChar)
1055 {
1056 writer.write(outputStringForChar);
1057 }
1058 else
1059 return i;
1060 }
1061 else
1062 return i;
1063 }
1064
1065 return i + 1;
1066
1067 }
1068 /**
1069 * Normalize the characters, but don't escape.
1070 *
1071 * @param ch The characters from the XML document.
1072 * @param start The start position in the array.
1073 * @param length The number of characters to read from the array.
1074 * @param isCData true if a CDATA block should be built around the characters.
1075 * @param useSystemLineSeparator true if the operating systems
1076 * end-of-line separator should be output rather than a new-line character.
1077 *
1078 * @throws IOException
1079 * @throws org.xml.sax.SAXException
1080 */
1081 void writeNormalizedChars(
1082 char ch[],
1083 int start,
1084 int length,
1085 boolean isCData,
1086 boolean useSystemLineSeparator)
1087 throws IOException, org.xml.sax.SAXException
1088 {
1089 final java.io.Writer writer = m_writer;
1090 int end = start + length;
1091
1092 for (int i = start; i < end; i++)
1093 {
1094 char c = ch[i];
1095
1096 if (CharInfo.S_LINEFEED == c && useSystemLineSeparator)
1097 {
1098 writer.write(m_lineSep, 0, m_lineSepLen);
1099 }
1100 else if (isCData && (!escapingNotNeeded(c)))
1101 {
1102 // if (i != 0)
1103 if (m_cdataTagOpen)
1104 closeCDATA();
1105
1106 // This needs to go into a function...
1107 if (Encodings.isHighUTF16Surrogate(c))
1108 {
1109 writeUTF16Surrogate(c, ch, i, end);
1110 i++ ; // process two input characters
1111 }
1112 else
1113 {
1114 writer.write("&#");
1115
1116 String intStr = Integer.toString((int) c);
1117
1118 writer.write(intStr);
1119 writer.write(';');
1120 }
1121
1122 // if ((i != 0) && (i < (end - 1)))
1123 // if (!m_cdataTagOpen && (i < (end - 1)))
1124 // {
1125 // writer.write(CDATA_DELIMITER_OPEN);
1126 // m_cdataTagOpen = true;
1127 // }
1128 }
1129 else if (
1130 isCData
1131 && ((i < (end - 2))
1132 && (']' == c)
1133 && (']' == ch[i + 1])
1134 && ('>' == ch[i + 2])))
1135 {
1136 writer.write(CDATA_CONTINUE);
1137
1138 i += 2;
1139 }
1140 else
1141 {
1142 if (escapingNotNeeded(c))
1143 {
1144 if (isCData && !m_cdataTagOpen)
1145 {
1146 writer.write(CDATA_DELIMITER_OPEN);
1147 m_cdataTagOpen = true;
1148 }
1149 writer.write(c);
1150 }
1151
1152 // This needs to go into a function...
1153 else if (Encodings.isHighUTF16Surrogate(c))
1154 {
1155 if (m_cdataTagOpen)
1156 closeCDATA();
1157 writeUTF16Surrogate(c, ch, i, end);
1158 i++; // process two input characters
1159 }
1160 else
1161 {
1162 if (m_cdataTagOpen)
1163 closeCDATA();
1164 writer.write("&#");
1165
1166 String intStr = Integer.toString((int) c);
1167
1168 writer.write(intStr);
1169 writer.write(';');
1170 }
1171 }
1172 }
1173
1174 }
1175
1176 /**
1177 * Ends an un-escaping section.
1178 *
1179 * @see #startNonEscaping
1180 *
1181 * @throws org.xml.sax.SAXException
1182 */
1183 public void endNonEscaping() throws org.xml.sax.SAXException
1184 {
1185 m_disableOutputEscapingStates.pop();
1186 }
1187
1188 /**
1189 * Starts an un-escaping section. All characters printed within an un-
1190 * escaping section are printed as is, without escaping special characters
1191 * into entity references. Only XML and HTML serializers need to support
1192 * this method.
1193 * <p> The contents of the un-escaping section will be delivered through the
1194 * regular <tt>characters</tt> event.
1195 *
1196 * @throws org.xml.sax.SAXException
1197 */
1198 public void startNonEscaping() throws org.xml.sax.SAXException
1199 {
1200 m_disableOutputEscapingStates.push(true);
1201 }
1202
1203 /**
1204 * Receive notification of cdata.
1205 *
1206 * <p>The Parser will call this method to report each chunk of
1207 * character data. SAX parsers may return all contiguous character
1208 * data in a single chunk, or they may split it into several
1209 * chunks; however, all of the characters in any single event
1210 * must come from the same external entity, so that the Locator
1211 * provides useful information.</p>
1212 *
1213 * <p>The application must not attempt to read from the array
1214 * outside of the specified range.</p>
1215 *
1216 * <p>Note that some parsers will report whitespace using the
1217 * ignorableWhitespace() method rather than this one (validating
1218 * parsers must do so).</p>
1219 *
1220 * @param ch The characters from the XML document.
1221 * @param start The start position in the array.
1222 * @param length The number of characters to read from the array.
1223 * @throws org.xml.sax.SAXException Any SAX exception, possibly
1224 * wrapping another exception.
1225 * @see #ignorableWhitespace
1226 * @see org.xml.sax.Locator
1227 *
1228 * @throws org.xml.sax.SAXException
1229 */
1230 protected void cdata(char ch[], int start, final int length)
1231 throws org.xml.sax.SAXException
1232 {
1233
1234 try
1235 {
1236 final int old_start = start;
1237 if (m_elemContext.m_startTagOpen)
1238 {
1239 closeStartTag();
1240 m_elemContext.m_startTagOpen = false;
1241 }
1242 m_ispreserve = true;
1243
1244 if (shouldIndent())
1245 indent();
1246
1247 boolean writeCDataBrackets =
1248 (((length >= 1) && escapingNotNeeded(ch[start])));
1249
1250 /* Write out the CDATA opening delimiter only if
1251 * we are supposed to, and if we are not already in
1252 * the middle of a CDATA section
1253 */
1254 if (writeCDataBrackets && !m_cdataTagOpen)
1255 {
1256 m_writer.write(CDATA_DELIMITER_OPEN);
1257 m_cdataTagOpen = true;
1258 }
1259
1260 // writer.write(ch, start, length);
1261 if (isEscapingDisabled())
1262 {
1263 charactersRaw(ch, start, length);
1264 }
1265 else
1266 writeNormalizedChars(ch, start, length, true, m_lineSepUse);
1267
1268 /* used to always write out CDATA closing delimiter here,
1269 * but now we delay, so that we can merge CDATA sections on output.
1270 * need to write closing delimiter later
1271 */
1272 if (writeCDataBrackets)
1273 {
1274 /* if the CDATA section ends with ] don't leave it open
1275 * as there is a chance that an adjacent CDATA sections
1276 * starts with ]>.
1277 * We don't want to merge ]] with > , or ] with ]>
1278 */
1279 if (ch[start + length - 1] == ']')
1280 closeCDATA();
1281 }
1282
1283 // time to fire off CDATA event
1284 if (m_tracer != null)
1285 super.fireCDATAEvent(ch, old_start, length);
1286 }
1287 catch (IOException ioe)
1288 {
1289 throw new org.xml.sax.SAXException(
1290 Utils.messages.createMessage(
1291 MsgKey.ER_OIERROR,
1292 null),
1293 ioe);
1294 //"IO error", ioe);
1295 }
1296 }
1297
1298 /**
1299 * Tell if the character escaping should be disabled for the current state.
1300 *
1301 * @return true if the character escaping should be disabled.
1302 */
1303 private boolean isEscapingDisabled()
1304 {
1305 return m_disableOutputEscapingStates.peekOrFalse();
1306 }
1307
1308 /**
1309 * If available, when the disable-output-escaping attribute is used,
1310 * output raw text without escaping.
1311 *
1312 * @param ch The characters from the XML document.
1313 * @param start The start position in the array.
1314 * @param length The number of characters to read from the array.
1315 *
1316 * @throws org.xml.sax.SAXException
1317 */
1318 protected void charactersRaw(char ch[], int start, int length)
1319 throws org.xml.sax.SAXException
1320 {
1321
1322 if (m_inEntityRef)
1323 return;
1324 try
1325 {
1326 if (m_elemContext.m_startTagOpen)
1327 {
1328 closeStartTag();
1329 m_elemContext.m_startTagOpen = false;
1330 }
1331
1332 m_ispreserve = true;
1333
1334 m_writer.write(ch, start, length);
1335 }
1336 catch (IOException e)
1337 {
1338 throw new SAXException(e);
1339 }
1340
1341 }
1342
1343 /**
1344 * Receive notification of character data.
1345 *
1346 * <p>The Parser will call this method to report each chunk of
1347 * character data. SAX parsers may return all contiguous character
1348 * data in a single chunk, or they may split it into several
1349 * chunks; however, all of the characters in any single event
1350 * must come from the same external entity, so that the Locator
1351 * provides useful information.</p>
1352 *
1353 * <p>The application must not attempt to read from the array
1354 * outside of the specified range.</p>
1355 *
1356 * <p>Note that some parsers will report whitespace using the
1357 * ignorableWhitespace() method rather than this one (validating
1358 * parsers must do so).</p>
1359 *
1360 * @param chars The characters from the XML document.
1361 * @param start The start position in the array.
1362 * @param length The number of characters to read from the array.
1363 * @throws org.xml.sax.SAXException Any SAX exception, possibly
1364 * wrapping another exception.
1365 * @see #ignorableWhitespace
1366 * @see org.xml.sax.Locator
1367 *
1368 * @throws org.xml.sax.SAXException
1369 */
1370 public void characters(final char chars[], final int start, final int length)
1371 throws org.xml.sax.SAXException
1372 {
1373 // It does not make sense to continue with rest of the method if the number of
1374 // characters to read from array is 0.
1375 // Section 7.6.1 of XSLT 1.0 (http://www.w3.org/TR/xslt#value-of) suggest no text node
1376 // is created if string is empty.
1377 if (length == 0 || (m_inEntityRef && !m_expandDTDEntities))
1378 return;
1379 if (m_elemContext.m_startTagOpen)
1380 {
1381 closeStartTag();
1382 m_elemContext.m_startTagOpen = false;
1383 }
1384 else if (m_needToCallStartDocument)
1385 {
1386 startDocumentInternal();
1387 }
1388
1389 if (m_cdataStartCalled || m_elemContext.m_isCdataSection)
1390 {
1391 /* either due to startCDATA() being called or due to
1392 * cdata-section-elements atribute, we need this as cdata
1393 */
1394 cdata(chars, start, length);
1395
1396 return;
1397 }
1398
1399 if (m_cdataTagOpen)
1400 closeCDATA();
1401 // the check with _escaping is a bit of a hack for XLSTC
1402
1403 if (m_disableOutputEscapingStates.peekOrFalse() || (!m_escaping))
1404 {
1405 charactersRaw(chars, start, length);
1406
1407 // time to fire off characters generation event
1408 if (m_tracer != null)
1409 super.fireCharEvent(chars, start, length);
1410
1411 return;
1412 }
1413
1414 if (m_elemContext.m_startTagOpen)
1415 {
1416 closeStartTag();
1417 m_elemContext.m_startTagOpen = false;
1418 }
1419
1420
1421 try
1422 {
1423 int i;
1424 char ch1;
1425 int startClean;
1426
1427 // skip any leading whitspace
1428 // don't go off the end and use a hand inlined version
1429 // of isWhitespace(ch)
1430 final int end = start + length;
1431 int lastDirty = start - 1; // last character that needed processing
1432 for (i = start;
1433 ((i < end)
1434 && ((ch1 = chars[i]) == 0x20
1435 || (ch1 == 0xA && m_lineSepUse)
1436 || ch1 == 0xD
1437 || ch1 == 0x09));
1438 i++)
1439 {
1440 /*
1441 * We are processing leading whitespace, but are doing the same
1442 * processing for dirty characters here as for non-whitespace.
1443 *
1444 */
1445 if (!m_charInfo.isTextASCIIClean(ch1))
1446 {
1447 lastDirty = processDirty(chars,end, i,ch1, lastDirty, true);
1448 i = lastDirty;
1449 }
1450 }
1451 /* If there is some non-whitespace, mark that we may need
1452 * to preserve this. This is only important if we have indentation on.
1453 */
1454 if (i < end)
1455 m_ispreserve = true;
1456
1457
1458 // int lengthClean; // number of clean characters in a row
1459 // final boolean[] isAsciiClean = m_charInfo.getASCIIClean();
1460
1461 final boolean isXML10 = XMLVERSION10.equals(getVersion());
1462 // we've skipped the leading whitespace, now deal with the rest
1463 for (; i < end; i++)
1464 {
1465 {
1466 // A tight loop to skip over common clean chars
1467 // This tight loop makes it easier for the JIT
1468 // to optimize.
1469 char ch2;
1470 while (i<end
1471 && ((ch2 = chars[i])<127)
1472 && m_charInfo.isTextASCIIClean(ch2))
1473 i++;
1474 if (i == end)
1475 break;
1476 }
1477
1478 final char ch = chars[i];
1479 /* The check for isCharacterInC0orC1Ranger and
1480 * isNELorLSEPCharacter has been added
1481 * to support Control Characters in XML 1.1
1482 */
1483 if (!isCharacterInC0orC1Range(ch) &&
1484 (isXML10 || !isNELorLSEPCharacter(ch)) &&
1485 (escapingNotNeeded(ch) && (!m_charInfo.isSpecialTextChar(ch)))
1486 || ('"' == ch))
1487 {
1488 ; // a character needing no special processing
1489 }
1490 else
1491 {
1492 lastDirty = processDirty(chars,end, i, ch, lastDirty, true);
1493 i = lastDirty;
1494 }
1495 }
1496
1497 // we've reached the end. Any clean characters at the
1498 // end of the array than need to be written out?
1499 startClean = lastDirty + 1;
1500 if (i > startClean)
1501 {
1502 int lengthClean = i - startClean;
1503 m_writer.write(chars, startClean, lengthClean);
1504 }
1505
1506 // For indentation purposes, mark that we've just writen text out
1507 m_isprevtext = true;
1508 }
1509 catch (IOException e)
1510 {
1511 throw new SAXException(e);
1512 }
1513
1514 // time to fire off characters generation event
1515 if (m_tracer != null)
1516 super.fireCharEvent(chars, start, length);
1517 }
1518 /**
1519 * This method checks if a given character is between C0 or C1 range
1520 * of Control characters.
1521 * This method is added to support Control Characters for XML 1.1
1522 * If a given character is TAB (0x09), LF (0x0A) or CR (0x0D), this method
1523 * return false. Since they are whitespace characters, no special processing is needed.
1524 *
1525 * @param ch
1526 * @return boolean
1527 */
1528 private static boolean isCharacterInC0orC1Range(char ch)
1529 {
1530 if(ch == 0x09 || ch == 0x0A || ch == 0x0D)
1531 return false;
1532 else
1533 return (ch >= 0x7F && ch <= 0x9F)|| (ch >= 0x01 && ch <= 0x1F);
1534 }
1535 /**
1536 * This method checks if a given character either NEL (0x85) or LSEP (0x2028)
1537 * These are new end of line charcters added in XML 1.1. These characters must be
1538 * written as Numeric Character References (NCR) in XML 1.1 output document.
1539 *
1540 * @param ch
1541 * @return boolean
1542 */
1543 private static boolean isNELorLSEPCharacter(char ch)
1544 {
1545 return (ch == 0x85 || ch == 0x2028);
1546 }
1547 /**
1548 * Process a dirty character and any preeceding clean characters
1549 * that were not yet processed.
1550 * @param chars array of characters being processed
1551 * @param end one (1) beyond the last character
1552 * in chars to be processed
1553 * @param i the index of the dirty character
1554 * @param ch the character in chars[i]
1555 * @param lastDirty the last dirty character previous to i
1556 * @param fromTextNode true if the characters being processed are
1557 * from a text node, false if they are from an attribute value.
1558 * @return the index of the last character processed
1559 */
1560 private int processDirty(
1561 char[] chars,
1562 int end,
1563 int i,
1564 char ch,
1565 int lastDirty,
1566 boolean fromTextNode) throws IOException
1567 {
1568 int startClean = lastDirty + 1;
1569 // if we have some clean characters accumulated
1570 // process them before the dirty one.
1571 if (i > startClean)
1572 {
1573 int lengthClean = i - startClean;
1574 m_writer.write(chars, startClean, lengthClean);
1575 }
1576
1577 // process the "dirty" character
1578 if (CharInfo.S_LINEFEED == ch && fromTextNode)
1579 {
1580 m_writer.write(m_lineSep, 0, m_lineSepLen);
1581 }
1582 else
1583 {
1584 startClean =
1585 accumDefaultEscape(
1586 m_writer,
1587 (char)ch,
1588 i,
1589 chars,
1590 end,
1591 fromTextNode,
1592 false);
1593 i = startClean - 1;
1594 }
1595 // Return the index of the last character that we just processed
1596 // which is a dirty character.
1597 return i;
1598 }
1599
1600 /**
1601 * Receive notification of character data.
1602 *
1603 * @param s The string of characters to process.
1604 *
1605 * @throws org.xml.sax.SAXException
1606 */
1607 public void characters(String s) throws org.xml.sax.SAXException
1608 {
1609 if (m_inEntityRef && !m_expandDTDEntities)
1610 return;
1611 final int length = s.length();
1612 if (length > m_charsBuff.length)
1613 {
1614 m_charsBuff = new char[length * 2 + 1];
1615 }
1616 s.getChars(0, length, m_charsBuff, 0);
1617 characters(m_charsBuff, 0, length);
1618 }
1619
1620 /**
1621 * Escape and writer.write a character.
1622 *
1623 * @param ch character to be escaped.
1624 * @param i index into character array.
1625 * @param chars non-null reference to character array.
1626 * @param len length of chars.
1627 * @param fromTextNode true if the characters being processed are
1628 * from a text node, false if the characters being processed are from
1629 * an attribute value.
1630 * @param escLF true if the linefeed should be escaped.
1631 *
1632 * @return i+1 if a character was written, i+2 if two characters
1633 * were written out, else return i.
1634 *
1635 * @throws org.xml.sax.SAXException
1636 */
1637 protected int accumDefaultEscape(
1638 Writer writer,
1639 char ch,
1640 int i,
1641 char[] chars,
1642 int len,
1643 boolean fromTextNode,
1644 boolean escLF)
1645 throws IOException
1646 {
1647
1648 int pos = accumDefaultEntity(writer, ch, i, chars, len, fromTextNode, escLF);
1649
1650 if (i == pos)
1651 {
1652 if (Encodings.isHighUTF16Surrogate(ch))
1653 {
1654
1655 // Should be the UTF-16 low surrogate of the hig/low pair.
1656 char next;
1657 // Unicode code point formed from the high/low pair.
1658 int codePoint = 0;
1659
1660 if (i + 1 >= len)
1661 {
1662 throw new IOException(
1663 Utils.messages.createMessage(
1664 MsgKey.ER_INVALID_UTF16_SURROGATE,
1665 new Object[] { Integer.toHexString(ch)}));
1666 //"Invalid UTF-16 surrogate detected: "
1667
1668 //+Integer.toHexString(ch)+ " ?");
1669 }
1670 else
1671 {
1672 next = chars[++i];
1673
1674 if (!(Encodings.isLowUTF16Surrogate(next)))
1675 throw new IOException(
1676 Utils.messages.createMessage(
1677 MsgKey
1678 .ER_INVALID_UTF16_SURROGATE,
1679 new Object[] {
1680 Integer.toHexString(ch)
1681 + " "
1682 + Integer.toHexString(next)}));
1683 //"Invalid UTF-16 surrogate detected: "
1684
1685 //+Integer.toHexString(ch)+" "+Integer.toHexString(next));
1686 codePoint = Encodings.toCodePoint(ch,next);
1687 }
1688
1689 writer.write("&#");
1690 writer.write(Integer.toString(codePoint));
1691 writer.write(';');
1692 pos += 2; // count the two characters that went into writing out this entity
1693 }
1694 else
1695 {
1696 /* This if check is added to support control characters in XML 1.1.
1697 * If a character is a Control Character within C0 and C1 range, it is desirable
1698 * to write it out as Numeric Character Reference(NCR) regardless of XML Version
1699 * being used for output document.
1700 */
1701 if (isCharacterInC0orC1Range(ch) ||
1702 (XMLVERSION11.equals(getVersion()) && isNELorLSEPCharacter(ch)))
1703 {
1704 writer.write("&#");
1705 writer.write(Integer.toString(ch));
1706 writer.write(';');
1707 }
1708 else if ((!escapingNotNeeded(ch) ||
1709 ( (fromTextNode && m_charInfo.isSpecialTextChar(ch))
1710 || (!fromTextNode && m_charInfo.isSpecialAttrChar(ch))))
1711 && m_elemContext.m_currentElemDepth > 0)
1712 {
1713 writer.write("&#");
1714 writer.write(Integer.toString(ch));
1715 writer.write(';');
1716 }
1717 else
1718 {
1719 writer.write(ch);
1720 }
1721 pos++; // count the single character that was processed
1722 }
1723
1724 }
1725 return pos;
1726 }
1727
1728 /**
1729 * Receive notification of the beginning of an element, although this is a
1730 * SAX method additional namespace or attribute information can occur before
1731 * or after this call, that is associated with this element.
1732 *
1733 *
1734 * @param namespaceURI The Namespace URI, or the empty string if the
1735 * element has no Namespace URI or if Namespace
1736 * processing is not being performed.
1737 * @param localName The local name (without prefix), or the
1738 * empty string if Namespace processing is not being
1739 * performed.
1740 * @param name The element type name.
1741 * @param atts The attributes attached to the element, if any.
1742 * @throws org.xml.sax.SAXException Any SAX exception, possibly
1743 * wrapping another exception.
1744 * @see org.xml.sax.ContentHandler#startElement
1745 * @see org.xml.sax.ContentHandler#endElement
1746 * @see org.xml.sax.AttributeList
1747 *
1748 * @throws org.xml.sax.SAXException
1749 */
1750 public void startElement(
1751 String namespaceURI,
1752 String localName,
1753 String name,
1754 Attributes atts)
1755 throws org.xml.sax.SAXException
1756 {
1757 if (m_inEntityRef)
1758 return;
1759
1760 if (m_needToCallStartDocument)
1761 {
1762 startDocumentInternal();
1763 m_needToCallStartDocument = false;
1764 }
1765 else if (m_cdataTagOpen)
1766 closeCDATA();
1767 try
1768 {
1769 if ((true == m_needToOutputDocTypeDecl)
1770 && (null != getDoctypeSystem()))
1771 {
1772 outputDocTypeDecl(name, true);
1773 }
1774
1775 m_needToOutputDocTypeDecl = false;
1776
1777 /* before we over-write the current elementLocalName etc.
1778 * lets close out the old one (if we still need to)
1779 */
1780 if (m_elemContext.m_startTagOpen)
1781 {
1782 closeStartTag();
1783 m_elemContext.m_startTagOpen = false;
1784 }
1785
1786 if (namespaceURI != null)
1787 ensurePrefixIsDeclared(namespaceURI, name);
1788
1789 m_ispreserve = false;
1790
1791 if (shouldIndent() && m_startNewLine)
1792 {
1793 indent();
1794 }
1795
1796 m_startNewLine = true;
1797
1798 final java.io.Writer writer = m_writer;
1799 writer.write('<');
1800 writer.write(name);
1801 }
1802 catch (IOException e)
1803 {
1804 throw new SAXException(e);
1805 }
1806
1807 // process the attributes now, because after this SAX call they might be gone
1808 if (atts != null)
1809 addAttributes(atts);
1810
1811 m_elemContext = m_elemContext.push(namespaceURI,localName,name);
1812 m_isprevtext = false;
1813
1814 if (m_tracer != null){
1815 firePseudoAttributes();
1816 }
1817
1818 }
1819
1820 /**
1821 * Receive notification of the beginning of an element, additional
1822 * namespace or attribute information can occur before or after this call,
1823 * that is associated with this element.
1824 *
1825 *
1826 * @param elementNamespaceURI The Namespace URI, or the empty string if the
1827 * element has no Namespace URI or if Namespace
1828 * processing is not being performed.
1829 * @param elementLocalName The local name (without prefix), or the
1830 * empty string if Namespace processing is not being
1831 * performed.
1832 * @param elementName The element type name.
1833 * @throws org.xml.sax.SAXException Any SAX exception, possibly
1834 * wrapping another exception.
1835 * @see org.xml.sax.ContentHandler#startElement
1836 * @see org.xml.sax.ContentHandler#endElement
1837 * @see org.xml.sax.AttributeList
1838 *
1839 * @throws org.xml.sax.SAXException
1840 */
1841 public void startElement(
1842 String elementNamespaceURI,
1843 String elementLocalName,
1844 String elementName)
1845 throws SAXException
1846 {
1847 startElement(elementNamespaceURI, elementLocalName, elementName, null);
1848 }
1849
1850 public void startElement(String elementName) throws SAXException
1851 {
1852 startElement(null, null, elementName, null);
1853 }
1854
1855 /**
1856 * Output the doc type declaration.
1857 *
1858 * @param name non-null reference to document type name.
1859 * NEEDSDOC @param closeDecl
1860 *
1861 * @throws java.io.IOException
1862 */
1863 void outputDocTypeDecl(String name, boolean closeDecl) throws SAXException
1864 {
1865 if (m_cdataTagOpen)
1866 closeCDATA();
1867 try
1868 {
1869 final java.io.Writer writer = m_writer;
1870 writer.write("<!DOCTYPE ");
1871 writer.write(name);
1872
1873 String doctypePublic = getDoctypePublic();
1874 if (null != doctypePublic)
1875 {
1876 writer.write(" PUBLIC \"");
1877 writer.write(doctypePublic);
1878 writer.write('\"');
1879 }
1880
1881 String doctypeSystem = getDoctypeSystem();
1882 if (null != doctypeSystem)
1883 {
1884 if (null == doctypePublic)
1885 writer.write(" SYSTEM \"");
1886 else
1887 writer.write(" \"");
1888
1889 writer.write(doctypeSystem);
1890
1891 if (closeDecl)
1892 {
1893 writer.write("\">");
1894 writer.write(m_lineSep, 0, m_lineSepLen);
1895 closeDecl = false; // done closing
1896 }
1897 else
1898 writer.write('\"');
1899 }
1900 boolean dothis = false;
1901 if (dothis)
1902 {
1903 // at one point this code seemed right,
1904 // but not anymore - Brian M.
1905 if (closeDecl)
1906 {
1907 writer.write('>');
1908 writer.write(m_lineSep, 0, m_lineSepLen);
1909 }
1910 }
1911 }
1912 catch (IOException e)
1913 {
1914 throw new SAXException(e);
1915 }
1916 }
1917
1918 /**
1919 * Process the attributes, which means to write out the currently
1920 * collected attributes to the writer. The attributes are not
1921 * cleared by this method
1922 *
1923 * @param writer the writer to write processed attributes to.
1924 * @param nAttrs the number of attributes in m_attributes
1925 * to be processed
1926 *
1927 * @throws java.io.IOException
1928 * @throws org.xml.sax.SAXException
1929 */
1930 public void processAttributes(java.io.Writer writer, int nAttrs) throws IOException, SAXException
1931 {
1932 /* real SAX attributes are not passed in, so process the
1933 * attributes that were collected after the startElement call.
1934 * _attribVector is a "cheap" list for Stream serializer output
1935 * accumulated over a series of calls to attribute(name,value)
1936 */
1937 String encoding = getEncoding();
1938 for (int i = 0; i < nAttrs; i++)
1939 {
1940 // elementAt is JDK 1.1.8
1941 final String name = m_attributes.getQName(i);
1942 final String value = m_attributes.getValue(i);
1943 writer.write(' ');
1944 writer.write(name);
1945 writer.write("=\"");
1946 writeAttrString(writer, value, encoding);
1947 writer.write('\"');
1948 }
1949 }
1950
1951 /**
1952 * Returns the specified <var>string</var> after substituting <VAR>specials</VAR>,
1953 * and UTF-16 surrogates for chracter references <CODE>&#xnn</CODE>.
1954 *
1955 * @param string String to convert to XML format.
1956 * @param encoding CURRENTLY NOT IMPLEMENTED.
1957 *
1958 * @throws java.io.IOException
1959 */
1960 public void writeAttrString(
1961 Writer writer,
1962 String string,
1963 String encoding)
1964 throws IOException
1965 {
1966 final int len = string.length();
1967 if (len > m_attrBuff.length)
1968 {
1969 m_attrBuff = new char[len*2 + 1];
1970 }
1971 string.getChars(0,len, m_attrBuff, 0);
1972 final char[] stringChars = m_attrBuff;
1973
1974 for (int i = 0; i < len; )
1975 {
1976 char ch = stringChars[i];
1977 if (escapingNotNeeded(ch) && (!m_charInfo.isSpecialAttrChar(ch)))
1978 {
1979 writer.write(ch);
1980 i++;
1981 }
1982 else
1983 { // I guess the parser doesn't normalize cr/lf in attributes. -sb
1984 // if ((CharInfo.S_CARRIAGERETURN == ch)
1985 // && ((i + 1) < len)
1986 // && (CharInfo.S_LINEFEED == stringChars[i + 1]))
1987 // {
1988 // i++;
1989 // ch = CharInfo.S_LINEFEED;
1990 // }
1991
1992 i = accumDefaultEscape(writer, ch, i, stringChars, len, false, true);
1993 }
1994 }
1995
1996 }
1997
1998 /**
1999 * Receive notification of the end of an element.
2000 *
2001 *
2002 * @param namespaceURI The Namespace URI, or the empty string if the
2003 * element has no Namespace URI or if Namespace
2004 * processing is not being performed.
2005 * @param localName The local name (without prefix), or the
2006 * empty string if Namespace processing is not being
2007 * performed.
2008 * @param name The element type name
2009 * @throws org.xml.sax.SAXException Any SAX exception, possibly
2010 * wrapping another exception.
2011 *
2012 * @throws org.xml.sax.SAXException
2013 */
2014 public void endElement(String namespaceURI, String localName, String name)
2015 throws org.xml.sax.SAXException
2016 {
2017
2018 if (m_inEntityRef)
2019 return;
2020
2021 // namespaces declared at the current depth are no longer valid
2022 // so get rid of them
2023 m_prefixMap.popNamespaces(m_elemContext.m_currentElemDepth, null);
2024
2025 try
2026 {
2027 final java.io.Writer writer = m_writer;
2028 if (m_elemContext.m_startTagOpen)
2029 {
2030 if (m_tracer != null)
2031 super.fireStartElem(m_elemContext.m_elementName);
2032 int nAttrs = m_attributes.getLength();
2033 if (nAttrs > 0)
2034 {
2035 processAttributes(m_writer, nAttrs);
2036 // clear attributes object for re-use with next element
2037 m_attributes.clear();
2038 }
2039 if (m_spaceBeforeClose)
2040 writer.write(" />");
2041 else
2042 writer.write("/>");
2043 /* don't need to pop cdataSectionState because
2044 * this element ended so quickly that we didn't get
2045 * to push the state.
2046 */
2047
2048 }
2049 else
2050 {
2051 if (m_cdataTagOpen)
2052 closeCDATA();
2053
2054 if (shouldIndent())
2055 indent(m_elemContext.m_currentElemDepth - 1);
2056 writer.write('<');
2057 writer.write('/');
2058 writer.write(name);
2059 writer.write('>');
2060 }
2061 }
2062 catch (IOException e)
2063 {
2064 throw new SAXException(e);
2065 }
2066
2067 if (!m_elemContext.m_startTagOpen && m_doIndent)
2068 {
2069 m_ispreserve = m_preserves.isEmpty() ? false : m_preserves.pop();
2070 }
2071
2072 m_isprevtext = false;
2073
2074 // fire off the end element event
2075 if (m_tracer != null)
2076 super.fireEndElem(name);
2077 m_elemContext = m_elemContext.m_prev;
2078 }
2079
2080 /**
2081 * Receive notification of the end of an element.
2082 * @param name The element type name
2083 * @throws org.xml.sax.SAXException Any SAX exception, possibly
2084 * wrapping another exception.
2085 */
2086 public void endElement(String name) throws org.xml.sax.SAXException
2087 {
2088 endElement(null, null, name);
2089 }
2090
2091 /**
2092 * Begin the scope of a prefix-URI Namespace mapping
2093 * just before another element is about to start.
2094 * This call will close any open tags so that the prefix mapping
2095 * will not apply to the current element, but the up comming child.
2096 *
2097 * @see org.xml.sax.ContentHandler#startPrefixMapping
2098 *
2099 * @param prefix The Namespace prefix being declared.
2100 * @param uri The Namespace URI the prefix is mapped to.
2101 *
2102 * @throws org.xml.sax.SAXException The client may throw
2103 * an exception during processing.
2104 *
2105 */
2106 public void startPrefixMapping(String prefix, String uri)
2107 throws org.xml.sax.SAXException
2108 {
2109 // the "true" causes the flush of any open tags
2110 startPrefixMapping(prefix, uri, true);
2111 }
2112
2113 /**
2114 * Handle a prefix/uri mapping, which is associated with a startElement()
2115 * that is soon to follow. Need to close any open start tag to make
2116 * sure than any name space attributes due to this event are associated wih
2117 * the up comming element, not the current one.
2118 * @see ExtendedContentHandler#startPrefixMapping
2119 *
2120 * @param prefix The Namespace prefix being declared.
2121 * @param uri The Namespace URI the prefix is mapped to.
2122 * @param shouldFlush true if any open tags need to be closed first, this
2123 * will impact which element the mapping applies to (open parent, or its up
2124 * comming child)
2125 * @return returns true if the call made a change to the current
2126 * namespace information, false if it did not change anything, e.g. if the
2127 * prefix/namespace mapping was already in scope from before.
2128 *
2129 * @throws org.xml.sax.SAXException The client may throw
2130 * an exception during processing.
2131 *
2132 *
2133 */
2134 public boolean startPrefixMapping(
2135 String prefix,
2136 String uri,
2137 boolean shouldFlush)
2138 throws org.xml.sax.SAXException
2139 {
2140
2141 /* Remember the mapping, and at what depth it was declared
2142 * This is one greater than the current depth because these
2143 * mappings will apply to the next depth. This is in
2144 * consideration that startElement() will soon be called
2145 */
2146
2147 boolean pushed;
2148 int pushDepth;
2149 if (shouldFlush)
2150 {
2151 flushPending();
2152 // the prefix mapping applies to the child element (one deeper)
2153 pushDepth = m_elemContext.m_currentElemDepth + 1;
2154 }
2155 else
2156 {
2157 // the prefix mapping applies to the current element
2158 pushDepth = m_elemContext.m_currentElemDepth;
2159 }
2160 pushed = m_prefixMap.pushNamespace(prefix, uri, pushDepth);
2161
2162 if (pushed)
2163 {
2164 /* Brian M.: don't know if we really needto do this. The
2165 * callers of this object should have injected both
2166 * startPrefixMapping and the attributes. We are
2167 * just covering our butt here.
2168 */
2169 String name;
2170 if (EMPTYSTRING.equals(prefix))
2171 {
2172 name = "xmlns";
2173 addAttributeAlways(XMLNS_URI, name, name, "CDATA", uri, false);
2174 }
2175 else
2176 {
2177 if (!EMPTYSTRING.equals(uri))
2178 // hack for XSLTC attribset16 test
2179 { // that maps ns1 prefix to "" URI
2180 name = "xmlns:" + prefix;
2181
2182 /* for something like xmlns:abc="w3.pretend.org"
2183 * the uri is the value, that is why we pass it in the
2184 * value, or 5th slot of addAttributeAlways()
2185 */
2186 addAttributeAlways(XMLNS_URI, prefix, name, "CDATA", uri, false);
2187 }
2188 }
2189 }
2190 return pushed;
2191 }
2192
2193 /**
2194 * Receive notification of an XML comment anywhere in the document. This
2195 * callback will be used for comments inside or outside the document
2196 * element, including comments in the external DTD subset (if read).
2197 * @param ch An array holding the characters in the comment.
2198 * @param start The starting position in the array.
2199 * @param length The number of characters to use from the array.
2200 * @throws org.xml.sax.SAXException The application may raise an exception.
2201 */
2202 public void comment(char ch[], int start, int length)
2203 throws org.xml.sax.SAXException
2204 {
2205
2206 int start_old = start;
2207 if (m_inEntityRef)
2208 return;
2209 if (m_elemContext.m_startTagOpen)
2210 {
2211 closeStartTag();
2212 m_elemContext.m_startTagOpen = false;
2213 }
2214 else if (m_needToCallStartDocument)
2215 {
2216 startDocumentInternal();
2217 m_needToCallStartDocument = false;
2218 }
2219
2220 try
2221 {
2222 if (shouldIndent())
2223 indent();
2224
2225 final int limit = start + length;
2226 boolean wasDash = false;
2227 if (m_cdataTagOpen)
2228 closeCDATA();
2229 final java.io.Writer writer = m_writer;
2230 writer.write(COMMENT_BEGIN);
2231 // Detect occurrences of two consecutive dashes, handle as necessary.
2232 for (int i = start; i < limit; i++)
2233 {
2234 if (wasDash && ch[i] == '-')
2235 {
2236 writer.write(ch, start, i - start);
2237 writer.write(" -");
2238 start = i + 1;
2239 }
2240 wasDash = (ch[i] == '-');
2241 }
2242
2243 // if we have some chars in the comment
2244 if (length > 0)
2245 {
2246 // Output the remaining characters (if any)
2247 final int remainingChars = (limit - start);
2248 if (remainingChars > 0)
2249 writer.write(ch, start, remainingChars);
2250 // Protect comment end from a single trailing dash
2251 if (ch[limit - 1] == '-')
2252 writer.write(' ');
2253 }
2254 writer.write(COMMENT_END);
2255 }
2256 catch (IOException e)
2257 {
2258 throw new SAXException(e);
2259 }
2260
2261 m_startNewLine = true;
2262 // time to generate comment event
2263 if (m_tracer != null)
2264 super.fireCommentEvent(ch, start_old,length);
2265 }
2266
2267 /**
2268 * Report the end of a CDATA section.
2269 * @throws org.xml.sax.SAXException The application may raise an exception.
2270 *
2271 * @see #startCDATA
2272 */
2273 public void endCDATA() throws org.xml.sax.SAXException
2274 {
2275 if (m_cdataTagOpen)
2276 closeCDATA();
2277 m_cdataStartCalled = false;
2278 }
2279
2280 /**
2281 * Report the end of DTD declarations.
2282 * @throws org.xml.sax.SAXException The application may raise an exception.
2283 * @see #startDTD
2284 */
2285 public void endDTD() throws org.xml.sax.SAXException
2286 {
2287 try
2288 {
2289 // Don't output doctype declaration until startDocumentInternal
2290 // has been called. Otherwise, it can appear before XML decl.
2291 if (m_needToCallStartDocument) {
2292 return;
2293 }
2294
2295 if (m_needToOutputDocTypeDecl)
2296 {
2297 outputDocTypeDecl(m_elemContext.m_elementName, false);
2298 m_needToOutputDocTypeDecl = false;
2299 }
2300 final java.io.Writer writer = m_writer;
2301 if (!m_inDoctype)
2302 writer.write("]>");
2303 else
2304 {
2305 writer.write('>');
2306 }
2307
2308 writer.write(m_lineSep, 0, m_lineSepLen);
2309 }
2310 catch (IOException e)
2311 {
2312 throw new SAXException(e);
2313 }
2314
2315 }
2316
2317 /**
2318 * End the scope of a prefix-URI Namespace mapping.
2319 * @see org.xml.sax.ContentHandler#endPrefixMapping
2320 *
2321 * @param prefix The prefix that was being mapping.
2322 * @throws org.xml.sax.SAXException The client may throw
2323 * an exception during processing.
2324 */
2325 public void endPrefixMapping(String prefix) throws org.xml.sax.SAXException
2326 { // do nothing
2327 }
2328
2329 /**
2330 * Receive notification of ignorable whitespace in element content.
2331 *
2332 * Not sure how to get this invoked quite yet.
2333 *
2334 * @param ch The characters from the XML document.
2335 * @param start The start position in the array.
2336 * @param length The number of characters to read from the array.
2337 * @throws org.xml.sax.SAXException Any SAX exception, possibly
2338 * wrapping another exception.
2339 * @see #characters
2340 *
2341 * @throws org.xml.sax.SAXException
2342 */
2343 public void ignorableWhitespace(char ch[], int start, int length)
2344 throws org.xml.sax.SAXException
2345 {
2346
2347 if (0 == length)
2348 return;
2349 characters(ch, start, length);
2350 }
2351
2352 /**
2353 * Receive notification of a skipped entity.
2354 * @see org.xml.sax.ContentHandler#skippedEntity
2355 *
2356 * @param name The name of the skipped entity. If it is a
2357 * parameter entity, the name will begin with '%',
2358 * and if it is the external DTD subset, it will be the string
2359 * "[dtd]".
2360 * @throws org.xml.sax.SAXException Any SAX exception, possibly wrapping
2361 * another exception.
2362 */
2363 public void skippedEntity(String name) throws org.xml.sax.SAXException
2364 { // TODO: Should handle
2365 }
2366
2367 /**
2368 * Report the start of a CDATA section.
2369 *
2370 * @throws org.xml.sax.SAXException The application may raise an exception.
2371 * @see #endCDATA
2372 */
2373 public void startCDATA() throws org.xml.sax.SAXException
2374 {
2375 m_cdataStartCalled = true;
2376 }
2377
2378 /**
2379 * Report the beginning of an entity.
2380 *
2381 * The start and end of the document entity are not reported.
2382 * The start and end of the external DTD subset are reported
2383 * using the pseudo-name "[dtd]". All other events must be
2384 * properly nested within start/end entity events.
2385 *
2386 * @param name The name of the entity. If it is a parameter
2387 * entity, the name will begin with '%'.
2388 * @throws org.xml.sax.SAXException The application may raise an exception.
2389 * @see #endEntity
2390 * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
2391 * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
2392 */
2393 public void startEntity(String name) throws org.xml.sax.SAXException
2394 {
2395 if (name.equals("[dtd]"))
2396 m_inExternalDTD = true;
2397
2398 if (!m_expandDTDEntities && !m_inExternalDTD) {
2399 /* Only leave the entity as-is if
2400 * we've been told not to expand them
2401 * and this is not the magic [dtd] name.
2402 */
2403 startNonEscaping();
2404 characters("&" + name + ';');
2405 endNonEscaping();
2406 }
2407
2408 m_inEntityRef = true;
2409 }
2410
2411 /**
2412 * For the enclosing elements starting tag write out
2413 * out any attributes followed by ">"
2414 *
2415 * @throws org.xml.sax.SAXException
2416 */
2417 protected void closeStartTag() throws SAXException
2418 {
2419 if (m_elemContext.m_startTagOpen)
2420 {
2421
2422 try
2423 {
2424 if (m_tracer != null)
2425 super.fireStartElem(m_elemContext.m_elementName);
2426 int nAttrs = m_attributes.getLength();
2427 if (nAttrs > 0)
2428 {
2429 processAttributes(m_writer, nAttrs);
2430 // clear attributes object for re-use with next element
2431 m_attributes.clear();
2432 }
2433 m_writer.write('>');
2434 }
2435 catch (IOException e)
2436 {
2437 throw new SAXException(e);
2438 }
2439
2440 /* whether Xalan or XSLTC, we have the prefix mappings now, so
2441 * lets determine if the current element is specified in the cdata-
2442 * section-elements list.
2443 */
2444 if (m_cdataSectionElements != null)
2445 m_elemContext.m_isCdataSection = isCdataSection();
2446
2447 if (m_doIndent)
2448 {
2449 m_isprevtext = false;
2450 m_preserves.push(m_ispreserve);
2451 }
2452 }
2453
2454 }
2455
2456 /**
2457 * Report the start of DTD declarations, if any.
2458 *
2459 * Any declarations are assumed to be in the internal subset unless
2460 * otherwise indicated.
2461 *
2462 * @param name The document type name.
2463 * @param publicId The declared public identifier for the
2464 * external DTD subset, or null if none was declared.
2465 * @param systemId The declared system identifier for the
2466 * external DTD subset, or null if none was declared.
2467 * @throws org.xml.sax.SAXException The application may raise an
2468 * exception.
2469 * @see #endDTD
2470 * @see #startEntity
2471 */
2472 public void startDTD(String name, String publicId, String systemId)
2473 throws org.xml.sax.SAXException
2474 {
2475 setDoctypeSystem(systemId);
2476 setDoctypePublic(publicId);
2477
2478 m_elemContext.m_elementName = name;
2479 m_inDoctype = true;
2480 }
2481
2482 /**
2483 * Returns the m_indentAmount.
2484 * @return int
2485 */
2486 public int getIndentAmount()
2487 {
2488 return m_indentAmount;
2489 }
2490
2491 /**
2492 * Sets the m_indentAmount.
2493 *
2494 * @param m_indentAmount The m_indentAmount to set
2495 */
2496 public void setIndentAmount(int m_indentAmount)
2497 {
2498 this.m_indentAmount = m_indentAmount;
2499 }
2500
2501 /**
2502 * Tell if, based on space preservation constraints and the doIndent property,
2503 * if an indent should occur.
2504 *
2505 * @return True if an indent should occur.
2506 */
2507 protected boolean shouldIndent()
2508 {
2509 return m_doIndent && (!m_ispreserve && !m_isprevtext);
2510 }
2511
2512 /**
2513 * Searches for the list of qname properties with the specified key in the
2514 * property list. If the key is not found in this property list, the default
2515 * property list, and its defaults, recursively, are then checked. The
2516 * method returns <code>null</code> if the property is not found.
2517 *
2518 * @param key the property key.
2519 * @param props the list of properties to search in.
2520 *
2521 * Sets the vector of local-name/URI pairs of the cdata section elements
2522 * specified in the cdata-section-elements property.
2523 *
2524 * This method is essentially a copy of getQNameProperties() from
2525 * OutputProperties. Eventually this method should go away and a call
2526 * to setCdataSectionElements(Vector v) should be made directly.
2527 */
2528 private void setCdataSectionElements(String key, Properties props)
2529 {
2530
2531 String s = props.getProperty(key);
2532
2533 if (null != s)
2534 {
2535 // Vector of URI/LocalName pairs
2536 Vector v = new Vector();
2537 int l = s.length();
2538 boolean inCurly = false;
2539 StringBuffer buf = new StringBuffer();
2540
2541 // parse through string, breaking on whitespaces. I do this instead
2542 // of a tokenizer so I can track whitespace inside of curly brackets,
2543 // which theoretically shouldn't happen if they contain legal URLs.
2544 for (int i = 0; i < l; i++)
2545 {
2546 char c = s.charAt(i);
2547
2548 if (Character.isWhitespace(c))
2549 {
2550 if (!inCurly)
2551 {
2552 if (buf.length() > 0)
2553 {
2554 addCdataSectionElement(buf.toString(), v);
2555 buf.setLength(0);
2556 }
2557 continue;
2558 }
2559 }
2560 else if ('{' == c)
2561 inCurly = true;
2562 else if ('}' == c)
2563 inCurly = false;
2564
2565 buf.append(c);
2566 }
2567
2568 if (buf.length() > 0)
2569 {
2570 addCdataSectionElement(buf.toString(), v);
2571 buf.setLength(0);
2572 }
2573 // call the official, public method to set the collected names
2574 setCdataSectionElements(v);
2575 }
2576
2577 }
2578
2579 /**
2580 * Adds a URI/LocalName pair of strings to the list.
2581 *
2582 * @param URI_and_localName String of the form "{uri}local" or "local"
2583 *
2584 * @return a QName object
2585 */
2586 private void addCdataSectionElement(String URI_and_localName, Vector v)
2587 {
2588
2589 StringTokenizer tokenizer =
2590 new StringTokenizer(URI_and_localName, "{}", false);
2591 String s1 = tokenizer.nextToken();
2592 String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
2593
2594 if (null == s2)
2595 {
2596 // add null URI and the local name
2597 v.addElement(null);
2598 v.addElement(s1);
2599 }
2600 else
2601 {
2602 // add URI, then local name
2603 v.addElement(s1);
2604 v.addElement(s2);
2605 }
2606 }
2607
2608 /**
2609 * Remembers the cdata sections specified in the cdata-section-elements.
2610 * The "official way to set URI and localName pairs.
2611 * This method should be used by both Xalan and XSLTC.
2612 *
2613 * @param URI_and_localNames a vector of pairs of Strings (URI/local)
2614 */
2615 public void setCdataSectionElements(Vector URI_and_localNames)
2616 {
2617 m_cdataSectionElements = URI_and_localNames;
2618 }
2619
2620 /**
2621 * Makes sure that the namespace URI for the given qualified attribute name
2622 * is declared.
2623 * @param ns the namespace URI
2624 * @param rawName the qualified name
2625 * @return returns null if no action is taken, otherwise it returns the
2626 * prefix used in declaring the namespace.
2627 * @throws SAXException
2628 */
2629 protected String ensureAttributesNamespaceIsDeclared(
2630 String ns,
2631 String localName,
2632 String rawName)
2633 throws org.xml.sax.SAXException
2634 {
2635
2636 if (ns != null && ns.length() > 0)
2637 {
2638
2639 // extract the prefix in front of the raw name
2640 int index = 0;
2641 String prefixFromRawName =
2642 (index = rawName.indexOf(":")) < 0
2643 ? ""
2644 : rawName.substring(0, index);
2645
2646 if (index > 0)
2647 {
2648 // we have a prefix, lets see if it maps to a namespace
2649 String uri = m_prefixMap.lookupNamespace(prefixFromRawName);
2650 if (uri != null && uri.equals(ns))
2651 {
2652 // the prefix in the raw name is already maps to the given namespace uri
2653 // so we don't need to do anything
2654 return null;
2655 }
2656 else
2657 {
2658 // The uri does not map to the prefix in the raw name,