Desktop tool for browsing account info from EVE-Online
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SAXHandler.java 34KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018
  1. /*--
  2. $Id: SAXHandler.java,v 1.73 2007/11/10 05:29:00 jhunter Exp $
  3. Copyright (C) 2000-2007 Jason Hunter & Brett McLaughlin.
  4. All rights reserved.
  5. Redistribution and use in source and binary forms, with or without
  6. modification, are permitted provided that the following conditions
  7. are met:
  8. 1. Redistributions of source code must retain the above copyright
  9. notice, this list of conditions, and the following disclaimer.
  10. 2. Redistributions in binary form must reproduce the above copyright
  11. notice, this list of conditions, and the disclaimer that follows
  12. these conditions in the documentation and/or other materials
  13. provided with the distribution.
  14. 3. The name "JDOM" must not be used to endorse or promote products
  15. derived from this software without prior written permission. For
  16. written permission, please contact <request_AT_jdom_DOT_org>.
  17. 4. Products derived from this software may not be called "JDOM", nor
  18. may "JDOM" appear in their name, without prior written permission
  19. from the JDOM Project Management <request_AT_jdom_DOT_org>.
  20. In addition, we request (but do not require) that you include in the
  21. end-user documentation provided with the redistribution and/or in the
  22. software itself an acknowledgement equivalent to the following:
  23. "This product includes software developed by the
  24. JDOM Project (http://www.jdom.org/)."
  25. Alternatively, the acknowledgment may be graphical using the logos
  26. available at http://www.jdom.org/images/logos.
  27. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  28. WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  29. OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  30. DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
  31. CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  32. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  33. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  34. USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  35. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  36. OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  37. OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  38. SUCH DAMAGE.
  39. This software consists of voluntary contributions made by many
  40. individuals on behalf of the JDOM Project and was originally
  41. created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
  42. Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
  43. on the JDOM Project, please see <http://www.jdom.org/>.
  44. */
  45. package org.jdom.input;
  46. import java.util.*;
  47. import org.jdom.*;
  48. import org.xml.sax.*;
  49. import org.xml.sax.ext.*;
  50. import org.xml.sax.helpers.*;
  51. /**
  52. * A support class for {@link SAXBuilder}.
  53. *
  54. * @version $Revision: 1.73 $, $Date: 2007/11/10 05:29:00 $
  55. * @author Brett McLaughlin
  56. * @author Jason Hunter
  57. * @author Philip Nelson
  58. * @author Bradley S. Huffman
  59. * @author phil@triloggroup.com
  60. */
  61. public class SAXHandler extends DefaultHandler implements LexicalHandler,
  62. DeclHandler,
  63. DTDHandler {
  64. private static final String CVS_ID =
  65. "@(#) $RCSfile: SAXHandler.java,v $ $Revision: 1.73 $ $Date: 2007/11/10 05:29:00 $ $Name: jdom_1_1 $";
  66. /** Hash table to map SAX attribute type names to JDOM attribute types. */
  67. private static final Map attrNameToTypeMap = new HashMap(13);
  68. /** <code>Document</code> object being built */
  69. private Document document;
  70. /** <code>Element</code> object being built */
  71. private Element currentElement;
  72. /** Indicator of where in the document we are */
  73. private boolean atRoot;
  74. /** Indicator of whether we are in the DocType. Note that the DTD consists
  75. * of both the internal subset (inside the <!DOCTYPE> tag) and the
  76. * external subset (in a separate .dtd file). */
  77. private boolean inDTD = false;
  78. /** Indicator of whether we are in the internal subset */
  79. private boolean inInternalSubset = false;
  80. /** Indicator of whether we previously were in a CDATA */
  81. private boolean previousCDATA = false;
  82. /** Indicator of whether we are in a CDATA */
  83. private boolean inCDATA = false;
  84. /** Indicator of whether we should expand entities */
  85. private boolean expand = true;
  86. /** Indicator of whether we are actively suppressing (non-expanding) a
  87. current entity */
  88. private boolean suppress = false;
  89. /** How many nested entities we're currently within */
  90. private int entityDepth = 0; // XXX may not be necessary anymore?
  91. /** Temporary holder for namespaces that have been declared with
  92. * startPrefixMapping, but are not yet available on the element */
  93. private List declaredNamespaces;
  94. /** Temporary holder for the internal subset */
  95. private StringBuffer internalSubset = new StringBuffer();
  96. /** Temporary holder for Text and CDATA */
  97. private TextBuffer textBuffer = new TextBuffer();
  98. /** The external entities defined in this document */
  99. private Map externalEntities;
  100. /** The JDOMFactory used for JDOM object creation */
  101. private JDOMFactory factory;
  102. /** Whether to ignore ignorable whitespace */
  103. private boolean ignoringWhite = false;
  104. /** Whether to ignore text containing all whitespace */
  105. private boolean ignoringBoundaryWhite = false;
  106. /** The SAX Locator object provided by the parser */
  107. private Locator locator;
  108. /**
  109. * Class initializer: Populate a table to translate SAX attribute
  110. * type names into JDOM attribute type value (integer).
  111. * <p>
  112. * <b>Note that all the mappings defined below are compliant with
  113. * the SAX 2.0 specification exception for "ENUMERATION" with is
  114. * specific to Crimson 1.1.X and Xerces 2.0.0-betaX which report
  115. * attributes of enumerated types with a type "ENUMERATION"
  116. * instead of the expected "NMTOKEN".
  117. * </p>
  118. * <p>
  119. * Note also that Xerces 1.4.X is not SAX 2.0 compliant either
  120. * but handling its case requires
  121. * {@link #getAttributeType specific code}.
  122. * </p>
  123. */
  124. static {
  125. attrNameToTypeMap.put("CDATA",
  126. new Integer(Attribute.CDATA_TYPE));
  127. attrNameToTypeMap.put("ID",
  128. new Integer(Attribute.ID_TYPE));
  129. attrNameToTypeMap.put("IDREF",
  130. new Integer(Attribute.IDREF_TYPE));
  131. attrNameToTypeMap.put("IDREFS",
  132. new Integer(Attribute.IDREFS_TYPE));
  133. attrNameToTypeMap.put("ENTITY",
  134. new Integer(Attribute.ENTITY_TYPE));
  135. attrNameToTypeMap.put("ENTITIES",
  136. new Integer(Attribute.ENTITIES_TYPE));
  137. attrNameToTypeMap.put("NMTOKEN",
  138. new Integer(Attribute.NMTOKEN_TYPE));
  139. attrNameToTypeMap.put("NMTOKENS",
  140. new Integer(Attribute.NMTOKENS_TYPE));
  141. attrNameToTypeMap.put("NOTATION",
  142. new Integer(Attribute.NOTATION_TYPE));
  143. attrNameToTypeMap.put("ENUMERATION",
  144. new Integer(Attribute.ENUMERATED_TYPE));
  145. }
  146. /**
  147. * This will create a new <code>SAXHandler</code> that listens to SAX
  148. * events and creates a JDOM Document. The objects will be constructed
  149. * using the default factory.
  150. */
  151. public SAXHandler() {
  152. this(null);
  153. }
  154. /**
  155. * This will create a new <code>SAXHandler</code> that listens to SAX
  156. * events and creates a JDOM Document. The objects will be constructed
  157. * using the provided factory.
  158. *
  159. * @param factory <code>JDOMFactory</code> to be used for constructing
  160. * objects
  161. */
  162. public SAXHandler(JDOMFactory factory) {
  163. if (factory != null) {
  164. this.factory = factory;
  165. } else {
  166. this.factory = new DefaultJDOMFactory();
  167. }
  168. atRoot = true;
  169. declaredNamespaces = new ArrayList();
  170. externalEntities = new HashMap();
  171. document = this.factory.document(null);
  172. }
  173. /**
  174. * Pushes an element onto the tree under construction. Allows subclasses
  175. * to put content under a dummy root element which is useful for building
  176. * content that would otherwise be a non-well formed document.
  177. *
  178. * @param element root element under which content will be built
  179. */
  180. protected void pushElement(Element element) {
  181. if (atRoot) {
  182. document.setRootElement(element); // XXX should we use a factory call?
  183. atRoot = false;
  184. }
  185. else {
  186. factory.addContent(currentElement, element);
  187. }
  188. currentElement = element;
  189. }
  190. /**
  191. * Returns the document. Should be called after parsing is complete.
  192. *
  193. * @return <code>Document</code> - Document that was built
  194. */
  195. public Document getDocument() {
  196. return document;
  197. }
  198. /**
  199. * Returns the factory used for constructing objects.
  200. *
  201. * @return <code>JDOMFactory</code> - the factory used for
  202. * constructing objects.
  203. *
  204. * @see #SAXHandler(org.jdom.JDOMFactory)
  205. */
  206. public JDOMFactory getFactory() {
  207. return factory;
  208. }
  209. /**
  210. * This sets whether or not to expand entities during the build.
  211. * A true means to expand entities as normal content. A false means to
  212. * leave entities unexpanded as <code>EntityRef</code> objects. The
  213. * default is true.
  214. *
  215. * @param expand <code>boolean</code> indicating whether entity expansion
  216. * should occur.
  217. */
  218. public void setExpandEntities(boolean expand) {
  219. this.expand = expand;
  220. }
  221. /**
  222. * Returns whether or not entities will be expanded during the
  223. * build.
  224. *
  225. * @return <code>boolean</code> - whether entity expansion
  226. * will occur during build.
  227. *
  228. * @see #setExpandEntities
  229. */
  230. public boolean getExpandEntities() {
  231. return expand;
  232. }
  233. /**
  234. * Specifies whether or not the parser should elminate whitespace in
  235. * element content (sometimes known as "ignorable whitespace") when
  236. * building the document. Only whitespace which is contained within
  237. * element content that has an element only content model will be
  238. * eliminated (see XML Rec 3.2.1). For this setting to take effect
  239. * requires that validation be turned on. The default value of this
  240. * setting is <code>false</code>.
  241. *
  242. * @param ignoringWhite Whether to ignore ignorable whitespace
  243. */
  244. public void setIgnoringElementContentWhitespace(boolean ignoringWhite) {
  245. this.ignoringWhite = ignoringWhite;
  246. }
  247. /**
  248. * Specifies whether or not the parser should elminate text() nodes
  249. * containing only whitespace when building the document. See
  250. * {@link SAXBuilder#setIgnoringBoundaryWhitespace(boolean)}.
  251. *
  252. * @param ignoringBoundaryWhite Whether to ignore only whitespace content
  253. */
  254. public void setIgnoringBoundaryWhitespace(boolean ignoringBoundaryWhite) {
  255. this.ignoringBoundaryWhite = ignoringBoundaryWhite;
  256. }
  257. /**
  258. * Returns whether or not the parser will elminate element content
  259. * containing only whitespace.
  260. *
  261. * @return <code>boolean</code> - whether only whitespace content will
  262. * be ignored during build.
  263. *
  264. * @see #setIgnoringBoundaryWhitespace
  265. */
  266. public boolean getIgnoringBoundaryWhitespace() {
  267. return ignoringBoundaryWhite;
  268. }
  269. /**
  270. * Returns whether or not the parser will elminate whitespace in
  271. * element content (sometimes known as "ignorable whitespace") when
  272. * building the document.
  273. *
  274. * @return <code>boolean</code> - whether ignorable whitespace will
  275. * be ignored during build.
  276. *
  277. * @see #setIgnoringElementContentWhitespace
  278. */
  279. public boolean getIgnoringElementContentWhitespace() {
  280. return ignoringWhite;
  281. }
  282. public void startDocument() {
  283. if (locator != null) {
  284. document.setBaseURI(locator.getSystemId());
  285. }
  286. }
  287. /**
  288. * This is called when the parser encounters an external entity
  289. * declaration.
  290. *
  291. * @param name entity name
  292. * @param publicID public id
  293. * @param systemID system id
  294. * @throws SAXException when things go wrong
  295. */
  296. public void externalEntityDecl(String name,
  297. String publicID, String systemID)
  298. throws SAXException {
  299. // Store the public and system ids for the name
  300. externalEntities.put(name, new String[]{publicID, systemID});
  301. if (!inInternalSubset) return;
  302. internalSubset.append(" <!ENTITY ")
  303. .append(name);
  304. appendExternalId(publicID, systemID);
  305. internalSubset.append(">\n");
  306. }
  307. /**
  308. * This handles an attribute declaration in the internal subset.
  309. *
  310. * @param eName <code>String</code> element name of attribute
  311. * @param aName <code>String</code> attribute name
  312. * @param type <code>String</code> attribute type
  313. * @param valueDefault <code>String</code> default value of attribute
  314. * @param value <code>String</code> value of attribute
  315. * @throws SAXException
  316. */
  317. public void attributeDecl(String eName, String aName, String type,
  318. String valueDefault, String value)
  319. throws SAXException {
  320. if (!inInternalSubset) return;
  321. internalSubset.append(" <!ATTLIST ")
  322. .append(eName)
  323. .append(' ')
  324. .append(aName)
  325. .append(' ')
  326. .append(type)
  327. .append(' ');
  328. if (valueDefault != null) {
  329. internalSubset.append(valueDefault);
  330. } else {
  331. internalSubset.append('\"')
  332. .append(value)
  333. .append('\"');
  334. }
  335. if ((valueDefault != null) && (valueDefault.equals("#FIXED"))) {
  336. internalSubset.append(" \"")
  337. .append(value)
  338. .append('\"');
  339. }
  340. internalSubset.append(">\n");
  341. }
  342. /**
  343. * Handle an element declaration in a DTD.
  344. *
  345. * @param name <code>String</code> name of element
  346. * @param model <code>String</code> model of the element in DTD syntax
  347. * @throws SAXException
  348. */
  349. public void elementDecl(String name, String model) throws SAXException {
  350. // Skip elements that come from the external subset
  351. if (!inInternalSubset) return;
  352. internalSubset.append(" <!ELEMENT ")
  353. .append(name)
  354. .append(' ')
  355. .append(model)
  356. .append(">\n");
  357. }
  358. /**
  359. * Handle an internal entity declaration in a DTD.
  360. *
  361. * @param name <code>String</code> name of entity
  362. * @param value <code>String</code> value of the entity
  363. * @throws SAXException
  364. */
  365. public void internalEntityDecl(String name, String value)
  366. throws SAXException {
  367. // Skip entities that come from the external subset
  368. if (!inInternalSubset) return;
  369. internalSubset.append(" <!ENTITY ");
  370. if (name.startsWith("%")) {
  371. internalSubset.append("% ").append(name.substring(1));
  372. } else {
  373. internalSubset.append(name);
  374. }
  375. internalSubset.append(" \"")
  376. .append(value)
  377. .append("\">\n");
  378. }
  379. /**
  380. * This will indicate that a processing instruction has been encountered.
  381. * (The XML declaration is not a processing instruction and will not
  382. * be reported.)
  383. *
  384. * @param target <code>String</code> target of PI
  385. * @param data <code>String</code> containing all data sent to the PI.
  386. * This typically looks like one or more attribute value
  387. * pairs.
  388. * @throws SAXException when things go wrong
  389. */
  390. public void processingInstruction(String target, String data)
  391. throws SAXException {
  392. if (suppress) return;
  393. flushCharacters();
  394. if (atRoot) {
  395. factory.addContent(document, factory.processingInstruction(target, data));
  396. } else {
  397. factory.addContent(getCurrentElement(),
  398. factory.processingInstruction(target, data));
  399. }
  400. }
  401. /**
  402. * This indicates that an unresolvable entity reference has been
  403. * encountered, normally because the external DTD subset has not been
  404. * read.
  405. *
  406. * @param name <code>String</code> name of entity
  407. * @throws SAXException when things go wrong
  408. */
  409. public void skippedEntity(String name)
  410. throws SAXException {
  411. // We don't handle parameter entity references.
  412. if (name.startsWith("%")) return;
  413. flushCharacters();
  414. factory.addContent(getCurrentElement(), factory.entityRef(name));
  415. }
  416. /**
  417. * This will add the prefix mapping to the JDOM
  418. * <code>Document</code> object.
  419. *
  420. * @param prefix <code>String</code> namespace prefix.
  421. * @param uri <code>String</code> namespace URI.
  422. */
  423. public void startPrefixMapping(String prefix, String uri)
  424. throws SAXException {
  425. if (suppress) return;
  426. Namespace ns = Namespace.getNamespace(prefix, uri);
  427. declaredNamespaces.add(ns);
  428. }
  429. /**
  430. * This reports the occurrence of an actual element. It will include
  431. * the element's attributes, with the exception of XML vocabulary
  432. * specific attributes, such as
  433. * <code>xmlns:[namespace prefix]</code> and
  434. * <code>xsi:schemaLocation</code>.
  435. *
  436. * @param namespaceURI <code>String</code> namespace URI this element
  437. * is associated with, or an empty
  438. * <code>String</code>
  439. * @param localName <code>String</code> name of element (with no
  440. * namespace prefix, if one is present)
  441. * @param qName <code>String</code> XML 1.0 version of element name:
  442. * [namespace prefix]:[localName]
  443. * @param atts <code>Attributes</code> list for this element
  444. * @throws SAXException when things go wrong
  445. */
  446. public void startElement(String namespaceURI, String localName,
  447. String qName, Attributes atts)
  448. throws SAXException {
  449. if (suppress) return;
  450. Element element = null;
  451. if ((namespaceURI != null) && (!namespaceURI.equals(""))) {
  452. String prefix = "";
  453. // Determine any prefix on the Element
  454. if (!qName.equals(localName)) {
  455. int split = qName.indexOf(":");
  456. prefix = qName.substring(0, split);
  457. }
  458. Namespace elementNamespace =
  459. Namespace.getNamespace(prefix, namespaceURI);
  460. element = factory.element(localName, elementNamespace);
  461. } else {
  462. element = factory.element(localName);
  463. }
  464. // Take leftover declared namespaces and add them to this element's
  465. // map of namespaces
  466. if (declaredNamespaces.size() > 0) {
  467. transferNamespaces(element);
  468. }
  469. // Handle attributes
  470. for (int i=0, len=atts.getLength(); i<len; i++) {
  471. Attribute attribute = null;
  472. String attLocalName = atts.getLocalName(i);
  473. String attQName = atts.getQName(i);
  474. int attType = getAttributeType(atts.getType(i));
  475. // Bypass any xmlns attributes which might appear, as we got
  476. // them already in startPrefixMapping().
  477. // This is sometimes necessary when SAXHandler is used with
  478. // another source than SAXBuilder, as with JDOMResult.
  479. if (attQName.startsWith("xmlns:") || attQName.equals("xmlns")) {
  480. continue;
  481. }
  482. // First clause per http://markmail.org/message/2p245ggcjst27xe6
  483. // patch from Mattias Jiderhamn
  484. if ("".equals(attLocalName) && attQName.indexOf(":") == -1) {
  485. attribute = factory.attribute(attQName, atts.getValue(i), attType);
  486. } else if (!attQName.equals(attLocalName)) {
  487. String attPrefix = attQName.substring(0, attQName.indexOf(":"));
  488. Namespace attNs = Namespace.getNamespace(attPrefix,
  489. atts.getURI(i));
  490. attribute = factory.attribute(attLocalName, atts.getValue(i),
  491. attType, attNs);
  492. } else {
  493. attribute = factory.attribute(attLocalName, atts.getValue(i),
  494. attType);
  495. }
  496. factory.setAttribute(element, attribute);
  497. }
  498. flushCharacters();
  499. if (atRoot) {
  500. document.setRootElement(element); // XXX should we use a factory call?
  501. atRoot = false;
  502. } else {
  503. factory.addContent(getCurrentElement(), element);
  504. }
  505. currentElement = element;
  506. }
  507. /**
  508. * This will take the supplied <code>{@link Element}</code> and
  509. * transfer its namespaces to the global namespace storage.
  510. *
  511. * @param element <code>Element</code> to read namespaces from.
  512. */
  513. private void transferNamespaces(Element element) {
  514. Iterator i = declaredNamespaces.iterator();
  515. while (i.hasNext()) {
  516. Namespace ns = (Namespace)i.next();
  517. if (ns != element.getNamespace()) {
  518. element.addNamespaceDeclaration(ns);
  519. }
  520. }
  521. declaredNamespaces.clear();
  522. }
  523. /**
  524. * This will report character data (within an element).
  525. *
  526. * @param ch <code>char[]</code> character array with character data
  527. * @param start <code>int</code> index in array where data starts.
  528. * @param length <code>int</code> length of data.
  529. * @throws SAXException
  530. */
  531. public void characters(char[] ch, int start, int length)
  532. throws SAXException {
  533. if (suppress || (length == 0))
  534. return;
  535. if (previousCDATA != inCDATA) {
  536. flushCharacters();
  537. }
  538. textBuffer.append(ch, start, length);
  539. }
  540. /**
  541. * Capture ignorable whitespace as text. If
  542. * setIgnoringElementContentWhitespace(true) has been called then this
  543. * method does nothing.
  544. *
  545. * @param ch <code>[]</code> - char array of ignorable whitespace
  546. * @param start <code>int</code> - starting position within array
  547. * @param length <code>int</code> - length of whitespace after start
  548. * @throws SAXException when things go wrong
  549. */
  550. public void ignorableWhitespace(char[] ch, int start, int length)
  551. throws SAXException {
  552. if (!ignoringWhite) {
  553. characters(ch, start, length);
  554. }
  555. }
  556. /**
  557. * This will flush any characters from SAX character calls we've
  558. * been buffering.
  559. *
  560. * @throws SAXException when things go wrong
  561. */
  562. protected void flushCharacters() throws SAXException {
  563. if (ignoringBoundaryWhite) {
  564. if (!textBuffer.isAllWhitespace()) {
  565. flushCharacters(textBuffer.toString());
  566. }
  567. }
  568. else {
  569. flushCharacters(textBuffer.toString());
  570. }
  571. textBuffer.clear();
  572. }
  573. /**
  574. * Flush the given string into the document. This is a protected method
  575. * so subclassers can control text handling without knowledge of the
  576. * internals of this class.
  577. *
  578. * @param data string to flush
  579. */
  580. protected void flushCharacters(String data) throws SAXException {
  581. if (data.length() == 0) {
  582. previousCDATA = inCDATA;
  583. return;
  584. }
  585. /**
  586. * This is commented out because of some problems with
  587. * the inline DTDs that Xerces seems to have.
  588. if (!inDTD) {
  589. if (inEntity) {
  590. getCurrentElement().setContent(factory.text(data));
  591. } else {
  592. getCurrentElement().addContent(factory.text(data));
  593. }
  594. */
  595. if (previousCDATA) {
  596. factory.addContent(getCurrentElement(), factory.cdata(data));
  597. }
  598. else {
  599. factory.addContent(getCurrentElement(), factory.text(data));
  600. }
  601. previousCDATA = inCDATA;
  602. }
  603. /**
  604. * Indicates the end of an element
  605. * (<code>&lt;/[element name]&gt;</code>) is reached. Note that
  606. * the parser does not distinguish between empty
  607. * elements and non-empty elements, so this will occur uniformly.
  608. *
  609. * @param namespaceURI <code>String</code> URI of namespace this
  610. * element is associated with
  611. * @param localName <code>String</code> name of element without prefix
  612. * @param qName <code>String</code> name of element in XML 1.0 form
  613. * @throws SAXException when things go wrong
  614. */
  615. public void endElement(String namespaceURI, String localName,
  616. String qName) throws SAXException {
  617. if (suppress) return;
  618. flushCharacters();
  619. if (!atRoot) {
  620. Parent p = currentElement.getParent();
  621. if (p instanceof Document) {
  622. atRoot = true;
  623. }
  624. else {
  625. currentElement = (Element) p;
  626. }
  627. }
  628. else {
  629. throw new SAXException(
  630. "Ill-formed XML document (missing opening tag for " +
  631. localName + ")");
  632. }
  633. }
  634. /**
  635. * This will signify that a DTD is being parsed, and can be
  636. * used to ensure that comments and other lexical structures
  637. * in the DTD are not added to the JDOM <code>Document</code>
  638. * object.
  639. *
  640. * @param name <code>String</code> name of element listed in DTD
  641. * @param publicID <code>String</code> public ID of DTD
  642. * @param systemID <code>String</code> system ID of DTD
  643. */
  644. public void startDTD(String name, String publicID, String systemID)
  645. throws SAXException {
  646. flushCharacters(); // Is this needed here?
  647. factory.addContent(document, factory.docType(name, publicID, systemID));
  648. inDTD = true;
  649. inInternalSubset = true;
  650. }
  651. /**
  652. * This signifies that the reading of the DTD is complete.
  653. *
  654. * @throws SAXException
  655. */
  656. public void endDTD() throws SAXException {
  657. document.getDocType().setInternalSubset(internalSubset.toString());
  658. inDTD = false;
  659. inInternalSubset = false;
  660. }
  661. public void startEntity(String name) throws SAXException {
  662. entityDepth++;
  663. if (expand || entityDepth > 1) {
  664. // Short cut out if we're expanding or if we're nested
  665. return;
  666. }
  667. // A "[dtd]" entity indicates the beginning of the external subset
  668. if (name.equals("[dtd]")) {
  669. inInternalSubset = false;
  670. return;
  671. }
  672. // Ignore DTD references, and translate the standard 5
  673. if ((!inDTD) &&
  674. (!name.equals("amp")) &&
  675. (!name.equals("lt")) &&
  676. (!name.equals("gt")) &&
  677. (!name.equals("apos")) &&
  678. (!name.equals("quot"))) {
  679. if (!expand) {
  680. String pub = null;
  681. String sys = null;
  682. String[] ids = (String[]) externalEntities.get(name);
  683. if (ids != null) {
  684. pub = ids[0]; // may be null, that's OK
  685. sys = ids[1]; // may be null, that's OK
  686. }
  687. /**
  688. * if no current element, this entity belongs to an attribute
  689. * in these cases, it is an error on the part of the parser
  690. * to call startEntity but this will help in some cases.
  691. * See org/xml/sax/ext/LexicalHandler.html#startEntity(java.lang.String)
  692. * for more information
  693. */
  694. if (!atRoot) {
  695. flushCharacters();
  696. EntityRef entity = factory.entityRef(name, pub, sys);
  697. // no way to tell if the entity was from an attribute or element so just assume element
  698. factory.addContent(getCurrentElement(), entity);
  699. }
  700. suppress = true;
  701. }
  702. }
  703. }
  704. public void endEntity(String name) throws SAXException {
  705. entityDepth--;
  706. if (entityDepth == 0) {
  707. // No way are we suppressing if not in an entity,
  708. // regardless of the "expand" value
  709. suppress = false;
  710. }
  711. if (name.equals("[dtd]")) {
  712. inInternalSubset = true;
  713. }
  714. }
  715. /**
  716. * Report a CDATA section
  717. *
  718. * @throws SAXException
  719. */
  720. public void startCDATA() throws SAXException {
  721. if (suppress) return;
  722. inCDATA = true;
  723. }
  724. /**
  725. * Report a CDATA section
  726. */
  727. public void endCDATA() throws SAXException {
  728. if (suppress) return;
  729. previousCDATA = true;
  730. inCDATA = false;
  731. }
  732. /**
  733. * This reports that a comments is parsed. If not in the
  734. * DTD, this comment is added to the current JDOM
  735. * <code>Element</code>, or the <code>Document</code> itself
  736. * if at that level.
  737. *
  738. * @param ch <code>ch[]</code> array of comment characters.
  739. * @param start <code>int</code> index to start reading from.
  740. * @param length <code>int</code> length of data.
  741. * @throws SAXException
  742. */
  743. public void comment(char[] ch, int start, int length)
  744. throws SAXException {
  745. if (suppress) return;
  746. flushCharacters();
  747. String commentText = new String(ch, start, length);
  748. if (inDTD && inInternalSubset && (expand == false)) {
  749. internalSubset.append(" <!--")
  750. .append(commentText)
  751. .append("-->\n");
  752. return;
  753. }
  754. if ((!inDTD) && (!commentText.equals(""))) {
  755. if (atRoot) {
  756. factory.addContent(document, factory.comment(commentText));
  757. } else {
  758. factory.addContent(getCurrentElement(), factory.comment(commentText));
  759. }
  760. }
  761. }
  762. /**
  763. * Handle the declaration of a Notation in a DTD
  764. *
  765. * @param name name of the notation
  766. * @param publicID the public ID of the notation
  767. * @param systemID the system ID of the notation
  768. */
  769. public void notationDecl(String name, String publicID, String systemID)
  770. throws SAXException {
  771. if (!inInternalSubset) return;
  772. internalSubset.append(" <!NOTATION ")
  773. .append(name);
  774. appendExternalId(publicID, systemID);
  775. internalSubset.append(">\n");
  776. }
  777. /**
  778. * Handler for unparsed entity declarations in the DTD
  779. *
  780. * @param name <code>String</code> of the unparsed entity decl
  781. * @param publicID <code>String</code> of the unparsed entity decl
  782. * @param systemID <code>String</code> of the unparsed entity decl
  783. * @param notationName <code>String</code> of the unparsed entity decl
  784. */
  785. public void unparsedEntityDecl(String name, String publicID,
  786. String systemID, String notationName)
  787. throws SAXException {
  788. if (!inInternalSubset) return;
  789. internalSubset.append(" <!ENTITY ")
  790. .append(name);
  791. appendExternalId(publicID, systemID);
  792. internalSubset.append(" NDATA ")
  793. .append(notationName);
  794. internalSubset.append(">\n");
  795. }
  796. /**
  797. * Appends an external ID to the internal subset buffer. Either publicID
  798. * or systemID may be null, but not both.
  799. *
  800. * @param publicID the public ID
  801. * @param systemID the system ID
  802. */
  803. private void appendExternalId(String publicID, String systemID) {
  804. if (publicID != null) {
  805. internalSubset.append(" PUBLIC \"")
  806. .append(publicID)
  807. .append('\"');
  808. }
  809. if (systemID != null) {
  810. if (publicID == null) {
  811. internalSubset.append(" SYSTEM ");
  812. }
  813. else {
  814. internalSubset.append(' ');
  815. }
  816. internalSubset.append('\"')
  817. .append(systemID)
  818. .append('\"');
  819. }
  820. }
  821. /**
  822. * Returns the being-parsed element.
  823. *
  824. * @return <code>Element</code> - element being built.
  825. * @throws SAXException
  826. */
  827. public Element getCurrentElement() throws SAXException {
  828. if (currentElement == null) {
  829. throw new SAXException(
  830. "Ill-formed XML document (multiple root elements detected)");
  831. }
  832. return currentElement;
  833. }
  834. /**
  835. * Returns the the JDOM Attribute type value from the SAX 2.0
  836. * attribute type string provided by the parser.
  837. *
  838. * @param typeName <code>String</code> the SAX 2.0 attribute
  839. * type string.
  840. *
  841. * @return <code>int</code> the JDOM attribute type.
  842. *
  843. * @see Attribute#setAttributeType
  844. * @see Attributes#getType
  845. */
  846. private static int getAttributeType(String typeName) {
  847. Integer type = (Integer)(attrNameToTypeMap.get(typeName));
  848. if (type == null) {
  849. if (typeName != null && typeName.length() > 0 &&
  850. typeName.charAt(0) == '(') {
  851. // Xerces 1.4.X reports attributes of enumerated type with
  852. // a type string equals to the enumeration definition, i.e.
  853. // starting with a parenthesis.
  854. return Attribute.ENUMERATED_TYPE;
  855. }
  856. else {
  857. return Attribute.UNDECLARED_TYPE;
  858. }
  859. } else {
  860. return type.intValue();
  861. }
  862. }
  863. /**
  864. * Receives an object for locating the origin of SAX document
  865. * events. This method is invoked by the SAX parser.
  866. * <p>
  867. * {@link org.jdom.JDOMFactory} implementations can use the
  868. * {@link #getDocumentLocator} method to get access to the
  869. * {@link Locator} during parse.
  870. * </p>
  871. *
  872. * @param locator <code>Locator</code> an object that can return
  873. * the location of any SAX document event.
  874. */
  875. public void setDocumentLocator(Locator locator) {
  876. this.locator = locator;
  877. }
  878. /**
  879. * Provides access to the {@link Locator} object provided by the
  880. * SAX parser.
  881. *
  882. * @return <code>Locator</code> an object that can return
  883. * the location of any SAX document event.
  884. */
  885. public Locator getDocumentLocator() {
  886. return locator;
  887. }
  888. }