1. /*
  2. * Copyright 1999-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: ToUnknownStream.java,v 1.8 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.io.OutputStream;
  22. import java.io.Writer;
  23. import java.util.Properties;
  24. import java.util.Vector;
  25. import javax.xml.transform.SourceLocator;
  26. import javax.xml.transform.Transformer;
  27. import org.w3c.dom.Node;
  28. import org.xml.sax.Attributes;
  29. import org.xml.sax.ContentHandler;
  30. import org.xml.sax.Locator;
  31. import org.xml.sax.SAXException;
  32. /**
  33. *This class wraps another SerializationHandler. The wrapped object will either
  34. * handler XML or HTML, which is not known until a little later when the first XML
  35. * tag is seen. If the first tag is <html> then the wrapped object is an HTML
  36. * handler, otherwise it is an XML handler.
  37. *
  38. * This class effectively caches the first few calls to it then passes them
  39. * on to the wrapped handler (once it exists). After that subsequent calls a
  40. * simply passed directly to the wrapped handler.
  41. *
  42. * The user of this class doesn't know if the output is ultimatley XML or HTML.
  43. */
  44. public class ToUnknownStream extends SerializerBase
  45. {
  46. /**
  47. * The wrapped handler, initially XML but possibly switched to HTML
  48. */
  49. private SerializationHandler m_handler;
  50. /**
  51. * A String with no characters
  52. */
  53. private static final String EMPTYSTRING = "";
  54. /**
  55. * true if the underlying handler (XML or HTML) is fully initialized
  56. */
  57. private boolean m_wrapped_handler_not_initialized = false;
  58. /**
  59. * the prefix of the very first tag in the document
  60. */
  61. private String m_firstElementPrefix;
  62. /**
  63. * the element name (including any prefix) of the very first tag in the document
  64. */
  65. private String m_firstElementName;
  66. /**
  67. * the namespace URI associated with the first element
  68. */
  69. private String m_firstElementURI;
  70. /**
  71. * the local name (no prefix) associated with the first element
  72. */
  73. private String m_firstElementLocalName = null;
  74. /**
  75. * true if the first tag has been emitted to the wrapped handler
  76. */
  77. private boolean m_firstTagNotEmitted = true;
  78. /**
  79. * A collection of namespace URI's (only for first element).
  80. * _namespacePrefix has the matching prefix for these URI's
  81. */
  82. private Vector m_namespaceURI = null;
  83. /**
  84. * A collection of namespace Prefix (only for first element)
  85. * _namespaceURI has the matching URIs for these prefix'
  86. */
  87. private Vector m_namespacePrefix = null;
  88. /**
  89. * true if startDocument() was called before the underlying handler
  90. * was initialized
  91. */
  92. private boolean m_needToCallStartDocument = false;
  93. /**
  94. * true if setVersion() was called before the underlying handler
  95. * was initialized
  96. */
  97. private boolean m_setVersion_called = false;
  98. /**
  99. * true if setDoctypeSystem() was called before the underlying handler
  100. * was initialized
  101. */
  102. private boolean m_setDoctypeSystem_called = false;
  103. /**
  104. * true if setDoctypePublic() was called before the underlying handler
  105. * was initialized
  106. */
  107. private boolean m_setDoctypePublic_called = false;
  108. /**
  109. * true if setMediaType() was called before the underlying handler
  110. * was initialized
  111. */
  112. private boolean m_setMediaType_called = false;
  113. /**
  114. * Default constructor.
  115. * Initially this object wraps an XML Stream object, so _handler is never null.
  116. * That may change later to an HTML Stream object.
  117. */
  118. public ToUnknownStream()
  119. {
  120. m_handler = new ToXMLStream();
  121. }
  122. /**
  123. * @see com.sun.org.apache.xml.internal.serializer.Serializer#asContentHandler()
  124. * @return the wrapped XML or HTML handler
  125. */
  126. public ContentHandler asContentHandler() throws IOException
  127. {
  128. /* don't return the real handler ( m_handler ) because
  129. * that would expose the real handler to the outside.
  130. * Keep m_handler private so it can be internally swapped
  131. * to an HTML handler.
  132. */
  133. return this;
  134. }
  135. /**
  136. * @see com.sun.org.apache.xml.internal.serializer.SerializationHandler#close()
  137. */
  138. public void close()
  139. {
  140. m_handler.close();
  141. }
  142. /**
  143. * @see com.sun.org.apache.xml.internal.serializer.Serializer#getOutputFormat()
  144. * @return the properties of the underlying handler
  145. */
  146. public Properties getOutputFormat()
  147. {
  148. return m_handler.getOutputFormat();
  149. }
  150. /**
  151. * @see com.sun.org.apache.xml.internal.serializer.Serializer#getOutputStream()
  152. * @return the OutputStream of the underlying XML or HTML handler
  153. */
  154. public OutputStream getOutputStream()
  155. {
  156. return m_handler.getOutputStream();
  157. }
  158. /**
  159. * @see com.sun.org.apache.xml.internal.serializer.Serializer#getWriter()
  160. * @return the Writer of the underlying XML or HTML handler
  161. */
  162. public Writer getWriter()
  163. {
  164. return m_handler.getWriter();
  165. }
  166. /**
  167. * passes the call on to the underlying HTML or XML handler
  168. * @see com.sun.org.apache.xml.internal.serializer.Serializer#reset()
  169. * @return ???
  170. */
  171. public boolean reset()
  172. {
  173. return m_handler.reset();
  174. }
  175. /**
  176. * Converts the DOM node to output
  177. * @param node the DOM node to transform to output
  178. * @see com.sun.org.apache.xml.internal.serializer.DOMSerializer#serialize(Node)
  179. *
  180. */
  181. public void serialize(Node node) throws IOException
  182. {
  183. if (m_firstTagNotEmitted)
  184. {
  185. flush();
  186. }
  187. m_handler.serialize(node);
  188. }
  189. /**
  190. * @see com.sun.org.apache.xml.internal.serializer.SerializationHandler#setEscaping(boolean)
  191. */
  192. public boolean setEscaping(boolean escape) throws SAXException
  193. {
  194. return m_handler.setEscaping(escape);
  195. }
  196. /**
  197. * Set the properties of the handler
  198. * @param format the output properties to set
  199. * @see com.sun.org.apache.xml.internal.serializer.Serializer#setOutputFormat(Properties)
  200. */
  201. public void setOutputFormat(Properties format)
  202. {
  203. m_handler.setOutputFormat(format);
  204. }
  205. /**
  206. * Sets the output stream to write to
  207. * @param output the OutputStream to write to
  208. * @see com.sun.org.apache.xml.internal.serializer.Serializer#setOutputStream(OutputStream)
  209. */
  210. public void setOutputStream(OutputStream output)
  211. {
  212. m_handler.setOutputStream(output);
  213. }
  214. /**
  215. * Sets the writer to write to
  216. * @param writer the writer to write to
  217. * @see com.sun.org.apache.xml.internal.serializer.Serializer#setWriter(Writer)
  218. */
  219. public void setWriter(Writer writer)
  220. {
  221. m_handler.setWriter(writer);
  222. }
  223. /**
  224. * Adds an attribute to the currenly open tag
  225. * @param uri the URI of a namespace
  226. * @param localName the attribute name, without prefix
  227. * @param rawName the attribute name, with prefix (if any)
  228. * @param type the type of the attribute, typically "CDATA"
  229. * @param value the value of the parameter
  230. * @see com.sun.org.apache.xml.internal.serializer.ExtendedContentHandler#addAttribute(String, String, String, String, String)
  231. */
  232. public void addAttribute(
  233. String uri,
  234. String localName,
  235. String rawName,
  236. String type,
  237. String value)
  238. throws SAXException
  239. {
  240. if (m_firstTagNotEmitted)
  241. {
  242. flush();
  243. }
  244. m_handler.addAttribute(uri, localName, rawName, type, value);
  245. }
  246. /**
  247. * Adds an attribute to the currenly open tag
  248. * @param name the attribute name, with prefix (if any)
  249. * @param value the value of the parameter
  250. * @see com.sun.org.apache.xml.internal.serializer.ExtendedContentHandler#addAttribute(String, String)
  251. */
  252. public void addAttribute(String rawName, String value)
  253. {
  254. if (m_firstTagNotEmitted)
  255. {
  256. flush();
  257. }
  258. m_handler.addAttribute(rawName, value);
  259. }
  260. /**
  261. * Adds a unique attribute to the currenly open tag
  262. */
  263. public void addUniqueAttribute(String rawName, String value, int flags)
  264. throws SAXException
  265. {
  266. if (m_firstTagNotEmitted)
  267. {
  268. flush();
  269. }
  270. m_handler.addUniqueAttribute(rawName, value, flags);
  271. }
  272. /**
  273. * Converts the String to a character array and calls the SAX method
  274. * characters(char[],int,int);
  275. *
  276. * @see com.sun.org.apache.xml.internal.serializer.ExtendedContentHandler#characters(String)
  277. */
  278. public void characters(String chars) throws SAXException
  279. {
  280. final int length = chars.length();
  281. if (length > m_charsBuff.length)
  282. {
  283. m_charsBuff = new char[length*2 + 1];
  284. }
  285. chars.getChars(0, length, m_charsBuff, 0);
  286. this.characters(m_charsBuff, 0, length);
  287. }
  288. /**
  289. * Pass the call on to the underlying handler
  290. * @see com.sun.org.apache.xml.internal.serializer.ExtendedContentHandler#endElement(String)
  291. */
  292. public void endElement(String elementName) throws SAXException
  293. {
  294. if (m_firstTagNotEmitted)
  295. {
  296. flush();
  297. }
  298. m_handler.endElement(elementName);
  299. }
  300. /**
  301. * @see org.xml.sax.ContentHandler#startPrefixMapping(String, String)
  302. * @param prefix The prefix that maps to the URI
  303. * @param uri The URI for the namespace
  304. */
  305. public void startPrefixMapping(String prefix, String uri) throws SAXException
  306. {
  307. this.startPrefixMapping(prefix,uri, true);
  308. }
  309. /**
  310. * This method is used when a prefix/uri namespace mapping
  311. * is indicated after the element was started with a
  312. * startElement() and before and endElement().
  313. * startPrefixMapping(prefix,uri) would be used before the
  314. * startElement() call.
  315. * @param uri the URI of the namespace
  316. * @param prefix the prefix associated with the given URI.
  317. *
  318. * @see com.sun.org.apache.xml.internal.serializer.ExtendedContentHandler#namespaceAfterStartElement(String, String)
  319. */
  320. public void namespaceAfterStartElement(String prefix, String uri)
  321. throws SAXException
  322. {
  323. // hack for XSLTC with finding URI for default namespace
  324. if (m_firstTagNotEmitted && m_firstElementURI == null && m_firstElementName != null)
  325. {
  326. String prefix1 = getPrefixPart(m_firstElementName);
  327. if (prefix1 == null && EMPTYSTRING.equals(prefix))
  328. {
  329. // the elements URI is not known yet, and it
  330. // doesn't have a prefix, and we are currently
  331. // setting the uri for prefix "", so we have
  332. // the uri for the element... lets remember it
  333. m_firstElementURI = uri;
  334. }
  335. }
  336. startPrefixMapping(prefix,uri, false);
  337. }
  338. public boolean startPrefixMapping(String prefix, String uri, boolean shouldFlush)
  339. throws SAXException
  340. {
  341. boolean pushed = false;
  342. if (m_firstTagNotEmitted)
  343. {
  344. if (m_firstElementName != null && shouldFlush)
  345. {
  346. /* we've already seen a startElement, and this is a prefix mapping
  347. * for the up coming element, so flush the old element
  348. * then send this event on its way.
  349. */
  350. flush();
  351. pushed = m_handler.startPrefixMapping(prefix, uri, shouldFlush);
  352. }
  353. else
  354. {
  355. if (m_namespacePrefix == null)
  356. {
  357. m_namespacePrefix = new Vector();
  358. m_namespaceURI = new Vector();
  359. }
  360. m_namespacePrefix.addElement(prefix);
  361. m_namespaceURI.addElement(uri);
  362. if (m_firstElementURI == null)
  363. {
  364. if (prefix.equals(m_firstElementPrefix))
  365. m_firstElementURI = uri;
  366. }
  367. }
  368. }
  369. else
  370. {
  371. pushed = m_handler.startPrefixMapping(prefix, uri, shouldFlush);
  372. }
  373. return pushed;
  374. }
  375. /**
  376. * This method cannot be cached because default is different in
  377. * HTML and XML (we need more than a boolean).
  378. */
  379. public void setVersion(String version)
  380. {
  381. m_handler.setVersion(version);
  382. // Cache call to setVersion()
  383. // super.setVersion(version);
  384. m_setVersion_called = true;
  385. }
  386. /**
  387. * @see org.xml.sax.ContentHandler#startDocument()
  388. */
  389. public void startDocument() throws SAXException
  390. {
  391. m_needToCallStartDocument = true;
  392. }
  393. public void startElement(String qName) throws SAXException
  394. {
  395. this.startElement(null, null, qName, null);
  396. }
  397. public void startElement(String namespaceURI, String localName, String qName) throws SAXException
  398. {
  399. this.startElement(namespaceURI, localName, qName, null);
  400. }
  401. public void startElement(
  402. String namespaceURI,
  403. String localName,
  404. String elementName,
  405. Attributes atts) throws SAXException
  406. {
  407. /* we are notified of the start of an element */
  408. if (m_firstTagNotEmitted)
  409. {
  410. /* we have not yet sent the first element on its way */
  411. if (m_firstElementName != null)
  412. {
  413. /* this is not the first element, but a later one.
  414. * But we have the old element pending, so flush it out,
  415. * then send this one on its way.
  416. */
  417. flush();
  418. m_handler.startElement(namespaceURI, localName, elementName, atts);
  419. }
  420. else
  421. {
  422. /* this is the very first element that we have seen,
  423. * so save it for flushing later. We may yet get to know its
  424. * URI due to added attributes.
  425. */
  426. m_wrapped_handler_not_initialized = true;
  427. m_firstElementName = elementName;
  428. // null if not known
  429. m_firstElementPrefix = getPrefixPartUnknown(elementName);
  430. // null if not known
  431. m_firstElementURI = namespaceURI;
  432. // null if not known
  433. m_firstElementLocalName = localName;
  434. if (m_tracer != null)
  435. firePseudoElement(elementName);
  436. /* we don't want to call our own addAttributes, which
  437. * merely delegates to the wrapped handler, but we want to
  438. * add these attributes to m_attributes. So me must call super.
  439. * addAttributes() In this case m_attributes is only used for the
  440. * first element, after that this class totally delegates to the
  441. * wrapped handler which is either XML or HTML.
  442. */
  443. if (atts != null)
  444. super.addAttributes(atts);
  445. // if there are attributes, then lets make the flush()
  446. // call the startElement on the handler and send the
  447. // attributes on their way.
  448. if (atts != null)
  449. flush();
  450. }
  451. }
  452. else
  453. {
  454. // this is not the first element, but a later one, so just
  455. // send it on its way.
  456. m_handler.startElement(namespaceURI, localName, elementName, atts);
  457. }
  458. }
  459. /**
  460. * Pass the call on to the underlying handler
  461. * @see com.sun.org.apache.xml.internal.serializer.ExtendedLexicalHandler#comment(String)
  462. */
  463. public void comment(String comment) throws SAXException
  464. {
  465. if (m_firstTagNotEmitted && m_firstElementName != null)
  466. {
  467. emitFirstTag();
  468. }
  469. else if (m_needToCallStartDocument)
  470. {
  471. m_handler.startDocument();
  472. m_needToCallStartDocument = false;
  473. }
  474. m_handler.comment(comment);
  475. }
  476. /**
  477. * Pass the call on to the underlying handler
  478. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#getDoctypePublic()
  479. */
  480. public String getDoctypePublic()
  481. {
  482. return m_handler.getDoctypePublic();
  483. }
  484. /**
  485. * Pass the call on to the underlying handler
  486. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#getDoctypeSystem()
  487. */
  488. public String getDoctypeSystem()
  489. {
  490. return m_handler.getDoctypeSystem();
  491. }
  492. /**
  493. * Pass the call on to the underlying handler
  494. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#getEncoding()
  495. */
  496. public String getEncoding()
  497. {
  498. return m_handler.getEncoding();
  499. }
  500. /**
  501. * Pass the call on to the underlying handler
  502. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#getIndent()
  503. */
  504. public boolean getIndent()
  505. {
  506. return m_handler.getIndent();
  507. }
  508. /**
  509. * Pass the call on to the underlying handler
  510. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#getIndentAmount()
  511. */
  512. public int getIndentAmount()
  513. {
  514. return m_handler.getIndentAmount();
  515. }
  516. /**
  517. * Pass the call on to the underlying handler
  518. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#getMediaType()
  519. */
  520. public String getMediaType()
  521. {
  522. return m_handler.getMediaType();
  523. }
  524. /**
  525. * Pass the call on to the underlying handler
  526. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#getOmitXMLDeclaration()
  527. */
  528. public boolean getOmitXMLDeclaration()
  529. {
  530. return m_handler.getOmitXMLDeclaration();
  531. }
  532. /**
  533. * Pass the call on to the underlying handler
  534. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#getStandalone()
  535. */
  536. public String getStandalone()
  537. {
  538. return m_handler.getStandalone();
  539. }
  540. /**
  541. * Pass the call on to the underlying handler
  542. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#getVersion()
  543. */
  544. public String getVersion()
  545. {
  546. return m_handler.getVersion();
  547. }
  548. /**
  549. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#setDoctype(String, String)
  550. */
  551. public void setDoctype(String system, String pub)
  552. {
  553. m_handler.setDoctypePublic(pub);
  554. m_handler.setDoctypeSystem(system);
  555. }
  556. /**
  557. * Set the doctype in the underlying XML handler. Remember that this method
  558. * was called, just in case we need to transfer this doctype to an HTML handler
  559. * @param doctype the public doctype to set
  560. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#setDoctypePublic(String)
  561. */
  562. public void setDoctypePublic(String doctype)
  563. {
  564. m_handler.setDoctypePublic(doctype);
  565. m_setDoctypePublic_called = true;
  566. }
  567. /**
  568. * Set the doctype in the underlying XML handler. Remember that this method
  569. * was called, just in case we need to transfer this doctype to an HTML handler
  570. * @param doctype the system doctype to set
  571. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#setDoctypeSystem(String)
  572. */
  573. public void setDoctypeSystem(String doctype)
  574. {
  575. m_handler.setDoctypeSystem(doctype);
  576. m_setDoctypeSystem_called = true;
  577. }
  578. /**
  579. * Pass the call on to the underlying handler
  580. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#setEncoding(String)
  581. */
  582. public void setEncoding(String encoding)
  583. {
  584. m_handler.setEncoding(encoding);
  585. }
  586. /**
  587. * Pass the call on to the underlying handler
  588. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#setIndent(boolean)
  589. */
  590. public void setIndent(boolean indent)
  591. {
  592. m_handler.setIndent(indent);
  593. }
  594. /**
  595. * Pass the call on to the underlying handler
  596. */
  597. public void setIndentAmount(int value)
  598. {
  599. m_handler.setIndentAmount(value);
  600. }
  601. /**
  602. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#setMediaType(String)
  603. */
  604. public void setMediaType(String mediaType)
  605. {
  606. m_handler.setMediaType(mediaType);
  607. m_setMediaType_called = true;
  608. }
  609. /**
  610. * Pass the call on to the underlying handler
  611. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#setOmitXMLDeclaration(boolean)
  612. */
  613. public void setOmitXMLDeclaration(boolean b)
  614. {
  615. m_handler.setOmitXMLDeclaration(b);
  616. }
  617. /**
  618. * Pass the call on to the underlying handler
  619. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#setStandalone(String)
  620. */
  621. public void setStandalone(String standalone)
  622. {
  623. m_handler.setStandalone(standalone);
  624. }
  625. /**
  626. * @see com.sun.org.apache.xml.internal.serializer.XSLOutputAttributes#setVersion(String)
  627. */
  628. /**
  629. * Pass the call on to the underlying handler
  630. * @see org.xml.sax.ext.DeclHandler#attributeDecl(String, String, String, String, String)
  631. */
  632. public void attributeDecl(
  633. String arg0,
  634. String arg1,
  635. String arg2,
  636. String arg3,
  637. String arg4)
  638. throws SAXException
  639. {
  640. m_handler.attributeDecl(arg0, arg1, arg2, arg3, arg4);
  641. }
  642. /**
  643. * Pass the call on to the underlying handler
  644. * @see org.xml.sax.ext.DeclHandler#elementDecl(String, String)
  645. */
  646. public void elementDecl(String arg0, String arg1) throws SAXException
  647. {
  648. if (m_firstTagNotEmitted)
  649. {
  650. emitFirstTag();
  651. }
  652. m_handler.elementDecl(arg0, arg1);
  653. }
  654. /**
  655. * Pass the call on to the underlying handler
  656. * @see org.xml.sax.ext.DeclHandler#externalEntityDecl(String, String, String)
  657. */
  658. public void externalEntityDecl(
  659. String name,
  660. String publicId,
  661. String systemId)
  662. throws SAXException
  663. {
  664. if (m_firstTagNotEmitted)
  665. {
  666. flush();
  667. }
  668. m_handler.externalEntityDecl(name, publicId, systemId);
  669. }
  670. /**
  671. * Pass the call on to the underlying handler
  672. * @see org.xml.sax.ext.DeclHandler#internalEntityDecl(String, String)
  673. */
  674. public void internalEntityDecl(String arg0, String arg1)
  675. throws SAXException
  676. {
  677. if (m_firstTagNotEmitted)
  678. {
  679. flush();
  680. }
  681. m_handler.internalEntityDecl(arg0, arg1);
  682. }
  683. /**
  684. * Pass the call on to the underlying handler
  685. * @see org.xml.sax.ContentHandler#characters(char[], int, int)
  686. */
  687. public void characters(char[] characters, int offset, int length)
  688. throws SAXException
  689. {
  690. if (m_firstTagNotEmitted)
  691. {
  692. flush();
  693. }
  694. m_handler.characters(characters, offset, length);
  695. }
  696. /**
  697. * Pass the call on to the underlying handler
  698. * @see org.xml.sax.ContentHandler#endDocument()
  699. */
  700. public void endDocument() throws SAXException
  701. {
  702. if (m_firstTagNotEmitted)
  703. {
  704. flush();
  705. }
  706. m_handler.endDocument();
  707. }
  708. /**
  709. * Pass the call on to the underlying handler
  710. * @see org.xml.sax.ContentHandler#endElement(String, String, String)
  711. */
  712. public void endElement(String namespaceURI, String localName, String qName)
  713. throws SAXException
  714. {
  715. if (m_firstTagNotEmitted)
  716. {
  717. flush();
  718. if (namespaceURI == null && m_firstElementURI != null)
  719. namespaceURI = m_firstElementURI;
  720. if (localName == null && m_firstElementLocalName != null)
  721. localName = m_firstElementLocalName;
  722. }
  723. m_handler.endElement(namespaceURI, localName, qName);
  724. }
  725. /**
  726. * Pass the call on to the underlying handler
  727. * @see org.xml.sax.ContentHandler#endPrefixMapping(String)
  728. */
  729. public void endPrefixMapping(String prefix) throws SAXException
  730. {
  731. m_handler.endPrefixMapping(prefix);
  732. }
  733. /**
  734. * Pass the call on to the underlying handler
  735. * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int)
  736. */
  737. public void ignorableWhitespace(char[] ch, int start, int length)
  738. throws SAXException
  739. {
  740. if (m_firstTagNotEmitted)
  741. {
  742. flush();
  743. }
  744. m_handler.ignorableWhitespace(ch, start, length);
  745. }
  746. /**
  747. * Pass the call on to the underlying handler
  748. * @see org.xml.sax.ContentHandler#processingInstruction(String, String)
  749. */
  750. public void processingInstruction(String target, String data)
  751. throws SAXException
  752. {
  753. if (m_firstTagNotEmitted)
  754. {
  755. flush();
  756. }
  757. m_handler.processingInstruction(target, data);
  758. }
  759. /**
  760. * Pass the call on to the underlying handler
  761. * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator)
  762. */
  763. public void setDocumentLocator(Locator locator)
  764. {
  765. m_handler.setDocumentLocator(locator);
  766. }
  767. /**
  768. * Pass the call on to the underlying handler
  769. * @see org.xml.sax.ContentHandler#skippedEntity(String)
  770. */
  771. public void skippedEntity(String name) throws SAXException
  772. {
  773. m_handler.skippedEntity(name);
  774. }
  775. /**
  776. * Pass the call on to the underlying handler
  777. * @see org.xml.sax.ext.LexicalHandler#comment(char[], int, int)
  778. */
  779. public void comment(char[] ch, int start, int length) throws SAXException
  780. {
  781. if (m_firstTagNotEmitted)
  782. {
  783. flush();
  784. }
  785. m_handler.comment(ch, start, length);
  786. }
  787. /**
  788. * Pass the call on to the underlying handler
  789. * @see org.xml.sax.ext.LexicalHandler#endCDATA()
  790. */
  791. public void endCDATA() throws SAXException
  792. {
  793. m_handler.endCDATA();
  794. }
  795. /**
  796. * Pass the call on to the underlying handler
  797. * @see org.xml.sax.ext.LexicalHandler#endDTD()
  798. */
  799. public void endDTD() throws SAXException
  800. {
  801. m_handler.endDTD();
  802. }
  803. /**
  804. * Pass the call on to the underlying handler
  805. * @see org.xml.sax.ext.LexicalHandler#endEntity(String)
  806. */
  807. public void endEntity(String name) throws SAXException
  808. {
  809. if (m_firstTagNotEmitted)
  810. {
  811. emitFirstTag();
  812. }
  813. m_handler.endEntity(name);
  814. }
  815. /**
  816. * Pass the call on to the underlying handler
  817. * @see org.xml.sax.ext.LexicalHandler#startCDATA()
  818. */
  819. public void startCDATA() throws SAXException
  820. {
  821. m_handler.startCDATA();
  822. }
  823. /**
  824. * Pass the call on to the underlying handler
  825. * @see org.xml.sax.ext.LexicalHandler#startDTD(String, String, String)
  826. */
  827. public void startDTD(String name, String publicId, String systemId)
  828. throws SAXException
  829. {
  830. m_handler.startDTD(name, publicId, systemId);
  831. }
  832. /**
  833. * Pass the call on to the underlying handler
  834. * @see org.xml.sax.ext.LexicalHandler#startEntity(String)
  835. */
  836. public void startEntity(String name) throws SAXException
  837. {
  838. m_handler.startEntity(name);
  839. }
  840. /**
  841. * Initialize the wrapped output stream (XML or HTML).
  842. * If the stream handler should be HTML, then replace the XML handler with
  843. * an HTML handler. After than send the starting method calls that were cached
  844. * to the wrapped handler.
  845. *
  846. */
  847. private void initStreamOutput() throws SAXException
  848. {
  849. // Try to rule out if this is an not to be an HTML document based on prefix
  850. boolean firstElementIsHTML = isFirstElemHTML();
  851. if (firstElementIsHTML)
  852. {
  853. // create an HTML output handler, and initialize it
  854. // keep a reference to the old handler, ... it will soon be gone
  855. SerializationHandler oldHandler = m_handler;
  856. /* We have to make sure we get an output properties with the proper
  857. * defaults for the HTML method. The easiest way to do this is to
  858. * have the OutputProperties class do it.
  859. */
  860. Properties htmlProperties =
  861. OutputPropertiesFactory.getDefaultMethodProperties(Method.HTML);
  862. Serializer serializer =
  863. SerializerFactory.getSerializer(htmlProperties);
  864. // The factory should be returning a ToStream
  865. // Don't know what to do if it doesn't
  866. // i.e. the user has over-ridden the content-handler property
  867. // for html
  868. m_handler = (SerializationHandler) serializer;
  869. //m_handler = new ToHTMLStream();
  870. Writer writer = oldHandler.getWriter();
  871. if (null != writer)
  872. m_handler.setWriter(writer);
  873. else
  874. {
  875. OutputStream os = oldHandler.getOutputStream();
  876. if (null != os)
  877. m_handler.setOutputStream(os);
  878. }
  879. // need to copy things from the old handler to the new one here
  880. // if (_setVersion_called)
  881. // {
  882. m_handler.setVersion(oldHandler.getVersion());
  883. // }
  884. // if (_setDoctypeSystem_called)
  885. // {
  886. m_handler.setDoctypeSystem(oldHandler.getDoctypeSystem());
  887. // }
  888. // if (_setDoctypePublic_called)
  889. // {
  890. m_handler.setDoctypePublic(oldHandler.getDoctypePublic());
  891. // }
  892. // if (_setMediaType_called)
  893. // {
  894. m_handler.setMediaType(oldHandler.getMediaType());
  895. // }
  896. m_handler.setTransformer(oldHandler.getTransformer());
  897. }
  898. /* Now that we have a real wrapped handler (XML or HTML) lets
  899. * pass any cached calls to it
  900. */
  901. // Call startDocument() if necessary
  902. if (m_needToCallStartDocument)
  903. {
  904. m_handler.startDocument();
  905. m_needToCallStartDocument = false;
  906. }
  907. // the wrapped handler is now fully initialized
  908. m_wrapped_handler_not_initialized = false;
  909. }
  910. private void emitFirstTag() throws SAXException
  911. {
  912. if (m_firstElementName != null)
  913. {
  914. if (m_wrapped_handler_not_initialized)
  915. {
  916. initStreamOutput();
  917. m_wrapped_handler_not_initialized = false;
  918. }
  919. // Output first tag
  920. m_handler.startElement(m_firstElementURI, null, m_firstElementName, m_attributes);
  921. // don't need the collected attributes of the first element anymore.
  922. m_attributes = null;
  923. // Output namespaces of first tag
  924. if (m_namespacePrefix != null)
  925. {
  926. final int n = m_namespacePrefix.size();
  927. for (int i = 0; i < n; i++)
  928. {
  929. final String prefix =
  930. (String) m_namespacePrefix.elementAt(i);
  931. final String uri = (String) m_namespaceURI.elementAt(i);
  932. m_handler.startPrefixMapping(prefix, uri, false);
  933. }
  934. m_namespacePrefix = null;
  935. m_namespaceURI = null;
  936. }
  937. m_firstTagNotEmitted = false;
  938. }
  939. }
  940. /**
  941. * Utility function for calls to local-name().
  942. *
  943. * Don't want to override static function on SerializerBase
  944. * So added Unknown suffix to method name.
  945. */
  946. private String getLocalNameUnknown(String value)
  947. {
  948. int idx = value.lastIndexOf(':');
  949. if (idx >= 0)
  950. value = value.substring(idx + 1);
  951. idx = value.lastIndexOf('@');
  952. if (idx >= 0)
  953. value = value.substring(idx + 1);
  954. return (value);
  955. }
  956. /**
  957. * Utility function to return prefix
  958. *
  959. * Don't want to override static function on SerializerBase
  960. * So added Unknown suffix to method name.
  961. */
  962. private String getPrefixPartUnknown(String qname)
  963. {
  964. final int index = qname.indexOf(':');
  965. return (index > 0) ? qname.substring(0, index) : EMPTYSTRING;
  966. }
  967. /**
  968. * Determine if the firts element in the document is <html> or <HTML>
  969. * This uses the cached first element name, first element prefix and the
  970. * cached namespaces from previous method calls
  971. *
  972. * @return true if the first element is an opening <html> tag
  973. */
  974. private boolean isFirstElemHTML()
  975. {
  976. boolean isHTML;
  977. // is the first tag html, not considering the prefix ?
  978. isHTML =
  979. getLocalNameUnknown(m_firstElementName).equalsIgnoreCase("html");
  980. // Try to rule out if this is not to be an HTML document based on URI
  981. if (isHTML
  982. && m_firstElementURI != null
  983. && !EMPTYSTRING.equals(m_firstElementURI))
  984. {
  985. // the <html> element has a non-trivial namespace
  986. isHTML = false;
  987. }
  988. // Try to rule out if this is an not to be an HTML document based on prefix
  989. if (isHTML && m_namespacePrefix != null)
  990. {
  991. /* the first element has a name of "html", but lets check the prefix.
  992. * If the prefix points to a namespace with a URL that is not ""
  993. * then the doecument doesn't start with an <html> tag, and isn't html
  994. */
  995. final int max = m_namespacePrefix.size();
  996. for (int i = 0; i < max; i++)
  997. {
  998. final String prefix = (String) m_namespacePrefix.elementAt(i);
  999. final String uri = (String) m_namespaceURI.elementAt(i);
  1000. if (m_firstElementPrefix != null
  1001. && m_firstElementPrefix.equals(prefix)
  1002. && !EMPTYSTRING.equals(uri))
  1003. {
  1004. // The first element has a prefix, so it can't be <html>
  1005. isHTML = false;
  1006. break;
  1007. }
  1008. }
  1009. }
  1010. return isHTML;
  1011. }
  1012. /**
  1013. * @see com.sun.org.apache.xml.internal.serializer.Serializer#asDOMSerializer()
  1014. */
  1015. public DOMSerializer asDOMSerializer() throws IOException
  1016. {
  1017. return m_handler.asDOMSerializer();
  1018. }
  1019. /**
  1020. * @param URI_and_localNames Vector a list of pairs of URI/localName
  1021. * specified in the cdata-section-elements attribute
  1022. * @see com.sun.org.apache.xml.internal.serializer.SerializationHandler#setCdataSectionElements(java.util.Vector)
  1023. */
  1024. public void setCdataSectionElements(Vector URI_and_localNames)
  1025. {
  1026. m_handler.setCdataSectionElements(URI_and_localNames);
  1027. }
  1028. /**
  1029. * @see com.sun.org.apache.xml.internal.serializer.ExtendedContentHandler#addAttributes(org.xml.sax.Attributes)
  1030. */
  1031. public void addAttributes(Attributes atts) throws SAXException
  1032. {
  1033. m_handler.addAttributes(atts);
  1034. }
  1035. /**
  1036. * Get the current namespace mappings.
  1037. * Simply returns the mappings of the wrapped handler.
  1038. * @see com.sun.org.apache.xml.internal.serializer.ExtendedContentHandler#getNamespaceMappings()
  1039. */
  1040. public NamespaceMappings getNamespaceMappings()
  1041. {
  1042. NamespaceMappings mappings = null;
  1043. if (m_handler != null)
  1044. {
  1045. mappings = m_handler.getNamespaceMappings();
  1046. }
  1047. return mappings;
  1048. }
  1049. /**
  1050. * @see com.sun.org.apache.xml.internal.serializer.SerializationHandler#flushPending()
  1051. */
  1052. public void flushPending() throws SAXException
  1053. {
  1054. flush();
  1055. m_handler.flushPending();
  1056. }
  1057. private void flush()
  1058. {
  1059. try
  1060. {
  1061. if (m_firstTagNotEmitted)
  1062. {
  1063. emitFirstTag();
  1064. }
  1065. if (m_needToCallStartDocument)
  1066. {
  1067. m_handler.startDocument();
  1068. m_needToCallStartDocument = false;
  1069. }
  1070. }
  1071. catch(SAXException e)
  1072. {
  1073. throw new RuntimeException(e.toString());
  1074. }
  1075. }
  1076. /**
  1077. * @see com.sun.org.apache.xml.internal.serializer.ExtendedContentHandler#getPrefix
  1078. */
  1079. public String getPrefix(String namespaceURI)
  1080. {
  1081. return m_handler.getPrefix(namespaceURI);
  1082. }
  1083. /**
  1084. * @see com.sun.org.apache.xml.internal.serializer.ExtendedContentHandler#entityReference(java.lang.String)
  1085. */
  1086. public void entityReference(String entityName) throws SAXException
  1087. {
  1088. m_handler.entityReference(entityName);
  1089. }
  1090. /**
  1091. * @see com.sun.org.apache.xml.internal.serializer.ExtendedContentHandler#getNamespaceURI(java.lang.String, boolean)
  1092. */
  1093. public String getNamespaceURI(String qname, boolean isElement)
  1094. {
  1095. return m_handler.getNamespaceURI(qname, isElement);
  1096. }
  1097. public String getNamespaceURIFromPrefix(String prefix)
  1098. {
  1099. return m_handler.getNamespaceURIFromPrefix(prefix);
  1100. }
  1101. public void setTransformer(Transformer t)
  1102. {
  1103. m_handler.setTransformer(t);
  1104. if ((t instanceof SerializerTrace) &&
  1105. (((SerializerTrace) t).hasTraceListeners())) {
  1106. m_tracer = (SerializerTrace) t;
  1107. } else {
  1108. m_tracer = null;
  1109. }
  1110. }
  1111. public Transformer getTransformer()
  1112. {
  1113. return m_handler.getTransformer();
  1114. }
  1115. /**
  1116. * @see com.sun.org.apache.xml.internal.serializer.SerializationHandler#setContentHandler(org.xml.sax.ContentHandler)
  1117. */
  1118. public void setContentHandler(ContentHandler ch)
  1119. {
  1120. m_handler.setContentHandler(ch);
  1121. }
  1122. /**
  1123. * This method is used to set the source locator, which might be used to
  1124. * generated an error message.
  1125. * @param locator the source locator
  1126. *
  1127. * @see com.sun.org.apache.xml.internal.serializer.ExtendedContentHandler#setSourceLocator(javax.xml.transform.SourceLocator)
  1128. */
  1129. public void setSourceLocator(SourceLocator locator)
  1130. {
  1131. m_handler.setSourceLocator(locator);
  1132. }
  1133. protected void firePseudoElement(String elementName)
  1134. {
  1135. if (m_tracer != null) {
  1136. StringBuffer sb = new StringBuffer();
  1137. sb.append('<');
  1138. sb.append(elementName);
  1139. // convert the StringBuffer to a char array and
  1140. // emit the trace event that these characters "might"
  1141. // be written
  1142. char ch[] = sb.toString().toCharArray();
  1143. m_tracer.fireGenerateEvent(
  1144. SerializerTrace.EVENTTYPE_OUTPUT_PSEUDO_CHARACTERS,
  1145. ch,
  1146. 0,
  1147. ch.length);
  1148. }
  1149. }
  1150. }