package org.apache.fop.fo.flow.table;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import org.xml.sax.Locator;
import org.apache.fop.apps.FOPException;
import org.apache.fop.complexscripts.bidi.DelimitedTextRange;
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.BreakPropertySet;
import org.apache.fop.fo.properties.CommonAccessibility;
import org.apache.fop.fo.properties.CommonAccessibilityHolder;
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.apache.fop.traits.Direction;
import org.apache.fop.traits.WritingMode;
import org.apache.fop.traits.WritingModeTraits;
import org.apache.fop.traits.WritingModeTraitsGetter;
public class Table extends TableFObj implements ColumnNumberManagerHolder, BreakPropertySet, WritingModeTraitsGetter,
CommonAccessibilityHolder {
private CommonAccessibility commonAccessibility;
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 ;
private int ;
private WritingModeTraits writingModeTraits;
private Length widowContentLimit;
private Length orphanContentLimit;
private List columns = new ArrayList();
private ColumnNumberManager columnNumberManager = new ColumnNumberManager();
private TableHeader ;
private TableFooter ;
private boolean tableColumnFound;
private boolean ;
private boolean ;
private boolean tableBodyFound;
private boolean hasExplicitColumns;
private boolean columnsFinalized;
private RowGroupBuilder rowGroupBuilder;
private PropertyList propList;
public Table(FONode parent) {
super(parent);
}
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
commonAccessibility = CommonAccessibility.getInstance(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();
writingModeTraits = new WritingModeTraits(
WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum()),
pList.getExplicit(PR_WRITING_MODE) != null);
widowContentLimit = pList.get(PR_X_WIDOW_CONTENT_LIMIT).getLength();
orphanContentLimit = pList.get(PR_X_ORPHAN_CONTENT_LIMIT).getLength();
if (!blockProgressionDimension.getOptimum(null).isAuto()) {
TableEventProducer eventProducer = TableEventProducer.Provider.get(
getUserAgent().getEventBroadcaster());
eventProducer.nonAutoBPDOnTable(this, getLocator());
}
if (tableLayout == EN_AUTO) {
getFOValidationEventProducer().unimplementedFeature(this, getName(),
"table-layout=\"auto\"", getLocator());
}
if (!isSeparateBorderModel()) {
if (borderCollapse == EN_COLLAPSE_WITH_PRECEDENCE) {
getFOValidationEventProducer().unimplementedFeature(this, getName(),
"border-collapse=\"collapse-with-precedence\"; defaulting to \"collapse\"", getLocator());
borderCollapse = EN_COLLAPSE;
}
if (getCommonBorderPaddingBackground().hasPadding(
ValidationPercentBaseContext.getPseudoContext())) {
TableEventProducer eventProducer = TableEventProducer.Provider.get(
getUserAgent().getEventBroadcaster());
eventProducer.noTablePaddingWithCollapsingBorderModel(this, getLocator());
}
}
this.propList = pList;
}
public void startOfNode() throws FOPException {
super.startOfNode();
getFOEventHandler().startTable(this);
}
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) {
if (getUserAgent().validateStrictly()) {
nodesOutOfOrderError(loc, "fo:table-footer", "(table-body+)", true);
}
if (!isSeparateBorderModel()) {
TableEventProducer eventProducer = TableEventProducer.Provider.get(
getUserAgent().getEventBroadcaster());
eventProducer.footerOrderCannotRecover(this, getName(), getLocator());
}
}
}
} else if ("table-body".equals(localName)) {
tableBodyFound = true;
} else {
invalidChildError(loc, nsURI, localName);
}
}
}
public void endOfNode() throws FOPException {
super.endOfNode();
getFOEventHandler().endTable(this);
}
public void finalizeNode() throws FOPException {
if (!tableBodyFound) {
missingChildElementError(
"(marker*,table-column*,table-header?,table-footer?"
+ ",table-body+)");
}
if (!hasChildren()) {
getParent().removeChild(this);
return;
}
if (!inMarker()) {
rowGroupBuilder.endTable();
for (int i = columns.size(); --i >= 0;) {
TableColumn col = (TableColumn) columns.get(i);
if (col != null) {
col.releasePropertyList();
}
}
this.propList = null;
rowGroupBuilder = null;
}
}
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(child);
}
break;
case FO_TABLE_HEADER:
case FO_TABLE_FOOTER:
case FO_TABLE_BODY:
if (!inMarker() && !columnsFinalized) {
columnsFinalized = true;
if (hasExplicitColumns) {
finalizeColumns();
rowGroupBuilder = new FixedColRowGroupBuilder(this);
} else {
rowGroupBuilder = new VariableColRowGroupBuilder(this);
}
}
switch (childId) {
case FO_TABLE_FOOTER:
tableFooter = (TableFooter) child;
break;
case FO_TABLE_HEADER:
tableHeader = (TableHeader) 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));
}
}
}
public CommonAccessibility getCommonAccessibility() {
return commonAccessibility;
}
public Table getTable() {
return this;
}
void ensureColumnNumber(int columnNumber) throws FOPException {
assert !hasExplicitColumns;
for (int i = columns.size() + 1; i <= columnNumber; i++) {
columns.add(createImplicitColumn(i));
}
}
private TableColumn createImplicitColumn(int colNumber)
throws FOPException {
TableColumn implicitColumn = new TableColumn(this, true);
PropertyList pList = new StaticPropertyList(
implicitColumn, this.propList);
implicitColumn.bind(pList);
implicitColumn.setColumnWidth(new TableColLength(1.0, implicitColumn));
implicitColumn.setColumnNumber(colNumber);
if (!isSeparateBorderModel()) {
implicitColumn.setCollapsedBorders(collapsingBorderModel);
}
return implicitColumn;
}
private void addColumnNode(TableColumn col) {
int colNumber = col.getColumnNumber();
int colRepeat = col.getNumberColumnsRepeated();
while (columns.size() < colNumber + colRepeat - 1) {
columns.add(null);
}
for (int i = colNumber - 1; i < colNumber + colRepeat - 1; i++) {
columns.set(i, col);
}
columnNumberManager.signalUsedColumnNumbers(colNumber, colNumber + colRepeat - 1);
}
boolean hasExplicitColumns() {
return hasExplicitColumns;
}
public boolean isAutoLayout() {
return (tableLayout == EN_AUTO);
}
public List getColumns() {
return columns;
}
public TableColumn getColumn(int index) {
return (TableColumn) columns.get(index);
}
public int getNumberOfColumns() {
return columns.size();
}
public TableHeader () {
return tableHeader;
}
public TableFooter () {
return tableFooter;
}
public boolean () {
return (this.tableOmitHeaderAtBreak == EN_TRUE);
}
public boolean () {
return (this.tableOmitFooterAtBreak == EN_TRUE);
}
public LengthRangeProperty getInlineProgressionDimension() {
return inlineProgressionDimension;
}
public LengthRangeProperty getBlockProgressionDimension() {
return blockProgressionDimension;
}
public CommonMarginBlock getCommonMarginBlock() {
return commonMarginBlock;
}
public CommonBorderPaddingBackground getCommonBorderPaddingBackground() {
return commonBorderPaddingBackground;
}
public int getBreakAfter() {
return breakAfter;
}
public int getBreakBefore() {
return breakBefore;
}
public KeepProperty getKeepWithNext() {
return keepWithNext;
}
public KeepProperty getKeepWithPrevious() {
return keepWithPrevious;
}
public KeepProperty getKeepTogether() {
return keepTogether;
}
public boolean mustKeepTogether() {
return !getKeepTogether().getWithinPage().isAuto()
|| !getKeepTogether().getWithinColumn().isAuto();
}
public int getBorderCollapse() {
return borderCollapse;
}
public boolean isSeparateBorderModel() {
return (getBorderCollapse() == EN_SEPARATE);
}
public LengthPairProperty getBorderSeparation() {
return borderSeparation;
}
public Direction getInlineProgressionDirection() {
return writingModeTraits.getInlineProgressionDirection();
}
public Direction getBlockProgressionDirection() {
return writingModeTraits.getBlockProgressionDirection();
}
public Direction getColumnProgressionDirection() {
return writingModeTraits.getColumnProgressionDirection();
}
public Direction getRowProgressionDirection() {
return writingModeTraits.getRowProgressionDirection();
}
public Direction getShiftDirection() {
return writingModeTraits.getShiftDirection();
}
public WritingMode getWritingMode() {
return writingModeTraits.getWritingMode();
}
public boolean getExplicitWritingMode() {
return writingModeTraits.getExplicitWritingMode();
}
public Length getWidowContentLimit() {
return widowContentLimit;
}
public Length getOrphanContentLimit() {
return orphanContentLimit;
}
public String getLocalName() {
return "table";
}
public int getNameId() {
return FO_TABLE;
}
public FONode clone(FONode parent, boolean removeChildren)
throws FOPException {
Table clone = (Table) super.clone(parent, removeChildren);
if (removeChildren) {
clone.columns = new ArrayList();
clone.columnsFinalized = false;
clone.columnNumberManager = new ColumnNumberManager();
clone.tableHeader = null;
clone.tableFooter = null;
clone.rowGroupBuilder = null;
}
return clone;
}
public ColumnNumberManager getColumnNumberManager() {
return columnNumberManager;
}
RowGroupBuilder getRowGroupBuilder() {
return rowGroupBuilder;
}
@Override
protected Stack<DelimitedTextRange> (Stack<DelimitedTextRange> ranges,
DelimitedTextRange currentRange) {
TableHeader header = getTableHeader();
if (header != null) {
ranges = header.collectDelimitedTextRanges(ranges);
}
TableFooter footer = getTableFooter();
if (footer != null) {
ranges = footer.collectDelimitedTextRanges(ranges);
}
for (Iterator it = getChildNodes(); (it != null) && it.hasNext();) {
ranges = ((FONode) it.next()).collectDelimitedTextRanges(ranges);
}
return ranges;
}
@Override
protected boolean isBidiBoundary(boolean propagate) {
return getExplicitWritingMode();
}
}