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 32KB

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