KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xml > serializer > ToStream


1 /*
2  * Copyright 2001-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 /*
17  * $Id: ToStream.java,v 1.29 2004/02/18 22:57:44 minchau Exp $
18  */

19 package org.apache.xml.serializer;
20
21 import java.io.IOException JavaDoc;
22 import java.io.OutputStream JavaDoc;
23 import java.io.UnsupportedEncodingException JavaDoc;
24 import java.io.Writer JavaDoc;
25 import java.util.Properties JavaDoc;
26 import java.util.StringTokenizer JavaDoc;
27 import java.util.Vector JavaDoc;
28
29 import javax.xml.transform.OutputKeys JavaDoc;
30 import javax.xml.transform.Transformer JavaDoc;
31
32 import org.apache.xml.res.XMLErrorResources;
33 import org.apache.xml.res.XMLMessages;
34 import org.apache.xml.utils.BoolStack;
35 import org.apache.xml.utils.FastStringBuffer;
36 import org.apache.xml.utils.QName;
37 import org.apache.xml.utils.TreeWalker;
38 import org.apache.xml.utils.WrappedRuntimeException;
39 import org.w3c.dom.Node JavaDoc;
40 import org.xml.sax.Attributes JavaDoc;
41 import org.xml.sax.ContentHandler JavaDoc;
42 import org.xml.sax.SAXException JavaDoc;
43
44 //import com.sun.media.sound.IESecurity;
45

46 /**
47  * This abstract class is a base class for other stream
48  * serializers (xml, html, text ...) that write output to a stream.
49  * @author Santiago Pericas-Geertsen
50  * @author G. Todd Miller
51  */

52 abstract public class ToStream extends SerializerBase
53 {
54
55     private static final String JavaDoc COMMENT_BEGIN = "<!--";
56     private static final String JavaDoc COMMENT_END = "-->";
57
58     /** Stack to keep track of disabling output escaping. */
59     protected BoolStack m_disableOutputEscapingStates = new BoolStack();
60
61     /**
62      * Boolean that tells if we already tried to get the converter.
63      */

64     boolean m_triedToGetConverter = false;
65     /**
66      * Method reference to the sun.io.CharToByteConverter#canConvert method
67      * for this encoding. Invalid if m_charToByteConverter is null.
68      */

69     java.lang.reflect.Method JavaDoc m_canConvertMeth;
70
71     /**
72      * Opaque reference to the sun.io.CharToByteConverter for this
73      * encoding.
74      */

75     Object JavaDoc m_charToByteConverter = null;
76
77     /**
78      * Stack to keep track of whether or not we need to
79      * preserve whitespace.
80      *
81      * Used to push/pop values used for the field m_ispreserve, but
82      * m_ispreserve is only relevant if m_doIndent is true.
83      * If m_doIndent is false this field has no impact.
84      *
85      */

86     protected BoolStack m_preserves = new BoolStack();
87
88     /**
89      * State flag to tell if preservation of whitespace
90      * is important.
91      *
92      * Used only in shouldIndent() but only if m_doIndent is true.
93      * If m_doIndent is false this flag has no impact.
94      *
95      */

96     protected boolean m_ispreserve = false;
97
98     /**
99      * State flag that tells if the previous node processed
100      * was text, so we can tell if we should preserve whitespace.
101      *
102      * Used in endDocument() and shouldIndent() but
103      * only if m_doIndent is true.
104      * If m_doIndent is false this flag has no impact.
105      */

106     protected boolean m_isprevtext = false;
107
108     /**
109      * The maximum character size before we have to resort
110      * to escaping.
111      */

112     protected int m_maxCharacter = Encodings.getLastPrintable();
113
114     /**
115      * The system line separator for writing out line breaks.
116      */

117     protected final char[] m_lineSep =
118         System.getProperty("line.separator").toCharArray();
119         
120     /**
121      * True if the the system line separator is to be used.
122      */

123     protected boolean m_lineSepUse = true;
124
125     /**
126      * The length of the line seperator, since the write is done
127      * one character at a time.
128      */

129     protected final int m_lineSepLen = m_lineSep.length;
130
131     /**
132      * Map that tells which characters should have special treatment, and it
133      * provides character to entity name lookup.
134      */

135     protected CharInfo m_charInfo;
136
137     /** True if we control the buffer, and we should flush the output on endDocument. */
138     boolean m_shouldFlush = true;
139
140     /**
141      * Add space before '/>' for XHTML.
142      */

143     protected boolean m_spaceBeforeClose = false;
144
145     /**
146      * Flag to signal that a newline should be added.
147      *
148      * Used only in indent() which is called only if m_doIndent is true.
149      * If m_doIndent is false this flag has no impact.
150      */

151     boolean m_startNewLine;
152
153     /**
154      * Tells if we're in an internal document type subset.
155      */

156     protected boolean m_inDoctype = false;
157
158     /**
159        * Flag to quickly tell if the encoding is UTF8.
160        */

161     boolean m_isUTF8 = false;
162
163     /** The xsl:output properties. */
164     protected Properties JavaDoc m_format;
165
166     /**
167      * remembers if we are in between the startCDATA() and endCDATA() callbacks
168      */

169     protected boolean m_cdataStartCalled = false;
170
171     /**
172      * Default constructor
173      */

174     public ToStream()
175     {
176     }
177
178     /**
179      * This helper method to writes out "]]>" when closing a CDATA section.
180      *
181      * @throws org.xml.sax.SAXException
182      */

183     protected void closeCDATA() throws org.xml.sax.SAXException JavaDoc
184     {
185         try
186         {
187             m_writer.write(CDATA_DELIMITER_CLOSE);
188             // write out a CDATA section closing "]]>"
189
m_cdataTagOpen = false; // Remember that we have done so.
190
}
191         catch (IOException JavaDoc e)
192         {
193             throw new SAXException JavaDoc(e);
194         }
195     }
196
197     /**
198      * Serializes the DOM node. Throws an exception only if an I/O
199      * exception occured while serializing.
200      *
201      * @param elem The element to serialize
202      *
203      * @param node Node to serialize.
204      * @throws IOException An I/O exception occured while serializing
205      */

206     public void serialize(Node JavaDoc node) throws IOException JavaDoc
207     {
208
209         try
210         {
211             TreeWalker walker =
212                 new TreeWalker(this, new org.apache.xml.utils.DOM2Helper());
213
214             walker.traverse(node);
215         }
216         catch (org.xml.sax.SAXException JavaDoc se)
217         {
218             throw new WrappedRuntimeException(se);
219         }
220     }
221
222     /**
223      * Return true if the character is the high member of a surrogate pair.
224      *
225      * NEEDSDOC @param c
226      *
227      * NEEDSDOC ($objectName$) @return
228      */

229     static final boolean isUTF16Surrogate(char c)
230     {
231         return (c & 0xFC00) == 0xD800;
232     }
233
234     /**
235      * Taken from XSLTC
236      */

237     private boolean m_escaping = true;
238
239     /**
240      * Flush the formatter's result stream.
241      *
242      * @throws org.xml.sax.SAXException
243      */

244     protected final void flushWriter() throws org.xml.sax.SAXException JavaDoc
245     {
246         final java.io.Writer JavaDoc writer = m_writer;
247         if (null != writer)
248         {
249             try
250             {
251                 if (writer instanceof WriterToUTF8Buffered)
252                 {
253                     if (m_shouldFlush)
254                          ((WriterToUTF8Buffered) writer).flush();
255                     else
256                          ((WriterToUTF8Buffered) writer).flushBuffer();
257                 }
258                 if (writer instanceof WriterToASCI)
259                 {
260                     if (m_shouldFlush)
261                         writer.flush();
262                 }
263                 else
264                 {
265                     // Flush always.
266
// Not a great thing if the writer was created
267
// by this class, but don't have a choice.
268
writer.flush();
269                 }
270             }
271             catch (IOException JavaDoc ioe)
272             {
273                 throw new org.xml.sax.SAXException JavaDoc(ioe);
274             }
275         }
276     }
277
278     /**
279      * Get the output stream where the events will be serialized to.
280      *
281      * @return reference to the result stream, or null of only a writer was
282      * set.
283      */

284     public OutputStream JavaDoc getOutputStream()
285     {
286
287         if (m_writer instanceof WriterToUTF8Buffered)
288             return ((WriterToUTF8Buffered) m_writer).getOutputStream();
289         if (m_writer instanceof WriterToASCI)
290             return ((WriterToASCI) m_writer).getOutputStream();
291         else
292             return null;
293     }
294
295     // Implement DeclHandler
296

297     /**
298      * Report an element type declaration.
299      *
300      * <p>The content model will consist of the string "EMPTY", the
301      * string "ANY", or a parenthesised group, optionally followed
302      * by an occurrence indicator. The model will be normalized so
303      * that all whitespace is removed,and will include the enclosing
304      * parentheses.</p>
305      *
306      * @param name The element type name.
307      * @param model The content model as a normalized string.
308      * @exception SAXException The application may raise an exception.
309      */

310     public void elementDecl(String JavaDoc name, String JavaDoc model) throws SAXException JavaDoc
311     {
312         // Do not inline external DTD
313
if (m_inExternalDTD)
314             return;
315         try
316         {
317             final java.io.Writer JavaDoc writer = m_writer;
318             if (m_needToOutputDocTypeDecl)
319             {
320                 outputDocTypeDecl(m_elemContext.m_elementName, false);
321                 m_needToOutputDocTypeDecl = false;
322             }
323             if (m_inDoctype)
324             {
325                 writer.write(" [");
326                 writer.write(m_lineSep, 0, m_lineSepLen);
327
328                 m_inDoctype = false;
329             }
330
331             writer.write("<!ELEMENT ");
332             writer.write(name);
333             writer.write(' ');
334             writer.write(model);
335             writer.write('>');
336             writer.write(m_lineSep, 0, m_lineSepLen);
337         }
338         catch (IOException JavaDoc e)
339         {
340             throw new SAXException JavaDoc(e);
341         }
342
343     }
344
345     /**
346      * Report an internal entity declaration.
347      *
348      * <p>Only the effective (first) declaration for each entity
349      * will be reported.</p>
350      *
351      * @param name The name of the entity. If it is a parameter
352      * entity, the name will begin with '%'.
353      * @param value The replacement text of the entity.
354      * @exception SAXException The application may raise an exception.
355      * @see #externalEntityDecl
356      * @see org.xml.sax.DTDHandler#unparsedEntityDecl
357      */

358     public void internalEntityDecl(String JavaDoc name, String JavaDoc value)
359         throws SAXException JavaDoc
360     {
361         // Do not inline external DTD
362
if (m_inExternalDTD)
363             return;
364         try
365         {
366             if (m_needToOutputDocTypeDecl)
367             {
368                 outputDocTypeDecl(m_elemContext.m_elementName, false);
369                 m_needToOutputDocTypeDecl = false;
370             }
371             if (m_inDoctype)
372             {
373                 final java.io.Writer JavaDoc writer = m_writer;
374                 writer.write(" [");
375                 writer.write(m_lineSep, 0, m_lineSepLen);
376
377                 m_inDoctype = false;
378             }
379
380             outputEntityDecl(name, value);
381         }
382         catch (IOException JavaDoc e)
383         {
384             throw new SAXException JavaDoc(e);
385         }
386
387     }
388
389     /**
390      * Output the doc type declaration.
391      *
392      * @param name non-null reference to document type name.
393      * NEEDSDOC @param value
394      *
395      * @throws org.xml.sax.SAXException
396      */

397     void outputEntityDecl(String JavaDoc name, String JavaDoc value) throws IOException JavaDoc
398     {
399         final java.io.Writer JavaDoc writer = m_writer;
400         writer.write("<!ENTITY ");
401         writer.write(name);
402         writer.write(" \"");
403         writer.write(value);
404         writer.write("\">");
405         writer.write(m_lineSep, 0, m_lineSepLen);
406     }
407
408     /**
409      * Output a system-dependent line break.
410      *
411      * @throws org.xml.sax.SAXException
412      */

413     protected final void outputLineSep() throws IOException JavaDoc
414     {
415
416         m_writer.write(m_lineSep, 0, m_lineSepLen);
417     }
418
419     /**
420      * Specifies an output format for this serializer. It the
421      * serializer has already been associated with an output format,
422      * it will switch to the new format. This method should not be
423      * called while the serializer is in the process of serializing
424      * a document.
425      *
426      * @param format The output format to use
427      */

428     public void setOutputFormat(Properties JavaDoc format)
429     {
430
431         boolean shouldFlush = m_shouldFlush;
432
433         init(m_writer, format, false, false);
434
435         m_shouldFlush = shouldFlush;
436     }
437
438     /**
439      * Initialize the serializer with the specified writer and output format.
440      * Must be called before calling any of the serialize methods.
441      *
442      * @param writer The writer to use
443      * @param format The output format
444      * @param shouldFlush True if the writer should be flushed at EndDocument.
445      */

446     private synchronized void init(
447         Writer JavaDoc writer,
448         Properties JavaDoc format,
449         boolean defaultProperties,
450         boolean shouldFlush)
451     {
452
453         m_shouldFlush = shouldFlush;
454
455         
456         // if we are tracing events we need to trace what
457
// characters are written to the output writer.
458
if (m_tracer != null
459          && !(writer instanceof SerializerTraceWriter) )
460             m_writer = new SerializerTraceWriter(writer, m_tracer);
461         else
462             m_writer = writer;
463         
464
465         m_format = format;
466         // m_cdataSectionNames =
467
// OutputProperties.getQNameProperties(
468
// OutputKeys.CDATA_SECTION_ELEMENTS,
469
// format);
470
setCdataSectionElements(OutputKeys.CDATA_SECTION_ELEMENTS, format);
471
472         setIndentAmount(
473             OutputPropertyUtils.getIntProperty(
474                 OutputPropertiesFactory.S_KEY_INDENT_AMOUNT,
475                 format));
476         setIndent(
477             OutputPropertyUtils.getBooleanProperty(OutputKeys.INDENT, format));
478
479         boolean shouldNotWriteXMLHeader =
480             OutputPropertyUtils.getBooleanProperty(
481                 OutputKeys.OMIT_XML_DECLARATION,
482                 format);
483         setOmitXMLDeclaration(shouldNotWriteXMLHeader);
484         setDoctypeSystem(format.getProperty(OutputKeys.DOCTYPE_SYSTEM));
485         String JavaDoc doctypePublic = format.getProperty(OutputKeys.DOCTYPE_PUBLIC);
486         setDoctypePublic(doctypePublic);
487
488         // if standalone was explicitly specified
489
if (format.get(OutputKeys.STANDALONE) != null)
490         {
491             String JavaDoc val = format.getProperty(OutputKeys.STANDALONE);
492             if (defaultProperties)
493                 setStandaloneInternal(val);
494             else
495                 setStandalone(val);
496         }
497
498         setMediaType(format.getProperty(OutputKeys.MEDIA_TYPE));
499
500         if (null != doctypePublic)
501         {
502             if (doctypePublic.startsWith("-//W3C//DTD XHTML"))
503                 m_spaceBeforeClose = true;
504         }
505
506         // initCharsMap();
507
String JavaDoc encoding = getEncoding();
508         if (null == encoding)
509         {
510             encoding =
511                 Encodings.getMimeEncoding(
512                     format.getProperty(OutputKeys.ENCODING));
513             setEncoding(encoding);
514         }
515
516         m_isUTF8 = encoding.equals(Encodings.DEFAULT_MIME_ENCODING);
517         m_maxCharacter = Encodings.getLastPrintable(encoding);
518
519         // Access this only from the Hashtable level... we don't want to
520
// get default properties.
521
String JavaDoc entitiesFileName =
522             (String JavaDoc) format.get(OutputPropertiesFactory.S_KEY_ENTITIES);
523
524         if (null != entitiesFileName)
525         {
526
527             String JavaDoc method =
528                 (String JavaDoc) format.get(OutputKeys.METHOD);
529             
530             m_charInfo = CharInfo.getCharInfo(entitiesFileName, method);
531         }
532
533     }
534
535     /**
536      * Initialize the serializer with the specified writer and output format.
537      * Must be called before calling any of the serialize methods.
538      *
539      * @param writer The writer to use
540      * @param format The output format
541      */

542     private synchronized void init(Writer JavaDoc writer, Properties JavaDoc format)
543     {
544         init(writer, format, false, false);
545     }
546     /**
547      * Initialize the serializer with the specified output stream and output
548      * format. Must be called before calling any of the serialize methods.
549      *
550      * @param output The output stream to use
551      * @param format The output format
552      * @param defaultProperties true if the properties are the default
553      * properties
554      *
555      * @throws UnsupportedEncodingException The encoding specified in the
556      * output format is not supported
557      */

558     protected synchronized void init(
559         OutputStream JavaDoc output,
560         Properties JavaDoc format,
561         boolean defaultProperties)
562         throws UnsupportedEncodingException JavaDoc
563     {
564
565         String JavaDoc encoding = getEncoding();
566         if (encoding == null)
567         {
568             // if not already set then get it from the properties
569
encoding =
570                 Encodings.getMimeEncoding(
571                     format.getProperty(OutputKeys.ENCODING));
572             setEncoding(encoding);
573         }
574
575         if (encoding.equalsIgnoreCase("UTF-8"))
576         {
577             m_isUTF8 = true;
578             // if (output instanceof java.io.BufferedOutputStream)
579
// {
580
// init(new WriterToUTF8(output), format, defaultProperties, true);
581
// }
582
// else if (output instanceof java.io.FileOutputStream)
583
// {
584
// init(new WriterToUTF8Buffered(output), format, defaultProperties, true);
585
// }
586
// else
587
// {
588
// // Not sure what to do in this case. I'm going to be conservative
589
// // and not buffer.
590
// init(new WriterToUTF8(output), format, defaultProperties, true);
591
// }
592

593
594                 init(
595                     new WriterToUTF8Buffered(output),
596                     format,
597                     defaultProperties,
598                     true);
599
600
601         }
602         else if (
603             encoding.equals("WINDOWS-1250")
604                 || encoding.equals("US-ASCII")
605                 || encoding.equals("ASCII"))
606         {
607             init(new WriterToASCI(output), format, defaultProperties, true);
608         }
609         else
610         {
611             Writer JavaDoc osw;
612
613             try
614             {
615                 osw = Encodings.getWriter(output, encoding);
616             }
617             catch (UnsupportedEncodingException JavaDoc uee)
618             {
619                 System.out.println(
620                     "Warning: encoding \""
621                         + encoding
622                         + "\" not supported"
623                         + ", using "
624                         + Encodings.DEFAULT_MIME_ENCODING);
625
626                 encoding = Encodings.DEFAULT_MIME_ENCODING;
627                 setEncoding(encoding);
628                 osw = Encodings.getWriter(output, encoding);
629             }
630
631             m_maxCharacter = Encodings.getLastPrintable(encoding);
632
633             init(osw, format, defaultProperties, true);
634         }
635
636     }
637
638     /**
639      * Returns the output format for this serializer.
640      *
641      * @return The output format in use
642      */

643     public Properties JavaDoc getOutputFormat()
644     {
645         return m_format;
646     }
647
648     /**
649      * Specifies a writer to which the document should be serialized.
650      * This method should not be called while the serializer is in
651      * the process of serializing a document.
652      *
653      * @param writer The output writer stream
654      */

655     public void setWriter(Writer JavaDoc writer)
656     {
657         // if we are tracing events we need to trace what
658
// characters are written to the output writer.
659
if (m_tracer != null
660          && !(writer instanceof SerializerTraceWriter) )
661             m_writer = new SerializerTraceWriter(writer, m_tracer);
662         else
663             m_writer = writer;
664     }
665     
666     /**
667      * Set if the operating systems end-of-line line separator should
668      * be used when serializing. If set false NL character
669      * (decimal 10) is left alone, otherwise the new-line will be replaced on
670      * output with the systems line separator. For example on UNIX this is
671      * NL, while on Windows it is two characters, CR NL, where CR is the
672      * carriage-return (decimal 13).
673      *
674      * @param use_sytem_line_break True if an input NL is replaced with the
675      * operating systems end-of-line separator.
676      * @return The previously set value of the serializer.
677      */

678     public boolean setLineSepUse(boolean use_sytem_line_break)
679     {
680         boolean oldValue = m_lineSepUse;
681         m_lineSepUse = use_sytem_line_break;
682         return oldValue;
683     }
684
685     /**
686      * Specifies an output stream to which the document should be
687      * serialized. This method should not be called while the
688      * serializer is in the process of serializing a document.
689      * <p>
690      * The encoding specified in the output properties is used, or
691      * if no encoding was specified, the default for the selected
692      * output method.
693      *
694      * @param output The output stream
695      */

696     public void setOutputStream(OutputStream JavaDoc output)
697     {
698
699         try
700         {
701             Properties JavaDoc format;
702             if (null == m_format)
703                 format =
704                     OutputPropertiesFactory.getDefaultMethodProperties(
705                         Method.XML);
706             else
707                 format = m_format;
708             init(output, format, true);
709         }
710         catch (UnsupportedEncodingException JavaDoc uee)
711         {
712
713             // Should have been warned in init, I guess...
714
}
715     }
716
717     /**
718      * @see org.apache.xml.serializer.SerializationHandler#setEscaping(boolean)
719      */

720     public boolean setEscaping(boolean escape)
721     {
722         final boolean temp = m_escaping;
723         m_escaping = escape;
724         return temp;
725
726     }
727
728
729     /**
730      * Might print a newline character and the indentation amount
731      * of the given depth.
732      *
733      * @param depth the indentation depth (element nesting depth)
734      *
735      * @throws org.xml.sax.SAXException if an error occurs during writing.
736      */

737     protected void indent(int depth) throws IOException JavaDoc
738     {
739
740         if (m_startNewLine)
741             outputLineSep();
742         /* For m_indentAmount > 0 this extra test might be slower
743          * but Xalan's default value is 0, so this extra test
744          * will run faster in that situation.
745          */

746         if (m_indentAmount > 0)
747             printSpace(depth * m_indentAmount);
748
749     }
750     
751     /**
752      * Indent at the current element nesting depth.
753      * @throws IOException
754      */

755     protected void indent() throws IOException JavaDoc
756     {
757         indent(m_elemContext.m_currentElemDepth);
758     }
759     /**
760      * Prints <var>n</var> spaces.
761      * @param pw The character output stream to use.
762      * @param n Number of spaces to print.
763      *
764      * @throws org.xml.sax.SAXException if an error occurs when writing.
765      */

766     private void printSpace(int n) throws IOException JavaDoc
767     {
768         final java.io.Writer JavaDoc writer = m_writer;
769         for (int i = 0; i < n; i++)
770         {
771             writer.write(' ');
772         }
773
774     }
775
776     /**
777      * Report an attribute type declaration.
778      *
779      * <p>Only the effective (first) declaration for an attribute will
780      * be reported. The type will be one of the strings "CDATA",
781      * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY",
782      * "ENTITIES", or "NOTATION", or a parenthesized token group with
783      * the separator "|" and all whitespace removed.</p>
784      *
785      * @param eName The name of the associated element.
786      * @param aName The name of the attribute.
787      * @param type A string representing the attribute type.
788      * @param valueDefault A string representing the attribute default
789      * ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if
790      * none of these applies.
791      * @param value A string representing the attribute's default value,
792      * or null if there is none.
793      * @exception SAXException The application may raise an exception.
794      */

795     public void attributeDecl(
796         String JavaDoc eName,
797         String JavaDoc aName,
798         String JavaDoc type,
799         String JavaDoc valueDefault,
800         String JavaDoc value)
801         throws SAXException JavaDoc
802     {
803         // Do not inline external DTD
804
if (m_inExternalDTD)
805             return;
806         try
807         {
808             final java.io.Writer JavaDoc writer = m_writer;
809             if (m_needToOutputDocTypeDecl)
810             {
811                 outputDocTypeDecl(m_elemContext.m_elementName, false);
812                 m_needToOutputDocTypeDecl = false;
813             }
814             if (m_inDoctype)
815             {
816                 writer.write(" [");
817                 writer.write(m_lineSep, 0, m_lineSepLen);
818
819                 m_inDoctype = false;
820             }
821
822             writer.write("<!ATTLIST ");
823             writer.write(eName);
824             writer.write(' ');
825
826             writer.write(aName);
827             writer.write(' ');
828             writer.write(type);
829             if (valueDefault != null)
830             {
831                 writer.write(' ');
832                 writer.write(valueDefault);
833             }
834
835             //writer.write(" ");
836
//writer.write(value);
837
writer.write('>');
838             writer.write(m_lineSep, 0, m_lineSepLen);
839         }
840         catch (IOException JavaDoc e)
841         {
842             throw new SAXException JavaDoc(e);
843         }
844     }
845
846     /**
847      * Get the character stream where the events will be serialized to.
848      *
849      * @return Reference to the result Writer, or null.
850      */

851     public Writer JavaDoc getWriter()
852     {
853         return m_writer;
854     }
855
856     /**
857      * Report a parsed external entity declaration.
858      *
859      * <p>Only the effective (first) declaration for each entity
860      * will be reported.</p>
861      *
862      * @param name The name of the entity. If it is a parameter
863      * entity, the name will begin with '%'.
864      * @param publicId The declared public identifier of the entity, or
865      * null if none was declared.
866      * @param systemId The declared system identifier of the entity.
867      * @exception SAXException The application may raise an exception.
868      * @see #internalEntityDecl
869      * @see org.xml.sax.DTDHandler#unparsedEntityDecl
870      */

871     public void externalEntityDecl(
872         String JavaDoc name,
873         String JavaDoc publicId,
874         String JavaDoc systemId)
875         throws SAXException JavaDoc
876     {
877     }
878
879     /**
880      * Tell if this character can be written without escaping.
881      */

882     protected boolean escapingNotNeeded(char ch)
883     {
884         if (ch < 127)
885         {
886             if (ch >= 0x20 || (0x0A == ch || 0x0D == ch || 0x09 == ch))
887                 return true;
888             else
889                 return false;
890         }
891
892         if (null == m_charToByteConverter && false == m_triedToGetConverter)
893         {
894             m_triedToGetConverter = true;
895             try
896             {
897                 m_charToByteConverter =
898                     Encodings.getCharToByteConverter(getEncoding());
899                 if (null != m_charToByteConverter)
900                 {
901                     Class JavaDoc argsTypes[] = new Class JavaDoc[1];
902                     argsTypes[0] = Character.TYPE;
903                     Class JavaDoc convClass = m_charToByteConverter.getClass();
904                     m_canConvertMeth =
905                         convClass.getMethod("canConvert", argsTypes);
906                 }
907             }
908             catch (Exception JavaDoc e)
909             {
910                 // This is just an assert: no action at the moment.
911
System.err.println("Warning: " + e.getMessage());
912             }
913         }
914         if (null != m_charToByteConverter)
915         {
916             try
917             {
918                 Object JavaDoc args[] = new Object JavaDoc[1];
919                 args[0] = new Character JavaDoc(ch);
920                 Boolean JavaDoc bool =
921                     (Boolean JavaDoc) m_canConvertMeth.invoke(
922                         m_charToByteConverter,
923                         args);
924                 return bool.booleanValue()
925                     ? !Character.isISOControl(ch)
926                     : false;
927             }
928             catch (java.lang.reflect.InvocationTargetException JavaDoc ite)
929             {
930                 // This is just an assert: no action at the moment.
931
System.err.println(
932                     "Warning: InvocationTargetException in canConvert!");
933             }
934             catch (java.lang.IllegalAccessException JavaDoc iae)
935             {
936                 // This is just an assert: no action at the moment.
937
System.err.println(
938                     "Warning: IllegalAccessException in canConvert!");
939             }
940         }
941         // fallback!
942
return (ch <= m_maxCharacter);
943     }
944
945     /**
946      * Once a surrogate has been detected, write out the pair of
947      * characters as a single character reference.
948      *
949      * @param c the first part of the surrogate.
950      * @param ch Character array.
951      * @param i position Where the surrogate was detected.
952      * @param end The end index of the significant characters.
953      * @throws IOException
954      * @throws org.xml.sax.SAXException if invalid UTF-16 surrogate detected.
955      */

956     protected void writeUTF16Surrogate(char c, char ch[], int i, int end)
957         throws IOException JavaDoc
958     {
959
960         // UTF-16 surrogate
961
int surrogateValue = getURF16SurrogateValue(c, ch, i, end);
962
963         final java.io.Writer JavaDoc writer = m_writer;
964         writer.write('&');
965         writer.write('#');
966
967         // writer.write('x');
968
writer.write(Integer.toString(surrogateValue));
969         writer.write(';');
970     }
971
972     /**
973      * Once a surrogate has been detected, get the pair as a single integer
974      * value.
975      *
976      * @param c the first part of the surrogate.
977      * @param ch Character array.
978      * @param i position Where the surrogate was detected.
979      * @param end The end index of the significant characters.
980      * @return the integer value of the UTF-16 surrogate.
981      * @throws org.xml.sax.SAXException if invalid UTF-16 surrogate detected.
982      */

983     int getURF16SurrogateValue(char c, char ch[], int i, int end)
984         throws IOException JavaDoc
985     {
986
987         int next;
988
989         if (i + 1 >= end)
990         {
991             throw new IOException JavaDoc(
992                 XMLMessages.createXMLMessage(
993                     XMLErrorResources.ER_INVALID_UTF16_SURROGATE,
994                     new Object JavaDoc[] { Integer.toHexString((int) c)}));
995             //"Invalid UTF-16 surrogate detected: "
996

997             //+Integer.toHexString((int)c)+ " ?");
998
}
999         else
1000        {
1001            next = ch[++i];
1002
1003            if (!(0xdc00 <= next && next < 0xe000))
1004                throw new IOException JavaDoc(
1005                    XMLMessages.createXMLMessage(
1006                        XMLErrorResources.ER_INVALID_UTF16_SURROGATE,
1007                        new Object JavaDoc[] {
1008                            Integer.toHexString((int) c)
1009                                + " "
1010                                + Integer.toHexString(next)}));
1011            //"Invalid UTF-16 surrogate detected: "
1012

1013            //+Integer.toHexString((int)c)+" "+Integer.toHexString(next));
1014
next = ((c - 0xd800) << 10) + next - 0xdc00 + 0x00010000;
1015        }
1016
1017        return next;
1018    }
1019
1020    /**
1021     * Handle one of the default entities, return false if it
1022     * is not a default entity.
1023     *
1024     * @param ch character to be escaped.
1025     * @param i index into character array.
1026     * @param chars non-null reference to character array.
1027     * @param len length of chars.
1028     * @param fromTextNode true if the characters being processed
1029     * are from a text node, false if they are from an attribute value
1030     * @param escLF true if the linefeed should be escaped.
1031     *
1032     * @return i+1 if the character was written, else i.
1033     *
1034     * @throws java.io.IOException
1035     */

1036    protected int accumDefaultEntity(
1037        java.io.Writer JavaDoc writer,
1038        char ch,
1039        int i,
1040        char[] chars,
1041        int len,
1042        boolean fromTextNode,
1043        boolean escLF)
1044        throws IOException JavaDoc
1045    {
1046
1047        if (!escLF && CharInfo.S_LINEFEED == ch)
1048        {
1049            writer.write(m_lineSep, 0, m_lineSepLen);
1050        }
1051        else
1052        {
1053            // if this is text node character and a special one of those,
1054
// or if this is a character from attribute value and a special one of those
1055
if ((fromTextNode && m_charInfo.isSpecialTextChar(ch)) || (!fromTextNode && m_charInfo.isSpecialAttrChar(ch)))
1056            {
1057                String JavaDoc entityRef = m_charInfo.getEntityNameForChar(ch);
1058
1059                if (null != entityRef)
1060                {
1061                    writer.write('&');
1062                    writer.write(entityRef);
1063                    writer.write(';');
1064                }
1065                else
1066                    return i;
1067            }
1068            else
1069                return i;
1070        }
1071
1072        return i + 1;
1073
1074    }
1075    /**
1076     * Normalize the characters, but don't escape.
1077     *
1078     * @param ch The characters from the XML document.
1079     * @param start The start position in the array.
1080     * @param length The number of characters to read from the array.
1081     * @param isCData true if a CDATA block should be built around the characters.
1082     * @param useSystemLineSeparator true if the operating systems
1083     * end-of-line separator should be output rather than a new-line character.
1084     *
1085     * @throws IOException
1086     * @throws org.xml.sax.SAXException
1087     */

1088    void writeNormalizedChars(
1089        char ch[],
1090        int start,
1091        int length,
1092        boolean isCData,
1093        boolean useSystemLineSeparator)
1094        throws IOException JavaDoc, org.xml.sax.SAXException JavaDoc
1095    {
1096        final java.io.Writer JavaDoc writer = m_writer;
1097        int end = start + length;
1098
1099        for (int i = start; i < end; i++)
1100        {
1101            char c = ch[i];
1102
1103            if (CharInfo.S_LINEFEED == c && useSystemLineSeparator)
1104            {
1105                writer.write(m_lineSep, 0, m_lineSepLen);
1106            }
1107            else if (isCData && (!escapingNotNeeded(c)))
1108            {
1109                // if (i != 0)
1110
if (m_cdataTagOpen)
1111                    closeCDATA();
1112
1113                // This needs to go into a function...
1114
if (isUTF16Surrogate(c))
1115                {
1116                    writeUTF16Surrogate(c, ch, i, end);
1117                    i++ ; // process two input characters
1118
}
1119                else
1120                {
1121                    writer.write("&#");
1122
1123                    String JavaDoc intStr = Integer.toString((int) c);
1124
1125                    writer.write(intStr);
1126                    writer.write(';');
1127                }
1128
1129                // if ((i != 0) && (i < (end - 1)))
1130
// if (!m_cdataTagOpen && (i < (end - 1)))
1131
// {
1132
// writer.write(CDATA_DELIMITER_OPEN);
1133
// m_cdataTagOpen = true;
1134
// }
1135
}
1136            else if (
1137                isCData
1138                    && ((i < (end - 2))
1139                        && (']' == c)
1140                        && (']' == ch[i + 1])
1141                        && ('>' == ch[i + 2])))
1142            {
1143                writer.write(CDATA_CONTINUE);
1144
1145                i += 2;
1146            }
1147            else
1148            {
1149                if (escapingNotNeeded(c))
1150                {
1151                    if (isCData && !m_cdataTagOpen)
1152                    {
1153                        writer.write(CDATA_DELIMITER_OPEN);
1154                        m_cdataTagOpen = true;
1155                    }
1156                    writer.write(c);
1157                }
1158
1159                // This needs to go into a function...
1160
else if (isUTF16Surrogate(c))
1161                {
1162                    if (m_cdataTagOpen)
1163                        closeCDATA();
1164                    writeUTF16Surrogate(c, ch, i, end);
1165                    i++; // process two input characters
1166
}
1167                else
1168                {
1169                    if (m_cdataTagOpen)
1170                        closeCDATA();
1171                    writer.write("&#");
1172
1173                    String JavaDoc intStr = Integer.toString((int) c);
1174
1175                    writer.write(intStr);
1176                    writer.write(';');
1177                }
1178            }
1179        }
1180
1181    }
1182
1183    /**
1184     * Ends an un-escaping section.
1185     *
1186     * @see #startNonEscaping
1187     *
1188     * @throws org.xml.sax.SAXException
1189     */

1190    public void endNonEscaping() throws org.xml.sax.SAXException JavaDoc
1191    {
1192        m_disableOutputEscapingStates.pop();
1193    }
1194
1195    /**
1196     * Starts an un-escaping section. All characters printed within an un-
1197     * escaping section are printed as is, without escaping special characters
1198     * into entity references. Only XML and HTML serializers need to support
1199     * this method.
1200     * <p> The contents of the un-escaping section will be delivered through the
1201     * regular <tt>characters</tt> event.
1202     *
1203     * @throws org.xml.sax.SAXException
1204     */

1205    public void startNonEscaping() throws org.xml.sax.SAXException JavaDoc
1206    {
1207        m_disableOutputEscapingStates.push(true);
1208    }
1209
1210    /**
1211     * Receive notification of cdata.
1212     *
1213     * <p>The Parser will call this method to report each chunk of
1214     * character data. SAX parsers may return all contiguous character
1215     * data in a single chunk, or they may split it into several
1216     * chunks; however, all of the characters in any single event
1217     * must come from the same external entity, so that the Locator
1218     * provides useful information.</p>
1219     *
1220     * <p>The application must not attempt to read from the array
1221     * outside of the specified range.</p>
1222     *
1223     * <p>Note that some parsers will report whitespace using the
1224     * ignorableWhitespace() method rather than this one (validating
1225     * parsers must do so).</p>
1226     *
1227     * @param ch The characters from the XML document.
1228     * @param start The start position in the array.
1229     * @param length The number of characters to read from the array.
1230     * @throws org.xml.sax.SAXException Any SAX exception, possibly
1231     * wrapping another exception.
1232     * @see #ignorableWhitespace
1233     * @see org.xml.sax.Locator
1234     *
1235     * @throws org.xml.sax.SAXException
1236     */

1237    protected void cdata(char ch[], int start, final int length)
1238        throws org.xml.sax.SAXException JavaDoc
1239    {
1240
1241        try
1242        {
1243            final int old_start = start;
1244            if (m_elemContext.m_startTagOpen)
1245            {
1246                closeStartTag();
1247                m_elemContext.m_startTagOpen = false;
1248            }
1249            m_ispreserve = true;
1250
1251            if (shouldIndent())
1252                indent();
1253
1254            boolean writeCDataBrackets =
1255                (((length >= 1) && escapingNotNeeded(ch[start])));
1256
1257            /* Write out the CDATA opening delimiter only if
1258             * we are supposed to, and if we are not already in
1259             * the middle of a CDATA section
1260             */

1261            if (writeCDataBrackets && !m_cdataTagOpen)
1262            {
1263                m_writer.write(CDATA_DELIMITER_OPEN);
1264                m_cdataTagOpen = true;
1265            }
1266
1267            // writer.write(ch, start, length);
1268
if (isEscapingDisabled())
1269            {
1270                charactersRaw(ch, start, length);
1271            }
1272            else
1273                writeNormalizedChars(ch, start, length, true, m_lineSepUse);
1274
1275            /* used to always write out CDATA closing delimiter here,
1276             * but now we delay, so that we can merge CDATA sections on output.
1277             * need to write closing delimiter later
1278             */

1279            if (writeCDataBrackets)
1280            {
1281                /* if the CDATA section ends with ] don't leave it open
1282                 * as there is a chance that an adjacent CDATA sections
1283                 * starts with ]>.
1284                 * We don't want to merge ]] with > , or ] with ]>
1285                 */

1286                if (ch[start + length - 1] == ']')
1287                    closeCDATA();
1288            }
1289
1290            // time to fire off CDATA event
1291
if (m_tracer != null)
1292                super.fireCDATAEvent(ch, old_start, length);
1293        }
1294        catch (IOException JavaDoc ioe)
1295        {
1296            throw new org.xml.sax.SAXException JavaDoc(
1297                XMLMessages.createXMLMessage(
1298                    XMLErrorResources.ER_OIERROR,
1299                    null),
1300                ioe);
1301            //"IO error", ioe);
1302
}
1303    }
1304
1305    /**
1306     * Tell if the character escaping should be disabled for the current state.
1307     *
1308     * @return true if the character escaping should be disabled.
1309     */

1310    private boolean isEscapingDisabled()
1311    {
1312        return m_disableOutputEscapingStates.peekOrFalse();
1313    }
1314
1315    /**
1316     * If available, when the disable-output-escaping attribute is used,
1317     * output raw text without escaping.
1318     *
1319     * @param ch The characters from the XML document.
1320     * @param start The start position in the array.
1321     * @param length The number of characters to read from the array.
1322     *
1323     * @throws org.xml.sax.SAXException
1324     */

1325    protected void charactersRaw(char ch[], int start, int length)
1326        throws org.xml.sax.SAXException JavaDoc
1327    {
1328
1329        if (m_inEntityRef)
1330            return;
1331        try
1332        {
1333            if (m_elemContext.m_startTagOpen)
1334            {
1335                closeStartTag();
1336                m_elemContext.m_startTagOpen = false;
1337            }
1338
1339            m_ispreserve = true;
1340
1341            m_writer.write(ch, start, length);
1342        }
1343        catch (IOException JavaDoc e)
1344        {
1345            throw new SAXException JavaDoc(e);
1346        }
1347
1348    }
1349
1350    /**
1351     * Receive notification of character data.
1352     *
1353     * <p>The Parser will call this method to report each chunk of
1354     * character data. SAX parsers may return all contiguous character
1355     * data in a single chunk, or they may split it into several
1356     * chunks; however, all of the characters in any single event
1357     * must come from the same external entity, so that the Locator
1358     * provides useful information.</p>
1359     *
1360     * <p>The application must not attempt to read from the array
1361     * outside of the specified range.</p>
1362     *
1363     * <p>Note that some parsers will report whitespace using the
1364     * ignorableWhitespace() method rather than this one (validating
1365     * parsers must do so).</p>
1366     *
1367     * @param chars The characters from the XML document.
1368     * @param start The start position in the array.
1369     * @param length The number of characters to read from the array.
1370     * @throws org.xml.sax.SAXException Any SAX exception, possibly
1371     * wrapping another exception.
1372     * @see #ignorableWhitespace
1373     * @see org.xml.sax.Locator
1374     *
1375     * @throws org.xml.sax.SAXException
1376     */

1377    public void characters(final char chars[], final int start, final int length)
1378        throws org.xml.sax.SAXException JavaDoc
1379    {
1380        if (m_elemContext.m_startTagOpen)
1381        {
1382            closeStartTag();
1383            m_elemContext.m_startTagOpen = false;
1384        }
1385        else if (m_needToCallStartDocument)
1386        {
1387            startDocumentInternal();
1388        }
1389
1390        if (m_cdataStartCalled || m_elemContext.m_isCdataSection)
1391        {
1392            /* either due to startCDATA() being called or due to
1393             * cdata-section-elements atribute, we need this as cdata
1394             */

1395            cdata(chars, start, length);
1396
1397            return;
1398        }
1399
1400        if (m_cdataTagOpen)
1401            closeCDATA();
1402        // the check with _escaping is a bit of a hack for XLSTC
1403

1404        if (m_disableOutputEscapingStates.peekOrFalse() || (!m_escaping))
1405        {
1406            charactersRaw(chars, start, length);
1407
1408            // time to fire off characters generation event
1409
if (m_tracer != null)
1410                super.fireCharEvent(chars, start, length);
1411
1412            return;
1413        }
1414
1415        if (m_elemContext.m_startTagOpen)
1416        {
1417            closeStartTag();
1418            m_elemContext.m_startTagOpen = false;
1419        }
1420
1421        
1422        try
1423        {
1424            int i;
1425            char ch1;
1426            int startClean;
1427            
1428            // skip any leading whitspace
1429
// don't go off the end and use a hand inlined version
1430
// of isWhitespace(ch)
1431
final int end = start + length;
1432            int lastDirty = start - 1; // last character that needed processing
1433
for (i = start;
1434                ((i < end)
1435                    && ((ch1 = chars[i]) == 0x20
1436                        || (ch1 == 0xA && m_lineSepUse)
1437                        || ch1 == 0xD
1438                        || ch1 == 0x09));
1439                i++)
1440            {
1441                /*
1442                 * We are processing leading whitespace, but are doing the same
1443                 * processing for dirty characters here as for non-whitespace.
1444                 *
1445                 */

1446                if (!m_charInfo.isTextASCIIClean(ch1))
1447                {
1448                    lastDirty = processDirty(chars,end, i,ch1, lastDirty, true);
1449                    i = lastDirty;
1450                }
1451            }
1452            /* If there is some non-whitespace, mark that we may need
1453             * to preserve this. This is only important if we have indentation on.
1454             */

1455            if (i < end)
1456                m_ispreserve = true;
1457                
1458
1459// int lengthClean; // number of clean characters in a row
1460
// final boolean[] isAsciiClean = m_charInfo.getASCIIClean();
1461

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                if (
1480                
1481                    (escapingNotNeeded(ch) && (!m_charInfo.isSpecialTextChar(ch)))
1482                        || ('"' == ch))
1483                {
1484                    ; // a character needing no special processing
1485
}
1486                else
1487                {
1488                    lastDirty = processDirty(chars,end, i, ch, lastDirty, true);
1489                    i = lastDirty;
1490                }
1491            }
1492            
1493            // we've reached the end. Any clean characters at the
1494
// end of the array than need to be written out?
1495
startClean = lastDirty + 1;
1496            if (i > startClean)
1497            {
1498                int lengthClean = i - startClean;
1499                m_writer.write(chars, startClean, lengthClean);
1500            }
1501
1502            // For indentation purposes, mark that we've just writen text out
1503
m_isprevtext = true;
1504        }
1505        catch (IOException JavaDoc e)
1506        {
1507            throw new SAXException JavaDoc(e);
1508        }
1509
1510        // time to fire off characters generation event
1511
if (m_tracer != null)
1512            super.fireCharEvent(chars, start, length);
1513    }
1514    /**
1515     * Process a dirty character and any preeceding clean characters
1516     * that were not yet processed.
1517     * @param chars array of characters being processed
1518     * @param end one (1) beyond the last character
1519     * in chars to be processed
1520     * @param i the index of the dirty character
1521     * @param ch the character in chars[i]
1522     * @param lastDirty the last dirty character previous to i
1523     * @param fromTextNode true if the characters being processed are
1524     * from a text node, false if they are from an attribute value.
1525     * @return the index of the last character processed
1526     */

1527    private int processDirty(
1528        char[] chars,
1529        int end,
1530        int i,
1531        char ch,
1532        int lastDirty,
1533        boolean fromTextNode) throws IOException JavaDoc
1534    {
1535        int startClean = lastDirty + 1;
1536        // if we have some clean characters accumulated
1537
// process them before the dirty one.
1538
if (i > startClean)
1539        {
1540            int lengthClean = i - startClean;
1541            m_writer.write(chars, startClean, lengthClean);
1542        }
1543
1544        // process the "dirty" character
1545
if (CharInfo.S_LINEFEED == ch && fromTextNode)
1546        {
1547            m_writer.write(m_lineSep, 0, m_lineSepLen);
1548        }
1549        else
1550        {
1551            startClean =
1552                accumDefaultEscape(
1553                    m_writer,
1554                    (char)ch,
1555                    i,
1556                    chars,
1557                    end,
1558                    fromTextNode,
1559                    false);
1560            i = startClean - 1;
1561        }
1562        // Return the index of the last character that we just processed
1563
// which is a dirty character.
1564
return i;
1565    }
1566
1567    /**
1568     * Receive notification of character data.
1569     *
1570     * @param s The string of characters to process.
1571     *
1572     * @throws org.xml.sax.SAXException
1573     */

1574    public void characters(String JavaDoc s) throws org.xml.sax.SAXException JavaDoc
1575    {
1576        final int length = s.length();
1577        if (length > m_charsBuff.length)
1578        {
1579            m_charsBuff = new char[length * 2 + 1];
1580        }
1581        s.getChars(0, length, m_charsBuff, 0);
1582        characters(m_charsBuff, 0, length);
1583    }
1584
1585    /**
1586     * Escape and writer.write a character.
1587     *
1588     * @param ch character to be escaped.
1589     * @param i index into character array.
1590     * @param chars non-null reference to character array.
1591     * @param len length of chars.
1592     * @param fromTextNode true if the characters being processed are
1593     * from a text node, false if the characters being processed are from
1594     * an attribute value.
1595     * @param escLF true if the linefeed should be escaped.
1596     *
1597     * @return i+1 if a character was written, i+2 if two characters
1598     * were written out, else return i.
1599     *
1600     * @throws org.xml.sax.SAXException
1601     */

1602    protected int accumDefaultEscape(
1603        Writer JavaDoc writer,
1604        char ch,
1605        int i,
1606        char[] chars,
1607        int len,
1608        boolean fromTextNode,
1609        boolean escLF)
1610        throws IOException JavaDoc
1611    {
1612
1613        int pos = accumDefaultEntity(writer, ch, i, chars, len, fromTextNode, escLF);
1614
1615        if (i == pos)
1616        {
1617            if (0xd800 <= ch && ch < 0xdc00)
1618            {
1619
1620                // UTF-16 surrogate
1621
int next;
1622
1623                if (i + 1 >= len)
1624                {
1625                    throw new IOException JavaDoc(
1626                        XMLMessages.createXMLMessage(
1627                            XMLErrorResources.ER_INVALID_UTF16_SURROGATE,
1628                            new Object JavaDoc[] { Integer.toHexString(ch)}));
1629                    //"Invalid UTF-16 surrogate detected: "
1630

1631                    //+Integer.toHexString(ch)+ " ?");
1632
}
1633                else
1634                {
1635                    next = chars[++i];
1636
1637                    if (!(0xdc00 <= next && next < 0xe000))
1638                        throw new IOException JavaDoc(
1639                            XMLMessages.createXMLMessage(
1640                                XMLErrorResources
1641                                    .ER_INVALID_UTF16_SURROGATE,
1642                                new Object JavaDoc[] {
1643                                    Integer.toHexString(ch)
1644                                        + " "
1645                                        + Integer.toHexString(next)}));
1646                    //"Invalid UTF-16 surrogate detected: "
1647

1648                    //+Integer.toHexString(ch)+" "+Integer.toHexString(next));
1649
next = ((ch - 0xd800) << 10) + next - 0xdc00 + 0x00010000;
1650                }
1651
1652                writer.write("&#");
1653                writer.write(Integer.toString(next));
1654                writer.write(';');
1655                pos += 2; // count the two characters that went into writing out this entity
1656
}
1657            else
1658            {
1659                if (!escapingNotNeeded(ch) ||
1660                    ( (fromTextNode && m_charInfo.isSpecialTextChar(ch))
1661                     || (!fromTextNode && m_charInfo.isSpecialAttrChar(ch))))
1662                {
1663                    writer.write("&#");
1664                    writer.write(Integer.toString(ch));
1665                    writer.write(';');
1666                }
1667                else
1668                {
1669                    writer.write(ch);
1670                }
1671                pos++; // count the single character that was processed
1672
}
1673
1674        }
1675        return pos;
1676    }
1677
1678    /**
1679     * Receive notification of the beginning of an element, although this is a
1680     * SAX method additional namespace or attribute information can occur before
1681     * or after this call, that is associated with this element.
1682     *
1683     *
1684     * @param namespaceURI The Namespace URI, or the empty string if the
1685     * element has no Namespace URI or if Namespace
1686     * processing is not being performed.
1687     * @param localName The local name (without prefix), or the
1688     * empty string if Namespace processing is not being
1689     * performed.
1690     * @param name The element type name.
1691     * @param atts The attributes attached to the element, if any.
1692     * @throws org.xml.sax.SAXException Any SAX exception, possibly
1693     * wrapping another exception.
1694     * @see org.xml.sax.ContentHandler#startElement
1695     * @see org.xml.sax.ContentHandler#endElement
1696     * @see org.xml.sax.AttributeList
1697     *
1698     * @throws org.xml.sax.SAXException
1699     */

1700    public void startElement(
1701        String JavaDoc namespaceURI,
1702        String JavaDoc localName,
1703        String JavaDoc name,
1704        Attributes JavaDoc atts)
1705        throws org.xml.sax.SAXException JavaDoc
1706    {
1707        if (m_inEntityRef)
1708            return;
1709
1710        if (m_needToCallStartDocument)
1711        {
1712            startDocumentInternal();
1713            m_needToCallStartDocument = false;
1714        }
1715        else if (m_cdataTagOpen)
1716            closeCDATA();
1717        try
1718        {
1719            if ((true == m_needToOutputDocTypeDecl)
1720                && (null != getDoctypeSystem()))
1721            {
1722                outputDocTypeDecl(name, true);
1723            }
1724
1725            m_needToOutputDocTypeDecl = false;
1726        
1727            /* before we over-write the current elementLocalName etc.
1728             * lets close out the old one (if we still need to)
1729             */

1730            if (m_elemContext.m_startTagOpen)
1731            {
1732                closeStartTag();
1733                m_elemContext.m_startTagOpen = false;
1734            }
1735
1736            if (namespaceURI != null)
1737                ensurePrefixIsDeclared(namespaceURI, name);
1738                
1739            m_ispreserve = false;
1740
1741            if (shouldIndent() && m_startNewLine)
1742            {
1743                indent();
1744            }
1745
1746            m_startNewLine = true;
1747
1748            final java.io.Writer JavaDoc writer = m_writer;
1749            writer.write('<');
1750            writer.write(name);
1751        }
1752        catch (IOException JavaDoc e)
1753        {
1754            throw new SAXException JavaDoc(e);
1755        }
1756
1757        // process the attributes now, because after this SAX call they might be gone
1758
if (atts != null)
1759            addAttributes(atts);
1760            
1761        m_elemContext = m_elemContext.push(namespaceURI,localName,name);
1762        m_isprevtext = false;
1763
1764        if (m_tracer != null)
1765            firePseudoAttributes();
1766    }
1767
1768    /**
1769      * Receive notification of the beginning of an element, additional
1770      * namespace or attribute information can occur before or after this call,
1771      * that is associated with this element.
1772      *
1773      *
1774      * @param namespaceURI The Namespace URI, or the empty string if the
1775      * element has no Namespace URI or if Namespace
1776      * processing is not being performed.
1777      * @param localName The local name (without prefix), or the
1778      * empty string if Namespace processing is not being
1779      * performed.
1780      * @param name The element type name.
1781      * @throws org.xml.sax.SAXException Any SAX exception, possibly
1782      * wrapping another exception.
1783      * @see org.xml.sax.ContentHandler#startElement
1784      * @see org.xml.sax.ContentHandler#endElement
1785      * @see org.xml.sax.AttributeList
1786      *
1787      * @throws org.xml.sax.SAXException
1788      */

1789    public void startElement(
1790        String JavaDoc elementNamespaceURI,
1791        String JavaDoc elementLocalName,
1792        String JavaDoc elementName)
1793        throws SAXException JavaDoc
1794    {
1795        startElement(elementNamespaceURI, elementLocalName, elementName, null);
1796    }
1797
1798    public void startElement(String JavaDoc elementName) throws SAXException JavaDoc
1799    {
1800        startElement(null, null, elementName, null);
1801    }
1802
1803    /**
1804     * Output the doc type declaration.
1805     *
1806     * @param name non-null reference to document type name.
1807     * NEEDSDOC @param closeDecl
1808     *
1809     * @throws java.io.IOException
1810     */

1811    void outputDocTypeDecl(String JavaDoc name, boolean closeDecl) throws SAXException JavaDoc
1812    {
1813        if (m_cdataTagOpen)
1814            closeCDATA();
1815        try
1816        {
1817            final java.io.Writer JavaDoc writer = m_writer;
1818            writer.write("<!DOCTYPE ");
1819            writer.write(name);
1820
1821            String JavaDoc doctypePublic = getDoctypePublic();
1822            if (null != doctypePublic)
1823            {
1824                writer.write(" PUBLIC \"");
1825                writer.write(doctypePublic);
1826                writer.write('\"');
1827            }
1828
1829            String JavaDoc doctypeSystem = getDoctypeSystem();
1830            if (null != doctypeSystem)
1831            {
1832                if (null == doctypePublic)
1833                    writer.write(" SYSTEM \"");
1834                else
1835                    writer.write(" \"");
1836
1837                writer.write(doctypeSystem);
1838
1839                if (closeDecl)
1840                {
1841                    writer.write("\">");
1842                    writer.write(m_lineSep, 0, m_lineSepLen);
1843                    closeDecl = false; // done closing
1844
}
1845                else
1846                    writer.write('\"');
1847            }
1848            boolean dothis = false;
1849            if (dothis)
1850            {
1851                // at one point this code seemed right,
1852
// but not anymore - bjm
1853
if (closeDecl)
1854                {
1855                    writer.write('>');
1856                    writer.write(m_lineSep, 0, m_lineSepLen);
1857                }
1858            }
1859        }
1860        catch (IOException JavaDoc e)
1861        {
1862            throw new SAXException JavaDoc(e);
1863        }
1864    }
1865
1866    /**
1867     * Process the attributes, which means to write out the currently
1868     * collected attributes to the writer. The attributes are not
1869     * cleared by this method
1870     *
1871     * @param writer the writer to write processed attributes to.
1872     * @param nAttrs the number of attributes in m_attributes
1873     * to be processed
1874     *
1875     * @throws java.io.IOException
1876     * @throws org.xml.sax.SAXException
1877     */

1878    public void processAttributes(java.io.Writer JavaDoc writer, int nAttrs) throws IOException JavaDoc, SAXException JavaDoc
1879    {
1880            /* real SAX attributes are not passed in, so process the
1881             * attributes that were collected after the startElement call.
1882             * _attribVector is a "cheap" list for Stream serializer output
1883             * accumulated over a series of calls to attribute(name,value)
1884             */

1885
1886            String JavaDoc encoding = getEncoding();
1887            for (int i = 0; i < nAttrs; i++)
1888            {
1889                // elementAt is JDK 1.1.8
1890
final String JavaDoc name = m_attributes.getQName(i);
1891                final String JavaDoc value = m_attributes.getValue(i);
1892                writer.write(' ');
1893                writer.write(name);
1894                writer.write("=\"");
1895                writeAttrString(writer, value, encoding);
1896                writer.write('\"');
1897            }
1898    }
1899
1900    /**
1901     * Returns the specified <var>string</var> after substituting <VAR>specials</VAR>,
1902     * and UTF-16 surrogates for chracter references <CODE>&amp;#xnn</CODE>.
1903     *
1904     * @param string String to convert to XML format.
1905     * @param encoding CURRENTLY NOT IMPLEMENTED.
1906     *
1907     * @throws java.io.IOException
1908     */

1909    public void writeAttrString(
1910        Writer JavaDoc writer,
1911        String JavaDoc string,
1912        String JavaDoc encoding)
1913        throws IOException JavaDoc
1914    {
1915        final int len = string.length();
1916        if (len > m_attrBuff.length)
1917        {
1918           m_attrBuff = new char[len*2 + 1];
1919        }
1920        string.getChars(0,len, m_attrBuff, 0);
1921        final char[] stringChars = m_attrBuff;
1922
1923        for (int i = 0; i < len; i++)
1924        {
1925            char ch = stringChars[i];
1926            if (escapingNotNeeded(ch) && (!m_charInfo.isSpecialAttrChar(ch)))
1927            {
1928                writer.write(ch);
1929            }
1930            else
1931            { // I guess the parser doesn't normalize cr/lf in attributes. -sb
1932
// if ((CharInfo.S_CARRIAGERETURN == ch)
1933
// && ((i + 1) < len)
1934
// && (CharInfo.S_LINEFEED == stringChars[i + 1]))
1935
// {
1936
// i++;
1937
// ch = CharInfo.S_LINEFEED;
1938
// }
1939

1940                accumDefaultEscape(writer, ch, i, stringChars, len, false, true);
1941            }
1942        }
1943
1944    }
1945
1946    /**
1947     * Receive notification of the end of an element.
1948     *
1949     *
1950     * @param namespaceURI The Namespace URI, or the empty string if the
1951     * element has no Namespace URI or if Namespace
1952     * processing is not being performed.
1953     * @param localName The local name (without prefix), or the
1954     * empty string if Namespace processing is not being
1955     * performed.
1956     * @param name The element type name
1957     * @throws org.xml.sax.SAXException Any SAX exception, possibly
1958     * wrapping another exception.
1959     *
1960     * @throws org.xml.sax.SAXException
1961     */

1962    public void endElement(String JavaDoc namespaceURI, String JavaDoc localName, String JavaDoc name)
1963        throws org.xml.sax.SAXException JavaDoc
1964    {
1965        if (m_inEntityRef)
1966            return;
1967
1968        // namespaces declared at the current depth are no longer valid
1969
// so get rid of them
1970
m_prefixMap.popNamespaces(m_elemContext.m_currentElemDepth, null);
1971
1972        try
1973        {
1974            final java.io.Writer JavaDoc writer = m_writer;
1975            if (m_elemContext.m_startTagOpen)
1976            {
1977                if (m_tracer != null)
1978                    super.fireStartElem(m_elemContext.m_elementName);
1979                int nAttrs = m_attributes.getLength();
1980                if (nAttrs > 0)
1981                {
1982                    processAttributes(m_writer, nAttrs);
1983                    // clear attributes object for re-use with next element
1984
m_attributes.clear();
1985                }
1986                if (m_spaceBeforeClose)
1987                    writer.write(" />");
1988                else
1989                    writer.write("/>");
1990                /* don't need to pop cdataSectionState because
1991                 * this element ended so quickly that we didn't get
1992                 * to push the state.
1993                 */

1994
1995            }
1996            else
1997            {
1998                if (m_cdataTagOpen)
1999                    closeCDATA();
2000
2001                if (shouldIndent())
2002                    indent(m_elemContext.m_currentElemDepth - 1);
2003                writer.write('<');
2004                writer.write('/');
2005                writer.write(name);
2006                writer.write('>');
2007            }
2008        }
2009        catch (IOException JavaDoc e)
2010        {
2011            throw new SAXException JavaDoc(e);
2012        }
2013
2014        if (!m_elemContext.m_startTagOpen && m_doIndent)
2015        {
2016            m_ispreserve = m_preserves.isEmpty() ? false : m_preserves.pop();
2017        }
2018
2019        m_isprevtext = false;
2020
2021        // fire off the end element event
2022
if (m_tracer != null)
2023            super.fireEndElem(name);
2024        m_elemContext = m_elemContext.m_prev;
2025    }
2026
2027    /**
2028     * Receive notification of the end of an element.
2029     * @param name The element type name
2030     * @throws org.xml.sax.SAXException Any SAX exception, possibly
2031     * wrapping another exception.
2032     */

2033    public void endElement(String JavaDoc name) throws org.xml.sax.SAXException JavaDoc
2034    {
2035        endElement(null, null, name);
2036    }
2037
2038    /**
2039     * Begin the scope of a prefix-URI Namespace mapping
2040     * just before another element is about to start.
2041     * This call will close any open tags so that the prefix mapping
2042     * will not apply to the current element, but the up comming child.
2043     *
2044     * @see org.xml.sax.ContentHandler#startPrefixMapping
2045     *
2046     * @param prefix The Namespace prefix being declared.
2047     * @param uri The Namespace URI the prefix is mapped to.
2048     *
2049     * @throws org.xml.sax.SAXException The client may throw
2050     * an exception during processing.
2051     *
2052     */

2053    public void startPrefixMapping(String JavaDoc prefix, String JavaDoc uri)
2054        throws org.xml.sax.SAXException JavaDoc
2055    {
2056        // the "true" causes the flush of any open tags
2057
startPrefixMapping(prefix, uri, true);
2058    }
2059
2060    /**
2061     * Handle a prefix/uri mapping, which is associated with a startElement()
2062     * that is soon to follow. Need to close any open start tag to make
2063     * sure than any name space attributes due to this event are associated wih
2064     * the up comming element, not the current one.
2065     * @see org.apache.xml.serializer.ExtendedContentHandler#startPrefixMapping
2066     *
2067     * @param prefix The Namespace prefix being declared.
2068     * @param uri The Namespace URI the prefix is mapped to.
2069     * @param shouldFlush true if any open tags need to be closed first, this
2070     * will impact which element the mapping applies to (open parent, or its up
2071     * comming child)
2072     * @return returns true if the call made a change to the current
2073     * namespace information, false if it did not change anything, e.g. if the
2074     * prefix/namespace mapping was already in scope from before.
2075     *
2076     * @throws org.xml.sax.SAXException The client may throw
2077     * an exception during processing.
2078     *
2079     *
2080     */

2081    public boolean startPrefixMapping(
2082        String JavaDoc prefix,
2083        String JavaDoc uri,
2084        boolean shouldFlush)
2085        throws org.xml.sax.SAXException JavaDoc
2086    {
2087
2088        /* Remember the mapping, and at what depth it was declared
2089         * This is one greater than the current depth because these
2090         * mappings will apply to the next depth. This is in
2091         * consideration that startElement() will soon be called
2092         */

2093
2094        boolean pushed;
2095        int pushDepth;
2096        if (shouldFlush)
2097        {
2098            flushPending();
2099            // the prefix mapping applies to the child element (one deeper)
2100
pushDepth = m_elemContext.m_currentElemDepth + 1;
2101        }
2102        else
2103        {
2104            // the prefix mapping applies to the current element
2105
pushDepth = m_elemContext.m_currentElemDepth;
2106        }
2107        pushed = m_prefixMap.pushNamespace(prefix, uri, pushDepth);
2108
2109        if (pushed)
2110        {
2111            /* bjm: don't know if we really needto do this. The
2112             * callers of this object should have injected both
2113             * startPrefixMapping and the attributes. We are
2114             * just covering our butt here.
2115             */

2116            String JavaDoc name;
2117            if (EMPTYSTRING.equals(prefix))
2118            {
2119                name = "xmlns";
2120                addAttributeAlways(XMLNS_URI, prefix, name, "CDATA", uri);
2121            }
2122            else
2123            {
2124                if (!EMPTYSTRING.equals(uri))
2125                    // hack for XSLTC attribset16 test
2126
{ // that maps ns1 prefix to "" URI
2127
name = "xmlns:" + prefix;
2128
2129                    /* for something like xmlns:abc="w3.pretend.org"
2130                     * the uri is the value, that is why we pass it in the
2131                     * value, or 5th slot of addAttributeAlways()
2132                     */

2133                    addAttributeAlways(XMLNS_URI, prefix, name, "CDATA", uri);
2134                }
2135            }
2136        }
2137        return pushed;
2138    }
2139
2140    /**
2141     * Receive notification of an XML comment anywhere in the document. This
2142     * callback will be used for comments inside or outside the document
2143     * element, including comments in the external DTD subset (if read).
2144     * @param ch An array holding the characters in the comment.
2145     * @param start The starting position in the array.
2146     * @param length The number of characters to use from the array.
2147     * @throws org.xml.sax.SAXException The application may raise an exception.
2148     */

2149    public void comment(char ch[], int start, int length)
2150        throws org.xml.sax.SAXException JavaDoc
2151    {
2152
2153        int start_old = start;
2154        if (m_inEntityRef)
2155            return;
2156        if (m_elemContext.m_startTagOpen)
2157        {
2158            closeStartTag();
2159            m_elemContext.m_startTagOpen = false;
2160        }
2161        else if (m_needToCallStartDocument)
2162        {
2163            startDocumentInternal();
2164            m_needToCallStartDocument = false;
2165        }
2166
2167        try
2168        {
2169            if (shouldIndent())
2170                indent();
2171
2172            final int limit = start + length;
2173            boolean wasDash = false;
2174            if (m_cdataTagOpen)
2175                closeCDATA();
2176            final java.io.Writer JavaDoc writer = m_writer;
2177            writer.write(COMMENT_BEGIN);
2178            // Detect occurrences of two consecutive dashes, handle as necessary.
2179
for (int i = start; i < limit; i++)
2180            {
2181                if (wasDash && ch[i] == '-')
2182                {
2183                    writer.write(ch, start, i - start);
2184                    writer.write(" -");
2185                    start = i + 1;
2186                }
2187                wasDash = (ch[i] == '-');
2188            }
2189
2190            // if we have some chars in the comment
2191
if (length > 0)
2192            {
2193                // Output the remaining characters (if any)
2194
final int remainingChars = (limit - start);
2195                if (remainingChars > 0)
2196                    writer.write(ch, start, remainingChars);
2197                // Protect comment end from a single trailing dash
2198
if (ch[limit - 1] == '-')
2199                    writer.write(' ');
2200            }
2201            writer.write(COMMENT_END);
2202        }
2203        catch (IOException JavaDoc e)
2204        {
2205            throw new SAXException JavaDoc(e);
2206        }
2207
2208        m_startNewLine = true;
2209        // time to generate comment event
2210
if (m_tracer != null)
2211            super.fireCommentEvent(ch, start_old,length);
2212    }
2213
2214    /**
2215     * Report the end of a CDATA section.
2216     * @throws org.xml.sax.SAXException The application may raise an exception.
2217     *
2218     * @see #startCDATA
2219     */

2220    public void endCDATA() throws org.xml.sax.SAXException JavaDoc
2221    {
2222        if (m_cdataTagOpen)
2223            closeCDATA();
2224        m_cdataStartCalled = false;
2225    }
2226
2227    /**
2228     * Report the end of DTD declarations.
2229     * @throws org.xml.sax.SAXException The application may raise an exception.
2230     * @see #startDTD
2231     */

2232    public void endDTD() throws org.xml.sax.SAXException JavaDoc
2233    {
2234        try
2235        {
2236            if (m_needToOutputDocTypeDecl)
2237            {
2238                outputDocTypeDecl(m_elemContext.m_elementName, false);
2239                m_needToOutputDocTypeDecl = false;
2240            }
2241            final java.io.Writer JavaDoc writer = m_writer;
2242            if (!m_inDoctype)
2243                writer.write("]>");
2244            else
2245            {
2246                writer.write('>');
2247            }
2248
2249            writer.write(m_lineSep, 0, m_lineSepLen);
2250        }
2251        catch (IOException JavaDoc e)
2252        {
2253            throw new SAXException JavaDoc(e);
2254        }
2255
2256    }
2257
2258    /**
2259     * End the scope of a prefix-URI Namespace mapping.
2260     * @see org.xml.sax.ContentHandler#endPrefixMapping
2261     *
2262     * @param prefix The prefix that was being mapping.
2263     * @throws org.xml.sax.SAXException The client may throw
2264     * an exception during processing.
2265     */

2266    public void endPrefixMapping(String JavaDoc prefix) throws org.xml.sax.SAXException JavaDoc
2267    { // do nothing
2268
}
2269
2270    /**
2271     * Receive notification of ignorable whitespace in element content.
2272     *
2273     * Not sure how to get this invoked quite yet.
2274     *
2275     * @param ch The characters from the XML document.
2276     * @param start The start position in the array.
2277     * @param length The number of characters to read from the array.
2278     * @throws org.xml.sax.SAXException Any SAX exception, possibly
2279     * wrapping another exception.
2280     * @see #characters
2281     *
2282     * @throws org.xml.sax.SAXException
2283     */

2284    public void ignorableWhitespace(char ch[], int start, int length)
2285        throws org.xml.sax.SAXException JavaDoc
2286    {
2287
2288        if (0 == length)
2289            return;
2290        characters(ch, start, length);
2291    }
2292
2293    /**
2294     * Receive notification of a skipped entity.
2295     * @see org.xml.sax.ContentHandler#skippedEntity
2296     *
2297     * @param name The name of the skipped entity. If it is a
2298     * parameter entity, the name will begin with '%',
2299     * and if it is the external DTD subset, it will be the string
2300     * "[dtd]".
2301     * @throws org.xml.sax.SAXException Any SAX exception, possibly wrapping
2302     * another exception.
2303     */

2304    public void skippedEntity(String JavaDoc name) throws org.xml.sax.SAXException JavaDoc
2305    { // TODO: Should handle
2306
}
2307
2308    /**
2309     * Report the start of a CDATA section.
2310     *
2311     * @throws org.xml.sax.SAXException The application may raise an exception.
2312     * @see #endCDATA
2313     */

2314    public void startCDATA() throws org.xml.sax.SAXException JavaDoc
2315    {
2316        m_cdataStartCalled = true;
2317    }
2318
2319    /**
2320     * Report the beginning of an entity.
2321     *
2322     * The start and end of the document entity are not reported.
2323     * The start and end of the external DTD subset are reported
2324     * using the pseudo-name "[dtd]". All other events must be
2325     * properly nested within start/end entity events.
2326     *
2327     * @param name The name of the entity. If it is a parameter
2328     * entity, the name will begin with '%'.
2329     * @throws org.xml.sax.SAXException The application may raise an exception.
2330     * @see #endEntity
2331     * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
2332     * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
2333     */

2334    public void startEntity(String JavaDoc name) throws org.xml.sax.SAXException JavaDoc
2335    {
2336        if (name.equals("[dtd]"))
2337            m_inExternalDTD = true;
2338        m_inEntityRef = true;
2339    }
2340
2341    /**
2342     * For the enclosing elements starting tag write out
2343     * out any attributes followed by ">"
2344     *
2345     * @throws org.xml.sax.SAXException
2346     */

2347    protected void closeStartTag() throws SAXException JavaDoc
2348    {
2349
2350        if (m_elemContext.m_startTagOpen)
2351        {
2352
2353            try
2354            {
2355                if (m_tracer != null)
2356                    super.fireStartElem(m_elemContext.m_elementName);
2357                int nAttrs = m_attributes.getLength();
2358                if (nAttrs > 0)
2359                {
2360                    processAttributes(m_writer, nAttrs);
2361                    // clear attributes object for re-use with next element
2362
m_attributes.clear();
2363                }
2364                m_writer.write('>');
2365            }
2366            catch (IOException JavaDoc e)
2367            {
2368                throw new SAXException JavaDoc(e);
2369            }
2370
2371            /* whether Xalan or XSLTC, we have the prefix mappings now, so
2372             * lets determine if the current element is specified in the cdata-
2373             * section-elements list.
2374             */

2375            if (m_cdataSectionElements != null)
2376                m_elemContext.m_isCdataSection = isCdataSection();
2377
2378            if (m_doIndent)
2379            {
2380                m_isprevtext = false;
2381                m_preserves.push(m_ispreserve);
2382            }
2383        }
2384
2385    }
2386
2387    /**
2388     * Report the start of DTD declarations, if any.
2389     *
2390     * Any declarations are assumed to be in the internal subset unless
2391     * otherwise indicated.
2392     *
2393     * @param name The document type name.
2394     * @param publicId The declared public identifier for the
2395     * external DTD subset, or null if none was declared.
2396     * @param systemId The declared system identifier for the
2397     * external DTD subset, or null if none was declared.
2398     * @throws org.xml.sax.SAXException The application may raise an
2399     * exception.
2400     * @see #endDTD
2401     * @see #startEntity
2402     */

2403    public void startDTD(String JavaDoc name, String JavaDoc publicId, String JavaDoc systemId)
2404        throws org.xml.sax.SAXException JavaDoc
2405    {
2406        setDoctypeSystem(systemId);
2407        setDoctypePublic(publicId);
2408
2409        m_elemContext.m_elementName = name;
2410        m_inDoctype = true;
2411    }
2412
2413    /**
2414     * Returns the m_indentAmount.
2415     * @return int
2416     */

2417    public int getIndentAmount()
2418    {
2419        return m_indentAmount;
2420    }
2421
2422    /**
2423     * Sets the m_indentAmount.
2424     *
2425     * @param m_indentAmount The m_indentAmount to set
2426     */

2427    public void setIndentAmount(int m_indentAmount)
2428    {
2429        this.m_indentAmount = m_indentAmount;
2430    }
2431
2432    /**
2433     * Tell if, based on space preservation constraints and the doIndent property,
2434     * if an indent should occur.
2435     *
2436     * @return True if an indent should occur.
2437     */

2438    protected boolean shouldIndent()
2439    {
2440        return m_doIndent && (!m_ispreserve && !m_isprevtext);
2441    }
2442
2443    /**
2444     * Searches for the list of qname properties with the specified key in the
2445     * property list. If the key is not found in this property list, the default
2446     * property list, and its defaults, recursively, are then checked. The
2447     * method returns <code>null</code> if the property is not found.
2448     *
2449     * @param key the property key.
2450     * @param props the list of properties to search in.
2451     *
2452     * Sets the vector of local-name/URI pairs of the cdata section elements
2453     * specified in the cdata-section-elements property.
2454     *
2455     * This method is essentially a copy of getQNameProperties() from
2456     * OutputProperties. Eventually this method should go away and a call
2457     * to setCdataSectionElements(Vector v) should be made directly.
2458     */

2459    private void setCdataSectionElements(String JavaDoc key, Properties JavaDoc props)
2460    {
2461
2462        String JavaDoc s = props.getProperty(key);
2463
2464        if (null != s)
2465        {
2466            // Vector of URI/LocalName pairs
2467
Vector JavaDoc v = new Vector JavaDoc();
2468            int l = s.length();
2469            boolean inCurly = false;
2470            FastStringBuffer buf = new FastStringBuffer();
2471
2472            // parse through string, breaking on whitespaces. I do this instead
2473
// of a tokenizer so I can track whitespace inside of curly brackets,
2474
// which theoretically shouldn't happen if they contain legal URLs.
2475
for (int i = 0; i < l; i++)
2476            {
2477                char c = s.charAt(i);
2478
2479                if (Character.isWhitespace(c))
2480                {
2481                    if (!inCurly)
2482                    {
2483                        if (buf.length() > 0)
2484                        {
2485                            addCdataSectionElement(buf.toString(), v);
2486                            buf.reset();
2487                        }
2488                        continue;
2489                    }
2490                }
2491                else if ('{' == c)
2492                    inCurly = true;
2493                else if ('}' == c)
2494                    inCurly = false;
2495
2496                buf.append(c);
2497            }
2498
2499            if (buf.length() > 0)
2500            {
2501                addCdataSectionElement(buf.toString(), v);
2502                buf.reset();
2503            }
2504            // call the official, public method to set the collected names
2505
setCdataSectionElements(v);
2506        }
2507
2508    }
2509
2510    /**
2511     * Adds a URI/LocalName pair of strings to the list.
2512     *
2513     * @param name String of the form "{uri}local" or "local"
2514     *
2515     * @return a QName object
2516     */

2517    private void addCdataSectionElement(String JavaDoc URI_and_localName, Vector JavaDoc v)
2518    {
2519
2520        StringTokenizer JavaDoc tokenizer =
2521            new StringTokenizer JavaDoc(URI_and_localName, "{}", false);
2522        QName qname;
2523        String JavaDoc s1 = tokenizer.nextToken();
2524        String JavaDoc s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
2525
2526        if (null == s2)
2527        {
2528            // add null URI and the local name
2529
v.addElement(null);
2530            v.addElement(s1);
2531        }
2532        else
2533        {
2534            // add URI, then local name
2535
v.addElement(s1);
2536            v.addElement(s2);
2537        }
2538    }
2539
2540    /**
2541     * Remembers the cdata sections specified in the cdata-section-elements.
2542     * The "official way to set URI and localName pairs.
2543     * This method should be used by both Xalan and XSLTC.
2544     *
2545     * @param Vector URI_and_localNames a vector of pairs of Strings (URI/local)
2546     */

2547    public void setCdataSectionElements(Vector JavaDoc URI_and_localNames)
2548    {
2549        m_cdataSectionElements = URI_and_localNames;
2550    }
2551
2552    /**
2553     * Makes sure that the namespace URI for the given qualified attribute name
2554     * is declared.
2555     * @param ns the namespace URI
2556     * @param rawName the qualified name
2557     * @return returns null if no action is taken, otherwise it returns the
2558     * prefix used in declaring the namespace.
2559     * @throws SAXException
2560     */

2561    protected String JavaDoc ensureAttributesNamespaceIsDeclared(
2562        String JavaDoc ns,
2563        String JavaDoc localName,
2564        String JavaDoc rawName)
2565        throws org.xml.sax.SAXException JavaDoc
2566    {
2567
2568        if (ns != null && ns.length() > 0)
2569        {
2570
2571            // extract the prefix in front of the raw name
2572
int index = 0;
2573            String JavaDoc prefixFromRawName =
2574                (index = rawName.indexOf(":")) < 0
2575                    ? ""
2576                    : rawName.substring(0, index);
2577
2578            if (index > 0)
2579            {
2580                // we have a prefix, lets see if it maps to a namespace
2581
String JavaDoc uri = m_prefixMap.lookupNamespace(prefixFromRawName);
2582                if (uri != null && uri.equals(ns))
2583                {
2584                    // the prefix in the raw name is already maps to the given namespace uri
2585
// so we don't need to do anything
2586
return null;
2587                }
2588                else
2589                {
2590                    // The uri does not map to the prefix in the raw name,
2591
// so lets make the mapping.
2592
this.startPrefixMapping(prefixFromRawName, ns, false);
2593                    this.addAttribute(
2594                        "http://www.w3.org/2000/xmlns/",
2595                        prefixFromRawName,
2596                        "xmlns:" + prefixFromRawName,
2597                        "CDATA",
2598                        ns);
2599                    return prefixFromRawName;
2600                }
2601            }
2602            else
2603            {
2604                // we don't have a prefix in the raw name.
2605
// Does the URI map to a prefix already?
2606
String JavaDoc prefix = m_prefixMap.lookupPrefix(ns);
2607                if (prefix == null)
2608                {
2609                    // uri is not associated with a prefix,
2610
// so lets generate a new prefix to use
2611
prefix = m_prefixMap.generateNextPrefix();
2612                    this.startPrefixMapping(prefix, ns, false);
2613                    this.addAttribute(
2614                        "http://www.w3.org/2000/xmlns/",
2615                        prefix,
2616                        "xmlns:" + prefix,
2617                        "CDATA",
2618                        ns);
2619                }
2620
2621                return prefix;
2622
2623            }
2624        }
2625        return null;
2626    }
2627
2628    void ensurePrefixIsDeclared(String JavaDoc ns, String JavaDoc rawName)
2629        throws org.xml.sax.SAXException JavaDoc
2630    {
2631
2632        if (ns != null && ns.length() > 0)
2633        {
2634            int index;
2635            String JavaDoc prefix =
2636                (index = rawName.indexOf(":")) < 0
2637                    ? ""
2638                    : rawName.substring(0, index);
2639
2640            if (null != prefix)
2641            {
2642                String JavaDoc foundURI = m_prefixMap.lookupNamespace(prefix);
2643
2644                if ((null == foundURI) || !foundURI.equals(ns))
2645                {
2646                    this.startPrefixMapping(prefix, ns);
2647
2648                    // Bugzilla1133: Generate attribute as well as namespace event.
2649
// SAX does expect both.
2650

2651                    this.addAttributeAlways(
2652                        "http://www.w3.org/2000/xmlns/",
2653                        prefix,
2654                        "xmlns" + (prefix.length() == 0 ? "" : ":") + prefix,
2655                        "CDATA",
2656                        ns);
2657                }
2658
2659            }
2660        }
2661    }
2662
2663    /**
2664     * This method flushes any pending events, which can be startDocument()
2665     * closing the opening tag of an element, or closing an open CDATA section.
2666     */

2667    public void flushPending() throws SAXException JavaDoc
2668    {
2669            if (m_needToCallStartDocument)
2670            {
2671                startDocumentInternal();
2672                m_needToCallStartDocument = false;
2673            }
2674            if (m_elemContext.m_startTagOpen)
2675            {
2676                closeStartTag();
2677                m_elemContext.m_startTagOpen = false;
2678            }
2679
2680            if (m_cdataTagOpen)
2681            {
2682                closeCDATA();
2683                m_cdataTagOpen = false;
2684            }
2685    }
2686
2687    public void setContentHandler(ContentHandler JavaDoc ch)
2688    {
2689        // this method is really only useful in the ToSAXHandler classes but it is
2690
// in the interface. If the method defined here is ever called
2691
// we are probably in trouble.
2692
}
2693
2694    /**
2695     * Adds the given attribute to the set of attributes, even if there is
2696     * nocurrently open element. This is useful if a SAX startPrefixMapping()
2697     * should need to add an attribute before the element name is seen.
2698     *
2699     * This method is a copy of its super classes method, except that some
2700     * tracing of events is done. This is so the tracing is only done for
2701     * stream serializers, not for SAX ones.
2702     *
2703     * @param uri the URI of the attribute
2704     * @param localName the local name of the attribute
2705     * @param rawName the qualified name of the attribute
2706     * @param type the type of the attribute (probably CDATA)
2707     * @param value the value of the attribute
2708     */

2709    public void addAttributeAlways(
2710        String JavaDoc uri,
2711        String JavaDoc localName,
2712        String JavaDoc rawName,
2713        String JavaDoc type,
2714        String JavaDoc value)
2715    {
2716
2717        int index;
2718        index = m_attributes.getIndex(rawName);
2719        if (index >= 0)
2720        {
2721            String JavaDoc old_value = null;
2722            if (m_tracer != null)
2723            {
2724                old_value = m_attributes.getValue(index);
2725                if (value.equals(old_value))
2726                    old_value = null;
2727            }
2728
2729            /* We've seen the attribute before.
2730             * We may have a null uri or localName, but all we really
2731             * want to re-set is the value anyway.
2732             */

2733            m_attributes.setValue(index, value);
2734            if (old_value != null)
2735                firePseudoAttributes();
2736
2737        }
2738        else
2739        {
2740            // the attribute doesn't exist yet, create it
2741
m_attributes.addAttribute(uri, localName, rawName, type, value);
2742            if (m_tracer != null)
2743                firePseudoAttributes();
2744        }
2745
2746    }
2747
2748    /**
2749     * To fire off the pseudo characters of attributes, as they currently
2750     * exist. This method should be called everytime an attribute is added,
2751     * or when an attribute value is changed, or an element is created.
2752     */

2753
2754    protected void firePseudoAttributes()
2755    {
2756        if (m_tracer != null)
2757        {
2758            try
2759            {
2760                // flush out the "<elemName" if not already flushed
2761
m_writer.flush();
2762                
2763                // make a StringBuffer to write the name="value" pairs to.
2764
StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
2765                int nAttrs = m_attributes.getLength();
2766                if (nAttrs > 0)
2767                {
2768                    // make a writer that internally appends to the same
2769
// StringBuffer
2770
java.io.Writer JavaDoc writer =
2771                        new ToStream.WritertoStringBuffer(sb);
2772
2773                    processAttributes(writer, nAttrs);
2774                    // Don't clear the attributes!
2775
// We only want to see what would be written out
2776
// at this point, we don't want to loose them.
2777
}
2778                sb.append('>'); // the potential > after the attributes.
2779
// convert the StringBuffer to a char array and
2780
// emit the trace event that these characters "might"
2781
// be written
2782
char ch[] = sb.toString().toCharArray();
2783                m_tracer.fireGenerateEvent(
2784                    SerializerTrace.EVENTTYPE_OUTPUT_PSEUDO_CHARACTERS,
2785                    ch,
2786                    0,
2787                    ch.length);
2788            }
2789            catch (IOException JavaDoc ioe)
2790            {
2791                // ignore ?
2792
}
2793            catch (SAXException JavaDoc se)
2794            {
2795                // ignore ?
2796
}
2797        }
2798    }
2799
2800    /**
2801     * This inner class is used only to collect attribute values
2802     * written by the method writeAttrString() into a string buffer.
2803     * In this manner trace events, and the real writing of attributes will use
2804     * the same code.
2805     */

2806    private class WritertoStringBuffer extends java.io.Writer JavaDoc
2807    {
2808        final private StringBuffer JavaDoc m_stringbuf;
2809        /**
2810         * @see java.io.Writer#write(char[], int, int)
2811         */

2812        WritertoStringBuffer(StringBuffer JavaDoc sb)
2813        {
2814            m_stringbuf = sb;
2815        }
2816
2817        public void write(char[] arg0, int arg1, int arg2) throws IOException JavaDoc
2818        {
2819            m_stringbuf.append(arg0, arg1, arg2);
2820        }
2821        /**
2822         * @see java.io.Writer#flush()
2823         */

2824        public void flush() throws IOException JavaDoc
2825        {
2826        }
2827        /**
2828         * @see java.io.Writer#close()
2829         */

2830        public void close() throws IOException JavaDoc
2831        {
2832        }
2833
2834        public void write(int i)
2835        {
2836            m_stringbuf.append((char) i);
2837        }
2838        
2839        public void write(String JavaDoc s)
2840        {
2841            m_stringbuf.append(s);
2842        }
2843    }
2844
2845    /**
2846     * @see org.apache.xml.serializer.SerializationHandler#setTransformer(Transformer)
2847     */

2848    public void setTransformer(Transformer JavaDoc transformer) {
2849        super.setTransformer(transformer);
2850        if (m_tracer != null
2851         && !(m_writer instanceof SerializerTraceWriter) )
2852            m_writer = new SerializerTraceWriter(m_writer, m_tracer);
2853        
2854        
2855    }
2856    /**
2857     * Try's to reset the super class and reset this class for
2858     * re-use, so that you don't need to create a new serializer
2859     * (mostly for performance reasons).
2860     *
2861     * @return true if the class was successfuly reset.
2862     */

2863    public boolean reset()
2864    {
2865        boolean wasReset = false;
2866        if (super.reset())
2867        {
2868            resetToStream();
2869            wasReset = true;
2870        }
2871        return wasReset;
2872    }
2873    
2874    /**
2875     * Reset all of the fields owned by ToStream class
2876     *
2877     */

2878    private void resetToStream()
2879    {
2880         this.m_canConvertMeth = null;
2881         this.m_cdataStartCalled = false;
2882         /* The stream is being reset. It is one of
2883          * ToXMLStream, ToHTMLStream ... and this type can't be changed
2884          * so neither should m_charInfo which is associated with the
2885          * type of Stream. Just leave m_charInfo as-is for the next re-use.
2886          *
2887          */

2888         // this.m_charInfo = null; // don't set to null
2889
this.m_charToByteConverter = null;
2890         this.m_disableOutputEscapingStates.clear();
2891         
2892         this.m_escaping = true;
2893         // Leave m_format alone for now - bjm
2894
// this.m_format = null;
2895
this.m_inDoctype = false;
2896         this.m_ispreserve = false;
2897         this.m_ispreserve = false;
2898         this.m_isprevtext = false;
2899         this.m_isUTF8 = false; // ?? used anywhere ??
2900
this.m_maxCharacter = Encodings.getLastPrintable();
2901         this.m_preserves.clear();
2902         this.m_shouldFlush = true;
2903         this.m_spaceBeforeClose = false;
2904         this.m_startNewLine = false;
2905         this.m_triedToGetConverter = false;
2906         this.m_lineSepUse = true;
2907         // DON'T SET THE WRITER TO NULL, IT MAY BE REUSED !!
2908
// this.m_writer = null;
2909

2910    }
2911}
2912
Popular Tags