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

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