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

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