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.

TableLayoutManager.java 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  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.layoutmgr.table;
  19. import java.util.ArrayList;
  20. import java.util.Iterator;
  21. import java.util.LinkedList;
  22. import java.util.List;
  23. import java.util.Map;
  24. import org.apache.commons.logging.Log;
  25. import org.apache.commons.logging.LogFactory;
  26. import org.apache.fop.area.Area;
  27. import org.apache.fop.area.Block;
  28. import org.apache.fop.datatypes.LengthBase;
  29. import org.apache.fop.fo.Constants;
  30. import org.apache.fop.fo.FONode;
  31. import org.apache.fop.fo.FObj;
  32. import org.apache.fop.fo.flow.Marker;
  33. import org.apache.fop.fo.flow.Markers;
  34. import org.apache.fop.fo.flow.RetrieveTableMarker;
  35. import org.apache.fop.fo.flow.table.Table;
  36. import org.apache.fop.fo.flow.table.TableColumn;
  37. import org.apache.fop.fo.properties.KeepProperty;
  38. import org.apache.fop.layoutmgr.BlockLevelEventProducer;
  39. import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
  40. import org.apache.fop.layoutmgr.BreakElement;
  41. import org.apache.fop.layoutmgr.BreakOpportunity;
  42. import org.apache.fop.layoutmgr.ConditionalElementListener;
  43. import org.apache.fop.layoutmgr.KnuthElement;
  44. import org.apache.fop.layoutmgr.KnuthGlue;
  45. import org.apache.fop.layoutmgr.LayoutContext;
  46. import org.apache.fop.layoutmgr.LeafPosition;
  47. import org.apache.fop.layoutmgr.ListElement;
  48. import org.apache.fop.layoutmgr.Position;
  49. import org.apache.fop.layoutmgr.PositionIterator;
  50. import org.apache.fop.layoutmgr.RelSide;
  51. import org.apache.fop.layoutmgr.TraitSetter;
  52. import org.apache.fop.traits.MinOptMax;
  53. import org.apache.fop.traits.SpaceVal;
  54. import org.apache.fop.util.BreakUtil;
  55. /**
  56. * LayoutManager for a table FO.
  57. * A table consists of columns, table header, table footer and multiple
  58. * table bodies.
  59. * The header, footer and body add the areas created from the table cells.
  60. * The table then creates areas for the columns, bodies and rows
  61. * the render background.
  62. */
  63. public class TableLayoutManager extends BlockStackingLayoutManager
  64. implements ConditionalElementListener, BreakOpportunity {
  65. /**
  66. * logging instance
  67. */
  68. private static Log log = LogFactory.getLog(TableLayoutManager.class);
  69. private TableContentLayoutManager contentLM;
  70. private ColumnSetup columns = null;
  71. private Block curBlockArea;
  72. private double tableUnit;
  73. private boolean autoLayout = true;
  74. private boolean discardBorderBefore;
  75. private boolean discardBorderAfter;
  76. private boolean discardPaddingBefore;
  77. private boolean discardPaddingAfter;
  78. private MinOptMax effSpaceBefore;
  79. private MinOptMax effSpaceAfter;
  80. private int halfBorderSeparationBPD;
  81. private int halfBorderSeparationIPD;
  82. /** See {@link TableLayoutManager#registerColumnBackgroundArea(TableColumn, Block, int)}. */
  83. private List columnBackgroundAreas;
  84. private Position auxiliaryPosition;
  85. // this holds a possible list of TCLMs that needed to have their addAreas() repeated
  86. private List<TableCellLayoutManager> savedTCLMs;
  87. private boolean areAllTCLMsSaved;
  88. private Markers tableMarkers;
  89. private Markers tableFragmentMarkers;
  90. private boolean hasRetrieveTableMarker;
  91. /**
  92. * Temporary holder of column background informations for a table-cell's area.
  93. *
  94. * @see TableLayoutManager#registerColumnBackgroundArea(TableColumn, Block, int)
  95. */
  96. private static final class ColumnBackgroundInfo {
  97. private TableColumn column;
  98. private Block backgroundArea;
  99. private int xShift;
  100. private ColumnBackgroundInfo(TableColumn column, Block backgroundArea, int xShift) {
  101. this.column = column;
  102. this.backgroundArea = backgroundArea;
  103. this.xShift = xShift;
  104. }
  105. }
  106. /**
  107. * Create a new table layout manager.
  108. * @param node the table FO
  109. */
  110. public TableLayoutManager(Table node) {
  111. super(node);
  112. this.columns = new ColumnSetup(node);
  113. }
  114. /** @return the table FO */
  115. public Table getTable() {
  116. return (Table)this.fobj;
  117. }
  118. /**
  119. * @return the column setup for this table.
  120. */
  121. public ColumnSetup getColumns() {
  122. return this.columns;
  123. }
  124. /** {@inheritDoc} */
  125. public void initialize() {
  126. foSpaceBefore = new SpaceVal(
  127. getTable().getCommonMarginBlock().spaceBefore, this).getSpace();
  128. foSpaceAfter = new SpaceVal(
  129. getTable().getCommonMarginBlock().spaceAfter, this).getSpace();
  130. startIndent = getTable().getCommonMarginBlock().startIndent.getValue(this);
  131. endIndent = getTable().getCommonMarginBlock().endIndent.getValue(this);
  132. if (getTable().isSeparateBorderModel()) {
  133. this.halfBorderSeparationBPD = getTable().getBorderSeparation().getBPD().getLength()
  134. .getValue(this) / 2;
  135. this.halfBorderSeparationIPD = getTable().getBorderSeparation().getIPD().getLength()
  136. .getValue(this) / 2;
  137. } else {
  138. this.halfBorderSeparationBPD = 0;
  139. this.halfBorderSeparationIPD = 0;
  140. }
  141. if (!getTable().isAutoLayout()
  142. && getTable().getInlineProgressionDimension().getOptimum(this).getEnum()
  143. != EN_AUTO) {
  144. autoLayout = false;
  145. }
  146. }
  147. private void resetSpaces() {
  148. this.discardBorderBefore = false;
  149. this.discardBorderAfter = false;
  150. this.discardPaddingBefore = false;
  151. this.discardPaddingAfter = false;
  152. this.effSpaceBefore = null;
  153. this.effSpaceAfter = null;
  154. }
  155. /**
  156. * @return half the value of border-separation.block-progression-dimension, or 0 if
  157. * border-collapse="collapse".
  158. */
  159. public int getHalfBorderSeparationBPD() {
  160. return halfBorderSeparationBPD;
  161. }
  162. /**
  163. * @return half the value of border-separation.inline-progression-dimension, or 0 if
  164. * border-collapse="collapse".
  165. */
  166. public int getHalfBorderSeparationIPD() {
  167. return halfBorderSeparationIPD;
  168. }
  169. /** {@inheritDoc} */
  170. public List getNextKnuthElements(LayoutContext context, int alignment) {
  171. List returnList = new LinkedList();
  172. /*
  173. * Compute the IPD and adjust it if necessary (overconstrained)
  174. */
  175. referenceIPD = context.getRefIPD();
  176. if (getTable().getInlineProgressionDimension().getOptimum(this).getEnum() != EN_AUTO) {
  177. int contentIPD = getTable().getInlineProgressionDimension().getOptimum(this)
  178. .getLength().getValue(this);
  179. updateContentAreaIPDwithOverconstrainedAdjust(contentIPD);
  180. } else {
  181. if (!getTable().isAutoLayout()) {
  182. BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
  183. getTable().getUserAgent().getEventBroadcaster());
  184. eventProducer.tableFixedAutoWidthNotSupported(this, getTable().getLocator());
  185. }
  186. updateContentAreaIPDwithOverconstrainedAdjust();
  187. }
  188. int sumOfColumns = columns.getSumOfColumnWidths(this);
  189. if (!autoLayout && sumOfColumns > getContentAreaIPD()) {
  190. log.debug(FONode.decorateWithContextInfo(
  191. "The sum of all column widths is larger than the specified table width.",
  192. getTable()));
  193. updateContentAreaIPDwithOverconstrainedAdjust(sumOfColumns);
  194. }
  195. int availableIPD = referenceIPD - getIPIndents();
  196. if (getContentAreaIPD() > availableIPD) {
  197. BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
  198. getTable().getUserAgent().getEventBroadcaster());
  199. eventProducer.objectTooWide(this, getTable().getName(),
  200. getContentAreaIPD(), context.getRefIPD(),
  201. getTable().getLocator());
  202. }
  203. /* initialize unit to determine computed values
  204. * for proportional-column-width()
  205. */
  206. if (tableUnit == 0.0) {
  207. this.tableUnit = columns.computeTableUnit(this);
  208. }
  209. if (!firstVisibleMarkServed) {
  210. addKnuthElementsForSpaceBefore(returnList, alignment);
  211. }
  212. if (getTable().isSeparateBorderModel()) {
  213. addKnuthElementsForBorderPaddingBefore(returnList, !firstVisibleMarkServed);
  214. firstVisibleMarkServed = true;
  215. // Border and padding to be repeated at each break
  216. // This must be done only in the separate-border model, as in collapsing
  217. // tables have no padding and borders are determined at the cell level
  218. addPendingMarks(context);
  219. }
  220. // Elements for the table-header/footer/body
  221. List contentKnuthElements;
  222. contentLM = new TableContentLayoutManager(this);
  223. LayoutContext childLC = LayoutContext.newInstance();
  224. /*
  225. childLC.setStackLimit(
  226. MinOptMax.subtract(context.getStackLimit(),
  227. stackSize));*/
  228. childLC.setRefIPD(context.getRefIPD());
  229. childLC.copyPendingMarksFrom(context);
  230. contentKnuthElements = contentLM.getNextKnuthElements(childLC, alignment);
  231. //Set index values on elements coming from the content LM
  232. Iterator iter = contentKnuthElements.iterator();
  233. while (iter.hasNext()) {
  234. ListElement el = (ListElement)iter.next();
  235. notifyPos(el.getPosition());
  236. }
  237. log.debug(contentKnuthElements);
  238. wrapPositionElements(contentKnuthElements, returnList);
  239. context.updateKeepWithPreviousPending(getKeepWithPrevious());
  240. context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
  241. context.updateKeepWithNextPending(getKeepWithNext());
  242. context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
  243. if (getTable().isSeparateBorderModel()) {
  244. addKnuthElementsForBorderPaddingAfter(returnList, true);
  245. }
  246. addKnuthElementsForSpaceAfter(returnList, alignment);
  247. if (!context.suppressBreakBefore()) {
  248. //addKnuthElementsForBreakBefore(returnList, context);
  249. int breakBefore = BreakUtil.compareBreakClasses(getTable().getBreakBefore(),
  250. childLC.getBreakBefore());
  251. if (breakBefore != Constants.EN_AUTO) {
  252. returnList.add(0, new BreakElement(getAuxiliaryPosition(), 0,
  253. -KnuthElement.INFINITE, breakBefore, context));
  254. }
  255. }
  256. //addKnuthElementsForBreakAfter(returnList, context);
  257. int breakAfter = BreakUtil.compareBreakClasses(getTable().getBreakAfter(),
  258. childLC.getBreakAfter());
  259. if (breakAfter != Constants.EN_AUTO) {
  260. returnList.add(new BreakElement(getAuxiliaryPosition(),
  261. 0, -KnuthElement.INFINITE, breakAfter, context));
  262. }
  263. setFinished(true);
  264. resetSpaces();
  265. return returnList;
  266. }
  267. /** {@inheritDoc} */
  268. public Position getAuxiliaryPosition() {
  269. /*
  270. * Redefined to return a LeafPosition instead of a NonLeafPosition. The
  271. * SpaceResolver.SpaceHandlingBreakPosition constructors unwraps all
  272. * NonLeafPositions, which can lead to a NPE when a break in a table occurs at a
  273. * page with different ipd.
  274. */
  275. if (auxiliaryPosition == null) {
  276. auxiliaryPosition = new LeafPosition(this, 0);
  277. }
  278. return auxiliaryPosition;
  279. }
  280. /**
  281. * Registers the given area, that will be used to render the part of column background
  282. * covered by a table-cell. If percentages are used to place the background image, the
  283. * final bpd of the (fraction of) table that will be rendered on the current page must
  284. * be known. The traits can't then be set when the areas for the cell are created
  285. * since at that moment this bpd is yet unknown. So they will instead be set in
  286. * TableLM's {@link #addAreas(PositionIterator, LayoutContext)} method.
  287. *
  288. * @param column the table-column element from which the cell gets background
  289. * informations
  290. * @param backgroundArea the block of the cell's dimensions that will hold the column
  291. * background
  292. * @param xShift additional amount by which the image must be shifted to be correctly
  293. * placed (to counterbalance the cell's start border)
  294. */
  295. void registerColumnBackgroundArea(TableColumn column, Block backgroundArea, int xShift) {
  296. addBackgroundArea(backgroundArea);
  297. if (columnBackgroundAreas == null) {
  298. columnBackgroundAreas = new ArrayList();
  299. }
  300. columnBackgroundAreas.add(new ColumnBackgroundInfo(column, backgroundArea, xShift));
  301. }
  302. /**
  303. * The table area is a reference area that contains areas for
  304. * columns, bodies, rows and the contents are in cells.
  305. *
  306. * @param parentIter the position iterator
  307. * @param layoutContext the layout context for adding areas
  308. */
  309. public void addAreas(PositionIterator parentIter,
  310. LayoutContext layoutContext) {
  311. getParentArea(null);
  312. addId();
  313. // add space before, in order to implement display-align = "center" or "after"
  314. if (layoutContext.getSpaceBefore() != 0) {
  315. addBlockSpacing(0.0, MinOptMax.getInstance(layoutContext.getSpaceBefore()));
  316. }
  317. int startXOffset = getTable().getCommonMarginBlock().startIndent.getValue(this);
  318. // add column, body then row areas
  319. // BPD of the table, i.e., height of its content; table's borders and paddings not counted
  320. int tableHeight = 0;
  321. //Body childLM;
  322. LayoutContext lc = LayoutContext.offspringOf(layoutContext);
  323. lc.setRefIPD(getContentAreaIPD());
  324. contentLM.setStartXOffset(startXOffset);
  325. contentLM.addAreas(parentIter, lc);
  326. tableHeight += contentLM.getUsedBPD();
  327. curBlockArea.setBPD(tableHeight);
  328. if (columnBackgroundAreas != null) {
  329. for (Iterator iter = columnBackgroundAreas.iterator(); iter.hasNext();) {
  330. ColumnBackgroundInfo b = (ColumnBackgroundInfo) iter.next();
  331. TraitSetter.addBackground(b.backgroundArea,
  332. b.column.getCommonBorderPaddingBackground(), this,
  333. b.xShift, -b.backgroundArea.getYOffset(),
  334. b.column.getColumnWidth().getValue(this), tableHeight);
  335. }
  336. columnBackgroundAreas.clear();
  337. }
  338. if (getTable().isSeparateBorderModel()) {
  339. TraitSetter.addBorders(curBlockArea,
  340. getTable().getCommonBorderPaddingBackground(),
  341. discardBorderBefore, discardBorderAfter, false, false, this);
  342. TraitSetter.addPadding(curBlockArea,
  343. getTable().getCommonBorderPaddingBackground(),
  344. discardPaddingBefore, discardPaddingAfter, false, false, this);
  345. }
  346. TraitSetter.addBackground(curBlockArea,
  347. getTable().getCommonBorderPaddingBackground(),
  348. this);
  349. TraitSetter.addMargins(curBlockArea,
  350. getTable().getCommonBorderPaddingBackground(),
  351. startIndent, endIndent,
  352. this);
  353. TraitSetter.addBreaks(curBlockArea,
  354. getTable().getBreakBefore(), getTable().getBreakAfter());
  355. TraitSetter.addSpaceBeforeAfter(curBlockArea, layoutContext.getSpaceAdjust(),
  356. effSpaceBefore, effSpaceAfter);
  357. flush();
  358. resetSpaces();
  359. curBlockArea = null;
  360. notifyEndOfLayout();
  361. }
  362. /**
  363. * Return an Area which can contain the passed childArea. The childArea
  364. * may not yet have any content, but it has essential traits set.
  365. * In general, if the LayoutManager already has an Area it simply returns
  366. * it. Otherwise, it makes a new Area of the appropriate class.
  367. * It gets a parent area for its area by calling its parent LM.
  368. * Finally, based on the dimensions of the parent area, it initializes
  369. * its own area. This includes setting the content IPD and the maximum
  370. * BPD.
  371. *
  372. * @param childArea the child area
  373. * @return the parent area of the child
  374. */
  375. public Area getParentArea(Area childArea) {
  376. if (curBlockArea == null) {
  377. curBlockArea = new Block();
  378. // Set up dimensions
  379. // Must get dimensions from parent area
  380. /*Area parentArea =*/ parentLayoutManager.getParentArea(curBlockArea);
  381. TraitSetter.setProducerID(curBlockArea, getTable().getId());
  382. curBlockArea.setIPD(getContentAreaIPD());
  383. setCurrentArea(curBlockArea);
  384. }
  385. return curBlockArea;
  386. }
  387. /**
  388. * Add the child area to this layout manager.
  389. *
  390. * @param childArea the child area to add
  391. */
  392. public void addChildArea(Area childArea) {
  393. if (curBlockArea != null) {
  394. curBlockArea.addBlock((Block) childArea);
  395. }
  396. }
  397. /**
  398. * Adds the given area to this layout manager's area, without updating the used bpd.
  399. *
  400. * @param background an area
  401. */
  402. void addBackgroundArea(Block background) {
  403. curBlockArea.addChildArea(background);
  404. }
  405. /** {@inheritDoc} */
  406. public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
  407. // TODO Auto-generated method stub
  408. return 0;
  409. }
  410. /** {@inheritDoc} */
  411. public void discardSpace(KnuthGlue spaceGlue) {
  412. // TODO Auto-generated method stub
  413. }
  414. /** {@inheritDoc} */
  415. public KeepProperty getKeepTogetherProperty() {
  416. return getTable().getKeepTogether();
  417. }
  418. /** {@inheritDoc} */
  419. public KeepProperty getKeepWithPreviousProperty() {
  420. return getTable().getKeepWithPrevious();
  421. }
  422. /** {@inheritDoc} */
  423. public KeepProperty getKeepWithNextProperty() {
  424. return getTable().getKeepWithNext();
  425. }
  426. // --------- Property Resolution related functions --------- //
  427. /**
  428. * {@inheritDoc}
  429. */
  430. public int getBaseLength(int lengthBase, FObj fobj) {
  431. // Special handler for TableColumn width specifications
  432. if (fobj instanceof TableColumn && fobj.getParent() == getFObj()) {
  433. switch (lengthBase) {
  434. case LengthBase.CONTAINING_BLOCK_WIDTH:
  435. return getContentAreaIPD();
  436. case LengthBase.TABLE_UNITS:
  437. return (int) this.tableUnit;
  438. default:
  439. log.error("Unknown base type for LengthBase.");
  440. return 0;
  441. }
  442. } else {
  443. switch (lengthBase) {
  444. case LengthBase.TABLE_UNITS:
  445. return (int) this.tableUnit;
  446. default:
  447. return super.getBaseLength(lengthBase, fobj);
  448. }
  449. }
  450. }
  451. /** {@inheritDoc} */
  452. public void notifySpace(RelSide side, MinOptMax effectiveLength) {
  453. if (RelSide.BEFORE == side) {
  454. if (log.isDebugEnabled()) {
  455. log.debug(this + ": Space " + side + ", "
  456. + this.effSpaceBefore + "-> " + effectiveLength);
  457. }
  458. this.effSpaceBefore = effectiveLength;
  459. } else {
  460. if (log.isDebugEnabled()) {
  461. log.debug(this + ": Space " + side + ", "
  462. + this.effSpaceAfter + "-> " + effectiveLength);
  463. }
  464. this.effSpaceAfter = effectiveLength;
  465. }
  466. }
  467. /** {@inheritDoc} */
  468. public void notifyBorder(RelSide side, MinOptMax effectiveLength) {
  469. if (effectiveLength == null) {
  470. if (RelSide.BEFORE == side) {
  471. this.discardBorderBefore = true;
  472. } else {
  473. this.discardBorderAfter = true;
  474. }
  475. }
  476. if (log.isDebugEnabled()) {
  477. log.debug(this + ": Border " + side + " -> " + effectiveLength);
  478. }
  479. }
  480. /** {@inheritDoc} */
  481. public void notifyPadding(RelSide side, MinOptMax effectiveLength) {
  482. if (effectiveLength == null) {
  483. if (RelSide.BEFORE == side) {
  484. this.discardPaddingBefore = true;
  485. } else {
  486. this.discardPaddingAfter = true;
  487. }
  488. }
  489. if (log.isDebugEnabled()) {
  490. log.debug(this + ": Padding " + side + " -> " + effectiveLength);
  491. }
  492. }
  493. /** {@inheritDoc} */
  494. public void reset() {
  495. super.reset();
  496. curBlockArea = null;
  497. tableUnit = 0.0;
  498. }
  499. /**
  500. * Saves a TableCellLayoutManager for later use.
  501. *
  502. * @param tclm a TableCellLayoutManager that has a RetrieveTableMarker
  503. */
  504. protected void saveTableHeaderTableCellLayoutManagers(TableCellLayoutManager tclm) {
  505. if (savedTCLMs == null) {
  506. savedTCLMs = new ArrayList<TableCellLayoutManager>();
  507. }
  508. if (!areAllTCLMsSaved) {
  509. savedTCLMs.add(tclm);
  510. }
  511. }
  512. /**
  513. * Calls addAreas() for each of the saved TableCellLayoutManagers.
  514. */
  515. protected void repeatAddAreasForSavedTableHeaderTableCellLayoutManagers() {
  516. if (savedTCLMs == null) {
  517. return;
  518. }
  519. // if we get to this stage then we are at the footer of the table fragment; this means that no more
  520. // different TCLM need to be saved (we already have all); we flag the list as being complete then
  521. areAllTCLMsSaved = true;
  522. for (int i = 0; i < savedTCLMs.size(); i++) {
  523. TableCellLayoutManager tclm = savedTCLMs.get(i);
  524. tclm.repeatAddAreas();
  525. }
  526. }
  527. /**
  528. * Resolves a RetrieveTableMarker by finding a qualifying Marker to which it is bound to.
  529. * @param rtm the RetrieveTableMarker to be resolved
  530. * @return a bound RetrieveTableMarker instance or null if no qualifying Marker found
  531. */
  532. public RetrieveTableMarker resolveRetrieveTableMarker(RetrieveTableMarker rtm) {
  533. String name = rtm.getRetrieveClassName();
  534. int originalPosition = rtm.getPosition();
  535. boolean changedPosition = false;
  536. Marker mark = null;
  537. // try the primary retrieve scope area, which is the same as table-fragment
  538. mark = (tableFragmentMarkers == null) ? null : tableFragmentMarkers.resolve(rtm);
  539. if (mark == null && rtm.getBoundary() != Constants.EN_TABLE_FRAGMENT) {
  540. rtm.changePositionTo(Constants.EN_LAST_ENDING);
  541. changedPosition = true;
  542. // try the page scope area
  543. mark = getCurrentPV().resolveMarker(rtm);
  544. if (mark == null && rtm.getBoundary() != Constants.EN_PAGE) {
  545. // try the table scope area
  546. mark = (tableMarkers == null) ? null : tableMarkers.resolve(rtm);
  547. }
  548. }
  549. if (changedPosition) {
  550. // so that the next time it is called looks unchanged
  551. rtm.changePositionTo(originalPosition);
  552. }
  553. if (mark == null) {
  554. log.debug("found no marker with name: " + name);
  555. return null;
  556. } else {
  557. rtm.bindMarker(mark);
  558. return rtm;
  559. }
  560. }
  561. /**
  562. * Register the markers for this table.
  563. *
  564. * @param marks the map of markers to add
  565. * @param starting if the area being added is starting or ending
  566. * @param isfirst if the area being added has is-first trait
  567. * @param islast if the area being added has is-last trait
  568. */
  569. public void registerMarkers(Map<String, Marker> marks, boolean starting, boolean isfirst,
  570. boolean islast) {
  571. if (tableMarkers == null) {
  572. tableMarkers = new Markers();
  573. }
  574. tableMarkers.register(marks, starting, isfirst, islast);
  575. if (tableFragmentMarkers == null) {
  576. tableFragmentMarkers = new Markers();
  577. }
  578. tableFragmentMarkers.register(marks, starting, isfirst, islast);
  579. }
  580. /**
  581. * Clears the list of markers in the current table fragment. Should be called just before starting a new
  582. * header (that belongs to the next table fragment).
  583. */
  584. protected void clearTableFragmentMarkers() {
  585. tableFragmentMarkers = null;
  586. }
  587. public void flagAsHavingRetrieveTableMarker() {
  588. hasRetrieveTableMarker = true;
  589. }
  590. protected void possiblyRegisterMarkersForTables(Map<String, Marker> markers, boolean isStarting,
  591. boolean isFirst, boolean isLast) {
  592. // note: if we allow table-footer after a table-body this check should not be made and the markers
  593. // should be registered regardless because the retrieval may be done only in the footer
  594. if (hasRetrieveTableMarker) {
  595. registerMarkers(markers, isStarting, isFirst, isLast);
  596. }
  597. super.possiblyRegisterMarkersForTables(markers, isStarting, isFirst, isLast);
  598. }
  599. }