/src/com/atlassian/uwc/converters/xml/DefaultXmlParser.java

https://bitbucket.org/atlassianlabs/universal-wiki-connector · Java · 255 lines · 136 code · 27 blank · 92 comment · 31 complexity · a44f2d6294c139ebd93d4a44321a51ce MD5 · raw file

  1. package com.atlassian.uwc.converters.xml;
  2. import java.util.Properties;
  3. import java.util.Stack;
  4. import org.apache.log4j.Logger;
  5. import org.xml.sax.Attributes;
  6. import org.xml.sax.SAXException;
  7. import org.xml.sax.helpers.DefaultHandler;
  8. import com.atlassian.uwc.ui.Page;
  9. /**
  10. * SAX Parser set up with XmlConverter to be the default event handler when parsing
  11. * Xml documents with the UWC Xml Framework. It will examine tags to see if any other events
  12. * parsers should be invoked.
  13. * @see <a href="http://confluence.atlassian.com/display/CONFEXT/UWC+Xml+Framework">UWC Xml Framework Documentation</a>
  14. */
  15. public class DefaultXmlParser extends DefaultHandler {
  16. /* Fields */
  17. /**
  18. * field maintaining the current output resulting from parsing
  19. */
  20. private static String output = "";
  21. /**
  22. * Page object associated with this parse. This is useful for associating metadata (labels, for example)
  23. * with the page.
  24. */
  25. private static Page page;
  26. /**
  27. * Keeps track of which tag/element we're currently examining, and any enclosing tags.
  28. */
  29. private static Stack<String> nested;
  30. /**
  31. * event managing object
  32. */
  33. private DefaultXmlEvents eventsHandler;
  34. /**
  35. * Should be set to false when invoking other event parsers.
  36. * This protects against looping by child classes.
  37. */
  38. private static boolean isOriginating = true; //protects against looping by child classes
  39. /**
  40. * logging object
  41. */
  42. Logger log = Logger.getLogger(this.getClass());
  43. /**
  44. * Miscellaneous properties are kept here.
  45. */
  46. private static Properties properties;
  47. /* Constants */
  48. /**
  49. * misc property key for a custom events handler
  50. */
  51. private static final String PROP_EVENTSHANDLER = "xmlevents";
  52. /**
  53. * misc property key for turning on the optional Xml Fragments Feature
  54. */
  55. public final static String PROP_XMLFRAGMENTS = "xml-fragments";
  56. /**
  57. * misc property key for turning on the optional Use Htmltidy Feature
  58. */
  59. public final static String PROP_USE_HTMLTIDY = "xml-use-htmltidy";
  60. /* Constructors */
  61. /**
  62. * Creates new DefaultXmlParser
  63. */
  64. public DefaultXmlParser() {
  65. clearOutput();
  66. }
  67. /**
  68. * Creates new DefaultXmlParser with given state handling objects
  69. * @param eventsHandler manages events that will be used while parsing
  70. * @param page page objected associated with this parse
  71. */
  72. public DefaultXmlParser(DefaultXmlEvents eventsHandler, Page page) {
  73. this();
  74. this.eventsHandler = eventsHandler;
  75. this.page = page;
  76. }
  77. /* SAX Handling methods */
  78. public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
  79. //check for special handling
  80. this.eventsHandler = getEventsHandler();
  81. DefaultHandler handler = eventsHandler.getEvent(qName);
  82. if (handler != null && isOriginating) {
  83. isOriginating = false;
  84. handler.startElement(uri, localName, qName, attributes);
  85. isOriginating = true;
  86. }
  87. //default start element code
  88. if (isOriginating) getNested().push(qName);
  89. }
  90. public void endElement(String uri, String localName, String qName) throws SAXException {
  91. //check for special handling
  92. eventsHandler = getEventsHandler();
  93. DefaultHandler handler = this.eventsHandler.getEvent(qName);
  94. if (handler != null && isOriginating) {
  95. isOriginating = false;
  96. handler.endElement(uri, localName, qName);
  97. isOriginating = true;
  98. }
  99. //default end element code
  100. if (isOriginating) getNested().pop();
  101. }
  102. public void characters(char[] ch,
  103. int start,
  104. int length) throws SAXException {
  105. //check for special handling
  106. this.eventsHandler = getEventsHandler();
  107. DefaultHandler handler = null;
  108. if (!getNested().isEmpty()) //we can only get eventhandlers associated with tagnames
  109. handler = this.eventsHandler.getEvent((String) getNested().peek());
  110. if (handler != null && isOriginating) {
  111. isOriginating = false;
  112. handler.characters(ch, start, length);
  113. }
  114. else { //default handling
  115. String content = String.copyValueOf(ch, start, length);
  116. if (content != null) this.output += content;
  117. }
  118. isOriginating = true;
  119. }
  120. public void startDocument() throws SAXException {
  121. //check for special handling
  122. this.eventsHandler = getEventsHandler();
  123. DefaultHandler handler = this.eventsHandler.getEvent(">doc");
  124. if (handler != null && isOriginating) {
  125. isOriginating = false;
  126. handler.startDocument();
  127. }
  128. isOriginating = true;
  129. }
  130. public void endDocument() throws SAXException {
  131. //check for special handling
  132. this.eventsHandler = getEventsHandler();
  133. DefaultHandler handler = this.eventsHandler.getEvent(">doc");
  134. if (handler != null && isOriginating) {
  135. isOriginating = false;
  136. handler.endDocument();
  137. }
  138. isOriginating = true;
  139. //default behavior
  140. String out = getOutput();
  141. out = out.replaceFirst("^(\n)", "");
  142. if (hasFragments()) out = out.replaceFirst("(\n)$", "");
  143. page.setConvertedText(out);
  144. }
  145. /* Getters and Setters */
  146. /**
  147. * @return saved output resulting from parse
  148. */
  149. public String getOutput() {
  150. return output;
  151. }
  152. /**
  153. * add string to saved output
  154. * @param string
  155. */
  156. protected void appendOutput(String string) {
  157. this.output += string;
  158. }
  159. /**
  160. * delete saved output
  161. */
  162. public void clearOutput() {
  163. output = "";
  164. }
  165. /**
  166. * @return the current events handler
  167. */
  168. private DefaultXmlEvents getEventsHandler() {
  169. if (eventsHandler == null) {
  170. if (getProperties() != null &&
  171. getProperties().containsKey(PROP_EVENTSHANDLER)) {
  172. this.eventsHandler = getCustomHandler();
  173. }
  174. else this.eventsHandler = new DefaultXmlEvents();
  175. }
  176. return this.eventsHandler;
  177. }
  178. /**
  179. * Creates a custom handler using the PROP_EVENTSHANDLER property
  180. * @return custom handler, or new DefaultXmlEvents if no custom handler exists
  181. */
  182. private DefaultXmlEvents getCustomHandler() {
  183. String xmleventsclass = getProperties().getProperty(PROP_EVENTSHANDLER);
  184. try {
  185. Class eventsClass = Class.forName(xmleventsclass);
  186. DefaultXmlEvents events = (DefaultXmlEvents) eventsClass.newInstance();
  187. return events;
  188. } catch (Exception e) {
  189. log.warn("Using DefaultXmlEvents. Could not use custom xml handler: " + xmleventsclass);
  190. }
  191. return new DefaultXmlEvents();
  192. }
  193. /**
  194. * @return stack of nested tags
  195. */
  196. private Stack getNested() {
  197. if (nested == null)
  198. nested = new Stack<String>();
  199. return nested;
  200. }
  201. /**
  202. * @return page associated with this parse
  203. */
  204. public Page getPage() {
  205. return page;
  206. }
  207. /**
  208. * @return true if Xml Fragments feature has been invoked in the properties
  209. */
  210. private boolean hasFragments() {
  211. return properties != null &&
  212. properties.containsKey(PROP_XMLFRAGMENTS) &&
  213. Boolean.parseBoolean(properties.getProperty(PROP_XMLFRAGMENTS));
  214. }
  215. /**
  216. * sets the misc properties object
  217. * @param properties
  218. */
  219. public void setProperties(Properties properties) {
  220. this.properties = properties;
  221. }
  222. /**
  223. * @return misc properties object
  224. */
  225. public Properties getProperties() {
  226. return properties;
  227. }
  228. }