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

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