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.

FONode.java 33KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.fo;
  19. // Java
  20. import java.util.ListIterator;
  21. import java.util.Map;
  22. import org.xml.sax.Attributes;
  23. import org.xml.sax.Locator;
  24. import org.xml.sax.helpers.LocatorImpl;
  25. import org.apache.commons.logging.Log;
  26. import org.apache.commons.logging.LogFactory;
  27. import org.apache.xmlgraphics.util.QName;
  28. import org.apache.fop.accessibility.StructureTreeElement;
  29. import org.apache.fop.apps.FOPException;
  30. import org.apache.fop.apps.FOUserAgent;
  31. import org.apache.fop.fo.extensions.ExtensionAttachment;
  32. import org.apache.fop.fo.extensions.ExtensionElementMapping;
  33. import org.apache.fop.fo.extensions.InternalElementMapping;
  34. import org.apache.fop.fo.extensions.svg.SVGElementMapping;
  35. import org.apache.fop.fo.pagination.Root;
  36. import org.apache.fop.util.CharUtilities;
  37. import org.apache.fop.util.ContentHandlerFactory;
  38. import org.apache.fop.util.text.AdvancedMessageFormat.Function;
  39. /**
  40. * Base class for nodes in the XML tree
  41. */
  42. public abstract class FONode implements Cloneable {
  43. /** the XSL-FO namespace URI */
  44. protected static final String FO_URI = FOElementMapping.URI;
  45. /** FOP's proprietary extension namespace URI */
  46. protected static final String FOX_URI = ExtensionElementMapping.URI;
  47. /** Parent FO node */
  48. protected FONode parent;
  49. /** pointer to the sibling nodes */
  50. protected FONode[] siblings;
  51. /**
  52. * Marks the location of this object from the input FO
  53. * <br>Call <code>locator.getSystemId()</code>,
  54. * <code>getLineNumber()</code>,
  55. * <code>getColumnNumber()</code> for file, line, column
  56. * information
  57. */
  58. protected Locator locator;
  59. /** Logger for fo-tree related messages **/
  60. protected static final Log log = LogFactory.getLog(FONode.class);
  61. /**
  62. * Base constructor
  63. *
  64. * @param parent parent of this node
  65. */
  66. protected FONode(FONode parent) {
  67. this.parent = parent;
  68. }
  69. /**
  70. * Performs a shallow cloning operation, sets the clone's parent,
  71. * and optionally cleans the list of child nodes
  72. *
  73. * @param cloneparent the intended parent of the clone
  74. * @param removeChildren if true, clean the list of child nodes
  75. * @return the cloned FO node
  76. * @throws FOPException if there's a problem while cloning the node
  77. */
  78. public FONode clone(FONode cloneparent, boolean removeChildren)
  79. throws FOPException {
  80. FONode foNode = (FONode) clone();
  81. foNode.parent = cloneparent;
  82. foNode.siblings = null;
  83. return foNode;
  84. }
  85. /** {@inheritDoc} */
  86. protected Object clone() {
  87. try {
  88. return super.clone();
  89. } catch (CloneNotSupportedException e) {
  90. throw new AssertionError(); // Can't happen
  91. }
  92. }
  93. /**
  94. * Bind the given <code>PropertyList</code> to this node
  95. * Does nothing by default. Subclasses should override this method
  96. * in case they want to use the properties available on the
  97. * <code>PropertyList</code>.
  98. *
  99. * @param propertyList the <code>PropertyList</code>
  100. * @throws FOPException if there was an error when
  101. * processing the <code>PropertyList</code>
  102. */
  103. public void bind(PropertyList propertyList) throws FOPException {
  104. //nop
  105. }
  106. /**
  107. * Set the location information for this element
  108. * @param locator the org.xml.sax.Locator object
  109. */
  110. public void setLocator(Locator locator) {
  111. if (locator != null) {
  112. //Create a copy of the locator so the info is preserved when we need to
  113. //give pointers during layout.
  114. this.locator = new LocatorImpl(locator);
  115. }
  116. }
  117. /**
  118. * Returns the <code>Locator</code> containing the location information for this
  119. * element, or <code>null</code> if not available
  120. *
  121. * @return the location information for this element or <code>null</code>, if not available
  122. */
  123. public Locator getLocator() {
  124. return this.locator;
  125. }
  126. /**
  127. * Recursively goes up the FOTree hierarchy until the <code>fo:root</code>
  128. * is found, which returns the parent <code>FOEventHandler</code>.
  129. * <br>(see also: {@link org.apache.fop.fo.pagination.Root#getFOEventHandler()})
  130. *
  131. * @return the FOEventHandler object that is the parent of the FO Tree
  132. */
  133. public FOEventHandler getFOEventHandler() {
  134. return parent.getFOEventHandler();
  135. }
  136. /**
  137. * Returns the context class providing information used during FO tree building.
  138. * @return the builder context
  139. */
  140. public FOTreeBuilderContext getBuilderContext() {
  141. return parent.getBuilderContext();
  142. }
  143. /**
  144. * Indicates whether this node is a child of an fo:marker.
  145. * @return true if this node is a child of an fo:marker
  146. */
  147. protected boolean inMarker() {
  148. return getBuilderContext().inMarker();
  149. }
  150. /**
  151. * Returns the user agent that is associated with the
  152. * tree's <code>FOEventHandler</code>.
  153. *
  154. * @return the user agent
  155. */
  156. public FOUserAgent getUserAgent() {
  157. return getFOEventHandler().getUserAgent();
  158. }
  159. /**
  160. * Returns the logger for the node.
  161. *
  162. * @return the logger
  163. */
  164. public Log getLogger() {
  165. return log;
  166. }
  167. /**
  168. * Initialize the node with its name, location information, and attributes
  169. * The attributes must be used immediately as the sax attributes
  170. * will be altered for the next element.
  171. *
  172. * @param elementName element name (e.g., "fo:block")
  173. * @param locator Locator object (ignored by default)
  174. * @param attlist Collection of attributes passed to us from the parser.
  175. * @param pList the property list of the parent node
  176. * @throws FOPException for errors or inconsistencies in the attributes
  177. */
  178. public void processNode(String elementName, Locator locator,
  179. Attributes attlist, PropertyList pList) throws FOPException {
  180. if (log.isDebugEnabled()) {
  181. log.debug("Unhandled element: " + elementName
  182. + (locator != null ? " at " + getLocatorString(locator) : ""));
  183. }
  184. }
  185. /**
  186. * Create a property list for this node. Return null if the node does not
  187. * need a property list.
  188. *
  189. * @param pList the closest parent propertylist.
  190. * @param foEventHandler The FOEventHandler where the PropertyListMaker
  191. * instance can be found.
  192. * @return A new property list.
  193. * @throws FOPException if there's a problem during processing
  194. */
  195. protected PropertyList createPropertyList(
  196. PropertyList pList,
  197. FOEventHandler foEventHandler)
  198. throws FOPException {
  199. return null;
  200. }
  201. /**
  202. * Checks to make sure, during SAX processing of input document, that the
  203. * incoming node is valid for this (parent) node (e.g., checking to
  204. * see that <code>fo:table</code> is not an immediate child of <code>fo:root</code>)
  205. * called from {@link FOTreeBuilder#startElement(String, String, String, Attributes)}
  206. * before constructing the child {@link FObj}.
  207. *
  208. * @param loc location in the FO source file
  209. * @param namespaceURI namespace of incoming node
  210. * @param localName name of the incoming node (without namespace prefix)
  211. * @throws ValidationException if incoming node not valid for parent
  212. */
  213. protected void validateChildNode(
  214. Locator loc,
  215. String namespaceURI,
  216. String localName)
  217. throws ValidationException {
  218. //nop
  219. }
  220. /**
  221. * Static version of {@link FONode#validateChildNode(Locator, String, String)} that
  222. * can be used by subclasses that need to validate children against a different node
  223. * (for example: <code>fo:wrapper</code> needs to check if the incoming node is a
  224. * valid child to its parent)
  225. *
  226. * @param fo the {@link FONode} to validate against
  227. * @param loc location in the source file
  228. * @param namespaceURI namespace of the incoming node
  229. * @param localName name of the incoming node (without namespace prefix)
  230. * @throws ValidationException if the incoming node is not a valid child for the given FO
  231. */
  232. protected static void validateChildNode(
  233. FONode fo,
  234. Locator loc,
  235. String namespaceURI,
  236. String localName)
  237. throws ValidationException {
  238. fo.validateChildNode(loc, namespaceURI, localName);
  239. }
  240. /**
  241. * Adds characters. Does nothing by default. To be overridden in subclasses
  242. * that allow <code>#PCDATA</code> content.
  243. *
  244. * @param data array of characters containing text to be added
  245. * @param start starting array element to add
  246. * @param end ending array element to add
  247. * @param pList currently applicable PropertyList
  248. * @param locator location in the XSL-FO source file.
  249. * @throws FOPException if there's a problem during processing
  250. * @deprecated Please override {@link #characters(char[], int, int, PropertyList, Locator)}
  251. * instead!
  252. */
  253. protected void addCharacters(char[] data, int start, int end,
  254. PropertyList pList,
  255. Locator locator) throws FOPException {
  256. // ignore
  257. }
  258. /**
  259. * Adds characters. Does nothing by default. To be overridden in subclasses
  260. * that allow <code>#PCDATA</code> content.
  261. *
  262. * @param data array of characters containing text to be added
  263. * @param start starting array element to add
  264. * @param length number of elements to add
  265. * @param pList currently applicable PropertyList
  266. * @param locator location in the XSL-FO source file.
  267. * @throws FOPException if there's a problem during processing
  268. */
  269. protected void characters(char[] data, int start, int length,
  270. PropertyList pList,
  271. Locator locator) throws FOPException {
  272. addCharacters(data, start, start + length, pList, locator);
  273. }
  274. /**
  275. * Called after processNode() is called. Subclasses can do additional processing.
  276. *
  277. * @throws FOPException if there's a problem during processing
  278. */
  279. protected void startOfNode() throws FOPException {
  280. // do nothing by default
  281. }
  282. /**
  283. * Primarily used for making final content model validation checks
  284. * and/or informing the {@link FOEventHandler} that the end of this FO
  285. * has been reached.
  286. * The default implementation simply calls {@link #finalizeNode()}, without
  287. * sending any event to the {@link FOEventHandler}.
  288. * <br/><i>Note: the recommended way to override this method in subclasses is</i>
  289. * <br/><br/><code>super.endOfNode(); // invoke finalizeNode()
  290. * <br/>getFOEventHandler().endXXX(); // send endOfNode() notification</code>
  291. *
  292. * @throws FOPException if there's a problem during processing
  293. */
  294. protected void endOfNode() throws FOPException {
  295. this.finalizeNode();
  296. }
  297. /**
  298. * Adds a node as a child of this node. The default implementation of this method
  299. * just ignores any child node being added.
  300. *
  301. * @param child child node to be added to the childNodes of this node
  302. * @throws FOPException if there's a problem during processing
  303. */
  304. protected void addChildNode(FONode child) throws FOPException {
  305. // do nothing by default
  306. }
  307. /**
  308. * Removes a child node. Used by the child nodes to remove themselves, for
  309. * example table-body if it has no children.
  310. *
  311. * @param child child node to be removed
  312. */
  313. public void removeChild(FONode child) {
  314. //nop
  315. }
  316. /**
  317. * Finalize this node.
  318. * This method can be overridden by subclasses to perform finishing
  319. * tasks (cleanup, validation checks, ...) without triggering
  320. * endXXX() events in the {@link FOEventHandler}.
  321. * The method is called by the default {@link #endOfNode()}
  322. * implementation.
  323. *
  324. * @throws FOPException in case there was an error
  325. */
  326. public void finalizeNode() throws FOPException {
  327. // do nothing by default
  328. }
  329. /**
  330. * Return the parent node of this node
  331. *
  332. * @return the parent node of this node
  333. */
  334. public FONode getParent() {
  335. return this.parent;
  336. }
  337. /**
  338. * Return an iterator over all the child nodes of this node.
  339. *
  340. * @return the iterator over the FO's childnodes
  341. */
  342. public FONodeIterator getChildNodes() {
  343. return null;
  344. }
  345. /**
  346. * Return an iterator over the object's child nodes starting
  347. * at the passed node.
  348. *
  349. * @param childNode First node in the iterator
  350. * @return the iterator, or <code>null</code> if
  351. * the given node is not a child of this node.
  352. */
  353. public FONodeIterator getChildNodes(FONode childNode) {
  354. return null;
  355. }
  356. /**
  357. * Return a {@link CharIterator} over all characters in this node
  358. *
  359. * @return an iterator for the characters in this node
  360. */
  361. public CharIterator charIterator() {
  362. return new OneCharIterator(CharUtilities.CODE_EOT);
  363. }
  364. /**
  365. * Helper function to standardize the names of all namespace URI - local
  366. * name pairs in text messages.
  367. * For readability, using fo:, fox:, svg:, for those namespaces even
  368. * though that prefix may not have been chosen in the document.
  369. * @param namespaceURI URI of node found
  370. * (e.g., "http://www.w3.org/1999/XSL/Format")
  371. * @param localName local name of node, (e.g., "root" for "fo:root")
  372. * @return the prefix:localname, if fo/fox/svg, or a longer representation
  373. * with the unabbreviated URI otherwise.
  374. */
  375. public static String getNodeString(String namespaceURI, String localName) {
  376. if (namespaceURI.equals(FOElementMapping.URI)) {
  377. return "fo:" + localName;
  378. } else if (namespaceURI.equals(ExtensionElementMapping.URI)) {
  379. return "fox:" + localName;
  380. } else if (namespaceURI.equals(InternalElementMapping.URI)) {
  381. return "foi:" + localName; // used FOP internally for accessibility
  382. } else if (namespaceURI.equals(SVGElementMapping.URI)) {
  383. return "svg:" + localName;
  384. } else {
  385. return "(Namespace URI: \"" + namespaceURI + "\", "
  386. + "Local Name: \"" + localName + "\")";
  387. }
  388. }
  389. /**
  390. * Returns an instance of the FOValidationEventProducer.
  391. * @return an event producer for FO validation
  392. */
  393. protected FOValidationEventProducer getFOValidationEventProducer() {
  394. return FOValidationEventProducer.Provider.get(
  395. getUserAgent().getEventBroadcaster());
  396. }
  397. /**
  398. * Helper function to standardize "too many" error exceptions
  399. * (e.g., two fo:declarations within fo:root)
  400. * @param loc org.xml.sax.Locator object of the error (*not* parent node)
  401. * @param nsURI namespace URI of incoming invalid node
  402. * @param lName local name (i.e., no prefix) of incoming node
  403. * @throws ValidationException the validation error provoked by the method call
  404. */
  405. protected void tooManyNodesError(Locator loc, String nsURI, String lName)
  406. throws ValidationException {
  407. tooManyNodesError(loc, new QName(nsURI, lName));
  408. }
  409. /**
  410. * Helper function to standardize "too many" error exceptions
  411. * (e.g., two <code>fo:declarations</code> within <code>fo:root</code>)
  412. *
  413. * @param loc org.xml.sax.Locator object of the error (*not* parent node)
  414. * @param offendingNode the qualified name of the offending node
  415. * @throws ValidationException the validation error provoked by the method call
  416. */
  417. protected void tooManyNodesError(Locator loc, QName offendingNode)
  418. throws ValidationException {
  419. getFOValidationEventProducer().tooManyNodes(this, getName(), offendingNode, loc);
  420. }
  421. /**
  422. * Helper function to standardize "too many" error exceptions
  423. * (e.g., two fo:declarations within fo:root)
  424. * This overloaded method helps make the caller code better self-documenting
  425. * @param loc org.xml.sax.Locator object of the error (*not* parent node)
  426. * @param offendingNode incoming node that would cause a duplication.
  427. * @throws ValidationException the validation error provoked by the method call
  428. */
  429. protected void tooManyNodesError(Locator loc, String offendingNode)
  430. throws ValidationException {
  431. tooManyNodesError(loc, new QName(FO_URI, offendingNode));
  432. }
  433. /**
  434. * Helper function to standardize "out of order" exceptions
  435. * (e.g., <code>fo:layout-master-set</code> appearing after <code>fo:page-sequence</code>)
  436. *
  437. * @param loc org.xml.sax.Locator object of the error (*not* parent node)
  438. * @param tooLateNode string name of node that should be earlier in document
  439. * @param tooEarlyNode string name of node that should be later in document
  440. * @throws ValidationException the validation error provoked by the method call
  441. */
  442. protected void nodesOutOfOrderError(Locator loc, String tooLateNode,
  443. String tooEarlyNode) throws ValidationException {
  444. nodesOutOfOrderError(loc, tooLateNode, tooEarlyNode, false);
  445. }
  446. /**
  447. * Helper function to standardize "out of order" exceptions
  448. * (e.g., fo:layout-master-set appearing after fo:page-sequence)
  449. * @param loc org.xml.sax.Locator object of the error (*not* parent node)
  450. * @param tooLateNode string name of node that should be earlier in document
  451. * @param tooEarlyNode string name of node that should be later in document
  452. * @param canRecover indicates whether FOP can recover from this problem and continue working
  453. * @throws ValidationException the validation error provoked by the method call
  454. */
  455. protected void nodesOutOfOrderError(Locator loc, String tooLateNode,
  456. String tooEarlyNode, boolean canRecover) throws ValidationException {
  457. getFOValidationEventProducer().nodeOutOfOrder(this, getName(),
  458. tooLateNode, tooEarlyNode, canRecover, loc);
  459. }
  460. /**
  461. * Helper function to return "invalid child" exceptions
  462. * (e.g., <code>fo:block</code> appearing immediately under <code>fo:root</code>)
  463. *
  464. * @param loc org.xml.sax.Locator object of the error (*not* parent node)
  465. * @param nsURI namespace URI of incoming invalid node
  466. * @param lName local name (i.e., no prefix) of incoming node
  467. * @throws ValidationException the validation error provoked by the method call
  468. */
  469. protected void invalidChildError(Locator loc, String nsURI, String lName)
  470. throws ValidationException {
  471. invalidChildError(loc, getName(), nsURI, lName, null);
  472. }
  473. /**
  474. * Helper function to return "invalid child" exceptions with more
  475. * complex validation rules (i.e., needing more explanation of the problem)
  476. *
  477. * @param loc org.xml.sax.Locator object of the error (*not* parent node)
  478. * @param parentName the name of the parent element
  479. * @param nsURI namespace URI of incoming invalid node
  480. * @param lName local name (i.e., no prefix) of incoming node
  481. * @param ruleViolated name of the rule violated (used to lookup a resource in a bundle)
  482. * @throws ValidationException the validation error provoked by the method call
  483. */
  484. protected void invalidChildError(Locator loc, String parentName, String nsURI, String lName,
  485. String ruleViolated)
  486. throws ValidationException {
  487. getFOValidationEventProducer().invalidChild(this, parentName,
  488. new QName(nsURI, lName), ruleViolated, loc);
  489. }
  490. /**
  491. * Helper function to throw an error caused by missing mandatory child elements.
  492. * (e.g., <code>fo:layout-master-set</code> not having any <code>fo:page-master</code>
  493. * child element.
  494. *
  495. * @param contentModel The XSL Content Model for the fo: object or a similar description
  496. * indicating the necessary child elements.
  497. * @throws ValidationException the validation error provoked by the method call
  498. */
  499. protected void missingChildElementError(String contentModel)
  500. throws ValidationException {
  501. getFOValidationEventProducer().missingChildElement(this, getName(),
  502. contentModel, false, locator);
  503. }
  504. /**
  505. * Helper function to throw an error caused by missing mandatory child elements.
  506. * E.g., fo:layout-master-set not having any page-master child element.
  507. * @param contentModel The XSL Content Model for the fo: object or a similar description
  508. * indicating the necessary child elements.
  509. * @param canRecover indicates whether FOP can recover from this problem and continue working
  510. * @throws ValidationException the validation error provoked by the method call
  511. */
  512. protected void missingChildElementError(String contentModel, boolean canRecover)
  513. throws ValidationException {
  514. getFOValidationEventProducer().missingChildElement(this, getName(),
  515. contentModel, canRecover, locator);
  516. }
  517. /**
  518. * Helper function to throw an error caused by missing mandatory properties
  519. *
  520. * @param propertyName the name of the missing property.
  521. * @throws ValidationException the validation error provoked by the method call
  522. */
  523. protected void missingPropertyError(String propertyName)
  524. throws ValidationException {
  525. getFOValidationEventProducer().missingProperty(this, getName(), propertyName, locator);
  526. }
  527. /**
  528. * Helper function to return "Error(line#/column#)" string for
  529. * above exception messages
  530. *
  531. * @param loc org.xml.sax.Locator object
  532. * @return String opening error text
  533. */
  534. protected static String errorText(Locator loc) {
  535. return "Error(" + getLocatorString(loc) + "): ";
  536. }
  537. /**
  538. * Helper function to return "Warning(line#/column#)" string for
  539. * warning messages
  540. *
  541. * @param loc org.xml.sax.Locator object
  542. * @return String opening warning text
  543. */
  544. protected static String warningText(Locator loc) {
  545. return "Warning(" + getLocatorString(loc) + "): ";
  546. }
  547. /**
  548. * Helper function to format a Locator instance.
  549. *
  550. * @param loc org.xml.sax.Locator object
  551. * @return String the formatted text
  552. */
  553. public static String getLocatorString(Locator loc) {
  554. if (loc == null) {
  555. return "Unknown location";
  556. } else {
  557. return loc.getLineNumber() + "/" + loc.getColumnNumber();
  558. }
  559. }
  560. /**
  561. * Decorates a log or warning message with context information on the given node.
  562. *
  563. * @param text the original message
  564. * @param node the context node
  565. * @return the decorated text
  566. */
  567. public static String decorateWithContextInfo(String text, FONode node) {
  568. if (node != null) {
  569. StringBuffer sb = new StringBuffer(text);
  570. sb.append(" (").append(node.getContextInfo()).append(")");
  571. return sb.toString();
  572. } else {
  573. return text;
  574. }
  575. }
  576. /**
  577. * Returns a String containing as much context information as possible about a node. Call
  578. * this method only in exceptional conditions because this method may perform quite extensive
  579. * information gathering inside the FO tree.
  580. * @return a String containing context information
  581. */
  582. // [GA] remove deprecation - no alternative specified
  583. // @deprecated Not localized! Should rename getContextInfoAlt() to getContextInfo() when done!
  584. public String getContextInfo() {
  585. StringBuffer sb = new StringBuffer();
  586. if (getLocalName() != null) {
  587. sb.append(getName());
  588. sb.append(", ");
  589. }
  590. if (this.locator != null) {
  591. sb.append("location: ");
  592. sb.append(getLocatorString(this.locator));
  593. } else {
  594. String s = gatherContextInfo();
  595. if (s != null) {
  596. sb.append("\"");
  597. sb.append(s);
  598. sb.append("\"");
  599. } else {
  600. sb.append("no context info available");
  601. }
  602. }
  603. if (sb.length() > 80) {
  604. sb.setLength(80);
  605. }
  606. return sb.toString();
  607. }
  608. /**
  609. * Returns a String containing as some context information about a node. It does not take the
  610. * locator into consideration and returns null if no useful context information can be found.
  611. * Call this method only in exceptional conditions because this method may perform quite
  612. * extensive information gathering inside the FO tree. All text returned by this method that
  613. * is not extracted from document content needs to be locale-independent.
  614. * @return a String containing context information
  615. */
  616. protected String getContextInfoAlt() {
  617. String s = gatherContextInfo();
  618. if (s != null) {
  619. StringBuffer sb = new StringBuffer();
  620. if (getLocalName() != null) {
  621. sb.append(getName());
  622. sb.append(", ");
  623. }
  624. sb.append("\"");
  625. sb.append(s);
  626. sb.append("\"");
  627. return sb.toString();
  628. } else {
  629. return null;
  630. }
  631. }
  632. /** Function for AdvancedMessageFormat to retrieve context info from an FONode. */
  633. public static class GatherContextInfoFunction implements Function {
  634. /** {@inheritDoc} */
  635. public Object evaluate(Map params) {
  636. Object obj = params.get("source");
  637. if (obj instanceof PropertyList) {
  638. PropertyList propList = (PropertyList)obj;
  639. obj = propList.getFObj();
  640. }
  641. if (obj instanceof FONode) {
  642. FONode node = (FONode)obj;
  643. return node.getContextInfoAlt();
  644. }
  645. return null;
  646. }
  647. /** {@inheritDoc} */
  648. public Object getName() {
  649. return "gatherContextInfo";
  650. }
  651. }
  652. /**
  653. * Gathers context information for the getContextInfo() method.
  654. * @return the collected context information or null, if none is available
  655. */
  656. protected String gatherContextInfo() {
  657. return null;
  658. }
  659. /**
  660. * Returns the root node of this tree
  661. *
  662. * @return the root node
  663. */
  664. public Root getRoot() {
  665. return parent.getRoot();
  666. }
  667. /**
  668. * Returns the fully qualified name of the node
  669. *
  670. * @return the fully qualified name of this node
  671. */
  672. public String getName() {
  673. return getName(getNormalNamespacePrefix());
  674. }
  675. /**
  676. * Returns the fully qualified name of the node
  677. *
  678. * @param prefix the namespace prefix to build the name with (may be null)
  679. * @return the fully qualified name of this node
  680. */
  681. public String getName(String prefix) {
  682. if (prefix != null) {
  683. StringBuffer sb = new StringBuffer();
  684. sb.append(prefix).append(':').append(getLocalName());
  685. return sb.toString();
  686. } else {
  687. return getLocalName();
  688. }
  689. }
  690. /**
  691. * Returns the local name (i.e. without namespace prefix) of the node
  692. *
  693. * @return the local name of this node
  694. */
  695. public abstract String getLocalName();
  696. /**
  697. * Returns the normally used namespace prefix for this node
  698. *
  699. * @return the normally used namespace prefix for this kind of node (ex. "fo" for XSL-FO)
  700. */
  701. public abstract String getNormalNamespacePrefix();
  702. /**
  703. * Returns the namespace URI for this node
  704. *
  705. * @return the namespace URI for this node
  706. */
  707. public String getNamespaceURI() {
  708. return null;
  709. }
  710. /**
  711. * Returns the {@link Constants} class integer value of this node
  712. *
  713. * @return the integer enumeration of this FO (e.g. {@link Constants#FO_ROOT})
  714. * if a formatting object, {@link Constants#FO_UNKNOWN_NODE} otherwise
  715. */
  716. public int getNameId() {
  717. return Constants.FO_UNKNOWN_NODE;
  718. }
  719. /**
  720. * This method is overridden by extension elements and allows the extension element
  721. * to return a pass-through attachment which the parent formatting objects should simply
  722. * carry with them but otherwise ignore. This mechanism is used to pass non-standard
  723. * information from the FO tree through to the layout engine and the renderers.
  724. *
  725. * @return the extension attachment if one is created by the extension element, null otherwise.
  726. */
  727. public ExtensionAttachment getExtensionAttachment() {
  728. return null;
  729. }
  730. /**
  731. * This method is overridden by extension elements and allows the extension element to return
  732. * a {@link ContentHandlerFactory}. This factory can create ContentHandler implementations that
  733. * handle foreign XML content by either building up a specific DOM, a Java object or something
  734. * else.
  735. *
  736. * @return the <code>ContentHandlerFactory</code> or <code>null</code> if not applicable
  737. */
  738. public ContentHandlerFactory getContentHandlerFactory() {
  739. return null;
  740. }
  741. /**
  742. * Returns <code>true</code> if <code>fo:marker</code> is allowed as
  743. * a child node.
  744. * <br>To be overridden <i>only</i> in extension nodes that need it.
  745. *
  746. * @return true if markers are valid children
  747. */
  748. protected boolean canHaveMarkers() {
  749. int foId = getNameId();
  750. switch (foId) {
  751. case Constants.FO_BASIC_LINK:
  752. case Constants.FO_BIDI_OVERRIDE:
  753. case Constants.FO_BLOCK:
  754. case Constants.FO_BLOCK_CONTAINER:
  755. case Constants.FO_FLOW:
  756. case Constants.FO_INLINE:
  757. case Constants.FO_INLINE_CONTAINER:
  758. case Constants.FO_LIST_BLOCK:
  759. case Constants.FO_LIST_ITEM:
  760. case Constants.FO_LIST_ITEM_BODY:
  761. case Constants.FO_LIST_ITEM_LABEL:
  762. case Constants.FO_TABLE:
  763. case Constants.FO_TABLE_BODY:
  764. case Constants.FO_TABLE_HEADER:
  765. case Constants.FO_TABLE_FOOTER:
  766. case Constants.FO_TABLE_CELL:
  767. case Constants.FO_TABLE_AND_CAPTION:
  768. case Constants.FO_TABLE_CAPTION:
  769. case Constants.FO_WRAPPER:
  770. return true;
  771. default:
  772. return false;
  773. }
  774. }
  775. /**
  776. * This method is used when adding child nodes to a FO that already
  777. * contains at least one child. In this case, the new child becomes a
  778. * sibling to the previous one
  779. *
  780. * @param precedingSibling the previous child
  781. * @param followingSibling the new child
  782. */
  783. protected static void attachSiblings(FONode precedingSibling,
  784. FONode followingSibling) {
  785. if (precedingSibling.siblings == null) {
  786. precedingSibling.siblings = new FONode[2];
  787. }
  788. if (followingSibling.siblings == null) {
  789. followingSibling.siblings = new FONode[2];
  790. }
  791. precedingSibling.siblings[1] = followingSibling;
  792. followingSibling.siblings[0] = precedingSibling;
  793. }
  794. /**
  795. * Base iterator interface over a FO's children
  796. */
  797. public interface FONodeIterator extends ListIterator {
  798. /**
  799. * Returns the parent node for this iterator's list
  800. * of child nodes
  801. *
  802. * @return the parent node
  803. */
  804. FObj parentNode();
  805. /**
  806. * Convenience method with return type of FONode
  807. * (semantically equivalent to: <code>(FONode) next();</code>)
  808. *
  809. * @return the next node (if any), as a type FONode
  810. */
  811. FONode nextNode();
  812. /**
  813. * Convenience method with return type of FONode
  814. * (semantically equivalent to: <code>(FONode) previous();</code>)
  815. *
  816. * @return the previous node (if any), as a type FONode
  817. */
  818. FONode previousNode();
  819. /**
  820. * Returns the first node in the list, and decreases the index,
  821. * so that a subsequent call to <code>hasPrevious()</code> will
  822. * return <code>false</code>
  823. *
  824. * @return the first node in the list
  825. */
  826. FONode firstNode();
  827. /**
  828. * Returns the last node in the list, and advances the
  829. * current position, so that a subsequent call to <code>hasNext()</code>
  830. * will return <code>false</code>
  831. *
  832. * @return the last node in the list
  833. */
  834. FONode lastNode();
  835. }
  836. public void setStructureTreeElement(StructureTreeElement structureTreeElement) {
  837. throw new UnsupportedOperationException();
  838. }
  839. }