001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: SerializerBase.java 471981 2006-11-07 04:28:00Z minchau $
020 */
021 package org.apache.xml.serializer;
022
023 import java.io.IOException;
024 import java.util.HashMap;
025 import java.util.Set;
026
027 import javax.xml.transform.OutputKeys;
028 import javax.xml.transform.SourceLocator;
029 import javax.xml.transform.Transformer;
030
031 import org.apache.xml.serializer.utils.MsgKey;
032 import org.apache.xml.serializer.utils.Utils;
033 import org.xml.sax.Attributes;
034 import org.xml.sax.ContentHandler;
035 import org.xml.sax.Locator;
036 import org.xml.sax.SAXException;
037 import org.xml.sax.SAXParseException;
038
039
040 /**
041 * This class acts as a base class for the XML "serializers"
042 * and the stream serializers.
043 * It contains a number of common fields and methods.
044 *
045 * @xsl.usage internal
046 */
047 public abstract class SerializerBase
048 implements SerializationHandler, SerializerConstants
049 {
050 SerializerBase() {
051 return;
052 }
053
054 /**
055 * The name of the package that this class is in.
056 * <p>
057 * Not a public API.
058 */
059 public static final String PKG_NAME;
060
061 /**
062 * The same as the name of the package that this class is in
063 * except that '.' are replaced with '/'.
064 * <p>
065 * Not a public API.
066 */
067 public static final String PKG_PATH;
068
069 static {
070 String fullyQualifiedName = SerializerBase.class.getName();
071 int lastDot = fullyQualifiedName.lastIndexOf('.');
072 if (lastDot < 0) {
073 PKG_NAME = "";
074 } else {
075 PKG_NAME = fullyQualifiedName.substring(0, lastDot);
076 }
077
078 StringBuffer sb = new StringBuffer();
079 for (int i = 0; i < PKG_NAME.length(); i++) {
080 char ch = PKG_NAME.charAt(i);
081 if (ch == '.')
082 sb.append('/');
083 else
084 sb.append(ch);
085 }
086 PKG_PATH = sb.toString();
087 }
088
089
090
091 /**
092 * To fire off the end element trace event
093 * @param name Name of element
094 */
095 protected void fireEndElem(String name)
096 throws org.xml.sax.SAXException
097 {
098 if (m_tracer != null)
099 {
100 flushMyWriter();
101 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDELEMENT,name, (Attributes)null);
102 }
103 }
104
105 /**
106 * Report the characters trace event
107 * @param chars content of characters
108 * @param start starting index of characters to output
109 * @param length number of characters to output
110 */
111 protected void fireCharEvent(char[] chars, int start, int length)
112 throws org.xml.sax.SAXException
113 {
114 if (m_tracer != null)
115 {
116 flushMyWriter();
117 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CHARACTERS, chars, start,length);
118 }
119 }
120
121 /**
122 * true if we still need to call startDocumentInternal()
123 */
124 protected boolean m_needToCallStartDocument = true;
125
126 /** True if a trailing "]]>" still needs to be written to be
127 * written out. Used to merge adjacent CDATA sections
128 */
129 protected boolean m_cdataTagOpen = false;
130
131 /**
132 * All the attributes of the current element, collected from
133 * startPrefixMapping() calls, or addAddtribute() calls, or
134 * from the SAX attributes in a startElement() call.
135 */
136 protected AttributesImplSerializer m_attributes = new AttributesImplSerializer();
137
138 /**
139 * Tells if we're in an EntityRef event.
140 */
141 protected boolean m_inEntityRef = false;
142
143 /** This flag is set while receiving events from the external DTD */
144 protected boolean m_inExternalDTD = false;
145
146 /**
147 * The System ID for the doc type.
148 */
149 protected String m_doctypeSystem;
150
151 /**
152 * The public ID for the doc type.
153 */
154 protected String m_doctypePublic;
155
156 /**
157 * Flag to tell that we need to add the doctype decl, which we can't do
158 * until the first element is encountered.
159 */
160 boolean m_needToOutputDocTypeDecl = true;
161
162 /**
163 * Tells if we should write the XML declaration.
164 */
165 protected boolean m_shouldNotWriteXMLHeader = false;
166
167 /**
168 * The standalone value for the doctype.
169 */
170 private String m_standalone;
171
172 /**
173 * True if standalone was specified.
174 */
175 protected boolean m_standaloneWasSpecified = false;
176
177 /**
178 * Flag to tell if indenting (pretty-printing) is on.
179 */
180 protected boolean m_doIndent = false;
181 /**
182 * Amount to indent.
183 */
184 protected int m_indentAmount = 0;
185
186 /**
187 * Tells the XML version, for writing out to the XML decl.
188 */
189 protected String m_version = null;
190
191 /**
192 * The mediatype. Not used right now.
193 */
194 protected String m_mediatype;
195
196 /**
197 * The transformer that was around when this output handler was created (if
198 * any).
199 */
200 private Transformer m_transformer;
201
202 /**
203 * Namespace support, that keeps track of currently defined
204 * prefix/uri mappings. As processed elements come and go, so do
205 * the associated mappings for that element.
206 */
207 protected NamespaceMappings m_prefixMap;
208
209 /**
210 * Handle for firing generate events. This interface may be implemented
211 * by the referenced transformer object.
212 */
213 protected SerializerTrace m_tracer;
214
215 protected SourceLocator m_sourceLocator;
216
217
218 /**
219 * The writer to send output to. This field is only used in the ToStream
220 * serializers, but exists here just so that the fireStartDoc() and
221 * other fire... methods can flush this writer when tracing.
222 */
223 protected java.io.Writer m_writer = null;
224
225 /**
226 * A reference to "stack frame" corresponding to
227 * the current element. Such a frame is pushed at a startElement()
228 * and popped at an endElement(). This frame contains information about
229 * the element, such as its namespace URI.
230 */
231 protected ElemContext m_elemContext = new ElemContext();
232
233 /**
234 * A utility buffer for converting Strings passed to
235 * character() methods to character arrays.
236 * Reusing this buffer means not creating a new character array
237 * everytime and it runs faster.
238 */
239 protected char[] m_charsBuff = new char[60];
240
241 /**
242 * A utility buffer for converting Strings passed to
243 * attribute methods to character arrays.
244 * Reusing this buffer means not creating a new character array
245 * everytime and it runs faster.
246 */
247 protected char[] m_attrBuff = new char[30];
248
249 /**
250 * Receive notification of a comment.
251 *
252 * @see ExtendedLexicalHandler#comment(String)
253 */
254 public void comment(String data) throws SAXException
255 {
256 m_docIsEmpty = false;
257
258 final int length = data.length();
259 if (length > m_charsBuff.length)
260 {
261 m_charsBuff = new char[length * 2 + 1];
262 }
263 data.getChars(0, length, m_charsBuff, 0);
264 comment(m_charsBuff, 0, length);
265 }
266
267 /**
268 * If at runtime, when the qname of the attribute is
269 * known, another prefix is specified for the attribute, then we can
270 * patch or hack the name with this method. For
271 * a qname of the form "ns?:otherprefix:name", this function patches the
272 * qname by simply ignoring "otherprefix".
273 * TODO: This method is a HACK! We do not have access to the
274 * XML file, it sometimes generates a NS prefix of the form "ns?" for
275 * an attribute.
276 */
277 protected String patchName(String qname)
278 {
279
280
281 final int lastColon = qname.lastIndexOf(':');
282
283 if (lastColon > 0) {
284 final int firstColon = qname.indexOf(':');
285 final String prefix = qname.substring(0, firstColon);
286 final String localName = qname.substring(lastColon + 1);
287
288 // If uri is "" then ignore prefix
289 final String uri = m_prefixMap.lookupNamespace(prefix);
290 if (uri != null && uri.length() == 0) {
291 return localName;
292 }
293 else if (firstColon != lastColon) {
294 return prefix + ':' + localName;
295 }
296 }
297 return qname;
298 }
299
300 /**
301 * Returns the local name of a qualified name. If the name has no prefix,
302 * then it works as the identity (SAX2).
303 * @param qname the qualified name
304 * @return the name, but excluding any prefix and colon.
305 */
306 protected static String getLocalName(String qname)
307 {
308 final int col = qname.lastIndexOf(':');
309 return (col > 0) ? qname.substring(col + 1) : qname;
310 }
311
312 /**
313 * Receive an object for locating the origin of SAX document events.
314 *
315 * @param locator An object that can return the location of any SAX document
316 * event.
317 *
318 * Receive an object for locating the origin of SAX document events.
319 *
320 * <p>SAX parsers are strongly encouraged (though not absolutely
321 * required) to supply a locator: if it does so, it must supply
322 * the locator to the application by invoking this method before
323 * invoking any of the other methods in the DocumentHandler
324 * interface.</p>
325 *
326 * <p>The locator allows the application to determine the end
327 * position of any document-related event, even if the parser is
328 * not reporting an error. Typically, the application will
329 * use this information for reporting its own errors (such as
330 * character content that does not match an application's
331 * business rules). The information returned by the locator
332 * is probably not sufficient for use with a search engine.</p>
333 *
334 * <p>Note that the locator will return correct information only
335 * during the invocation of the events in this interface. The
336 * application should not attempt to use it at any other time.</p>
337 */
338 public void setDocumentLocator(Locator locator)
339 {
340 return;
341
342 // I don't do anything with this yet.
343 }
344
345 /**
346 * Adds the given attribute to the set of collected attributes , but only if
347 * there is a currently open element.
348 *
349 * An element is currently open if a startElement() notification has
350 * occured but the start of the element has not yet been written to the
351 * output. In the stream case this means that we have not yet been forced
352 * to close the elements opening tag by another notification, such as a
353 * character notification.
354 *
355 * @param uri the URI of the attribute
356 * @param localName the local name of the attribute
357 * @param rawName the qualified name of the attribute
358 * @param type the type of the attribute (probably CDATA)
359 * @param value the value of the attribute
360 * @param XSLAttribute true if this attribute is coming from an xsl:attriute element
361 * @see ExtendedContentHandler#addAttribute(String, String, String, String, String)
362 */
363 public void addAttribute(
364 String uri,
365 String localName,
366 String rawName,
367 String type,
368 String value,
369 boolean XSLAttribute)
370 throws SAXException
371 {
372 if (m_elemContext.m_startTagOpen)
373 {
374 addAttributeAlways(uri, localName, rawName, type, value, XSLAttribute);
375 }
376
377 }
378
379 /**
380 * Adds the given attribute to the set of attributes, even if there is
381 * no currently open element. This is useful if a SAX startPrefixMapping()
382 * should need to add an attribute before the element name is seen.
383 *
384 * @param uri the URI of the attribute
385 * @param localName the local name of the attribute
386 * @param rawName the qualified name of the attribute
387 * @param type the type of the attribute (probably CDATA)
388 * @param value the value of the attribute
389 * @param XSLAttribute true if this attribute is coming from an xsl:attribute element
390 * @return true if the attribute was added,
391 * false if an existing value was replaced.
392 */
393 public boolean addAttributeAlways(
394 String uri,
395 String localName,
396 String rawName,
397 String type,
398 String value,
399 boolean XSLAttribute)
400 {
401 boolean was_added;
402 // final int index =
403 // (localName == null || uri == null) ?
404 // m_attributes.getIndex(rawName):m_attributes.getIndex(uri, localName);
405 int index;
406 // if (localName == null || uri == null){
407 // index = m_attributes.getIndex(rawName);
408 // }
409 // else {
410 // index = m_attributes.getIndex(uri, localName);
411 // }
412 if (localName == null || uri == null || uri.length() == 0)
413 index = m_attributes.getIndex(rawName);
414 else {
415 index = m_attributes.getIndex(uri,localName);
416 }
417 if (index >= 0)
418 {
419 /* We've seen the attribute before.
420 * We may have a null uri or localName, but all
421 * we really want to re-set is the value anyway.
422 */
423 m_attributes.setValue(index,value);
424 was_added = false;
425 }
426 else
427 {
428 // the attribute doesn't exist yet, create it
429 m_attributes.addAttribute(uri, localName, rawName, type, value);
430 was_added = true;
431 }
432 return was_added;
433
434 }
435
436
437 /**
438 * Adds the given attribute to the set of collected attributes,
439 * but only if there is a currently open element.
440 *
441 * @param name the attribute's qualified name
442 * @param value the value of the attribute
443 */
444 public void addAttribute(String name, final String value)
445 {
446 if (m_elemContext.m_startTagOpen)
447 {
448 final String patchedName = patchName(name);
449 final String localName = getLocalName(patchedName);
450 final String uri = getNamespaceURI(patchedName, false);
451
452 addAttributeAlways(uri,localName, patchedName, "CDATA", value, false);
453 }
454 }
455
456 /**
457 * Adds the given xsl:attribute to the set of collected attributes,
458 * but only if there is a currently open element.
459 *
460 * @param name the attribute's qualified name (prefix:localName)
461 * @param value the value of the attribute
462 * @param uri the URI that the prefix of the name points to
463 */
464 public void addXSLAttribute(String name, final String value, final String uri)
465 {
466 if (m_elemContext.m_startTagOpen)
467 {
468 final String patchedName = patchName(name);
469 final String localName = getLocalName(patchedName);
470
471 addAttributeAlways(uri,localName, patchedName, "CDATA", value, true);
472 }
473 }
474
475 /**
476 * Add the given attributes to the currently collected ones. These
477 * attributes are always added, regardless of whether on not an element
478 * is currently open.
479 * @param atts List of attributes to add to this list
480 */
481 public void addAttributes(Attributes atts) throws SAXException
482 {
483
484 int nAtts = atts.getLength();
485
486 for (int i = 0; i < nAtts; i++)
487 {
488 String uri = atts.getURI(i);
489
490 if (null == uri)
491 uri = "";
492
493 addAttributeAlways(
494 uri,
495 atts.getLocalName(i),
496 atts.getQName(i),
497 atts.getType(i),
498 atts.getValue(i),
499 false);
500
501 }
502 }
503
504 /**
505 * Return a {@link ContentHandler} interface into this serializer.
506 * If the serializer does not support the {@link ContentHandler}
507 * interface, it should return null.
508 *
509 * @return A {@link ContentHandler} interface into this serializer,
510 * or null if the serializer is not SAX 2 capable
511 * @throws IOException An I/O exception occured
512 */
513 public ContentHandler asContentHandler() throws IOException
514 {
515 return this;
516 }
517
518 /**
519 * Report the end of an entity.
520 *
521 * @param name The name of the entity that is ending.
522 * @throws org.xml.sax.SAXException The application may raise an exception.
523 * @see #startEntity
524 */
525 public void endEntity(String name) throws org.xml.sax.SAXException
526 {
527 if (name.equals("[dtd]"))
528 m_inExternalDTD = false;
529 m_inEntityRef = false;
530
531 if (m_tracer != null)
532 this.fireEndEntity(name);
533 }
534
535 /**
536 * Flush and close the underlying java.io.Writer. This method applies to
537 * ToStream serializers, not ToSAXHandler serializers.
538 * @see ToStream
539 */
540 public void close()
541 {
542 // do nothing (base behavior)
543 }
544
545 /**
546 * Initialize global variables
547 */
548 protected void initCDATA()
549 {
550 // CDATA stack
551 // _cdataStack = new Stack();
552 // _cdataStack.push(new Integer(-1)); // push dummy value
553 }
554
555 /**
556 * Returns the character encoding to be used in the output document.
557 * @return the character encoding to be used in the output document.
558 */
559 public String getEncoding()
560 {
561 return getOutputProperty(OutputKeys.ENCODING);
562 }
563
564 /**
565 * Sets the character encoding coming from the xsl:output encoding stylesheet attribute.
566 * @param m_encoding the character encoding
567 */
568 public void setEncoding(String encoding)
569 {
570 setOutputProperty(OutputKeys.ENCODING,encoding);
571 }
572
573 /**
574 * Sets the value coming from the xsl:output omit-xml-declaration stylesheet attribute
575 * @param b true if the XML declaration is to be omitted from the output
576 * document.
577 */
578 public void setOmitXMLDeclaration(boolean b)
579 {
580 String val = b ? "yes":"no";
581 setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,val);
582 }
583
584
585 /**
586 * @return true if the XML declaration is to be omitted from the output
587 * document.
588 */
589 public boolean getOmitXMLDeclaration()
590 {
591 return m_shouldNotWriteXMLHeader;
592 }
593
594 /**
595 * Returns the previously set value of the value to be used as the public
596 * identifier in the document type declaration (DTD).
597 *
598 *@return the public identifier to be used in the DOCTYPE declaration in the
599 * output document.
600 */
601 public String getDoctypePublic()
602 {
603 return m_doctypePublic;
604 }
605
606 /** Set the value coming from the xsl:output doctype-public stylesheet attribute.
607 * @param doctypePublic the public identifier to be used in the DOCTYPE
608 * declaration in the output document.
609 */
610 public void setDoctypePublic(String doctypePublic)
611 {
612 setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, doctypePublic);
613 }
614
615
616 /**
617 * Returns the previously set value of the value to be used
618 * as the system identifier in the document type declaration (DTD).
619 * @return the system identifier to be used in the DOCTYPE declaration in
620 * the output document.
621 *
622 */
623 public String getDoctypeSystem()
624 {
625 return m_doctypeSystem;
626 }
627
628 /** Set the value coming from the xsl:output doctype-system stylesheet attribute.
629 * @param doctypeSystem the system identifier to be used in the DOCTYPE
630 * declaration in the output document.
631 */
632 public void setDoctypeSystem(String doctypeSystem)
633 {
634 setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doctypeSystem);
635 }
636
637 /** Set the value coming from the xsl:output doctype-public and doctype-system stylesheet properties
638 * @param doctypeSystem the system identifier to be used in the DOCTYPE
639 * declaration in the output document.
640 * @param doctypePublic the public identifier to be used in the DOCTYPE
641 * declaration in the output document.
642 */
643 public void setDoctype(String doctypeSystem, String doctypePublic)
644 {
645 setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doctypeSystem);
646 setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, doctypePublic);
647 }
648
649 /**
650 * Sets the value coming from the xsl:output standalone stylesheet attribute.
651 * @param standalone a value of "yes" indicates that the
652 * <code>standalone</code> delaration is to be included in the output
653 * document. This method remembers if the value was explicitly set using
654 * this method, verses if the value is the default value.
655 */
656 public void setStandalone(String standalone)
657 {
658 setOutputProperty(OutputKeys.STANDALONE, standalone);
659 }
660 /**
661 * Sets the XSL standalone attribute, but does not remember if this is a
662 * default or explicite setting.
663 * @param standalone "yes" | "no"
664 */
665 protected void setStandaloneInternal(String standalone)
666 {
667 if ("yes".equals(standalone))
668 m_standalone = "yes";
669 else
670 m_standalone = "no";
671
672 }
673
674 /**
675 * Gets the XSL standalone attribute
676 * @return a value of "yes" if the <code>standalone</code> delaration is to
677 * be included in the output document.
678 * @see XSLOutputAttributes#getStandalone()
679 */
680 public String getStandalone()
681 {
682 return m_standalone;
683 }
684
685 /**
686 * @return true if the output document should be indented to visually
687 * indicate its structure.
688 */
689 public boolean getIndent()
690 {
691 return m_doIndent;
692 }
693 /**
694 * Gets the mediatype the media-type or MIME type associated with the output
695 * document.
696 * @return the mediatype the media-type or MIME type associated with the
697 * output document.
698 */
699 public String getMediaType()
700 {
701 return m_mediatype;
702 }
703
704 /**
705 * Gets the version of the output format.
706 * @return the version of the output format.
707 */
708 public String getVersion()
709 {
710 return m_version;
711 }
712
713 /**
714 * Sets the value coming from the xsl:output version attribute.
715 * @param version the version of the output format.
716 * @see SerializationHandler#setVersion(String)
717 */
718 public void setVersion(String version)
719 {
720 setOutputProperty(OutputKeys.VERSION, version);
721 }
722
723 /**
724 * Sets the value coming from the xsl:output media-type stylesheet attribute.
725 * @param mediaType the non-null media-type or MIME type associated with the
726 * output document.
727 * @see javax.xml.transform.OutputKeys#MEDIA_TYPE
728 * @see SerializationHandler#setMediaType(String)
729 */
730 public void setMediaType(String mediaType)
731 {
732 setOutputProperty(OutputKeys.MEDIA_TYPE,mediaType);
733 }
734
735 /**
736 * @return the number of spaces to indent for each indentation level.
737 */
738 public int getIndentAmount()
739 {
740 return m_indentAmount;
741 }
742
743 /**
744 * Sets the indentation amount.
745 * @param m_indentAmount The m_indentAmount to set
746 */
747 public void setIndentAmount(int m_indentAmount)
748 {
749 this.m_indentAmount = m_indentAmount;
750 }
751
752 /**
753 * Sets the value coming from the xsl:output indent stylesheet
754 * attribute.
755 * @param doIndent true if the output document should be indented to
756 * visually indicate its structure.
757 * @see XSLOutputAttributes#setIndent(boolean)
758 */
759 public void setIndent(boolean doIndent)
760 {
761 String val = doIndent ? "yes":"no";
762 setOutputProperty(OutputKeys.INDENT,val);
763 }
764
765 /**
766 * This method is used when a prefix/uri namespace mapping
767 * is indicated after the element was started with a
768 * startElement() and before and endElement().
769 * startPrefixMapping(prefix,uri) would be used before the
770 * startElement() call.
771 * @param uri the URI of the namespace
772 * @param prefix the prefix associated with the given URI.
773 *
774 * @see ExtendedContentHandler#namespaceAfterStartElement(String, String)
775 */
776 public void namespaceAfterStartElement(String uri, String prefix)
777 throws SAXException
778 {
779 // default behavior is to do nothing
780 }
781
782 /**
783 * Return a {@link DOMSerializer} interface into this serializer. If the
784 * serializer does not support the {@link DOMSerializer} interface, it should
785 * return null.
786 *
787 * @return A {@link DOMSerializer} interface into this serializer, or null
788 * if the serializer is not DOM capable
789 * @throws IOException An I/O exception occured
790 * @see Serializer#asDOMSerializer()
791 */
792 public DOMSerializer asDOMSerializer() throws IOException
793 {
794 return this;
795 }
796
797 /**
798 * Tell if two strings are equal, without worry if the first string is null.
799 *
800 * @param p String reference, which may be null.
801 * @param t String reference, which may be null.
802 *
803 * @return true if strings are equal.
804 */
805 private static final boolean subPartMatch(String p, String t)
806 {
807 return (p == t) || ((null != p) && (p.equals(t)));
808 }
809
810 /**
811 * Returns the local name of a qualified name.
812 * If the name has no prefix,
813 * then it works as the identity (SAX2).
814 *
815 * @param qname a qualified name
816 * @return returns the prefix of the qualified name,
817 * or null if there is no prefix.
818 */
819 protected static final String getPrefixPart(String qname)
820 {
821 final int col = qname.indexOf(':');
822 return (col > 0) ? qname.substring(0, col) : null;
823 //return (col > 0) ? qname.substring(0,col) : "";
824 }
825
826 /**
827 * Some users of the serializer may need the current namespace mappings
828 * @return the current namespace mappings (prefix/uri)
829 * @see ExtendedContentHandler#getNamespaceMappings()
830 */
831 public NamespaceMappings getNamespaceMappings()
832 {
833 return m_prefixMap;
834 }
835
836 /**
837 * Returns the prefix currently pointing to the given URI (if any).
838 * @param namespaceURI the uri of the namespace in question
839 * @return a prefix pointing to the given URI (if any).
840 * @see ExtendedContentHandler#getPrefix(String)
841 */
842 public String getPrefix(String namespaceURI)
843 {
844 String prefix = m_prefixMap.lookupPrefix(namespaceURI);
845 return prefix;
846 }
847
848 /**
849 * Returns the URI of an element or attribute. Note that default namespaces
850 * do not apply directly to attributes.
851 * @param qname a qualified name
852 * @param isElement true if the qualified name is the name of
853 * an element.
854 * @return returns the namespace URI associated with the qualified name.
855 */
856 public String getNamespaceURI(String qname, boolean isElement)
857 {
858 String uri = EMPTYSTRING;
859 int col = qname.lastIndexOf(':');
860 final String prefix = (col > 0) ? qname.substring(0, col) : EMPTYSTRING;
861
862 if (!EMPTYSTRING.equals(prefix) || isElement)
863 {
864 if (m_prefixMap != null)
865 {
866 uri = m_prefixMap.lookupNamespace(prefix);
867 if (uri == null && !prefix.equals(XMLNS_PREFIX))
868 {
869 throw new RuntimeException(
870 Utils.messages.createMessage(
871 MsgKey.ER_NAMESPACE_PREFIX,
872 new Object[] { qname.substring(0, col) } ));
873 }
874 }
875 }
876 return uri;
877 }
878
879 /**
880 * Returns the URI of prefix (if any)
881 *
882 * @param prefix the prefix whose URI is searched for
883 * @return the namespace URI currently associated with the
884 * prefix, null if the prefix is undefined.
885 */
886 public String getNamespaceURIFromPrefix(String prefix)
887 {
888 String uri = null;
889 if (m_prefixMap != null)
890 uri = m_prefixMap.lookupNamespace(prefix);
891 return uri;
892 }
893
894 /**
895 * Entity reference event.
896 *
897 * @param name Name of entity
898 *
899 * @throws org.xml.sax.SAXException
900 */
901 public void entityReference(String name) throws org.xml.sax.SAXException
902 {
903
904 flushPending();
905
906 startEntity(name);
907 endEntity(name);
908
909 if (m_tracer != null)
910 fireEntityReference(name);
911 }
912
913 /**
914 * Sets the transformer associated with this serializer
915 * @param t the transformer associated with this serializer.
916 * @see SerializationHandler#setTransformer(Transformer)
917 */
918 public void setTransformer(Transformer t)
919 {
920 m_transformer = t;
921
922 // If this transformer object implements the SerializerTrace interface
923 // then assign m_tracer to the transformer object so it can be used
924 // to fire trace events.
925 if ((m_transformer instanceof SerializerTrace) &&
926 (((SerializerTrace) m_transformer).hasTraceListeners())) {
927 m_tracer = (SerializerTrace) m_transformer;
928 } else {
929 m_tracer = null;
930 }
931 }
932 /**
933 * Gets the transformer associated with this serializer
934 * @return returns the transformer associated with this serializer.
935 * @see SerializationHandler#getTransformer()
936 */
937 public Transformer getTransformer()
938 {
939 return m_transformer;
940 }
941
942 /**
943 * This method gets the nodes value as a String and uses that String as if
944 * it were an input character notification.
945 * @param node the Node to serialize
946 * @throws org.xml.sax.SAXException
947 */
948 public void characters(org.w3c.dom.Node node)
949 throws org.xml.sax.SAXException
950 {
951 flushPending();
952 String data = node.getNodeValue();
953 if (data != null)
954 {
955 final int length = data.length();
956 if (length > m_charsBuff.length)
957 {
958 m_charsBuff = new char[length * 2 + 1];
959 }
960 data.getChars(0, length, m_charsBuff, 0);
961 characters(m_charsBuff, 0, length);
962 }
963 }
964
965
966 /**
967 * @see org.xml.sax.ErrorHandler#error(SAXParseException)
968 */
969 public void error(SAXParseException exc) throws SAXException {
970 }
971
972 /**
973 * @see org.xml.sax.ErrorHandler#fatalError(SAXParseException)
974 */
975 public void fatalError(SAXParseException exc) throws SAXException {
976
977 m_elemContext.m_startTagOpen = false;
978
979 }
980
981 /**
982 * @see org.xml.sax.ErrorHandler#warning(SAXParseException)
983 */
984 public void warning(SAXParseException exc) throws SAXException
985 {
986 }
987
988 /**
989 * To fire off start entity trace event
990 * @param name Name of entity
991 */
992 protected void fireStartEntity(String name)
993 throws org.xml.sax.SAXException
994 {
995 if (m_tracer != null)
996 {
997 flushMyWriter();
998 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENTITYREF, name);
999 }
1000 }
1001
1002 /**
1003 * Report the characters event
1004 * @param chars content of characters
1005 * @param start starting index of characters to output
1006 * @param length number of characters to output
1007 */
1008 // protected void fireCharEvent(char[] chars, int start, int length)
1009 // throws org.xml.sax.SAXException
1010 // {
1011 // if (m_tracer != null)
1012 // m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CHARACTERS, chars, start,length);
1013 // }
1014 //
1015
1016 /**
1017 * This method is only used internally when flushing the writer from the
1018 * various fire...() trace events. Due to the writer being wrapped with
1019 * SerializerTraceWriter it may cause the flush of these trace events:
1020 * EVENTTYPE_OUTPUT_PSEUDO_CHARACTERS
1021 * EVENTTYPE_OUTPUT_CHARACTERS
1022 * which trace the output written to the output stream.
1023 *
1024 */
1025 private void flushMyWriter()
1026 {
1027 if (m_writer != null)
1028 {
1029 try
1030 {
1031 m_writer.flush();
1032 }
1033 catch(IOException ioe)
1034 {
1035
1036 }
1037 }
1038 }
1039 /**
1040 * Report the CDATA trace event
1041 * @param chars content of CDATA
1042 * @param start starting index of characters to output
1043 * @param length number of characters to output
1044 */
1045 protected void fireCDATAEvent(char[] chars, int start, int length)
1046 throws org.xml.sax.SAXException
1047 {
1048 if (m_tracer != null)
1049 {
1050 flushMyWriter();
1051 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CDATA, chars, start,length);
1052 }
1053 }
1054
1055 /**
1056 * Report the comment trace event
1057 * @param chars content of comment
1058 * @param start starting index of comment to output
1059 * @param length number of characters to output
1060 */
1061 protected void fireCommentEvent(char[] chars, int start, int length)
1062 throws org.xml.sax.SAXException
1063 {
1064 if (m_tracer != null)
1065 {
1066 flushMyWriter();
1067 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_COMMENT, new String(chars, start, length));
1068 }
1069 }
1070
1071
1072 /**
1073 * To fire off end entity trace event
1074 * @param name Name of entity
1075 */
1076 public void fireEndEntity(String name)
1077 throws org.xml.sax.SAXException
1078 {
1079 if (m_tracer != null)
1080 flushMyWriter();
1081 // we do not need to handle this.
1082 }
1083
1084 /**
1085 * To fire off start document trace event
1086 */
1087 protected void fireStartDoc()
1088 throws org.xml.sax.SAXException
1089 {
1090 if (m_tracer != null)
1091 {
1092 flushMyWriter();
1093 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_STARTDOCUMENT);
1094 }
1095 }
1096
1097
1098 /**
1099 * To fire off end document trace event
1100 */
1101 protected void fireEndDoc()
1102 throws org.xml.sax.SAXException
1103 {
1104 if (m_tracer != null)
1105 {
1106 flushMyWriter();
1107 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDDOCUMENT);
1108 }
1109 }
1110
1111 /**
1112 * Report the start element trace event. This trace method needs to be
1113 * called just before the attributes are cleared.
1114 *
1115 * @param elemName the qualified name of the element
1116 *
1117 */
1118 protected void fireStartElem(String elemName)
1119 throws org.xml.sax.SAXException
1120 {
1121 if (m_tracer != null)
1122 {
1123 flushMyWriter();
1124 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_STARTELEMENT,
1125 elemName, m_attributes);
1126 }
1127 }
1128
1129
1130 /**
1131 * To fire off the end element event
1132 * @param name Name of element
1133 */
1134 // protected void fireEndElem(String name)
1135 // throws org.xml.sax.SAXException
1136 // {
1137 // if (m_tracer != null)
1138 // m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDELEMENT,name, (Attributes)null);
1139 // }
1140
1141
1142 /**
1143 * To fire off the PI trace event
1144 * @param name Name of PI
1145 */
1146 protected void fireEscapingEvent(String name, String data)
1147 throws org.xml.sax.SAXException
1148 {
1149
1150 if (m_tracer != null)
1151 {
1152 flushMyWriter();
1153 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_PI,name, data);
1154 }
1155 }
1156
1157
1158 /**
1159 * To fire off the entity reference trace event
1160 * @param name Name of entity reference
1161 */
1162 protected void fireEntityReference(String name)
1163 throws org.xml.sax.SAXException
1164 {
1165 if (m_tracer != null)
1166 {
1167 flushMyWriter();
1168 m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENTITYREF,name, (Attributes)null);
1169 }
1170 }
1171
1172 /**
1173 * Receive notification of the beginning of a document.
1174 * This method is never a self generated call,
1175 * but only called externally.
1176 *
1177 * <p>The SAX parser will invoke this method only once, before any
1178 * other methods in this interface or in DTDHandler (except for
1179 * setDocumentLocator).</p>
1180 *
1181 * @throws org.xml.sax.SAXException Any SAX exception, possibly
1182 * wrapping another exception.
1183 *
1184 * @throws org.xml.sax.SAXException
1185 */
1186 public void startDocument() throws org.xml.sax.SAXException
1187 {
1188
1189 // if we do get called with startDocument(), handle it right away
1190 startDocumentInternal();
1191 m_needToCallStartDocument = false;
1192 return;
1193 }
1194
1195 /**
1196 * This method handles what needs to be done at a startDocument() call,
1197 * whether from an external caller, or internally called in the
1198 * serializer. For historical reasons the serializer is flexible to
1199 * startDocument() not always being called.
1200 * Even if no external call is
1201 * made into startDocument() this method will always be called as a self
1202 * generated internal startDocument, it handles what needs to be done at a
1203 * startDocument() call.
1204 *
1205 * This method exists just to make sure that startDocument() is only ever
1206 * called from an external caller, which in principle is just a matter of
1207 * style.
1208 *
1209 * @throws SAXException
1210 */
1211 protected void startDocumentInternal() throws org.xml.sax.SAXException
1212 {
1213 if (m_tracer != null)
1214 this.fireStartDoc();
1215 }
1216 /**
1217 * This method is used to set the source locator, which might be used to
1218 * generated an error message.
1219 * @param locator the source locator
1220 *
1221 * @see ExtendedContentHandler#setSourceLocator(javax.xml.transform.SourceLocator)
1222 */
1223 public void setSourceLocator(SourceLocator locator)
1224 {
1225 m_sourceLocator = locator;
1226 }
1227
1228
1229 /**
1230 * Used only by TransformerSnapshotImpl to restore the serialization
1231 * to a previous state.
1232 *
1233 * @param mappings NamespaceMappings
1234 */
1235 public void setNamespaceMappings(NamespaceMappings mappings) {
1236 m_prefixMap = mappings;
1237 }
1238
1239 public boolean reset()
1240 {
1241 resetSerializerBase();
1242 return true;
1243 }
1244
1245 /**
1246 * Reset all of the fields owned by SerializerBase
1247 *
1248 */
1249 private void resetSerializerBase()
1250 {
1251 this.m_attributes.clear();
1252 this.m_CdataElems = null;
1253 this.m_cdataTagOpen = false;
1254 this.m_docIsEmpty = true;
1255 this.m_doctypePublic = null;
1256 this.m_doctypeSystem = null;
1257 this.m_doIndent = false;
1258 this.m_elemContext = new ElemContext();
1259 this.m_indentAmount = 0;
1260 this.m_inEntityRef = false;
1261 this.m_inExternalDTD = false;
1262 this.m_mediatype = null;
1263 this.m_needToCallStartDocument = true;
1264 this.m_needToOutputDocTypeDecl = false;
1265 if (m_OutputProps != null)
1266 this.m_OutputProps.clear();
1267 if (m_OutputPropsDefault != null)
1268 this.m_OutputPropsDefault.clear();
1269 if (this.m_prefixMap != null)
1270 this.m_prefixMap.reset();
1271 this.m_shouldNotWriteXMLHeader = false;
1272 this.m_sourceLocator = null;
1273 this.m_standalone = null;
1274 this.m_standaloneWasSpecified = false;
1275 this.m_StringOfCDATASections = null;
1276 this.m_tracer = null;
1277 this.m_transformer = null;
1278 this.m_version = null;
1279 // don't set writer to null, so that it might be re-used
1280 //this.m_writer = null;
1281 }
1282
1283 /**
1284 * Returns true if the serializer is used for temporary output rather than
1285 * final output.
1286 *
1287 * This concept is made clear in the XSLT 2.0 draft.
1288 */
1289 final boolean inTemporaryOutputState()
1290 {
1291 /* This is a hack. We should really be letting the serializer know
1292 * that it is in temporary output state with an explicit call, but
1293 * from a pragmatic point of view (for now anyways) having no output
1294 * encoding at all, not even the default UTF-8 indicates that the serializer
1295 * is being used for temporary RTF.
1296 */
1297 return (getEncoding() == null);
1298
1299 }
1300
1301 /**
1302 * This method adds an attribute the the current element,
1303 * but should not be used for an xsl:attribute child.
1304 * @see ExtendedContentHandler#addAttribute(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)
1305 */
1306 public void addAttribute(String uri, String localName, String rawName, String type, String value) throws SAXException
1307 {
1308 if (m_elemContext.m_startTagOpen)
1309 {
1310 addAttributeAlways(uri, localName, rawName, type, value, false);
1311 }
1312 }
1313
1314 /**
1315 * @see org.xml.sax.DTDHandler#notationDecl(java.lang.String, java.lang.String, java.lang.String)
1316 */
1317 public void notationDecl(String arg0, String arg1, String arg2)
1318 throws SAXException {
1319 // This method just provides a definition to satisfy the interface
1320 // A particular sub-class of SerializerBase provides the implementation (if desired)
1321 }
1322
1323 /**
1324 * @see org.xml.sax.DTDHandler#unparsedEntityDecl(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
1325 */
1326 public void unparsedEntityDecl(
1327 String arg0,
1328 String arg1,
1329 String arg2,
1330 String arg3)
1331 throws SAXException {
1332 // This method just provides a definition to satisfy the interface
1333 // A particular sub-class of SerializerBase provides the implementation (if desired)
1334 }
1335
1336 /**
1337 * If set to false the serializer does not expand DTD entities,
1338 * but leaves them as is, the default value is true.
1339 */
1340 public void setDTDEntityExpansion(boolean expand) {
1341 // This method just provides a definition to satisfy the interface
1342 // A particular sub-class of SerializerBase provides the implementation (if desired)
1343 }
1344
1345
1346 /**
1347 * The CDATA section names stored in a whitespace separateed list with
1348 * each element being a word of the form "{uri}localName" This list
1349 * comes from the cdata-section-elements attribute.
1350 *
1351 * This field replaces m_cdataSectionElements Vector.
1352 */
1353 protected String m_StringOfCDATASections = null;
1354
1355 boolean m_docIsEmpty = true;
1356 void initCdataElems(String s)
1357 {
1358 if (s != null)
1359 {
1360 int max = s.length();
1361
1362 // true if we are in the middle of a pair of curly braces that delimit a URI
1363 boolean inCurly = false;
1364
1365 // true if we found a URI but haven't yet processed the local name
1366 boolean foundURI = false;
1367
1368 StringBuffer buf = new StringBuffer();
1369 String uri = null;
1370 String localName = null;
1371
1372 // parse through string, breaking on whitespaces. I do this instead
1373 // of a tokenizer so I can track whitespace inside of curly brackets,
1374 // which theoretically shouldn't happen if they contain legal URLs.
1375
1376
1377 for (int i = 0; i < max; i++)
1378 {
1379
1380 char c = s.charAt(i);
1381
1382 if (Character.isWhitespace(c))
1383 {
1384 if (!inCurly)
1385 {
1386 if (buf.length() > 0)
1387 {
1388 localName = buf.toString();
1389 if (!foundURI)
1390 uri = "";
1391 addCDATAElement(uri,localName);
1392 buf.setLength(0);
1393 foundURI = false;
1394 }
1395 continue;
1396 }
1397 else
1398 buf.append(c); // add whitespace to the URI
1399 }
1400 else if ('{' == c) // starting a URI
1401 inCurly = true;
1402 else if ('}' == c)
1403 {
1404 // we just ended a URI, add the URI to the vector
1405 foundURI = true;
1406 uri = buf.toString();
1407 buf.setLength(0);
1408 inCurly = false;
1409 }
1410 else
1411 {
1412 // append non-whitespace, non-curly to current URI or localName being gathered.
1413 buf.append(c);
1414 }
1415
1416 }
1417
1418 if (buf.length() > 0)
1419 {
1420 // We have one last localName to process.
1421 localName = buf.toString();
1422 if (!foundURI)
1423 uri = "";
1424 addCDATAElement(uri,localName);
1425 }
1426 }
1427 }
1428 protected java.util.Hashtable m_CdataElems = null;
1429 private void addCDATAElement(String uri, String localName)
1430 {
1431 if (m_CdataElems == null) {
1432 m_CdataElems = new java.util.Hashtable();
1433 }
1434
1435 java.util.Hashtable h = (java.util.Hashtable) m_CdataElems.get(localName);
1436 if (h == null) {
1437 h = new java.util.Hashtable();
1438 m_CdataElems.put(localName,h);
1439 }
1440 h.put(uri,uri);
1441
1442 }
1443
1444
1445 /**
1446 * Return true if nothing has been sent to this result tree yet.
1447 * <p>
1448 * This is not a public API.
1449 *
1450 * @xsl.usage internal
1451 */
1452 public boolean documentIsEmpty() {
1453 // If we haven't called startDocument() yet, then this document is empty
1454 return m_docIsEmpty && (m_elemContext.m_currentElemDepth == 0);
1455 }
1456
1457 /**
1458 * Return true if the current element in m_elemContext
1459 * is a CDATA section.
1460 * CDATA sections are specified in the <xsl:output> attribute
1461 * cdata-section-names or in the JAXP equivalent property.
1462 * In any case the format of the value of such a property is:
1463 * <pre>
1464 * "{uri1}localName1 {uri2}localName2 . . . "
1465 * </pre>
1466 *
1467 * <p>
1468 * This method is not a public API, but is only used internally by the serializer.
1469 */
1470 protected boolean isCdataSection()
1471 {
1472
1473 boolean b = false;
1474
1475 if (null != m_StringOfCDATASections)
1476 {
1477 if (m_elemContext.m_elementLocalName == null)
1478 {
1479 String localName = getLocalName(m_elemContext.m_elementName);
1480 m_elemContext.m_elementLocalName = localName;
1481 }
1482
1483 if ( m_elemContext.m_elementURI == null) {
1484
1485 m_elemContext.m_elementURI = getElementURI();
1486 }
1487 else if ( m_elemContext.m_elementURI.length() == 0) {
1488 if ( m_elemContext.m_elementName == null) {
1489 m_elemContext.m_elementName = m_elemContext.m_elementLocalName;
1490 // leave URI as "", meaning in no namespace
1491 }
1492 else if (m_elemContext.m_elementLocalName.length() < m_elemContext.m_elementName.length()){
1493 // We were told the URI was "", yet the name has a prefix since the name is longer than the localname.
1494 // So we will fix that incorrect information here.
1495 m_elemContext.m_elementURI = getElementURI();
1496 }
1497 }
1498
1499 java.util.Hashtable h = (java.util.Hashtable) m_CdataElems.get(m_elemContext.m_elementLocalName);
1500 if (h != null)
1501 {
1502 Object obj = h.get(m_elemContext.m_elementURI);
1503 if (obj != null)
1504 b = true;
1505 }
1506
1507 }
1508 return b;
1509 }
1510
1511 /**
1512 * Before this call m_elementContext.m_elementURI is null,
1513 * which means it is not yet known. After this call it
1514 * is non-null, but possibly "" meaning that it is in the
1515 * default namespace.
1516 *
1517 * @return The URI of the element, never null, but possibly "".
1518 */
1519 private String getElementURI() {
1520 String uri = null;
1521 // At this point in processing we have received all the
1522 // namespace mappings
1523 // As we still don't know the elements namespace,
1524 // we now figure it out.
1525
1526 String prefix = getPrefixPart(m_elemContext.m_elementName);
1527
1528 if (prefix == null) {
1529 // no prefix so lookup the URI of the default namespace
1530 uri = m_prefixMap.lookupNamespace("");
1531 } else {
1532 uri = m_prefixMap.lookupNamespace(prefix);
1533 }
1534 if (uri == null) {
1535 // We didn't find the namespace for the
1536 // prefix ... ouch, that shouldn't happen.
1537 // This is a hack, we really don't know
1538 // the namespace
1539 uri = EMPTYSTRING;
1540 }
1541
1542 return uri;
1543 }
1544
1545
1546 /**
1547 * Get the value of an output property,
1548 * the explicit value, if any, otherwise the
1549 * default value, if any, otherwise null.
1550 */
1551 public String getOutputProperty(String name) {
1552 String val = getOutputPropertyNonDefault(name);
1553 // If no explicit value, try to get the default value
1554 if (val == null)
1555 val = getOutputPropertyDefault(name);
1556 return val;
1557
1558 }
1559 /**
1560 * Get the value of an output property,
1561 * not the default value. If there is a default
1562 * value, but no non-default value this method
1563 * will return null.
1564 * <p>
1565 *
1566 */
1567 public String getOutputPropertyNonDefault(String name )
1568 {
1569 return getProp(name,false);
1570 }
1571
1572 /**
1573 * Return a {@link DOM3Serializer} interface into this serializer. If the
1574 * serializer does not support the {@link DOM3Serializer} interface, it should
1575 * return null.
1576 *
1577 * @return A {@link DOM3Serializer} interface into this serializer, or null
1578 * if the serializer is not DOM capable
1579 * @throws IOException An I/O exception occured
1580 * @see org.apache.xml.serializer.Serializer#asDOM3Serializer()
1581 */
1582 public Object asDOM3Serializer() throws IOException
1583 {
1584 return new org.apache.xml.serializer.dom3.DOM3SerializerImpl(this);
1585 }
1586 /**
1587 * Get the default value of an xsl:output property,
1588 * which would be null only if no default value exists
1589 * for the property.
1590 */
1591 public String getOutputPropertyDefault(String name) {
1592 return getProp(name, true);
1593 }
1594
1595 /**
1596 * Set the value for the output property, typically from
1597 * an xsl:output element, but this does not change what
1598 * the default value is.
1599 */
1600 public void setOutputProperty(String name, String val) {
1601 setProp(name,val,false);
1602
1603 }
1604
1605 /**
1606 * Set the default value for an output property, but this does
1607 * not impact any explicitly set value.
1608 */
1609 public void setOutputPropertyDefault(String name, String val) {
1610 setProp(name,val,true);
1611
1612 }
1613
1614 /**
1615 * A mapping of keys to explicitly set values, for example if
1616 * and <xsl:output/> has an "encoding" attribute, this
1617 * map will have what that attribute maps to.
1618 */
1619 private HashMap m_OutputProps;
1620 /**
1621 * A mapping of keys to default values, for example if
1622 * the default value of the encoding is "UTF-8" then this
1623 * map will have that "encoding" maps to "UTF-8".
1624 */
1625 private HashMap m_OutputPropsDefault;
1626
1627 Set getOutputPropDefaultKeys() {
1628 return m_OutputPropsDefault.keySet();
1629 }
1630 Set getOutputPropKeys() {
1631 return m_OutputProps.keySet();
1632 }
1633
1634 private String getProp(String name, boolean defaultVal) {
1635 if (m_OutputProps == null) {
1636 m_OutputProps = new HashMap();
1637 m_OutputPropsDefault = new HashMap();
1638 }
1639
1640 String val;
1641 if (defaultVal)
1642 val = (String) m_OutputPropsDefault.get(name);
1643 else
1644 val = (String) m_OutputProps.get(name);
1645
1646 return val;
1647
1648 }
1649 /**
1650 *
1651 * @param name The name of the property, e.g. "{http://myprop}indent-tabs" or "indent".
1652 * @param val The value of the property, e.g. "4"
1653 * @param defaultVal true if this is a default value being set for the property as
1654 * opposed to a user define on, set say explicitly in the stylesheet or via JAXP
1655 */
1656 void setProp(String name, String val, boolean defaultVal) {
1657 if (m_OutputProps == null) {
1658 m_OutputProps = new HashMap();
1659 m_OutputPropsDefault = new HashMap();
1660 }
1661
1662 if (defaultVal)
1663 m_OutputPropsDefault.put(name,val);
1664 else {
1665 if (OutputKeys.CDATA_SECTION_ELEMENTS.equals(name) && val != null) {
1666 initCdataElems(val);
1667 String oldVal = (String) m_OutputProps.get(name);
1668 String newVal;
1669 if (oldVal == null)
1670 newVal = oldVal + ' ' + val;
1671 else
1672 newVal = val;
1673 m_OutputProps.put(name,newVal);
1674 }
1675 else {
1676 m_OutputProps.put(name,val);
1677 }
1678 }
1679
1680
1681 }
1682
1683 /**
1684 * Get the first char of the local name
1685 * @param name Either a local name, or a local name
1686 * preceeded by a uri enclosed in curly braces.
1687 */
1688 static char getFirstCharLocName(String name) {
1689 final char first;
1690 int i = name.indexOf('}');
1691 if (i < 0)
1692 first = name.charAt(0);
1693 else
1694 first = name.charAt(i+1);
1695 return first;
1696 }
1697 }
1698
1699