1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /*
  17. * $Id: SerializerBase.java,v 1.10 2004/02/17 04:18:18 minchau Exp $
  18. */
  19. package com.sun.org.apache.xml.internal.serializer;
  20. import java.io.IOException;
  21. import java.util.Hashtable;
  22. import java.util.Stack;
  23. import java.util.Vector;
  24. import javax.xml.transform.SourceLocator;
  25. import javax.xml.transform.Transformer;
  26. import com.sun.org.apache.xml.internal.res.XMLErrorResources;
  27. import com.sun.org.apache.xml.internal.res.XMLMessages;
  28. import com.sun.org.apache.xml.internal.utils.BoolStack;
  29. import org.xml.sax.Attributes;
  30. import org.xml.sax.ContentHandler;
  31. import org.xml.sax.Locator;
  32. import org.xml.sax.SAXException;
  33. import org.xml.sax.SAXParseException;
  34. /**
  35. * This class acts as a base class for the XML "serializers"
  36. * and the stream serializers.
  37. * It contains a number of common fields and methods.
  38. * @author Santiago Pericas-Geertsen
  39. * @author G. Todd Miller
  40. */
  41. abstract public class SerializerBase
  42. implements SerializationHandler, SerializerConstants, com.sun.org.apache.xml.internal.dtm.ref.dom2dtm.DOM2DTM.CharacterNodeHandler
  43. {
  44. /**
  45. * To fire off the end element trace event
  46. * @param name Name of element
  47. */
  48. protected void fireEndElem(String name)
  49. throws org.xml.sax.SAXException
  50. {
  51. if (m_tracer != null)
  52. {
  53. flushMyWriter();
  54. m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDELEMENT,name, (Attributes)null);
  55. }
  56. }
  57. /**
  58. * Report the characters trace event
  59. * @param chars content of characters
  60. * @param start starting index of characters to output
  61. * @param length number of characters to output
  62. */
  63. protected void fireCharEvent(char[] chars, int start, int length)
  64. throws org.xml.sax.SAXException
  65. {
  66. if (m_tracer != null)
  67. {
  68. flushMyWriter();
  69. m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CHARACTERS, chars, start,length);
  70. }
  71. }
  72. /**
  73. * true if we still need to call startDocumentInternal()
  74. */
  75. protected boolean m_needToCallStartDocument = true;
  76. /** True if a trailing "]]>" still needs to be written to be
  77. * written out. Used to merge adjacent CDATA sections
  78. */
  79. protected boolean m_cdataTagOpen = false;
  80. /**
  81. * All the attributes of the current element, collected from
  82. * startPrefixMapping() calls, or addAddtribute() calls, or
  83. * from the SAX attributes in a startElement() call.
  84. */
  85. protected AttributesImplSerializer m_attributes = new AttributesImplSerializer();
  86. /**
  87. * Tells if we're in an EntityRef event.
  88. */
  89. protected boolean m_inEntityRef = false;
  90. /** This flag is set while receiving events from the external DTD */
  91. protected boolean m_inExternalDTD = false;
  92. /**
  93. * The System ID for the doc type.
  94. */
  95. private String m_doctypeSystem;
  96. /**
  97. * The public ID for the doc type.
  98. */
  99. private String m_doctypePublic;
  100. /**
  101. * Flag to tell that we need to add the doctype decl, which we can't do
  102. * until the first element is encountered.
  103. */
  104. boolean m_needToOutputDocTypeDecl = true;
  105. /**
  106. * The character encoding. Must match the encoding used for the
  107. * printWriter.
  108. */
  109. private String m_encoding = null;
  110. /**
  111. * Tells if we should write the XML declaration.
  112. */
  113. private boolean m_shouldNotWriteXMLHeader = false;
  114. /**
  115. * The standalone value for the doctype.
  116. */
  117. private String m_standalone;
  118. /**
  119. * True if standalone was specified.
  120. */
  121. protected boolean m_standaloneWasSpecified = false;
  122. /**
  123. * Flag to tell if indenting (pretty-printing) is on.
  124. */
  125. protected boolean m_doIndent = false;
  126. /**
  127. * Amount to indent.
  128. */
  129. protected int m_indentAmount = 0;
  130. /**
  131. * Tells the XML version, for writing out to the XML decl.
  132. */
  133. private String m_version = null;
  134. /**
  135. * The mediatype. Not used right now.
  136. */
  137. private String m_mediatype;
  138. /**
  139. * The transformer that was around when this output handler was created (if
  140. * any).
  141. */
  142. private Transformer m_transformer;
  143. /**
  144. * Pairs of local names and corresponding URIs of CDATA sections. This list
  145. * comes from the cdata-section-elements attribute. Every second one is a
  146. * local name, and every other second one is the URI for the local name.
  147. */
  148. protected Vector m_cdataSectionElements = null;
  149. /**
  150. * Namespace support, that keeps track of currently defined
  151. * prefix/uri mappings. As processed elements come and go, so do
  152. * the associated mappings for that element.
  153. */
  154. protected NamespaceMappings m_prefixMap;
  155. /**
  156. * Handle for firing generate events. This interface may be implemented
  157. * by the referenced transformer object.
  158. */
  159. protected SerializerTrace m_tracer;
  160. protected SourceLocator m_sourceLocator;
  161. /**
  162. * The writer to send output to. This field is only used in the ToStream
  163. * serializers, but exists here just so that the fireStartDoc() and
  164. * other fire... methods can flush this writer when tracing.
  165. */
  166. protected java.io.Writer m_writer = null;
  167. /**
  168. * A reference to "stack frame" corresponding to
  169. * the current element. Such a frame is pushed at a startElement()
  170. * and popped at an endElement(). This frame contains information about
  171. * the element, such as its namespace URI.
  172. */
  173. protected ElemContext m_elemContext = new ElemContext();
  174. /**
  175. * A utility buffer for converting Strings passed to
  176. * character() methods to character arrays.
  177. * Reusing this buffer means not creating a new character array
  178. * everytime and it runs faster.
  179. */
  180. protected char[] m_charsBuff = new char[60];
  181. /**
  182. * A utility buffer for converting Strings passed to
  183. * attribute methods to character arrays.
  184. * Reusing this buffer means not creating a new character array
  185. * everytime and it runs faster.
  186. */
  187. protected char[] m_attrBuff = new char[30];
  188. /**
  189. * Receive notification of a comment.
  190. *
  191. * @see com.sun.org.apache.xml.internal.serializer.ExtendedLexicalHandler#comment(String)
  192. */
  193. public void comment(String data) throws SAXException
  194. {
  195. final int length = data.length();
  196. if (length > m_charsBuff.length)
  197. {
  198. m_charsBuff = new char[length * 2 + 1];
  199. }
  200. data.getChars(0, length, m_charsBuff, 0);
  201. comment(m_charsBuff, 0, length);
  202. }
  203. /**
  204. * TODO: This method is a HACK! Since XSLTC does not have access to the
  205. * XML file, it sometimes generates a NS prefix of the form "ns?" for
  206. * an attribute. If at runtime, when the qname of the attribute is
  207. * known, another prefix is specified for the attribute, then we can get
  208. * a qname of the form "ns?:otherprefix:name". This function patches the
  209. * qname by simply ignoring "otherprefix".
  210. */
  211. protected String patchName(String qname)
  212. {
  213. final int lastColon = qname.lastIndexOf(':');
  214. if (lastColon > 0) {
  215. final int firstColon = qname.indexOf(':');
  216. final String prefix = qname.substring(0, firstColon);
  217. final String localName = qname.substring(lastColon + 1);
  218. // If uri is "" then ignore prefix
  219. final String uri = m_prefixMap.lookupNamespace(prefix);
  220. if (uri != null && uri.length() == 0) {
  221. return localName;
  222. }
  223. else if (firstColon != lastColon) {
  224. return prefix + ':' + localName;
  225. }
  226. }
  227. return qname;
  228. }
  229. /**
  230. * Returns the local name of a qualified name. If the name has no prefix,
  231. * then it works as the identity (SAX2).
  232. * @param qname the qualified name
  233. * @return the name, but excluding any prefix and colon.
  234. */
  235. protected static String getLocalName(String qname)
  236. {
  237. final int col = qname.lastIndexOf(':');
  238. return (col > 0) ? qname.substring(col + 1) : qname;
  239. }
  240. /**
  241. * Receive an object for locating the origin of SAX document events.
  242. *
  243. * @param locator An object that can return the location of any SAX document
  244. * event.
  245. *
  246. * Receive an object for locating the origin of SAX document events.
  247. *
  248. * <p>SAX parsers are strongly encouraged (though not absolutely
  249. * required) to supply a locator: if it does so, it must supply
  250. * the locator to the application by invoking this method before
  251. * invoking any of the other methods in the DocumentHandler
  252. * interface.</p>
  253. *
  254. * <p>The locator allows the application to determine the end
  255. * position of any document-related event, even if the parser is
  256. * not reporting an error. Typically, the application will
  257. * use this information for reporting its own errors (such as
  258. * character content that does not match an application's
  259. * business rules). The information returned by the locator
  260. * is probably not sufficient for use with a search engine.</p>
  261. *
  262. * <p>Note that the locator will return correct information only
  263. * during the invocation of the events in this interface. The
  264. * application should not attempt to use it at any other time.</p>
  265. */
  266. public void setDocumentLocator(Locator locator)
  267. {
  268. return;
  269. // I don't do anything with this yet.
  270. }
  271. /**
  272. * Adds the given attribute to the set of collected attributes , but only if
  273. * there is a currently open element.
  274. *
  275. * An element is currently open if a startElement() notification has
  276. * occured but the start of the element has not yet been written to the
  277. * output. In the stream case this means that we have not yet been forced
  278. * to close the elements opening tag by another notification, such as a
  279. * character notification.
  280. *
  281. * @param uri the URI of the attribute
  282. * @param localName the local name of the attribute
  283. * @param rawName the qualified name of the attribute
  284. * @param type the type of the attribute (probably CDATA)
  285. * @param value the value of the attribute
  286. * @see com.sun.org.apache.xml.internal.serializer.ExtendedContentHandler#addAttribute(String, String, String, String, String)
  287. */
  288. public void addAttribute(
  289. String uri,
  290. String localName,
  291. String rawName,
  292. String type,
  293. String value)
  294. throws SAXException
  295. {
  296. if (m_elemContext.m_startTagOpen)
  297. {
  298. addAttributeAlways(uri, localName, rawName, type, value);
  299. }
  300. }
  301. /**
  302. * Adds the given attribute to the set of attributes, even if there is
  303. * no currently open element. This is useful if a SAX startPrefixMapping()
  304. * should need to add an attribute before the element name is seen.
  305. *
  306. * @param uri the URI of the attribute
  307. * @param localName the local name of the attribute
  308. * @param rawName the qualified name of the attribute
  309. * @param type the type of the attribute (probably CDATA)
  310. * @param value the value of the attribute
  311. */
  312. public void addAttributeAlways(
  313. String uri,
  314. String localName,
  315. String rawName,
  316. String type,
  317. String value)
  318. {
  319. // final int index =
  320. // (localName == null || uri == null) ?
  321. // m_attributes.getIndex(rawName):m_attributes.getIndex(uri, localName);
  322. int index;
  323. // if (localName == null || uri == null){
  324. // index = m_attributes.getIndex(rawName);
  325. // }
  326. // else {
  327. // index = m_attributes.getIndex(uri, localName);
  328. // }
  329. index = m_attributes.getIndex(rawName);
  330. if (index >= 0)
  331. {
  332. /* We've seen the attribute before.
  333. * We may have a null uri or localName, but all
  334. * we really want to re-set is the value anyway.
  335. */
  336. m_attributes.setValue(index,value);
  337. }
  338. else
  339. {
  340. // the attribute doesn't exist yet, create it
  341. m_attributes.addAttribute(uri, localName, rawName, type, value);
  342. }
  343. }
  344. /**
  345. * Adds the given attribute to the set of collected attributes,
  346. * but only if there is a currently open element. This method is only
  347. * called by XSLTC.
  348. *
  349. * @param name the attribute's qualified name
  350. * @param value the value of the attribute
  351. */
  352. public void addAttribute(String name, final String value)
  353. {
  354. if (m_elemContext.m_startTagOpen)
  355. {
  356. final String patchedName = patchName(name);
  357. final String localName = getLocalName(patchedName);
  358. final String uri = getNamespaceURI(patchedName, false);
  359. addAttributeAlways(uri,localName, patchedName, "CDATA", value);
  360. }
  361. }
  362. /**
  363. * Add the given attributes to the currently collected ones. These
  364. * attributes are always added, regardless of whether on not an element
  365. * is currently open.
  366. * @param atts List of attributes to add to this list
  367. */
  368. public void addAttributes(Attributes atts) throws SAXException
  369. {
  370. int nAtts = atts.getLength();
  371. for (int i = 0; i < nAtts; i++)
  372. {
  373. String uri = atts.getURI(i);
  374. if (null == uri)
  375. uri = "";
  376. addAttributeAlways(
  377. uri,
  378. atts.getLocalName(i),
  379. atts.getQName(i),
  380. atts.getType(i),
  381. atts.getValue(i));
  382. }
  383. }
  384. /**
  385. * Return a {@link ContentHandler} interface into this serializer.
  386. * If the serializer does not support the {@link ContentHandler}
  387. * interface, it should return null.
  388. *
  389. * @return A {@link ContentHandler} interface into this serializer,
  390. * or null if the serializer is not SAX 2 capable
  391. * @throws IOException An I/O exception occured
  392. */
  393. public ContentHandler asContentHandler() throws IOException
  394. {
  395. return this;
  396. }
  397. /**
  398. * Report the end of an entity.
  399. *
  400. * @param name The name of the entity that is ending.
  401. * @throws org.xml.sax.SAXException The application may raise an exception.
  402. * @see #startEntity
  403. */
  404. public void endEntity(String name) throws org.xml.sax.SAXException
  405. {
  406. if (name.equals("[dtd]"))
  407. m_inExternalDTD = false;
  408. m_inEntityRef = false;
  409. if (m_tracer != null)
  410. this.fireEndEntity(name);
  411. }
  412. /**
  413. * Flush and close the underlying java.io.Writer. This method applies to
  414. * ToStream serializers, not ToSAXHandler serializers.
  415. * @see com.sun.org.apache.xml.internal.serializer.ToStream
  416. */
  417. public void close()
  418. {
  419. // do nothing (base behavior)
  420. }
  421. /**
  422. * Initialize global variables
  423. */
  424. protected void initCDATA()
  425. {
  426. // CDATA stack
  427. // _cdataStack = new Stack();
  428. // _cdataStack.push(new Integer(-1)); // push dummy value
  429. }
  430. /**
  431. * Returns the character encoding to be used in the output document.
  432. * @return the character encoding to be used in the output document.
  433. */
  434. public String getEncoding()
  435. {
  436. return m_encoding;
  437. }
  438. /**
  439. * Sets the character encoding coming from the xsl:output encoding stylesheet attribute.
  440. * @param encoding the character encoding
  441. */
  442. public void setEncoding(String m_encoding)
  443. {
  444. this.m_encoding = m_encoding;
  445. }
  446. /**
  447. * Sets the value coming from the xsl:output omit-xml-declaration stylesheet attribute
  448. * @param b true if the XML declaration is to be omitted from the output
  449. * document.
  450. */
  451. public void setOmitXMLDeclaration(boolean b)
  452. {
  453. this.m_shouldNotWriteXMLHeader = b;
  454. }
  455. /**
  456. * @return true if the XML declaration is to be omitted from the output
  457. * document.
  458. */
  459. public boolean getOmitXMLDeclaration()
  460. {
  461. return m_shouldNotWriteXMLHeader;
  462. }
  463. /**
  464. * Returns the previously set value of the value to be used as the public
  465. * identifier in the document type declaration (DTD).
  466. *
  467. *@return the public identifier to be used in the DOCTYPE declaration in the
  468. * output document.
  469. */
  470. public String getDoctypePublic()
  471. {
  472. return m_doctypePublic;
  473. }
  474. /** Set the value coming from the xsl:output doctype-public stylesheet attribute.
  475. * @param doctypePublic the public identifier to be used in the DOCTYPE
  476. * declaration in the output document.
  477. */
  478. public void setDoctypePublic(String doctypePublic)
  479. {
  480. this.m_doctypePublic = doctypePublic;
  481. }
  482. /**
  483. * Returns the previously set value of the value to be used
  484. * as the system identifier in the document type declaration (DTD).
  485. * @return the system identifier to be used in the DOCTYPE declaration in
  486. * the output document.
  487. *
  488. */
  489. public String getDoctypeSystem()
  490. {
  491. return m_doctypeSystem;
  492. }
  493. /** Set the value coming from the xsl:output doctype-system stylesheet attribute.
  494. * @param doctypeSystem the system identifier to be used in the DOCTYPE
  495. * declaration in the output document.
  496. */
  497. public void setDoctypeSystem(String doctypeSystem)
  498. {
  499. this.m_doctypeSystem = doctypeSystem;
  500. }
  501. /** Set the value coming from the xsl:output doctype-public and doctype-system stylesheet properties
  502. * @param doctypeSystem the system identifier to be used in the DOCTYPE
  503. * declaration in the output document.
  504. * @param doctypePublic the public identifier to be used in the DOCTYPE
  505. * declaration in the output document.
  506. */
  507. public void setDoctype(String doctypeSystem, String doctypePublic)
  508. {
  509. this.m_doctypeSystem = doctypeSystem;
  510. this.m_doctypePublic = doctypePublic;
  511. }
  512. /**
  513. * Sets the value coming from the xsl:output standalone stylesheet attribute.
  514. * @param standalone a value of "yes" indicates that the
  515. * <code>standalone</code> delaration is to be included in the output
  516. * document. This method remembers if the value was explicitly set using
  517. * this method, verses if the value is the default value.
  518. */
  519. public void setStandalone(String standalone)
  520. {
  521. if (standalone != null)
  522. {
  523. m_standaloneWasSpecified = true;
  524. setStandaloneInternal(standalone);
  525. }
  526. }
  527. /**
  528. * Sets the XSL standalone attribute, but does not remember if this is a
  529. * default or explicite setting.
  530. * @param standalone "yes" | "no"
  531. */
  532. protected void setStandaloneInternal(String standalone)
  533. {
  534. if ("yes".equals(standalone))
  535. m_standalone = "yes";
  536. else
  537. m_standalone = "no";
  538. }
  539. /**
  540. * Gets the XSL standalone attribute
  541. * @return a value of "yes" if the <code>standalone</code> delaration is to
  542. * be included in the output document.
  543. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#getStandalone()
  544. */
  545. public String getStandalone()
  546. {
  547. return m_standalone;
  548. }
  549. /**
  550. * @return true if the output document should be indented to visually
  551. * indicate its structure.
  552. */
  553. public boolean getIndent()
  554. {
  555. return m_doIndent;
  556. }
  557. /**
  558. * Gets the mediatype the media-type or MIME type associated with the output
  559. * document.
  560. * @return the mediatype the media-type or MIME type associated with the
  561. * output document.
  562. */
  563. public String getMediaType()
  564. {
  565. return m_mediatype;
  566. }
  567. /**
  568. * Gets the version of the output format.
  569. * @return the version of the output format.
  570. */
  571. public String getVersion()
  572. {
  573. return m_version;
  574. }
  575. /**
  576. * Sets the value coming from the xsl:output version attribute.
  577. * @param version the version of the output format.
  578. * @see com.sun.org.apache.xml.internal.serializer.SerializationHandler#setVersion(String)
  579. */
  580. public void setVersion(String version)
  581. {
  582. m_version = version;
  583. }
  584. /**
  585. * Sets the value coming from the xsl:output media-type stylesheet attribute.
  586. * @param mediaType the non-null media-type or MIME type associated with the
  587. * output document.
  588. * @see javax.xml.transform.OutputKeys#MEDIA_TYPE
  589. * @see com.sun.org.apache.xml.internal.serializer.SerializationHandler#setMediaType(String)
  590. */
  591. public void setMediaType(String mediaType)
  592. {
  593. m_mediatype = mediaType;
  594. }
  595. /**
  596. * @return the number of spaces to indent for each indentation level.
  597. */
  598. public int getIndentAmount()
  599. {
  600. return m_indentAmount;
  601. }
  602. /**
  603. * Sets the indentation amount.
  604. * @param m_indentAmount The m_indentAmount to set
  605. */
  606. public void setIndentAmount(int m_indentAmount)
  607. {
  608. this.m_indentAmount = m_indentAmount;
  609. }
  610. /**
  611. * Sets the value coming from the xsl:output indent stylesheet
  612. * attribute.
  613. * @param doIndent true if the output document should be indented to
  614. * visually indicate its structure.
  615. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#setIndent(boolean)
  616. */
  617. public void setIndent(boolean doIndent)
  618. {
  619. m_doIndent = doIndent;
  620. }
  621. /**
  622. * This method is used when a prefix/uri namespace mapping
  623. * is indicated after the element was started with a
  624. * startElement() and before and endElement().
  625. * startPrefixMapping(prefix,uri) would be used before the
  626. * startElement() call.
  627. * @param uri the URI of the namespace
  628. * @param prefix the prefix associated with the given URI.
  629. *
  630. * @see com.sun.org.apache.xml.internal.serializer.ExtendedContentHandler#namespaceAfterStartElement(String, String)
  631. */
  632. public void namespaceAfterStartElement(String uri, String prefix)
  633. throws SAXException
  634. {
  635. // default behavior is to do nothing
  636. }
  637. /**
  638. * Return a {@link DOMSerializer} interface into this serializer. If the
  639. * serializer does not support the {@link DOMSerializer} interface, it should
  640. * return null.
  641. *
  642. * @return A {@link DOMSerializer} interface into this serializer, or null
  643. * if the serializer is not DOM capable
  644. * @throws IOException An I/O exception occured
  645. * @see com.sun.org.apache.xml.internal.serializer.Serializer#asDOMSerializer()
  646. */
  647. public DOMSerializer asDOMSerializer() throws IOException
  648. {
  649. return this;
  650. }
  651. /**
  652. * Push a boolean state based on if the name of the element
  653. * is found in the list of qnames. A state is only pushed if
  654. * there were some cdata-section-names were specified.
  655. *
  656. * @param namespaceURI Should be a non-null reference to the namespace URL
  657. * of the element that owns the state, or empty string.
  658. * @param localName Should be a non-null reference to the local name
  659. * of the element that owns the state.
  660. *
  661. * Hidden parameters are the vector of qualified elements specified in
  662. * cdata-section-names attribute, and the m_cdataSectionStates stack
  663. * onto which whether the current element is in the list is pushed (true or
  664. * false). Other hidden parameters are the current elements namespaceURI,
  665. * localName and qName
  666. */
  667. protected boolean isCdataSection()
  668. {
  669. boolean b = false;
  670. if (null != m_cdataSectionElements)
  671. {
  672. if (m_elemContext.m_elementLocalName == null)
  673. m_elemContext.m_elementLocalName =
  674. getLocalName(m_elemContext.m_elementName);
  675. if (m_elemContext.m_elementURI == null)
  676. {
  677. String prefix = getPrefixPart(m_elemContext.m_elementName);
  678. if (prefix != null)
  679. m_elemContext.m_elementURI =
  680. m_prefixMap.lookupNamespace(prefix);
  681. }
  682. if ((null != m_elemContext.m_elementURI)
  683. && m_elemContext.m_elementURI.length() == 0)
  684. m_elemContext.m_elementURI = null;
  685. int nElems = m_cdataSectionElements.size();
  686. // loop through 2 at a time, as these are pairs of URI and localName
  687. for (int i = 0; i < nElems; i += 2)
  688. {
  689. String uri = (String) m_cdataSectionElements.elementAt(i);
  690. String loc = (String) m_cdataSectionElements.elementAt(i + 1);
  691. if (loc.equals(m_elemContext.m_elementLocalName)
  692. && subPartMatch(m_elemContext.m_elementURI, uri))
  693. {
  694. b = true;
  695. break;
  696. }
  697. }
  698. }
  699. return b;
  700. }
  701. /**
  702. * Tell if two strings are equal, without worry if the first string is null.
  703. *
  704. * @param p String reference, which may be null.
  705. * @param t String reference, which may be null.
  706. *
  707. * @return true if strings are equal.
  708. */
  709. private static final boolean subPartMatch(String p, String t)
  710. {
  711. return (p == t) || ((null != p) && (p.equals(t)));
  712. }
  713. /**
  714. * Returns the local name of a qualified name.
  715. * If the name has no prefix,
  716. * then it works as the identity (SAX2).
  717. *
  718. * @param qname a qualified name
  719. * @return returns the prefix of the qualified name,
  720. * or null if there is no prefix.
  721. */
  722. protected static final String getPrefixPart(String qname)
  723. {
  724. final int col = qname.indexOf(':');
  725. return (col > 0) ? qname.substring(0, col) : null;
  726. //return (col > 0) ? qname.substring(0,col) : "";
  727. }
  728. /**
  729. * Some users of the serializer may need the current namespace mappings
  730. * @return the current namespace mappings (prefix/uri)
  731. * @see com.sun.org.apache.xml.internal.serializer.ExtendedContentHandler#getNamespaceMappings()
  732. */
  733. public NamespaceMappings getNamespaceMappings()
  734. {
  735. return m_prefixMap;
  736. }
  737. /**
  738. * Returns the prefix currently pointing to the given URI (if any).
  739. * @param namespaceURI the uri of the namespace in question
  740. * @return a prefix pointing to the given URI (if any).
  741. * @see com.sun.org.apache.xml.internal.serializer.ExtendedContentHandler#getPrefix(String)
  742. */
  743. public String getPrefix(String namespaceURI)
  744. {
  745. String prefix = m_prefixMap.lookupPrefix(namespaceURI);
  746. return prefix;
  747. }
  748. /**
  749. * Returns the URI of an element or attribute. Note that default namespaces
  750. * do not apply directly to attributes.
  751. * @param qname a qualified name
  752. * @param isElement true if the qualified name is the name of
  753. * an element.
  754. * @return returns the namespace URI associated with the qualified name.
  755. */
  756. public String getNamespaceURI(String qname, boolean isElement)
  757. {
  758. String uri = EMPTYSTRING;
  759. int col = qname.lastIndexOf(':');
  760. final String prefix = (col > 0) ? qname.substring(0, col) : EMPTYSTRING;
  761. if (!EMPTYSTRING.equals(prefix) || isElement)
  762. {
  763. if (m_prefixMap != null)
  764. {
  765. uri = m_prefixMap.lookupNamespace(prefix);
  766. if (uri == null && !prefix.equals(XMLNS_PREFIX))
  767. {
  768. throw new RuntimeException(
  769. XMLMessages.createXMLMessage(
  770. XMLErrorResources.ER_NAMESPACE_PREFIX,
  771. new Object[] { qname.substring(0, col) } ));
  772. }
  773. }
  774. }
  775. return uri;
  776. }
  777. /**
  778. * Returns the URI of prefix (if any)
  779. *
  780. * @param prefix the prefix whose URI is searched for
  781. * @return the namespace URI currently associated with the
  782. * prefix, null if the prefix is undefined.
  783. */
  784. public String getNamespaceURIFromPrefix(String prefix)
  785. {
  786. String uri = null;
  787. if (m_prefixMap != null)
  788. uri = m_prefixMap.lookupNamespace(prefix);
  789. return uri;
  790. }
  791. /**
  792. * Entity reference event.
  793. *
  794. * @param name Name of entity
  795. *
  796. * @throws org.xml.sax.SAXException
  797. */
  798. public void entityReference(String name) throws org.xml.sax.SAXException
  799. {
  800. flushPending();
  801. startEntity(name);
  802. endEntity(name);
  803. if (m_tracer != null)
  804. fireEntityReference(name);
  805. }
  806. /**
  807. * Sets the transformer associated with this serializer
  808. * @param t the transformer associated with this serializer.
  809. * @see com.sun.org.apache.xml.internal.serializer.SerializationHandler#setTransformer(Transformer)
  810. */
  811. public void setTransformer(Transformer t)
  812. {
  813. m_transformer = t;
  814. // If this transformer object implements the SerializerTrace interface
  815. // then assign m_tracer to the transformer object so it can be used
  816. // to fire trace events.
  817. if ((m_transformer instanceof SerializerTrace) &&
  818. (((SerializerTrace) m_transformer).hasTraceListeners())) {
  819. m_tracer = (SerializerTrace) m_transformer;
  820. } else {
  821. m_tracer = null;
  822. }
  823. }
  824. /**
  825. * Gets the transformer associated with this serializer
  826. * @return returns the transformer associated with this serializer.
  827. * @see com.sun.org.apache.xml.internal.serializer.SerializationHandler#getTransformer()
  828. */
  829. public Transformer getTransformer()
  830. {
  831. return m_transformer;
  832. }
  833. /**
  834. * This method gets the nodes value as a String and uses that String as if
  835. * it were an input character notification.
  836. * @param node the Node to serialize
  837. * @throws org.xml.sax.SAXException
  838. */
  839. public void characters(org.w3c.dom.Node node)
  840. throws org.xml.sax.SAXException
  841. {
  842. flushPending();
  843. String data = node.getNodeValue();
  844. if (data != null)
  845. {
  846. final int length = data.length();
  847. if (length > m_charsBuff.length)
  848. {
  849. m_charsBuff = new char[length * 2 + 1];
  850. }
  851. data.getChars(0, length, m_charsBuff, 0);
  852. characters(m_charsBuff, 0, length);
  853. }
  854. }
  855. /**
  856. * @see org.xml.sax.ErrorHandler#error(SAXParseException)
  857. */
  858. public void error(SAXParseException exc) throws SAXException {
  859. }
  860. /**
  861. * @see org.xml.sax.ErrorHandler#fatalError(SAXParseException)
  862. */
  863. public void fatalError(SAXParseException exc) throws SAXException {
  864. m_elemContext.m_startTagOpen = false;
  865. }
  866. /**
  867. * @see org.xml.sax.ErrorHandler#warning(SAXParseException)
  868. */
  869. public void warning(SAXParseException exc) throws SAXException
  870. {
  871. }
  872. /**
  873. * To fire off start entity trace event
  874. * @param name Name of entity
  875. */
  876. protected void fireStartEntity(String name)
  877. throws org.xml.sax.SAXException
  878. {
  879. if (m_tracer != null)
  880. {
  881. flushMyWriter();
  882. m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENTITYREF, name);
  883. }
  884. }
  885. /**
  886. * Report the characters event
  887. * @param chars content of characters
  888. * @param start starting index of characters to output
  889. * @param length number of characters to output
  890. */
  891. // protected void fireCharEvent(char[] chars, int start, int length)
  892. // throws org.xml.sax.SAXException
  893. // {
  894. // if (m_tracer != null)
  895. // m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CHARACTERS, chars, start,length);
  896. // }
  897. //
  898. /**
  899. * This method is only used internally when flushing the writer from the
  900. * various fire...() trace events. Due to the writer being wrapped with
  901. * SerializerTraceWriter it may cause the flush of these trace events:
  902. * EVENTTYPE_OUTPUT_PSEUDO_CHARACTERS
  903. * EVENTTYPE_OUTPUT_CHARACTERS
  904. * which trace the output written to the output stream.
  905. *
  906. */
  907. private void flushMyWriter()
  908. {
  909. if (m_writer != null)
  910. {
  911. try
  912. {
  913. m_writer.flush();
  914. }
  915. catch(IOException ioe)
  916. {
  917. }
  918. }
  919. }
  920. /**
  921. * Report the CDATA trace event
  922. * @param chars content of CDATA
  923. * @param start starting index of characters to output
  924. * @param length number of characters to output
  925. */
  926. protected void fireCDATAEvent(char[] chars, int start, int length)
  927. throws org.xml.sax.SAXException
  928. {
  929. if (m_tracer != null)
  930. {
  931. flushMyWriter();
  932. m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CDATA, chars, start,length);
  933. }
  934. }
  935. /**
  936. * Report the comment trace event
  937. * @param chars content of comment
  938. * @param start starting index of comment to output
  939. * @param length number of characters to output
  940. */
  941. protected void fireCommentEvent(char[] chars, int start, int length)
  942. throws org.xml.sax.SAXException
  943. {
  944. if (m_tracer != null)
  945. {
  946. flushMyWriter();
  947. m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_COMMENT, new String(chars, start, length));
  948. }
  949. }
  950. /**
  951. * To fire off end entity trace event
  952. * @param name Name of entity
  953. */
  954. public void fireEndEntity(String name)
  955. throws org.xml.sax.SAXException
  956. {
  957. if (m_tracer != null)
  958. flushMyWriter();
  959. // we do not need to handle this.
  960. }
  961. /**
  962. * To fire off start document trace event
  963. */
  964. protected void fireStartDoc()
  965. throws org.xml.sax.SAXException
  966. {
  967. if (m_tracer != null)
  968. {
  969. flushMyWriter();
  970. m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_STARTDOCUMENT);
  971. }
  972. }
  973. /**
  974. * To fire off end document trace event
  975. */
  976. protected void fireEndDoc()
  977. throws org.xml.sax.SAXException
  978. {
  979. if (m_tracer != null)
  980. {
  981. flushMyWriter();
  982. m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDDOCUMENT);
  983. }
  984. }
  985. /**
  986. * Report the start element trace event. This trace method needs to be
  987. * called just before the attributes are cleared.
  988. *
  989. * @param elemName the qualified name of the element
  990. *
  991. */
  992. protected void fireStartElem(String elemName)
  993. throws org.xml.sax.SAXException
  994. {
  995. if (m_tracer != null)
  996. {
  997. flushMyWriter();
  998. m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_STARTELEMENT,
  999. elemName, m_attributes);
  1000. }
  1001. }
  1002. /**
  1003. * To fire off the end element event
  1004. * @param name Name of element
  1005. */
  1006. // protected void fireEndElem(String name)
  1007. // throws org.xml.sax.SAXException
  1008. // {
  1009. // if (m_tracer != null)
  1010. // m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDELEMENT,name, (Attributes)null);
  1011. // }
  1012. /**
  1013. * To fire off the PI trace event
  1014. * @param name Name of PI
  1015. */
  1016. protected void fireEscapingEvent(String name, String data)
  1017. throws org.xml.sax.SAXException
  1018. {
  1019. if (m_tracer != null)
  1020. {
  1021. flushMyWriter();
  1022. m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_PI,name, data);
  1023. }
  1024. }
  1025. /**
  1026. * To fire off the entity reference trace event
  1027. * @param name Name of entity reference
  1028. */
  1029. protected void fireEntityReference(String name)
  1030. throws org.xml.sax.SAXException
  1031. {
  1032. if (m_tracer != null)
  1033. {
  1034. flushMyWriter();
  1035. m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENTITYREF,name, (Attributes)null);
  1036. }
  1037. }
  1038. /**
  1039. * Receive notification of the beginning of a document.
  1040. * This method is never a self generated call,
  1041. * but only called externally.
  1042. *
  1043. * <p>The SAX parser will invoke this method only once, before any
  1044. * other methods in this interface or in DTDHandler (except for
  1045. * setDocumentLocator).</p>
  1046. *
  1047. * @throws org.xml.sax.SAXException Any SAX exception, possibly
  1048. * wrapping another exception.
  1049. *
  1050. * @throws org.xml.sax.SAXException
  1051. */
  1052. public void startDocument() throws org.xml.sax.SAXException
  1053. {
  1054. // if we do get called with startDocument(), handle it right away
  1055. startDocumentInternal();
  1056. m_needToCallStartDocument = false;
  1057. return;
  1058. }
  1059. /**
  1060. * This method handles what needs to be done at a startDocument() call,
  1061. * whether from an external caller, or internally called in the
  1062. * serializer. Historically Xalan has not always called a startDocument()
  1063. * although it always calls endDocument() on the serializer.
  1064. * So the serializer must be flexible for that. Even if no external call is
  1065. * made into startDocument() this method will always be called as a self
  1066. * generated internal startDocument, it handles what needs to be done at a
  1067. * startDocument() call.
  1068. *
  1069. * This method exists just to make sure that startDocument() is only ever
  1070. * called from an external caller, which in principle is just a matter of
  1071. * style.
  1072. *
  1073. * @throws SAXException
  1074. */
  1075. protected void startDocumentInternal() throws org.xml.sax.SAXException
  1076. {
  1077. if (m_tracer != null)
  1078. this.fireStartDoc();
  1079. }
  1080. /**
  1081. * This method is used to set the source locator, which might be used to
  1082. * generated an error message.
  1083. * @param locator the source locator
  1084. *
  1085. * @see com.sun.org.apache.xml.internal.serializer.ExtendedContentHandler#setSourceLocator(javax.xml.transform.SourceLocator)
  1086. */
  1087. public void setSourceLocator(SourceLocator locator)
  1088. {
  1089. m_sourceLocator = locator;
  1090. }
  1091. /**
  1092. * Used only by TransformerSnapshotImpl to restore the serialization
  1093. * to a previous state.
  1094. *
  1095. * @param NamespaceMappings
  1096. */
  1097. public void setNamespaceMappings(NamespaceMappings mappings) {
  1098. m_prefixMap = mappings;
  1099. }
  1100. public boolean reset()
  1101. {
  1102. resetSerializerBase();
  1103. return true;
  1104. }
  1105. /**
  1106. * Reset all of the fields owned by SerializerBase
  1107. *
  1108. */
  1109. private void resetSerializerBase()
  1110. {
  1111. this.m_attributes.clear();
  1112. this.m_cdataSectionElements = null;
  1113. this.m_elemContext = new ElemContext();
  1114. this.m_doctypePublic = null;
  1115. this.m_doctypeSystem = null;
  1116. this.m_doIndent = false;
  1117. this.m_encoding = null;
  1118. this.m_indentAmount = 0;
  1119. this.m_inEntityRef = false;
  1120. this.m_inExternalDTD = false;
  1121. this.m_mediatype = null;
  1122. this.m_needToCallStartDocument = true;
  1123. this.m_needToOutputDocTypeDecl = false;
  1124. if (this.m_prefixMap != null)
  1125. this.m_prefixMap.reset();
  1126. this.m_shouldNotWriteXMLHeader = false;
  1127. this.m_sourceLocator = null;
  1128. this.m_standalone = null;
  1129. this.m_standaloneWasSpecified = false;
  1130. this.m_tracer = null;
  1131. this.m_transformer = null;
  1132. this.m_version = null;
  1133. // don't set writer to null, so that it might be re-used
  1134. //this.m_writer = null;
  1135. }
  1136. }