diff options
author | Vincent Hennebert <vhennebert@apache.org> | 2007-11-05 17:42:37 +0000 |
---|---|---|
committer | Vincent Hennebert <vhennebert@apache.org> | 2007-11-05 17:42:37 +0000 |
commit | 740a2f598cf99291aa781eee9e1d59957ae3099e (patch) | |
tree | bf4c38bb397cb1de0b9806f325a2a4a96590b070 /src/java/org/apache/fop/fo/flow/table/Table.java | |
parent | a09f99a58e99a5179d74e5b00f7d246a03790e81 (diff) | |
download | xmlgraphics-fop-740a2f598cf99291aa781eee9e1d59957ae3099e.tar.gz xmlgraphics-fop-740a2f598cf99291aa781eee9e1d59957ae3099e.zip |
Moved table-related FObj into new o.a.fop.fo.flow.table package
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@592103 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org/apache/fop/fo/flow/table/Table.java')
-rw-r--r-- | src/java/org/apache/fop/fo/flow/table/Table.java | 555 |
1 files changed, 555 insertions, 0 deletions
diff --git a/src/java/org/apache/fop/fo/flow/table/Table.java b/src/java/org/apache/fop/fo/flow/table/Table.java new file mode 100644 index 000000000..07fc95f7e --- /dev/null +++ b/src/java/org/apache/fop/fo/flow/table/Table.java @@ -0,0 +1,555 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.fo.flow.table; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.datatypes.Length; +import org.apache.fop.datatypes.ValidationPercentBaseContext; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.PropertyList; +import org.apache.fop.fo.StaticPropertyList; +import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; +import org.apache.fop.fo.properties.CommonMarginBlock; +import org.apache.fop.fo.properties.KeepProperty; +import org.apache.fop.fo.properties.LengthPairProperty; +import org.apache.fop.fo.properties.LengthRangeProperty; +import org.apache.fop.fo.properties.TableColLength; +import org.xml.sax.Locator; + +/** + * Class modelling the fo:table object. + */ +public class Table extends TableFObj implements ColumnNumberManagerHolder { + + /** properties */ + private CommonBorderPaddingBackground commonBorderPaddingBackground; + private CommonMarginBlock commonMarginBlock; + private LengthRangeProperty blockProgressionDimension; + private int borderCollapse; + private LengthPairProperty borderSeparation; + private int breakAfter; + private int breakBefore; + private LengthRangeProperty inlineProgressionDimension; + private KeepProperty keepTogether; + private KeepProperty keepWithNext; + private KeepProperty keepWithPrevious; + private int tableLayout; + private int tableOmitFooterAtBreak; + private int tableOmitHeaderAtBreak; + // Unused but valid items, commented out for performance: + // private CommonAccessibility commonAccessibility; + // private CommonAural commonAural; + // private CommonRelativePosition commonRelativePosition; + // private int intrusionDisplace; + // private int writingMode; + + /** extension properties */ + private Length widowContentLimit; + private Length orphanContentLimit; + + /** collection of columns in this table */ + private List columns = new ArrayList(); + + private ColumnNumberManager columnNumberManager = new ColumnNumberManager(); + + /** the table-header and -footer */ + private TableBody tableHeader = null; + private TableBody tableFooter = null; + + /** used for validation */ + private boolean tableColumnFound = false; + private boolean tableHeaderFound = false; + private boolean tableFooterFound = false; + private boolean tableBodyFound = false; + + private boolean hasExplicitColumns = false; + private boolean columnsFinalized = false; + private RowGroupBuilder rowGroupBuilder; + + /** + * The table's property list. Used in case the table has + * no explicit columns, as a parent property list to + * internally generated TableColumns + */ + private PropertyList propList; + + /** + * @param parent FONode that is the parent of this object + */ + public Table(FONode parent) { + super(parent); + } + + /** + * {@inheritDoc} + */ + public void bind(PropertyList pList) throws FOPException { + super.bind(pList); + commonBorderPaddingBackground = pList.getBorderPaddingBackgroundProps(); + commonMarginBlock = pList.getMarginBlockProps(); + blockProgressionDimension = pList.get(PR_BLOCK_PROGRESSION_DIMENSION).getLengthRange(); + borderCollapse = pList.get(PR_BORDER_COLLAPSE).getEnum(); + borderSeparation = pList.get(PR_BORDER_SEPARATION).getLengthPair(); + breakAfter = pList.get(PR_BREAK_AFTER).getEnum(); + breakBefore = pList.get(PR_BREAK_BEFORE).getEnum(); + inlineProgressionDimension = pList.get(PR_INLINE_PROGRESSION_DIMENSION).getLengthRange(); + keepTogether = pList.get(PR_KEEP_TOGETHER).getKeep(); + keepWithNext = pList.get(PR_KEEP_WITH_NEXT).getKeep(); + keepWithPrevious = pList.get(PR_KEEP_WITH_PREVIOUS).getKeep(); + tableLayout = pList.get(PR_TABLE_LAYOUT).getEnum(); + tableOmitFooterAtBreak = pList.get(PR_TABLE_OMIT_FOOTER_AT_BREAK).getEnum(); + tableOmitHeaderAtBreak = pList.get(PR_TABLE_OMIT_HEADER_AT_BREAK).getEnum(); + + //Bind extension properties + widowContentLimit = pList.get(PR_X_WIDOW_CONTENT_LIMIT).getLength(); + orphanContentLimit = pList.get(PR_X_ORPHAN_CONTENT_LIMIT).getLength(); + + if (!blockProgressionDimension.getOptimum(null).isAuto()) { + attributeWarning("only a value of \"auto\" for block-progression-dimension has a well-specified" + + " behavior on fo:table. Falling back to \"auto\""); + // Anyway, the bpd of a table is not used by the layout code + } + if (tableLayout == EN_AUTO) { + attributeWarning("table-layout=\"auto\" is currently not supported by FOP"); + } + if (!isSeparateBorderModel() + && getCommonBorderPaddingBackground().hasPadding( + ValidationPercentBaseContext.getPseudoContext())) { + //See "17.6.2 The collapsing border model" in CSS2 + attributeWarning("In collapsing border model a table does not have padding" + + " (see http://www.w3.org/TR/REC-CSS2/tables.html#collapsing-borders)" + + ", but a non-zero value for padding was found. The padding will be ignored."); + } + + /* Store reference to the property list, so + * new lists can be created in case the table has no + * explicit columns + * (see addDefaultColumn()) + */ + this.propList = pList; + } + + /** + * {@inheritDoc} + */ + protected void startOfNode() throws FOPException { + super.startOfNode(); + getFOEventHandler().startTable(this); + } + + /** + * {@inheritDoc} + * XSL Content Model: (marker*,table-column*,table-header?,table-footer?,table-body+) + */ + protected void validateChildNode(Locator loc, String nsURI, String localName) + throws ValidationException { + if (FO_URI.equals(nsURI)) { + if ("marker".equals(localName)) { + if (tableColumnFound || tableHeaderFound || tableFooterFound + || tableBodyFound) { + nodesOutOfOrderError(loc, "fo:marker", + "(table-column*,table-header?,table-footer?,table-body+)"); + } + } else if ("table-column".equals(localName)) { + tableColumnFound = true; + if (tableHeaderFound || tableFooterFound || tableBodyFound) { + nodesOutOfOrderError(loc, "fo:table-column", + "(table-header?,table-footer?,table-body+)"); + } + } else if ("table-header".equals(localName)) { + if (tableHeaderFound) { + tooManyNodesError(loc, "table-header"); + } else { + tableHeaderFound = true; + if (tableFooterFound || tableBodyFound) { + nodesOutOfOrderError(loc, "fo:table-header", + "(table-footer?,table-body+)"); + } + } + } else if ("table-footer".equals(localName)) { + if (tableFooterFound) { + tooManyNodesError(loc, "table-footer"); + } else { + tableFooterFound = true; + if (tableBodyFound && getUserAgent().validateStrictly()) { + nodesOutOfOrderError(loc, "fo:table-footer", + "(table-body+)"); + } + } + } else if ("table-body".equals(localName)) { + tableBodyFound = true; + } else { + invalidChildError(loc, nsURI, localName); + } + } else { + invalidChildError(loc, nsURI, localName); + } + } + + /** + * {@inheritDoc} + */ + protected void endOfNode() throws FOPException { + + if (!tableBodyFound) { + missingChildElementError( + "(marker*,table-column*,table-header?,table-footer?" + + ",table-body+)"); + } + if (!inMarker()) { + /* clean up */ + for (int i = columns.size(); --i >= 0;) { + TableColumn col = (TableColumn) columns.get(i); + if (col != null) { + col.releasePropertyList(); + } + } + this.propList = null; + rowGroupBuilder = null; + } + getFOEventHandler().endTable(this); + + } + + /** + * {@inheritDoc} + */ + protected void addChildNode(FONode child) throws FOPException { + + int childId = child.getNameId(); + + switch (childId) { + case FO_TABLE_COLUMN: + hasExplicitColumns = true; + if (!inMarker()) { + addColumnNode((TableColumn) child); + } else { + columns.add((TableColumn) child); + } + break; + case FO_TABLE_HEADER: + case FO_TABLE_FOOTER: + case FO_TABLE_BODY: + if (!columnsFinalized) { + columnsFinalized = true; + if (hasExplicitColumns) { + finalizeColumns(); + rowGroupBuilder = new FixedColRowGroupBuilder(this); + } else { + rowGroupBuilder = new VariableColRowGroupBuilder(this); + } + + } + switch (childId) { + case FO_TABLE_FOOTER: + tableFooter = (TableBody) child; + break; + case FO_TABLE_HEADER: + tableHeader = (TableBody) child; + break; + default: + super.addChildNode(child); + } + break; + default: + super.addChildNode(child); + } + } + + private void finalizeColumns() throws FOPException { + for (int i = 0; i < columns.size(); i++) { + if (columns.get(i) == null) { + columns.set(i, createImplicitColumn(i + 1)); + } + } + } + + /** {@inheritDoc} */ + public Table getTable() { + return this; + } + + /** + * Creates the appropriate number of additional implicit columns to match the given + * column number. Used when the table has no explicit column: the number of columns is + * then determined by the row that has the most columns. + * + * @param columnNumber the table must at least have this number of column + * @throws FOPException if there was an error creating the property list for implicit + * columns + */ + void ensureColumnNumber(int columnNumber) throws FOPException { + assert !hasExplicitColumns; + for (int i = columns.size() + 1; i <= columnNumber; i++) { + columns.add(createImplicitColumn(i)); + } + ((VariableColRowGroupBuilder) rowGroupBuilder).ensureNumberOfColumns(columnNumber); + if (tableHeader != null) { + for (Iterator iter = tableHeader.getRowGroups().iterator(); iter.hasNext();) { + VariableColRowGroupBuilder.fillWithEmptyGridUnits((List) iter.next(), + columnNumber); + } + } + if (tableFooter != null) { + for (Iterator iter = tableFooter.getRowGroups().iterator(); iter.hasNext();) { + VariableColRowGroupBuilder.fillWithEmptyGridUnits((List) iter.next(), + columnNumber); + } + } + FONodeIterator bodyIter = getChildNodes(); + if (bodyIter != null) { + while (bodyIter.hasNext()) { + FONode node = bodyIter.nextNode(); + if (node instanceof TableBody) { // AFAIK, may be a marker + for (Iterator iter = ((TableBody) node).getRowGroups().iterator(); + iter.hasNext();) { + VariableColRowGroupBuilder.fillWithEmptyGridUnits((List) iter.next(), + columnNumber); + } + } + } + } + } + + private TableColumn createImplicitColumn(int colNumber) + throws FOPException { + TableColumn implicitColumn = new TableColumn(this, true); + PropertyList pList = new StaticPropertyList( + implicitColumn, this.propList); + pList.setWritingMode(); + implicitColumn.bind(pList); + implicitColumn.setColumnWidth(new TableColLength(1.0, implicitColumn)); + implicitColumn.setColumnNumber(colNumber); + return implicitColumn; + } + + /** + * Adds a column to the columns List, and updates the columnIndex + * used for determining initial values for column-number + * + * @param col the column to add + * @throws FOPException + */ + private void addColumnNode(TableColumn col) { + + int colNumber = col.getColumnNumber(); + int colRepeat = col.getNumberColumnsRepeated(); + + /* add nulls for non-occupied indices between + * the last column up to and including the current one + */ + while (columns.size() < colNumber + colRepeat - 1) { + columns.add(null); + } + + // in case column is repeated: + // for the time being, add the same column + // (colRepeat - 1) times to the columns list + // TODO: need to force the column-number (?) + for (int i = colNumber - 1; i < colNumber + colRepeat - 1; i++) { + columns.set(i, col); + } + + columnNumberManager.signalUsedColumnNumbers(colNumber, colNumber + colRepeat - 1); + } + + boolean hasExplicitColumns() { + return hasExplicitColumns; + } + + /** @return true of table-layout="auto" */ + public boolean isAutoLayout() { + return (tableLayout == EN_AUTO); + } + + /** + * Returns the list of table-column elements. + * + * @return a list of {@link TableColumn} elements, may contain null elements + */ + public List getColumns() { + return columns; + } + + /** + * Returns the column at the given index. + * + * @param index index of the column to be retrieved, 0-based + * @return the corresponding column (may be an implicitly created column) + */ + TableColumn getColumn(int index) { + return (TableColumn) columns.get(index); + } + + /** + * Returns the number of columns of this table. + * + * @return the number of columns, implicit or explicit, in this table + */ + int getNumberOfColumns() { + return columns.size(); + } + + /** @return the body for the table-header. */ + public TableBody getTableHeader() { + return tableHeader; + } + + /** @return the body for the table-footer. */ + public TableBody getTableFooter() { + return tableFooter; + } + + /** @return true if the table-header should be omitted at breaks */ + public boolean omitHeaderAtBreak() { + return (this.tableOmitHeaderAtBreak == EN_TRUE); + } + + /** @return true if the table-footer should be omitted at breaks */ + public boolean omitFooterAtBreak() { + return (this.tableOmitFooterAtBreak == EN_TRUE); + } + + /** + * @return the "inline-progression-dimension" property. + */ + public LengthRangeProperty getInlineProgressionDimension() { + return inlineProgressionDimension; + } + + /** + * @return the "block-progression-dimension" property. + */ + public LengthRangeProperty getBlockProgressionDimension() { + return blockProgressionDimension; + } + + /** + * @return the Common Margin Properties-Block. + */ + public CommonMarginBlock getCommonMarginBlock() { + return commonMarginBlock; + } + + /** + * @return the Common Border, Padding, and Background Properties. + */ + public CommonBorderPaddingBackground getCommonBorderPaddingBackground() { + return commonBorderPaddingBackground; + } + + /** @return the "break-after" property. */ + public int getBreakAfter() { + return breakAfter; + } + + /** @return the "break-before" property. */ + public int getBreakBefore() { + return breakBefore; + } + + /** @return the "keep-with-next" property. */ + public KeepProperty getKeepWithNext() { + return keepWithNext; + } + + /** @return the "keep-with-previous" property. */ + public KeepProperty getKeepWithPrevious() { + return keepWithPrevious; + } + + /** @return the "keep-together" property. */ + public KeepProperty getKeepTogether() { + return keepTogether; + } + + /** + * Convenience method to check if a keep-together constraint is specified. + * @return true if keep-together is active. + */ + public boolean mustKeepTogether() { + return !getKeepTogether().getWithinPage().isAuto() + || !getKeepTogether().getWithinColumn().isAuto(); + } + + /** @return the "border-collapse" property. */ + public int getBorderCollapse() { + return borderCollapse; + } + + /** @return true if the separate border model is active */ + public boolean isSeparateBorderModel() { + return (getBorderCollapse() == EN_SEPARATE); + } + + /** @return the "border-separation" property. */ + public LengthPairProperty getBorderSeparation() { + return borderSeparation; + } + + /** @return the "fox:widow-content-limit" extension property */ + public Length getWidowContentLimit() { + return widowContentLimit; + } + + /** @return the "fox:orphan-content-limit" extension property */ + public Length getOrphanContentLimit() { + return orphanContentLimit; + } + + /** {@inheritDoc} */ + public String getLocalName() { + return "table"; + } + + /** {@inheritDoc} */ + public int getNameId() { + return FO_TABLE; + } + + /** + * {@inheritDoc} + */ + public FONode clone(FONode parent, boolean removeChildren) + throws FOPException { + Table clone = (Table) super.clone(parent, removeChildren); + clone.columnsFinalized = false; + if (removeChildren) { + clone.columns = new ArrayList(); + clone.tableHeader = null; + clone.tableFooter = null; + } + return clone; + } + + /** {@inheritDoc} */ + public ColumnNumberManager getColumnNumberManager() { + return columnNumberManager; + } + + RowGroupBuilder getRowGroupBuilder() { + return rowGroupBuilder; + } +} |