1 /*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5 /*
6 * Copyright 2001-2004 The Apache Software Foundation.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20 /*
21 * $Id: ToStream.java,v 1.4 2005/11/10 06:43:26 suresh_emailid Exp $
22 */
23 package com.sun.org.apache.xml.internal.serializer;
24
25 import com.sun.org.apache.xalan.internal.utils.SecuritySupport;
26 import java.io.IOException;
27 import java.io.OutputStream;
28 import java.io.UnsupportedEncodingException;
29 import java.io.Writer;
30 import java.util.Properties;
31 import java.util.StringTokenizer;
32 import java.util.Vector;
33
34 import javax.xml.transform.ErrorListener;
35 import javax.xml.transform.OutputKeys;
36 import javax.xml.transform.Transformer;
37 import javax.xml.transform.TransformerException;
38
39 import com.sun.org.apache.xml.internal.serializer.utils.MsgKey;
40 import com.sun.org.apache.xml.internal.serializer.utils.Utils;
41 import com.sun.org.apache.xml.internal.serializer.utils.WrappedRuntimeException;
42 import org.w3c.dom.Node;
43 import org.xml.sax.Attributes;
44 import org.xml.sax.ContentHandler;
45 import org.xml.sax.SAXException;
46
47 //import com.sun.media.sound.IESecurity;
48
49 /**
50 * This abstract class is a base class for other stream
51 * serializers (xml, html, text ...) that write output to a stream.
52 *
169 protected boolean m_spaceBeforeClose = false;
170
171 /**
172 * Flag to signal that a newline should be added.
173 *
174 * Used only in indent() which is called only if m_doIndent is true.
175 * If m_doIndent is false this flag has no impact.
176 */
177 boolean m_startNewLine;
178
179 /**
180 * Tells if we're in an internal document type subset.
181 */
182 protected boolean m_inDoctype = false;
183
184 /**
185 * Flag to quickly tell if the encoding is UTF8.
186 */
187 boolean m_isUTF8 = false;
188
189 /** The xsl:output properties. */
190 protected Properties m_format;
191
192 /**
193 * remembers if we are in between the startCDATA() and endCDATA() callbacks
194 */
195 protected boolean m_cdataStartCalled = false;
196
197 /**
198 * If this flag is true DTD entity references are not left as-is,
199 * which is exiting older behavior.
200 */
201 private boolean m_expandDTDEntities = true;
202
203
204 /**
205 * Default constructor
206 */
207 public ToStream()
208 {
209 }
210
211 /**
289 if (writer instanceof WriterToASCI)
290 {
291 if (m_shouldFlush)
292 writer.flush();
293 }
294 else
295 {
296 // Flush always.
297 // Not a great thing if the writer was created
298 // by this class, but don't have a choice.
299 writer.flush();
300 }
301 }
302 catch (IOException ioe)
303 {
304 throw new org.xml.sax.SAXException(ioe);
305 }
306 }
307 }
308
309 /**
310 * Get the output stream where the events will be serialized to.
311 *
312 * @return reference to the result stream, or null of only a writer was
313 * set.
314 */
315 public OutputStream getOutputStream()
316 {
317
318 if (m_writer instanceof WriterToUTF8Buffered)
319 return ((WriterToUTF8Buffered) m_writer).getOutputStream();
320 if (m_writer instanceof WriterToASCI)
321 return ((WriterToASCI) m_writer).getOutputStream();
322 else
323 return null;
324 }
325
326 // Implement DeclHandler
327
328 /**
329 * Report an element type declaration.
330 *
331 * <p>The content model will consist of the string "EMPTY", the
332 * string "ANY", or a parenthesised group, optionally followed
333 * by an occurrence indicator. The model will be normalized so
334 * that all whitespace is removed,and will include the enclosing
335 * parentheses.</p>
336 *
337 * @param name The element type name.
338 * @param model The content model as a normalized string.
339 * @exception SAXException The application may raise an exception.
340 */
341 public void elementDecl(String name, String model) throws SAXException
342 {
343 // Do not inline external DTD
402 * @throws org.xml.sax.SAXException
403 */
404 void outputEntityDecl(String name, String value) throws IOException
405 {
406 final java.io.Writer writer = m_writer;
407 writer.write("<!ENTITY ");
408 writer.write(name);
409 writer.write(" \"");
410 writer.write(value);
411 writer.write("\">");
412 writer.write(m_lineSep, 0, m_lineSepLen);
413 }
414
415 /**
416 * Output a system-dependent line break.
417 *
418 * @throws org.xml.sax.SAXException
419 */
420 protected final void outputLineSep() throws IOException
421 {
422
423 m_writer.write(m_lineSep, 0, m_lineSepLen);
424 }
425
426 /**
427 * Specifies an output format for this serializer. It the
428 * serializer has already been associated with an output format,
429 * it will switch to the new format. This method should not be
430 * called while the serializer is in the process of serializing
431 * a document.
432 *
433 * @param format The output format to use
434 */
435 public void setOutputFormat(Properties format)
436 {
437
438 boolean shouldFlush = m_shouldFlush;
439
440 init(m_writer, format, false, false);
441
442 m_shouldFlush = shouldFlush;
443 }
444
445 /**
446 * Initialize the serializer with the specified writer and output format.
447 * Must be called before calling any of the serialize methods.
448 * This method can be called multiple times and the xsl:output properties
449 * passed in the 'format' parameter are accumulated across calls.
450 *
451 * @param writer The writer to use
452 * @param format The output format
453 * @param shouldFlush True if the writer should be flushed at EndDocument.
454 */
455 private synchronized void init(
456 Writer writer,
457 Properties format,
458 boolean defaultProperties,
459 boolean shouldFlush)
460 {
461
462 m_shouldFlush = shouldFlush;
463
464
465 // if we are tracing events we need to trace what
466 // characters are written to the output writer.
467 if (m_tracer != null
468 && !(writer instanceof SerializerTraceWriter) )
469 m_writer = new SerializerTraceWriter(writer, m_tracer);
470 else
471 m_writer = writer;
472
473
474 m_format = format;
475 // m_cdataSectionNames =
476 // OutputProperties.getQNameProperties(
477 // OutputKeys.CDATA_SECTION_ELEMENTS,
478 // format);
479 setCdataSectionElements(OutputKeys.CDATA_SECTION_ELEMENTS, format);
480
481 setIndentAmount(
482 OutputPropertyUtils.getIntProperty(
483 OutputPropertiesFactory.S_KEY_INDENT_AMOUNT,
484 format));
485 setIndent(
486 OutputPropertyUtils.getBooleanProperty(OutputKeys.INDENT, format));
487
488 {
489 String sep =
490 format.getProperty(OutputPropertiesFactory.S_KEY_LINE_SEPARATOR);
491 if (sep != null) {
492 m_lineSep = sep.toCharArray();
493 m_lineSepLen = sep.length();
494 }
495 }
496
497 boolean shouldNotWriteXMLHeader =
498 OutputPropertyUtils.getBooleanProperty(
499 OutputKeys.OMIT_XML_DECLARATION,
500 format);
501 setOmitXMLDeclaration(shouldNotWriteXMLHeader);
502 setDoctypeSystem(format.getProperty(OutputKeys.DOCTYPE_SYSTEM));
503 String doctypePublic = format.getProperty(OutputKeys.DOCTYPE_PUBLIC);
504 setDoctypePublic(doctypePublic);
505
506 // if standalone was explicitly specified
507 if (format.get(OutputKeys.STANDALONE) != null)
508 {
509 String val = format.getProperty(OutputKeys.STANDALONE);
510 if (defaultProperties)
511 setStandaloneInternal(val);
512 else
513 setStandalone(val);
514 }
515
516 setMediaType(format.getProperty(OutputKeys.MEDIA_TYPE));
517
518 if (null != doctypePublic)
519 {
520 if (doctypePublic.startsWith("-//W3C//DTD XHTML"))
521 m_spaceBeforeClose = true;
522 }
523
524 /*
525 * This code is added for XML 1.1 Version output.
526 */
527 String version = getVersion();
528 if (null == version)
529 {
530 version = format.getProperty(OutputKeys.VERSION);
531 setVersion(version);
532 }
533
534 // initCharsMap();
535 String encoding = getEncoding();
536 if (null == encoding)
537 {
538 encoding =
539 Encodings.getMimeEncoding(
540 format.getProperty(OutputKeys.ENCODING));
541 setEncoding(encoding);
542 }
543
544 m_isUTF8 = encoding.equals(Encodings.DEFAULT_MIME_ENCODING);
545
546 // Access this only from the Hashtable level... we don't want to
547 // get default properties.
548 String entitiesFileName =
549 (String) format.get(OutputPropertiesFactory.S_KEY_ENTITIES);
550
551 if (null != entitiesFileName)
552 {
553
554 String method =
555 (String) format.get(OutputKeys.METHOD);
556
557 m_charInfo = CharInfo.getCharInfo(entitiesFileName, method);
558 }
559
560 }
561
562 /**
563 * Initialize the serializer with the specified writer and output format.
564 * Must be called before calling any of the serialize methods.
565 *
566 * @param writer The writer to use
567 * @param format The output format
568 */
569 private synchronized void init(Writer writer, Properties format)
570 {
571 init(writer, format, false, false);
572 }
573 /**
574 * Initialize the serializer with the specified output stream and output
575 * format. Must be called before calling any of the serialize methods.
576 *
577 * @param output The output stream to use
578 * @param format The output format
579 * @param defaultProperties true if the properties are the default
580 * properties
581 *
582 * @throws UnsupportedEncodingException The encoding specified in the
583 * output format is not supported
584 */
585 protected synchronized void init(
586 OutputStream output,
587 Properties format,
588 boolean defaultProperties)
589 throws UnsupportedEncodingException
590 {
591
592 String encoding = getEncoding();
593 if (encoding == null)
594 {
595 // if not already set then get it from the properties
596 encoding =
597 Encodings.getMimeEncoding(
598 format.getProperty(OutputKeys.ENCODING));
599 setEncoding(encoding);
600 }
601
602 if (encoding.equalsIgnoreCase("UTF-8"))
603 {
604 m_isUTF8 = true;
605 // if (output instanceof java.io.BufferedOutputStream)
606 // {
607 // init(new WriterToUTF8(output), format, defaultProperties, true);
608 // }
609 // else if (output instanceof java.io.FileOutputStream)
610 // {
611 // init(new WriterToUTF8Buffered(output), format, defaultProperties, true);
612 // }
613 // else
614 // {
615 // // Not sure what to do in this case. I'm going to be conservative
616 // // and not buffer.
617 // init(new WriterToUTF8(output), format, defaultProperties, true);
618 // }
619
620
621 init(
622 new WriterToUTF8Buffered(output),
623 format,
624 defaultProperties,
625 true);
626
627
628 }
629 else if (
630 encoding.equals("WINDOWS-1250")
631 || encoding.equals("US-ASCII")
632 || encoding.equals("ASCII"))
633 {
634 init(new WriterToASCI(output), format, defaultProperties, true);
635 }
636 else
637 {
638 Writer osw;
639
640 try
641 {
642 osw = Encodings.getWriter(output, encoding);
643 }
644 catch (UnsupportedEncodingException uee)
645 {
646 System.out.println(
647 "Warning: encoding \""
648 + encoding
649 + "\" not supported"
650 + ", using "
651 + Encodings.DEFAULT_MIME_ENCODING);
652
653 encoding = Encodings.DEFAULT_MIME_ENCODING;
654 setEncoding(encoding);
655 osw = Encodings.getWriter(output, encoding);
656 }
657
658 init(osw, format, defaultProperties, true);
659 }
660
661 }
662
663 /**
664 * Returns the output format for this serializer.
665 *
666 * @return The output format in use
667 */
668 public Properties getOutputFormat()
669 {
670 return m_format;
671 }
672
673 /**
674 * Specifies a writer to which the document should be serialized.
675 * This method should not be called while the serializer is in
676 * the process of serializing a document.
677 *
678 * @param writer The output writer stream
679 */
680 public void setWriter(Writer writer)
681 {
682 // if we are tracing events we need to trace what
683 // characters are written to the output writer.
684 if (m_tracer != null
685 && !(writer instanceof SerializerTraceWriter) )
686 m_writer = new SerializerTraceWriter(writer, m_tracer);
687 else
688 m_writer = writer;
689 }
690
691 /**
692 * Set if the operating systems end-of-line line separator should
693 * be used when serializing. If set false NL character
694 * (decimal 10) is left alone, otherwise the new-line will be replaced on
695 * output with the systems line separator. For example on UNIX this is
696 * NL, while on Windows it is two characters, CR NL, where CR is the
697 * carriage-return (decimal 13).
698 *
699 * @param use_sytem_line_break True if an input NL is replaced with the
700 * operating systems end-of-line separator.
701 * @return The previously set value of the serializer.
702 */
703 public boolean setLineSepUse(boolean use_sytem_line_break)
704 {
705 boolean oldValue = m_lineSepUse;
706 m_lineSepUse = use_sytem_line_break;
707 return oldValue;
708 }
709
710 /**
711 * Specifies an output stream to which the document should be
712 * serialized. This method should not be called while the
713 * serializer is in the process of serializing a document.
714 * <p>
715 * The encoding specified in the output properties is used, or
716 * if no encoding was specified, the default for the selected
717 * output method.
718 *
719 * @param output The output stream
720 */
721 public void setOutputStream(OutputStream output)
722 {
723
724 try
725 {
726 Properties format;
727 if (null == m_format)
728 format =
729 OutputPropertiesFactory.getDefaultMethodProperties(
730 Method.XML);
731 else
732 format = m_format;
733 init(output, format, true);
734 }
735 catch (UnsupportedEncodingException uee)
736 {
737
738 // Should have been warned in init, I guess...
739 }
740 }
741
742 /**
743 * @see SerializationHandler#setEscaping(boolean)
744 */
745 public boolean setEscaping(boolean escape)
746 {
747 final boolean temp = m_escaping;
748 m_escaping = escape;
749 return temp;
750
751 }
752
753
754 /**
755 * Might print a newline character and the indentation amount
756 * of the given depth.
757 *
758 * @param depth the indentation depth (element nesting depth)
759 *
760 * @throws org.xml.sax.SAXException if an error occurs during writing.
761 */
2438 if (m_tracer != null)
2439 super.fireStartElem(m_elemContext.m_elementName);
2440 int nAttrs = m_attributes.getLength();
2441 if (nAttrs > 0)
2442 {
2443 processAttributes(m_writer, nAttrs);
2444 // clear attributes object for re-use with next element
2445 m_attributes.clear();
2446 }
2447 m_writer.write('>');
2448 }
2449 catch (IOException e)
2450 {
2451 throw new SAXException(e);
2452 }
2453
2454 /* whether Xalan or XSLTC, we have the prefix mappings now, so
2455 * lets determine if the current element is specified in the cdata-
2456 * section-elements list.
2457 */
2458 if (m_cdataSectionElements != null)
2459 m_elemContext.m_isCdataSection = isCdataSection();
2460
2461 if (m_doIndent)
2462 {
2463 m_isprevtext = false;
2464 m_preserves.push(m_ispreserve);
2465 }
2466 }
2467
2468 }
2469
2470 /**
2471 * Report the start of DTD declarations, if any.
2472 *
2473 * Any declarations are assumed to be in the internal subset unless
2474 * otherwise indicated.
2475 *
2476 * @param name The document type name.
2477 * @param publicId The declared public identifier for the
2478 * external DTD subset, or null if none was declared.
2515 /**
2516 * Tell if, based on space preservation constraints and the doIndent property,
2517 * if an indent should occur.
2518 *
2519 * @return True if an indent should occur.
2520 */
2521 protected boolean shouldIndent()
2522 {
2523 return m_doIndent && (!m_ispreserve && !m_isprevtext) && (m_elemContext.m_currentElemDepth > 0 || m_isStandalone);
2524 }
2525
2526 /**
2527 * Searches for the list of qname properties with the specified key in the
2528 * property list. If the key is not found in this property list, the default
2529 * property list, and its defaults, recursively, are then checked. The
2530 * method returns <code>null</code> if the property is not found.
2531 *
2532 * @param key the property key.
2533 * @param props the list of properties to search in.
2534 *
2535 * Sets the vector of local-name/URI pairs of the cdata section elements
2536 * specified in the cdata-section-elements property.
2537 *
2538 * This method is essentially a copy of getQNameProperties() from
2539 * OutputProperties. Eventually this method should go away and a call
2540 * to setCdataSectionElements(Vector v) should be made directly.
2541 */
2542 private void setCdataSectionElements(String key, Properties props)
2543 {
2544
2545 String s = props.getProperty(key);
2546
2547 if (null != s)
2548 {
2549 // Vector of URI/LocalName pairs
2550 Vector v = new Vector();
2551 int l = s.length();
2552 boolean inCurly = false;
2553 StringBuffer buf = new StringBuffer();
2554
2555 // parse through string, breaking on whitespaces. I do this instead
2556 // of a tokenizer so I can track whitespace inside of curly brackets,
2557 // which theoretically shouldn't happen if they contain legal URLs.
2558 for (int i = 0; i < l; i++)
2559 {
2560 char c = s.charAt(i);
2561
2562 if (Character.isWhitespace(c))
2563 {
2564 if (!inCurly)
2565 {
2566 if (buf.length() > 0)
2567 {
2568 addCdataSectionElement(buf.toString(), v);
2569 buf.setLength(0);
2570 }
2571 continue;
2572 }
2573 }
2580 }
2581
2582 if (buf.length() > 0)
2583 {
2584 addCdataSectionElement(buf.toString(), v);
2585 buf.setLength(0);
2586 }
2587 // call the official, public method to set the collected names
2588 setCdataSectionElements(v);
2589 }
2590
2591 }
2592
2593 /**
2594 * Adds a URI/LocalName pair of strings to the list.
2595 *
2596 * @param URI_and_localName String of the form "{uri}local" or "local"
2597 *
2598 * @return a QName object
2599 */
2600 private void addCdataSectionElement(String URI_and_localName, Vector v)
2601 {
2602
2603 StringTokenizer tokenizer =
2604 new StringTokenizer(URI_and_localName, "{}", false);
2605 String s1 = tokenizer.nextToken();
2606 String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
2607
2608 if (null == s2)
2609 {
2610 // add null URI and the local name
2611 v.addElement(null);
2612 v.addElement(s1);
2613 }
2614 else
2615 {
2616 // add URI, then local name
2617 v.addElement(s1);
2618 v.addElement(s2);
2619 }
2620 }
2621
2622 /**
2623 * Remembers the cdata sections specified in the cdata-section-elements.
2624 * The "official way to set URI and localName pairs.
2625 * This method should be used by both Xalan and XSLTC.
2626 *
2627 * @param URI_and_localNames a vector of pairs of Strings (URI/local)
2628 */
2629 public void setCdataSectionElements(Vector URI_and_localNames)
2630 {
2631 m_cdataSectionElements = URI_and_localNames;
2632 }
2633
2634 /**
2635 * Makes sure that the namespace URI for the given qualified attribute name
2636 * is declared.
2637 * @param ns the namespace URI
2638 * @param rawName the qualified name
2639 * @return returns null if no action is taken, otherwise it returns the
2640 * prefix used in declaring the namespace.
2641 * @throws SAXException
2642 */
2643 protected String ensureAttributesNamespaceIsDeclared(
2644 String ns,
2645 String localName,
2646 String rawName)
2647 throws org.xml.sax.SAXException
2648 {
2649
2650 if (ns != null && ns.length() > 0)
2651 {
3067 this.m_ispreserve = false;
3068 this.m_isprevtext = false;
3069 this.m_isUTF8 = false; // ?? used anywhere ??
3070 this.m_preserves.clear();
3071 this.m_shouldFlush = true;
3072 this.m_spaceBeforeClose = false;
3073 this.m_startNewLine = false;
3074 this.m_lineSepUse = true;
3075 // DON'T SET THE WRITER TO NULL, IT MAY BE REUSED !!
3076 // this.m_writer = null;
3077 this.m_expandDTDEntities = true;
3078
3079 }
3080
3081 /**
3082 * Sets the character encoding coming from the xsl:output encoding stylesheet attribute.
3083 * @param encoding the character encoding
3084 */
3085 public void setEncoding(String encoding)
3086 {
3087 String old = getEncoding();
3088 super.setEncoding(encoding);
3089 if (old == null || !old.equals(encoding)) {
3090 // If we have changed the setting of the
3091 m_encodingInfo = Encodings.getEncodingInfo(encoding);
3092
3093 if (encoding != null && m_encodingInfo.name == null) {
3094 // We tried to get an EncodingInfo for Object for the given
3095 // encoding, but it came back with an internall null name
3096 // so the encoding is not supported by the JDK, issue a message.
3097 String msg = Utils.messages.createMessage(
3098 MsgKey.ER_ENCODING_NOT_SUPPORTED,new Object[]{ encoding });
3099 try
3100 {
3101 // Prepare to issue the warning message
3102 Transformer tran = super.getTransformer();
3103 if (tran != null) {
3104 ErrorListener errHandler = tran.getErrorListener();
3105 // Issue the warning message
3106 if (null != errHandler && m_sourceLocator != null)
3107 errHandler.warning(new TransformerException(msg, m_sourceLocator));
3108 else
3109 System.out.println(msg);
3110 }
3111 else
3112 System.out.println(msg);
3113 }
3114 catch (Exception e){}
3115 }
3116 }
3117 return;
3118 }
3119
3120 /**
3121 * Simple stack for boolean values.
3122 *
3123 * This class is a copy of the one in com.sun.org.apache.xml.internal.utils.
3124 * It exists to cut the serializers dependancy on that package.
3125 * A minor changes from that package are:
3126 * doesn't implement Clonable
3127 *
3128 * @xsl.usage internal
3129 */
3130 static final class BoolStack
3131 {
3132
3133 /** Array of boolean values */
3134 private boolean m_values[];
3135
3136 /** Array size allocated */
3137 private int m_allocatedSize;
3368 final java.io.Writer writer = m_writer;
3369 if (m_needToOutputDocTypeDecl)
3370 {
3371 outputDocTypeDecl(m_elemContext.m_elementName, false);
3372 m_needToOutputDocTypeDecl = false;
3373 }
3374 if (m_inDoctype)
3375 {
3376 writer.write(" [");
3377 writer.write(m_lineSep, 0, m_lineSepLen);
3378 m_inDoctype = false;
3379 }
3380 }
3381
3382 /**
3383 * If set to false the serializer does not expand DTD entities,
3384 * but leaves them as is, the default value is true;
3385 */
3386 public void setDTDEntityExpansion(boolean expand) {
3387 m_expandDTDEntities = expand;
3388 }
3389 }
|
1 /*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5 /*
6 * Licensed to the Apache Software Foundation (ASF) under one
7 * or more contributor license agreements. See the NOTICE file
8 * distributed with this work for additional information
9 * regarding copyright ownership. The ASF licenses this file
10 * to you under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing, software
17 * distributed under the License is distributed on an "AS IS" BASIS,
18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 * See the License for the specific language governing permissions and
20 * limitations under the License.
21 */
22 /*
23 * $Id: ToStream.java,v 1.4 2005/11/10 06:43:26 suresh_emailid Exp $
24 */
25 package com.sun.org.apache.xml.internal.serializer;
26
27 import com.sun.org.apache.xalan.internal.utils.SecuritySupport;
28 import java.io.IOException;
29 import java.io.OutputStream;
30 import java.io.OutputStreamWriter;
31 import java.io.UnsupportedEncodingException;
32 import java.io.Writer;
33 import java.util.Enumeration;
34 import java.util.Iterator;
35 import java.util.Properties;
36 import java.util.Set;
37 import java.util.StringTokenizer;
38 import java.util.ArrayList;
39
40 import javax.xml.transform.ErrorListener;
41 import javax.xml.transform.OutputKeys;
42 import javax.xml.transform.Transformer;
43 import javax.xml.transform.TransformerException;
44
45 import com.sun.org.apache.xml.internal.serializer.utils.MsgKey;
46 import com.sun.org.apache.xml.internal.serializer.utils.Utils;
47 import com.sun.org.apache.xml.internal.serializer.utils.WrappedRuntimeException;
48 import org.w3c.dom.Node;
49 import org.xml.sax.Attributes;
50 import org.xml.sax.ContentHandler;
51 import org.xml.sax.SAXException;
52
53 //import com.sun.media.sound.IESecurity;
54
55 /**
56 * This abstract class is a base class for other stream
57 * serializers (xml, html, text ...) that write output to a stream.
58 *
175 protected boolean m_spaceBeforeClose = false;
176
177 /**
178 * Flag to signal that a newline should be added.
179 *
180 * Used only in indent() which is called only if m_doIndent is true.
181 * If m_doIndent is false this flag has no impact.
182 */
183 boolean m_startNewLine;
184
185 /**
186 * Tells if we're in an internal document type subset.
187 */
188 protected boolean m_inDoctype = false;
189
190 /**
191 * Flag to quickly tell if the encoding is UTF8.
192 */
193 boolean m_isUTF8 = false;
194
195 /**
196 * remembers if we are in between the startCDATA() and endCDATA() callbacks
197 */
198 protected boolean m_cdataStartCalled = false;
199
200 /**
201 * If this flag is true DTD entity references are not left as-is,
202 * which is exiting older behavior.
203 */
204 private boolean m_expandDTDEntities = true;
205
206
207 /**
208 * Default constructor
209 */
210 public ToStream()
211 {
212 }
213
214 /**
292 if (writer instanceof WriterToASCI)
293 {
294 if (m_shouldFlush)
295 writer.flush();
296 }
297 else
298 {
299 // Flush always.
300 // Not a great thing if the writer was created
301 // by this class, but don't have a choice.
302 writer.flush();
303 }
304 }
305 catch (IOException ioe)
306 {
307 throw new org.xml.sax.SAXException(ioe);
308 }
309 }
310 }
311
312 OutputStream m_outputStream;
313 /**
314 * Get the output stream where the events will be serialized to.
315 *
316 * @return reference to the result stream, or null of only a writer was
317 * set.
318 */
319 public OutputStream getOutputStream()
320 {
321 return m_outputStream;
322 }
323
324 // Implement DeclHandler
325
326 /**
327 * Report an element type declaration.
328 *
329 * <p>The content model will consist of the string "EMPTY", the
330 * string "ANY", or a parenthesised group, optionally followed
331 * by an occurrence indicator. The model will be normalized so
332 * that all whitespace is removed,and will include the enclosing
333 * parentheses.</p>
334 *
335 * @param name The element type name.
336 * @param model The content model as a normalized string.
337 * @exception SAXException The application may raise an exception.
338 */
339 public void elementDecl(String name, String model) throws SAXException
340 {
341 // Do not inline external DTD
400 * @throws org.xml.sax.SAXException
401 */
402 void outputEntityDecl(String name, String value) throws IOException
403 {
404 final java.io.Writer writer = m_writer;
405 writer.write("<!ENTITY ");
406 writer.write(name);
407 writer.write(" \"");
408 writer.write(value);
409 writer.write("\">");
410 writer.write(m_lineSep, 0, m_lineSepLen);
411 }
412
413 /**
414 * Output a system-dependent line break.
415 *
416 * @throws org.xml.sax.SAXException
417 */
418 protected final void outputLineSep() throws IOException
419 {
420 m_writer.write(m_lineSep, 0, m_lineSepLen);
421 }
422
423 void setProp(String name, String val, boolean defaultVal) {
424 if (val != null) {
425
426 char first = getFirstCharLocName(name);
427 switch (first) {
428 case 'c':
429 if (OutputKeys.CDATA_SECTION_ELEMENTS.equals(name)) {
430 addCdataSectionElements(val); // val is cdataSectionNames
431 }
432 break;
433 case 'd':
434 if (OutputKeys.DOCTYPE_SYSTEM.equals(name)) {
435 this.m_doctypeSystem = val;
436 } else if (OutputKeys.DOCTYPE_PUBLIC.equals(name)) {
437 this.m_doctypePublic = val;
438 if (val.startsWith("-//W3C//DTD XHTML"))
439 m_spaceBeforeClose = true;
440 }
441 break;
442 case 'e':
443 String newEncoding = val;
444 if (OutputKeys.ENCODING.equals(name)) {
445 String possible_encoding = Encodings.getMimeEncoding(val);
446 if (possible_encoding != null) {
447 // if the encoding is being set, try to get the
448 // preferred
449 // mime-name and set it too.
450 super.setProp("mime-name", possible_encoding,
451 defaultVal);
452 }
453 final String oldExplicitEncoding = getOutputPropertyNonDefault(OutputKeys.ENCODING);
454 final String oldDefaultEncoding = getOutputPropertyDefault(OutputKeys.ENCODING);
455 if ( (defaultVal && ( oldDefaultEncoding == null || !oldDefaultEncoding.equalsIgnoreCase(newEncoding)))
456 || ( !defaultVal && (oldExplicitEncoding == null || !oldExplicitEncoding.equalsIgnoreCase(newEncoding) ))) {
457 // We are trying to change the default or the non-default setting of the encoding to a different value
458 // from what it was
459
460 EncodingInfo encodingInfo = Encodings.getEncodingInfo(newEncoding);
461 if (newEncoding != null && encodingInfo.name == null) {
462 // We tried to get an EncodingInfo for Object for the given
463 // encoding, but it came back with an internall null name
464 // so the encoding is not supported by the JDK, issue a message.
465 final String msg = Utils.messages.createMessage(
466 MsgKey.ER_ENCODING_NOT_SUPPORTED,new Object[]{ newEncoding });
467
468 final String msg2 =
469 "Warning: encoding \"" + newEncoding + "\" not supported, using "
470 + Encodings.DEFAULT_MIME_ENCODING;
471 try {
472 // Prepare to issue the warning message
473 final Transformer tran = super.getTransformer();
474 if (tran != null) {
475 final ErrorListener errHandler = tran
476 .getErrorListener();
477 // Issue the warning message
478 if (null != errHandler
479 && m_sourceLocator != null) {
480 errHandler
481 .warning(new TransformerException(
482 msg, m_sourceLocator));
483 errHandler
484 .warning(new TransformerException(
485 msg2, m_sourceLocator));
486 } else {
487 System.out.println(msg);
488 System.out.println(msg2);
489 }
490 } else {
491 System.out.println(msg);
492 System.out.println(msg2);
493 }
494 } catch (Exception e) {
495 }
496
497 // We said we are using UTF-8, so use it
498 newEncoding = Encodings.DEFAULT_MIME_ENCODING;
499 val = Encodings.DEFAULT_MIME_ENCODING; // to store the modified value into the properties a little later
500 encodingInfo = Encodings.getEncodingInfo(newEncoding);
501 }
502 // The encoding was good, or was forced to UTF-8 above
503
504
505 // If there is already a non-default set encoding and we
506 // are trying to set the default encoding, skip the this block
507 // as the non-default value is already the one to use.
508 if (defaultVal == false || oldExplicitEncoding == null) {
509 m_encodingInfo = encodingInfo;
510 if (newEncoding != null)
511 m_isUTF8 = newEncoding.equals(Encodings.DEFAULT_MIME_ENCODING);
512
513 // if there was a previously set OutputStream
514 OutputStream os = getOutputStream();
515 if (os != null) {
516 Writer w = getWriter();
517
518 // If the writer was previously set, but
519 // set by the user, or if the new encoding is the same
520 // as the old encoding, skip this block
521 String oldEncoding = getOutputProperty(OutputKeys.ENCODING);
522 if ((w == null || !m_writer_set_by_user)
523 && !newEncoding.equalsIgnoreCase(oldEncoding)) {
524 // Make the change of encoding in our internal
525 // table, then call setOutputStreamInternal
526 // which will stomp on the old Writer (if any)
527 // with a new Writer with the new encoding.
528 super.setProp(name, val, defaultVal);
529 setOutputStreamInternal(os,false);
530 }
531 }
532 }
533 }
534 }
535 break;
536 case 'i':
537 if (OutputPropertiesFactory.S_KEY_INDENT_AMOUNT.equals(name)) {
538 setIndentAmount(Integer.parseInt(val));
539 } else if (OutputKeys.INDENT.equals(name)) {
540 boolean b = "yes".equals(val) ? true : false;
541 m_doIndent = b;
542 }
543
544 break;
545 case 'l':
546 if (OutputPropertiesFactory.S_KEY_LINE_SEPARATOR.equals(name)) {
547 m_lineSep = val.toCharArray();
548 m_lineSepLen = m_lineSep.length;
549 }
550
551 break;
552 case 'm':
553 if (OutputKeys.MEDIA_TYPE.equals(name)) {
554 m_mediatype = val;
555 }
556 break;
557 case 'o':
558 if (OutputKeys.OMIT_XML_DECLARATION.equals(name)) {
559 boolean b = "yes".equals(val) ? true : false;
560 this.m_shouldNotWriteXMLHeader = b;
561 }
562 break;
563 case 's':
564 // if standalone was explicitly specified
565 if (OutputKeys.STANDALONE.equals(name)) {
566 if (defaultVal) {
567 setStandaloneInternal(val);
568 } else {
569 m_standaloneWasSpecified = true;
570 setStandaloneInternal(val);
571 }
572 }
573
574 break;
575 case 'v':
576 if (OutputKeys.VERSION.equals(name)) {
577 m_version = val;
578 }
579 break;
580 default:
581 break;
582
583 }
584 super.setProp(name, val, defaultVal);
585 }
586 }
587
588 /**
589 * Specifies an output format for this serializer. It the
590 * serializer has already been associated with an output format,
591 * it will switch to the new format. This method should not be
592 * called while the serializer is in the process of serializing
593 * a document.
594 *
595 * @param format The output format to use
596 */
597 public void setOutputFormat(Properties format)
598 {
599 boolean shouldFlush = m_shouldFlush;
600
601 if (format != null)
602 {
603 // Set the default values first,
604 // and the non-default values after that,
605 // just in case there is some unexpected
606 // residual values left over from over-ridden default values
607 Enumeration propNames;
608 propNames = format.propertyNames();
609 while (propNames.hasMoreElements())
610 {
611 String key = (String) propNames.nextElement();
612 // Get the value, possibly a default value
613 String value = format.getProperty(key);
614 // Get the non-default value (if any).
615 String explicitValue = (String) format.get(key);
616 if (explicitValue == null && value != null) {
617 // This is a default value
618 this.setOutputPropertyDefault(key,value);
619 }
620 if (explicitValue != null) {
621 // This is an explicit non-default value
622 this.setOutputProperty(key,explicitValue);
623 }
624 }
625 }
626
627 // Access this only from the Hashtable level... we don't want to
628 // get default properties.
629 String entitiesFileName =
630 (String) format.get(OutputPropertiesFactory.S_KEY_ENTITIES);
631
632 if (null != entitiesFileName)
633 {
634
635 String method =
636 (String) format.get(OutputKeys.METHOD);
637
638 m_charInfo = CharInfo.getCharInfo(entitiesFileName, method);
639 }
640
641
642
643
644 m_shouldFlush = shouldFlush;
645 }
646
647 /**
648 * Returns the output format for this serializer.
649 *
650 * @return The output format in use
651 */
652 public Properties getOutputFormat() {
653 Properties def = new Properties();
654 {
655 Set<String> s = getOutputPropDefaultKeys();
656 for (String key : s) {
657 String val = getOutputPropertyDefault(key);
658 def.put(key, val);
659 }
660 }
661
662 Properties props = new Properties(def);
663 {
664 Set<String> s = getOutputPropKeys();
665 for (String key : s) {
666 String val = getOutputPropertyNonDefault(key);
667 if (val != null)
668 props.put(key, val);
669 }
670 }
671 return props;
672 }
673
674 /**
675 * Specifies a writer to which the document should be serialized.
676 * This method should not be called while the serializer is in
677 * the process of serializing a document.
678 *
679 * @param writer The output writer stream
680 */
681 public void setWriter(Writer writer)
682 {
683 setWriterInternal(writer, true);
684 }
685
686 private boolean m_writer_set_by_user;
687 private void setWriterInternal(Writer writer, boolean setByUser) {
688 m_writer_set_by_user = setByUser;
689 m_writer = writer;
690 // if we are tracing events we need to trace what
691 // characters are written to the output writer.
692 if (m_tracer != null) {
693 boolean noTracerYet = true;
694 Writer w2 = m_writer;
695 while (w2 instanceof WriterChain) {
696 if (w2 instanceof SerializerTraceWriter) {
697 noTracerYet = false;
698 break;
699 }
700 w2 = ((WriterChain)w2).getWriter();
701 }
702 if (noTracerYet)
703 m_writer = new SerializerTraceWriter(m_writer, m_tracer);
704 }
705 }
706
707 /**
708 * Set if the operating systems end-of-line line separator should
709 * be used when serializing. If set false NL character
710 * (decimal 10) is left alone, otherwise the new-line will be replaced on
711 * output with the systems line separator. For example on UNIX this is
712 * NL, while on Windows it is two characters, CR NL, where CR is the
713 * carriage-return (decimal 13).
714 *
715 * @param use_sytem_line_break True if an input NL is replaced with the
716 * operating systems end-of-line separator.
717 * @return The previously set value of the serializer.
718 */
719 public boolean setLineSepUse(boolean use_sytem_line_break)
720 {
721 boolean oldValue = m_lineSepUse;
722 m_lineSepUse = use_sytem_line_break;
723 return oldValue;
724 }
725
726 /**
727 * Specifies an output stream to which the document should be
728 * serialized. This method should not be called while the
729 * serializer is in the process of serializing a document.
730 * <p>
731 * The encoding specified in the output properties is used, or
732 * if no encoding was specified, the default for the selected
733 * output method.
734 *
735 * @param output The output stream
736 */
737 public void setOutputStream(OutputStream output)
738 {
739 setOutputStreamInternal(output, true);
740 }
741
742 private void setOutputStreamInternal(OutputStream output, boolean setByUser)
743 {
744 m_outputStream = output;
745 String encoding = getOutputProperty(OutputKeys.ENCODING);
746 if (Encodings.DEFAULT_MIME_ENCODING.equalsIgnoreCase(encoding))
747 {
748 // We wrap the OutputStream with a writer, but
749 // not one set by the user
750 try {
751 setWriterInternal(new WriterToUTF8Buffered(output), false);
752 } catch (UnsupportedEncodingException e) {
753 e.printStackTrace();
754 }
755 } else if (
756 "WINDOWS-1250".equals(encoding)
757 || "US-ASCII".equals(encoding)
758 || "ASCII".equals(encoding))
759 {
760 setWriterInternal(new WriterToASCI(output), false);
761 } else if (encoding != null) {
762 Writer osw = null;
763 try
764 {
765 osw = Encodings.getWriter(output, encoding);
766 }
767 catch (UnsupportedEncodingException uee)
768 {
769 osw = null;
770 }
771
772
773 if (osw == null) {
774 System.out.println(
775 "Warning: encoding \""
776 + encoding
777 + "\" not supported"
778 + ", using "
779 + Encodings.DEFAULT_MIME_ENCODING);
780
781 encoding = Encodings.DEFAULT_MIME_ENCODING;
782 setEncoding(encoding);
783 try {
784 osw = Encodings.getWriter(output, encoding);
785 } catch (UnsupportedEncodingException e) {
786 // We can't really get here, UTF-8 is always supported
787 // This try-catch exists to make the compiler happy
788 e.printStackTrace();
789 }
790 }
791 setWriterInternal(osw,false);
792 }
793 else {
794 // don't have any encoding, but we have an OutputStream
795 Writer osw = new OutputStreamWriter(output);
796 setWriterInternal(osw,false);
797 }
798 }
799
800
801 /**
802 * @see SerializationHandler#setEscaping(boolean)
803 */
804 public boolean setEscaping(boolean escape)
805 {
806 final boolean temp = m_escaping;
807 m_escaping = escape;
808 return temp;
809
810 }
811
812
813 /**
814 * Might print a newline character and the indentation amount
815 * of the given depth.
816 *
817 * @param depth the indentation depth (element nesting depth)
818 *
819 * @throws org.xml.sax.SAXException if an error occurs during writing.
820 */
2497 if (m_tracer != null)
2498 super.fireStartElem(m_elemContext.m_elementName);
2499 int nAttrs = m_attributes.getLength();
2500 if (nAttrs > 0)
2501 {
2502 processAttributes(m_writer, nAttrs);
2503 // clear attributes object for re-use with next element
2504 m_attributes.clear();
2505 }
2506 m_writer.write('>');
2507 }
2508 catch (IOException e)
2509 {
2510 throw new SAXException(e);
2511 }
2512
2513 /* whether Xalan or XSLTC, we have the prefix mappings now, so
2514 * lets determine if the current element is specified in the cdata-
2515 * section-elements list.
2516 */
2517 if (m_StringOfCDATASections != null)
2518 m_elemContext.m_isCdataSection = isCdataSection();
2519
2520 if (m_doIndent)
2521 {
2522 m_isprevtext = false;
2523 m_preserves.push(m_ispreserve);
2524 }
2525 }
2526
2527 }
2528
2529 /**
2530 * Report the start of DTD declarations, if any.
2531 *
2532 * Any declarations are assumed to be in the internal subset unless
2533 * otherwise indicated.
2534 *
2535 * @param name The document type name.
2536 * @param publicId The declared public identifier for the
2537 * external DTD subset, or null if none was declared.
2574 /**
2575 * Tell if, based on space preservation constraints and the doIndent property,
2576 * if an indent should occur.
2577 *
2578 * @return True if an indent should occur.
2579 */
2580 protected boolean shouldIndent()
2581 {
2582 return m_doIndent && (!m_ispreserve && !m_isprevtext) && (m_elemContext.m_currentElemDepth > 0 || m_isStandalone);
2583 }
2584
2585 /**
2586 * Searches for the list of qname properties with the specified key in the
2587 * property list. If the key is not found in this property list, the default
2588 * property list, and its defaults, recursively, are then checked. The
2589 * method returns <code>null</code> if the property is not found.
2590 *
2591 * @param key the property key.
2592 * @param props the list of properties to search in.
2593 *
2594 * Sets the ArrayList of local-name/URI pairs of the cdata section elements
2595 * specified in the cdata-section-elements property.
2596 *
2597 * This method is essentially a copy of getQNameProperties() from
2598 * OutputProperties. Eventually this method should go away and a call
2599 * to setCdataSectionElements(ArrayList<String> v) should be made directly.
2600 */
2601 private void setCdataSectionElements(String key, Properties props)
2602 {
2603
2604 String s = props.getProperty(key);
2605
2606 if (null != s)
2607 {
2608 // ArrayList<String> of URI/LocalName pairs
2609 ArrayList<String> v = new ArrayList<>();
2610 int l = s.length();
2611 boolean inCurly = false;
2612 StringBuilder buf = new StringBuilder();
2613
2614 // parse through string, breaking on whitespaces. I do this instead
2615 // of a tokenizer so I can track whitespace inside of curly brackets,
2616 // which theoretically shouldn't happen if they contain legal URLs.
2617 for (int i = 0; i < l; i++)
2618 {
2619 char c = s.charAt(i);
2620
2621 if (Character.isWhitespace(c))
2622 {
2623 if (!inCurly)
2624 {
2625 if (buf.length() > 0)
2626 {
2627 addCdataSectionElement(buf.toString(), v);
2628 buf.setLength(0);
2629 }
2630 continue;
2631 }
2632 }
2639 }
2640
2641 if (buf.length() > 0)
2642 {
2643 addCdataSectionElement(buf.toString(), v);
2644 buf.setLength(0);
2645 }
2646 // call the official, public method to set the collected names
2647 setCdataSectionElements(v);
2648 }
2649
2650 }
2651
2652 /**
2653 * Adds a URI/LocalName pair of strings to the list.
2654 *
2655 * @param URI_and_localName String of the form "{uri}local" or "local"
2656 *
2657 * @return a QName object
2658 */
2659 private void addCdataSectionElement(String URI_and_localName, ArrayList<String> v)
2660 {
2661
2662 StringTokenizer tokenizer =
2663 new StringTokenizer(URI_and_localName, "{}", false);
2664 String s1 = tokenizer.nextToken();
2665 String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
2666
2667 if (null == s2)
2668 {
2669 // add null URI and the local name
2670 v.add(null);
2671 v.add(s1);
2672 }
2673 else
2674 {
2675 // add URI, then local name
2676 v.add(s1);
2677 v.add(s2);
2678 }
2679 }
2680
2681 /**
2682 * Remembers the cdata sections specified in the cdata-section-elements.
2683 * The "official way to set URI and localName pairs.
2684 * This method should be used by both Xalan and XSLTC.
2685 *
2686 * @param URI_and_localNames an ArrayList of pairs of Strings (URI/local)
2687 */
2688 public void setCdataSectionElements(ArrayList<String> URI_and_localNames)
2689 {
2690 // convert to the new way.
2691 if (URI_and_localNames != null)
2692 {
2693 final int len = URI_and_localNames.size() - 1;
2694 if (len > 0)
2695 {
2696 final StringBuilder sb = new StringBuilder();
2697 for (int i = 0; i < len; i += 2)
2698 {
2699 // whitspace separated "{uri1}local1 {uri2}local2 ..."
2700 if (i != 0)
2701 sb.append(' ');
2702 final String uri = (String) URI_and_localNames.get(i);
2703 final String localName =
2704 (String) URI_and_localNames.get(i + 1);
2705 if (uri != null)
2706 {
2707 // If there is no URI don't put this in, just the localName then.
2708 sb.append('{');
2709 sb.append(uri);
2710 sb.append('}');
2711 }
2712 sb.append(localName);
2713 }
2714 m_StringOfCDATASections = sb.toString();
2715 }
2716 }
2717 initCdataElems(m_StringOfCDATASections);
2718 }
2719
2720 /**
2721 * Makes sure that the namespace URI for the given qualified attribute name
2722 * is declared.
2723 * @param ns the namespace URI
2724 * @param rawName the qualified name
2725 * @return returns null if no action is taken, otherwise it returns the
2726 * prefix used in declaring the namespace.
2727 * @throws SAXException
2728 */
2729 protected String ensureAttributesNamespaceIsDeclared(
2730 String ns,
2731 String localName,
2732 String rawName)
2733 throws org.xml.sax.SAXException
2734 {
2735
2736 if (ns != null && ns.length() > 0)
2737 {
3153 this.m_ispreserve = false;
3154 this.m_isprevtext = false;
3155 this.m_isUTF8 = false; // ?? used anywhere ??
3156 this.m_preserves.clear();
3157 this.m_shouldFlush = true;
3158 this.m_spaceBeforeClose = false;
3159 this.m_startNewLine = false;
3160 this.m_lineSepUse = true;
3161 // DON'T SET THE WRITER TO NULL, IT MAY BE REUSED !!
3162 // this.m_writer = null;
3163 this.m_expandDTDEntities = true;
3164
3165 }
3166
3167 /**
3168 * Sets the character encoding coming from the xsl:output encoding stylesheet attribute.
3169 * @param encoding the character encoding
3170 */
3171 public void setEncoding(String encoding)
3172 {
3173 setOutputProperty(OutputKeys.ENCODING,encoding);
3174 }
3175
3176 /**
3177 * Simple stack for boolean values.
3178 *
3179 * This class is a copy of the one in com.sun.org.apache.xml.internal.utils.
3180 * It exists to cut the serializers dependancy on that package.
3181 * A minor changes from that package are:
3182 * doesn't implement Clonable
3183 *
3184 * @xsl.usage internal
3185 */
3186 static final class BoolStack
3187 {
3188
3189 /** Array of boolean values */
3190 private boolean m_values[];
3191
3192 /** Array size allocated */
3193 private int m_allocatedSize;
3424 final java.io.Writer writer = m_writer;
3425 if (m_needToOutputDocTypeDecl)
3426 {
3427 outputDocTypeDecl(m_elemContext.m_elementName, false);
3428 m_needToOutputDocTypeDecl = false;
3429 }
3430 if (m_inDoctype)
3431 {
3432 writer.write(" [");
3433 writer.write(m_lineSep, 0, m_lineSepLen);
3434 m_inDoctype = false;
3435 }
3436 }
3437
3438 /**
3439 * If set to false the serializer does not expand DTD entities,
3440 * but leaves them as is, the default value is true;
3441 */
3442 public void setDTDEntityExpansion(boolean expand) {
3443 m_expandDTDEntities = expand;
3444 }
3445
3446 /**
3447 * Remembers the cdata sections specified in the cdata-section-elements by appending the given
3448 * cdata section elements to the list. This method can be called multiple times, but once an
3449 * element is put in the list of cdata section elements it can not be removed.
3450 * This method should be used by both Xalan and XSLTC.
3451 *
3452 * @param URI_and_localNames a whitespace separated list of element names, each element
3453 * is a URI in curly braces (optional) and a local name. An example of such a parameter is:
3454 * "{http://company.com}price {myURI2}book chapter"
3455 */
3456 public void addCdataSectionElements(String URI_and_localNames)
3457 {
3458 if (URI_and_localNames != null)
3459 initCdataElems(URI_and_localNames);
3460 if (m_StringOfCDATASections == null)
3461 m_StringOfCDATASections = URI_and_localNames;
3462 else
3463 m_StringOfCDATASections += (" " + URI_and_localNames);
3464 }
3465 }
|