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

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