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.

Table.java 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  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.flow.table;
  19. import java.util.ArrayList;
  20. import java.util.List;
  21. import org.xml.sax.Locator;
  22. import org.apache.fop.apps.FOPException;
  23. import org.apache.fop.datatypes.Length;
  24. import org.apache.fop.datatypes.ValidationPercentBaseContext;
  25. import org.apache.fop.fo.FONode;
  26. import org.apache.fop.fo.PropertyList;
  27. import org.apache.fop.fo.StaticPropertyList;
  28. import org.apache.fop.fo.ValidationException;
  29. import org.apache.fop.fo.properties.BreakPropertySet;
  30. import org.apache.fop.fo.properties.CommonAccessibility;
  31. import org.apache.fop.fo.properties.CommonAccessibilityHolder;
  32. import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
  33. import org.apache.fop.fo.properties.CommonMarginBlock;
  34. import org.apache.fop.fo.properties.KeepProperty;
  35. import org.apache.fop.fo.properties.LengthPairProperty;
  36. import org.apache.fop.fo.properties.LengthRangeProperty;
  37. import org.apache.fop.fo.properties.TableColLength;
  38. import org.apache.fop.traits.Direction;
  39. import org.apache.fop.traits.WritingMode;
  40. import org.apache.fop.traits.WritingModeTraits;
  41. import org.apache.fop.traits.WritingModeTraitsGetter;
  42. /**
  43. * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_table">
  44. * <code>fo:table</code></a> object.
  45. */
  46. public class Table extends TableFObj implements ColumnNumberManagerHolder, BreakPropertySet, WritingModeTraitsGetter,
  47. CommonAccessibilityHolder {
  48. // The value of FO traits (refined properties) that apply to fo:table.
  49. private CommonAccessibility commonAccessibility;
  50. private CommonBorderPaddingBackground commonBorderPaddingBackground;
  51. private CommonMarginBlock commonMarginBlock;
  52. private LengthRangeProperty blockProgressionDimension;
  53. private int borderCollapse;
  54. private LengthPairProperty borderSeparation;
  55. private int breakAfter;
  56. private int breakBefore;
  57. private LengthRangeProperty inlineProgressionDimension;
  58. private KeepProperty keepTogether;
  59. private KeepProperty keepWithNext;
  60. private KeepProperty keepWithPrevious;
  61. private int tableLayout;
  62. private int tableOmitFooterAtBreak;
  63. private int tableOmitHeaderAtBreak;
  64. private WritingModeTraits writingModeTraits;
  65. // Unused but valid items, commented out for performance:
  66. // private CommonAural commonAural;
  67. // private CommonRelativePosition commonRelativePosition;
  68. // private int intrusionDisplace;
  69. // End of FO trait values
  70. /** extension properties */
  71. private Length widowContentLimit;
  72. private Length orphanContentLimit;
  73. /** collection of columns in this table */
  74. private List columns = new ArrayList();
  75. private ColumnNumberManager columnNumberManager = new ColumnNumberManager();
  76. /** the table-header and -footer */
  77. private TableHeader tableHeader = null;
  78. private TableFooter tableFooter = null;
  79. /** used for validation */
  80. private boolean tableColumnFound = false;
  81. private boolean tableHeaderFound = false;
  82. private boolean tableFooterFound = false;
  83. private boolean tableBodyFound = false;
  84. private boolean hasExplicitColumns = false;
  85. private boolean columnsFinalized = false;
  86. private RowGroupBuilder rowGroupBuilder;
  87. /**
  88. * The table's property list. Used in case the table has
  89. * no explicit columns, as a parent property list to
  90. * internally generated TableColumns
  91. */
  92. private PropertyList propList;
  93. /**
  94. * Construct a Table instance with the given {@link FONode}
  95. * as parent.
  96. *
  97. * @param parent {@link FONode} that is the parent of this object
  98. */
  99. public Table(FONode parent) {
  100. super(parent);
  101. }
  102. /**
  103. * {@inheritDoc}
  104. */
  105. public void bind(PropertyList pList) throws FOPException {
  106. super.bind(pList);
  107. commonAccessibility = CommonAccessibility.getInstance(pList);
  108. commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps();
  109. commonMarginBlock = pList.getMarginBlockProps();
  110. blockProgressionDimension = pList.get(PR_BLOCK_PROGRESSION_DIMENSION).getLengthRange();
  111. borderCollapse = pList.get(PR_BORDER_COLLAPSE).getEnum();
  112. borderSeparation = pList.get(PR_BORDER_SEPARATION).getLengthPair();
  113. breakAfter = pList.get(PR_BREAK_AFTER).getEnum();
  114. breakBefore = pList.get(PR_BREAK_BEFORE).getEnum();
  115. inlineProgressionDimension = pList.get(PR_INLINE_PROGRESSION_DIMENSION).getLengthRange();
  116. keepTogether = pList.get(PR_KEEP_TOGETHER).getKeep();
  117. keepWithNext = pList.get(PR_KEEP_WITH_NEXT).getKeep();
  118. keepWithPrevious = pList.get(PR_KEEP_WITH_PREVIOUS).getKeep();
  119. tableLayout = pList.get(PR_TABLE_LAYOUT).getEnum();
  120. tableOmitFooterAtBreak = pList.get(PR_TABLE_OMIT_FOOTER_AT_BREAK).getEnum();
  121. tableOmitHeaderAtBreak = pList.get(PR_TABLE_OMIT_HEADER_AT_BREAK).getEnum();
  122. writingModeTraits = new WritingModeTraits
  123. ( WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum()) );
  124. //Bind extension properties
  125. widowContentLimit = pList.get(PR_X_WIDOW_CONTENT_LIMIT).getLength();
  126. orphanContentLimit = pList.get(PR_X_ORPHAN_CONTENT_LIMIT).getLength();
  127. if (!blockProgressionDimension.getOptimum(null).isAuto()) {
  128. TableEventProducer eventProducer = TableEventProducer.Provider.get(
  129. getUserAgent().getEventBroadcaster());
  130. eventProducer.nonAutoBPDOnTable(this, getLocator());
  131. // Anyway, the bpd of a table is not used by the layout code
  132. }
  133. if (tableLayout == EN_AUTO) {
  134. getFOValidationEventProducer().unimplementedFeature(this, getName(),
  135. "table-layout=\"auto\"", getLocator());
  136. }
  137. if (!isSeparateBorderModel()) {
  138. if (borderCollapse == EN_COLLAPSE_WITH_PRECEDENCE) {
  139. getFOValidationEventProducer().unimplementedFeature(this, getName(),
  140. "border-collapse=\"collapse-with-precedence\"; defaulting to \"collapse\"", getLocator());
  141. borderCollapse = EN_COLLAPSE;
  142. }
  143. if (getCommonBorderPaddingBackground().hasPadding(
  144. ValidationPercentBaseContext.getPseudoContext())) {
  145. //See "17.6.2 The collapsing border model" in CSS2
  146. TableEventProducer eventProducer = TableEventProducer.Provider.get(
  147. getUserAgent().getEventBroadcaster());
  148. eventProducer.noTablePaddingWithCollapsingBorderModel(this, getLocator());
  149. }
  150. }
  151. /* Store reference to the property list, so
  152. * new lists can be created in case the table has no
  153. * explicit columns
  154. * (see addDefaultColumn())
  155. */
  156. this.propList = pList;
  157. }
  158. /** {@inheritDoc} */
  159. protected void startOfNode() throws FOPException {
  160. super.startOfNode();
  161. getFOEventHandler().startTable(this);
  162. }
  163. /**
  164. * {@inheritDoc}
  165. * <br>XSL Content Model: (marker*,table-column*,table-header?,table-footer?,table-body+)
  166. */
  167. protected void validateChildNode(Locator loc, String nsURI, String localName)
  168. throws ValidationException {
  169. if (FO_URI.equals(nsURI)) {
  170. if ("marker".equals(localName)) {
  171. if (tableColumnFound || tableHeaderFound || tableFooterFound
  172. || tableBodyFound) {
  173. nodesOutOfOrderError(loc, "fo:marker",
  174. "(table-column*,table-header?,table-footer?,table-body+)");
  175. }
  176. } else if ("table-column".equals(localName)) {
  177. tableColumnFound = true;
  178. if (tableHeaderFound || tableFooterFound || tableBodyFound) {
  179. nodesOutOfOrderError(loc, "fo:table-column",
  180. "(table-header?,table-footer?,table-body+)");
  181. }
  182. } else if ("table-header".equals(localName)) {
  183. if (tableHeaderFound) {
  184. tooManyNodesError(loc, "table-header");
  185. } else {
  186. tableHeaderFound = true;
  187. if (tableFooterFound || tableBodyFound) {
  188. nodesOutOfOrderError(loc, "fo:table-header",
  189. "(table-footer?,table-body+)");
  190. }
  191. }
  192. } else if ("table-footer".equals(localName)) {
  193. if (tableFooterFound) {
  194. tooManyNodesError(loc, "table-footer");
  195. } else {
  196. tableFooterFound = true;
  197. if (tableBodyFound) {
  198. if (getUserAgent().validateStrictly()) {
  199. nodesOutOfOrderError(loc, "fo:table-footer", "(table-body+)", true);
  200. }
  201. if (!isSeparateBorderModel()) {
  202. TableEventProducer eventProducer = TableEventProducer.Provider.get(
  203. getUserAgent().getEventBroadcaster());
  204. eventProducer.footerOrderCannotRecover(this, getName(), getLocator());
  205. }
  206. }
  207. }
  208. } else if ("table-body".equals(localName)) {
  209. tableBodyFound = true;
  210. } else {
  211. invalidChildError(loc, nsURI, localName);
  212. }
  213. }
  214. }
  215. /** {@inheritDoc} */
  216. protected void endOfNode() throws FOPException {
  217. super.endOfNode();
  218. getFOEventHandler().endTable(this);
  219. }
  220. /** {@inheritDoc} */
  221. public void finalizeNode() throws FOPException {
  222. if (!tableBodyFound) {
  223. missingChildElementError(
  224. "(marker*,table-column*,table-header?,table-footer?"
  225. + ",table-body+)");
  226. }
  227. if (!hasChildren()) {
  228. getParent().removeChild(this);
  229. return;
  230. }
  231. if (!inMarker()) {
  232. rowGroupBuilder.endTable();
  233. /* clean up */
  234. for (int i = columns.size(); --i >= 0;) {
  235. TableColumn col = (TableColumn) columns.get(i);
  236. if (col != null) {
  237. col.releasePropertyList();
  238. }
  239. }
  240. this.propList = null;
  241. rowGroupBuilder = null;
  242. }
  243. }
  244. /** {@inheritDoc} */
  245. protected void addChildNode(FONode child) throws FOPException {
  246. int childId = child.getNameId();
  247. switch (childId) {
  248. case FO_TABLE_COLUMN:
  249. hasExplicitColumns = true;
  250. if (!inMarker()) {
  251. addColumnNode((TableColumn) child);
  252. } else {
  253. columns.add(child);
  254. }
  255. break;
  256. case FO_TABLE_HEADER:
  257. case FO_TABLE_FOOTER:
  258. case FO_TABLE_BODY:
  259. if (!inMarker() && !columnsFinalized) {
  260. columnsFinalized = true;
  261. if (hasExplicitColumns) {
  262. finalizeColumns();
  263. rowGroupBuilder = new FixedColRowGroupBuilder(this);
  264. } else {
  265. rowGroupBuilder = new VariableColRowGroupBuilder(this);
  266. }
  267. }
  268. switch (childId) {
  269. case FO_TABLE_FOOTER:
  270. tableFooter = (TableFooter) child;
  271. break;
  272. case FO_TABLE_HEADER:
  273. tableHeader = (TableHeader) child;
  274. break;
  275. default:
  276. super.addChildNode(child);
  277. }
  278. break;
  279. default:
  280. super.addChildNode(child);
  281. }
  282. }
  283. private void finalizeColumns() throws FOPException {
  284. for (int i = 0; i < columns.size(); i++) {
  285. if (columns.get(i) == null) {
  286. columns.set(i, createImplicitColumn(i + 1));
  287. }
  288. }
  289. }
  290. /** {@inheritDoc} */
  291. public CommonAccessibility getCommonAccessibility() {
  292. return commonAccessibility;
  293. }
  294. /** {@inheritDoc} */
  295. public Table getTable() {
  296. return this;
  297. }
  298. /**
  299. * Creates the appropriate number of additional implicit columns to match the given
  300. * column number. Used when the table has no explicit column: the number of columns is
  301. * then determined by the row that has the most columns.
  302. *
  303. * @param columnNumber the table must at least have this number of column
  304. * @throws FOPException if there was an error creating the property list for implicit
  305. * columns
  306. */
  307. void ensureColumnNumber(int columnNumber) throws FOPException {
  308. assert !hasExplicitColumns;
  309. for (int i = columns.size() + 1; i <= columnNumber; i++) {
  310. columns.add(createImplicitColumn(i));
  311. }
  312. }
  313. private TableColumn createImplicitColumn(int colNumber)
  314. throws FOPException {
  315. TableColumn implicitColumn = new TableColumn(this, true);
  316. PropertyList pList = new StaticPropertyList(
  317. implicitColumn, this.propList);
  318. implicitColumn.bind(pList);
  319. implicitColumn.setColumnWidth(new TableColLength(1.0, implicitColumn));
  320. implicitColumn.setColumnNumber(colNumber);
  321. if (!isSeparateBorderModel()) {
  322. implicitColumn.setCollapsedBorders(collapsingBorderModel); // TODO
  323. }
  324. return implicitColumn;
  325. }
  326. /**
  327. * Adds a column to the columns List, and updates the columnIndex
  328. * used for determining initial values for column-number
  329. *
  330. * @param col the column to add
  331. */
  332. private void addColumnNode(TableColumn col) {
  333. int colNumber = col.getColumnNumber();
  334. int colRepeat = col.getNumberColumnsRepeated();
  335. /* add nulls for non-occupied indices between
  336. * the last column up to and including the current one
  337. */
  338. while (columns.size() < colNumber + colRepeat - 1) {
  339. columns.add(null);
  340. }
  341. // in case column is repeated:
  342. // for the time being, add the same column
  343. // (colRepeat - 1) times to the columns list
  344. // TODO: need to force the column-number (?)
  345. for (int i = colNumber - 1; i < colNumber + colRepeat - 1; i++) {
  346. columns.set(i, col);
  347. }
  348. columnNumberManager.signalUsedColumnNumbers(colNumber, colNumber + colRepeat - 1);
  349. }
  350. boolean hasExplicitColumns() {
  351. return hasExplicitColumns;
  352. }
  353. /** @return true of table-layout="auto" */
  354. public boolean isAutoLayout() {
  355. return (tableLayout == EN_AUTO);
  356. }
  357. /**
  358. * Returns the list of table-column elements.
  359. *
  360. * @return a list of {@link TableColumn} elements, may contain null elements
  361. */
  362. public List getColumns() {
  363. return columns;
  364. }
  365. /**
  366. * Returns the column at the given index.
  367. *
  368. * @param index index of the column to be retrieved, 0-based
  369. * @return the corresponding column (may be an implicitly created column)
  370. */
  371. public TableColumn getColumn(int index) {
  372. return (TableColumn) columns.get(index);
  373. }
  374. /**
  375. * Returns the number of columns of this table.
  376. *
  377. * @return the number of columns, implicit or explicit, in this table
  378. */
  379. public int getNumberOfColumns() {
  380. return columns.size();
  381. }
  382. /** @return the body for the table-header. */
  383. public TableHeader getTableHeader() {
  384. return tableHeader;
  385. }
  386. /** @return the body for the table-footer. */
  387. public TableFooter getTableFooter() {
  388. return tableFooter;
  389. }
  390. /** @return true if the table-header should be omitted at breaks */
  391. public boolean omitHeaderAtBreak() {
  392. return (this.tableOmitHeaderAtBreak == EN_TRUE);
  393. }
  394. /** @return true if the table-footer should be omitted at breaks */
  395. public boolean omitFooterAtBreak() {
  396. return (this.tableOmitFooterAtBreak == EN_TRUE);
  397. }
  398. /**
  399. * @return the "inline-progression-dimension" FO trait.
  400. */
  401. public LengthRangeProperty getInlineProgressionDimension() {
  402. return inlineProgressionDimension;
  403. }
  404. /**
  405. * @return the "block-progression-dimension" FO trait.
  406. */
  407. public LengthRangeProperty getBlockProgressionDimension() {
  408. return blockProgressionDimension;
  409. }
  410. /**
  411. * @return the Common Margin Properties-Block.
  412. */
  413. public CommonMarginBlock getCommonMarginBlock() {
  414. return commonMarginBlock;
  415. }
  416. /**
  417. * @return the Common Border, Padding, and Background Properties.
  418. */
  419. public CommonBorderPaddingBackground getCommonBorderPaddingBackground() {
  420. return commonBorderPaddingBackground;
  421. }
  422. /** @return the "break-after" FO trait. */
  423. public int getBreakAfter() {
  424. return breakAfter;
  425. }
  426. /** @return the "break-before" FO trait. */
  427. public int getBreakBefore() {
  428. return breakBefore;
  429. }
  430. /** @return the "keep-with-next" FO trait. */
  431. public KeepProperty getKeepWithNext() {
  432. return keepWithNext;
  433. }
  434. /** @return the "keep-with-previous" FO trait. */
  435. public KeepProperty getKeepWithPrevious() {
  436. return keepWithPrevious;
  437. }
  438. /** @return the "keep-together" FO trait. */
  439. public KeepProperty getKeepTogether() {
  440. return keepTogether;
  441. }
  442. /**
  443. * Convenience method to check if a keep-together constraint is specified.
  444. * @return true if keep-together is active.
  445. */
  446. public boolean mustKeepTogether() {
  447. return !getKeepTogether().getWithinPage().isAuto()
  448. || !getKeepTogether().getWithinColumn().isAuto();
  449. }
  450. /** @return the "border-collapse" FO trait. */
  451. public int getBorderCollapse() {
  452. return borderCollapse;
  453. }
  454. /** @return true if the separate border model is active */
  455. public boolean isSeparateBorderModel() {
  456. return (getBorderCollapse() == EN_SEPARATE);
  457. }
  458. /** @return the "border-separation" FO trait. */
  459. public LengthPairProperty getBorderSeparation() {
  460. return borderSeparation;
  461. }
  462. /** {@inheritDoc} */
  463. public Direction getInlineProgressionDirection() {
  464. return writingModeTraits.getInlineProgressionDirection();
  465. }
  466. /** {@inheritDoc} */
  467. public Direction getBlockProgressionDirection() {
  468. return writingModeTraits.getBlockProgressionDirection();
  469. }
  470. /** {@inheritDoc} */
  471. public Direction getColumnProgressionDirection() {
  472. return writingModeTraits.getColumnProgressionDirection();
  473. }
  474. /** {@inheritDoc} */
  475. public Direction getRowProgressionDirection() {
  476. return writingModeTraits.getRowProgressionDirection();
  477. }
  478. /** {@inheritDoc} */
  479. public Direction getShiftDirection() {
  480. return writingModeTraits.getShiftDirection();
  481. }
  482. /** {@inheritDoc} */
  483. public WritingMode getWritingMode() {
  484. return writingModeTraits.getWritingMode();
  485. }
  486. /** @return the "fox:widow-content-limit" extension FO trait */
  487. public Length getWidowContentLimit() {
  488. return widowContentLimit;
  489. }
  490. /** @return the "fox:orphan-content-limit" extension FO trait */
  491. public Length getOrphanContentLimit() {
  492. return orphanContentLimit;
  493. }
  494. /** {@inheritDoc} */
  495. public String getLocalName() {
  496. return "table";
  497. }
  498. /**
  499. * {@inheritDoc}
  500. * @return {@link org.apache.fop.fo.Constants#FO_TABLE}
  501. */
  502. public int getNameId() {
  503. return FO_TABLE;
  504. }
  505. /** {@inheritDoc} */
  506. public FONode clone(FONode parent, boolean removeChildren)
  507. throws FOPException {
  508. Table clone = (Table) super.clone(parent, removeChildren);
  509. if (removeChildren) {
  510. clone.columns = new ArrayList();
  511. clone.columnsFinalized = false;
  512. clone.columnNumberManager = new ColumnNumberManager();
  513. clone.tableHeader = null;
  514. clone.tableFooter = null;
  515. clone.rowGroupBuilder = null;
  516. }
  517. return clone;
  518. }
  519. /** {@inheritDoc} */
  520. public ColumnNumberManager getColumnNumberManager() {
  521. return columnNumberManager;
  522. }
  523. RowGroupBuilder getRowGroupBuilder() {
  524. return rowGroupBuilder;
  525. }
  526. }