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

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