/* * 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 org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; import org.apache.fop.layoutmgr.table.CollapsingBorderModel; /** * This class represents one grid unit inside a table. */ public class GridUnit { /** * Indicates that the grid unit is in the first row of the table part (header, footer, * body). */ public static final int FIRST_IN_PART = 0; /** * Indicates that the grid unit is in the last row of the table part (header, footer, * body). */ public static final int LAST_IN_PART = 1; /** Indicates that the primary grid unit has a pending keep-with-next. */ public static final int KEEP_WITH_NEXT_PENDING = 2; /** Indicates that the primary grid unit has a pending keep-with-previous. */ public static final int KEEP_WITH_PREVIOUS_PENDING = 3; /** Primary grid unit */ private PrimaryGridUnit primary; /** Table cell which occupies this grid unit */ protected TableCell cell; /** Table row occupied by this grid unit (may be null). */ private TableRow row; /** index of grid unit within cell in column direction */ private int colSpanIndex; /** index of grid unit within cell in row direction */ private int rowSpanIndex; /** flags for the grid unit */ private byte flags; /** the border-before specification */ ConditionalBorder borderBefore; /** the border-after specification */ ConditionalBorder borderAfter; /** the border-start specification */ BorderSpecification borderStart; /** the border-end specification */ BorderSpecification borderEnd; /** The border model helper associated with the table */ protected CollapsingBorderModel collapsingBorderModel; /** * Creates a new grid unit. * * @param table the containing table * @param colSpanIndex index of this grid unit in the span, in column direction * @param rowSpanIndex index of this grid unit in the span, in row direction */ protected GridUnit(Table table, int colSpanIndex, int rowSpanIndex) { this(colSpanIndex, rowSpanIndex); setBorders(table); } /** * Creates a new grid unit. * * @param cell table cell which occupies this grid unit * @param colSpanIndex index of this grid unit in the span, in column direction * @param rowSpanIndex index of this grid unit in the span, in row direction */ protected GridUnit(TableCell cell, int colSpanIndex, int rowSpanIndex) { this(colSpanIndex, rowSpanIndex); this.cell = cell; setBorders(cell.getTable()); } /** * Creates a new grid unit. * * @param primary the before-start grid unit of the cell containing this grid unit * @param colSpanIndex index of this grid unit in the span, in column direction * @param rowSpanIndex index of this grid unit in the span, in row direction */ GridUnit(PrimaryGridUnit primary, int colSpanIndex, int rowSpanIndex) { this(primary.getCell(), colSpanIndex, rowSpanIndex); this.primary = primary; } private GridUnit(int colSpanIndex, int rowSpanIndex) { this.colSpanIndex = colSpanIndex; this.rowSpanIndex = rowSpanIndex; } private void setBorders(Table table/*TODO*/) { if (!table.isSeparateBorderModel()) { collapsingBorderModel = CollapsingBorderModel.getBorderModelFor(table .getBorderCollapse()); setBordersFromCell(); } } /** * Prepares the borders of this grid unit for upcoming resolution, in the collapsing * model. */ protected void setBordersFromCell() { borderBefore = cell.borderBefore.copy(); if (rowSpanIndex > 0) { borderBefore.normal = BorderSpecification.getDefaultBorder(); } borderAfter = cell.borderAfter.copy(); if (!isLastGridUnitRowSpan()) { borderAfter.normal = BorderSpecification.getDefaultBorder(); } if (colSpanIndex == 0) { borderStart = cell.borderStart; } else { borderStart = BorderSpecification.getDefaultBorder(); } if (isLastGridUnitColSpan()) { borderEnd = cell.borderEnd; } else { borderEnd = BorderSpecification.getDefaultBorder(); } } /** * Returns the table cell associated with this grid unit. * @return the table cell */ public TableCell getCell() { return cell; } /** * Returns the fo:table-row element (if any) this grid unit belongs to. * * @return the row containing this grid unit, or null if there is no fo:table-row * element in the corresponding table-part */ public TableRow getRow() { return row; } void setRow(TableRow row) { this.row = row; } /** * Returns the before-start grid unit of the cell containing this grid unit. * * @return the before-start grid unit of the cell containing this grid unit. */ public PrimaryGridUnit getPrimary() { return primary; } /** * Is this grid unit the before-start grid unit of the cell? * * @return true if this grid unit is the before-start grid unit of the cell */ public boolean isPrimary() { return false; } /** * Does this grid unit belong to an empty cell? * * @return true if this grid unit belongs to an empty cell */ public boolean isEmpty() { return cell == null; } /** @return true if the grid unit is the last in column spanning direction */ public boolean isLastGridUnitColSpan() { return (colSpanIndex == cell.getNumberColumnsSpanned() - 1); } /** @return true if the grid unit is the last in row spanning direction */ public boolean isLastGridUnitRowSpan() { return (rowSpanIndex == cell.getNumberRowsSpanned() - 1); } /** * @return the index of the grid unit inside a cell in row direction */ public int getRowSpanIndex() { return rowSpanIndex; } /** * @return the index of the grid unit inside a cell in column direction */ public int getColSpanIndex() { return colSpanIndex; } /** * Returns the resolved border-before of this grid unit, in the collapsing-border * model. * * @param which one of {@link ConditionalBorder#NORMAL}, * {@link ConditionalBorder#LEADING_TRAILING} or {@link ConditionalBorder#REST} * @return the corresponding border */ public BorderInfo getBorderBefore(int which) { switch (which) { case ConditionalBorder.NORMAL: return borderBefore.normal.getBorderInfo(); case ConditionalBorder.LEADING_TRAILING: return borderBefore.leadingTrailing.getBorderInfo(); case ConditionalBorder.REST: return borderBefore.rest.getBorderInfo(); default: assert false; return null; } } /** * Returns the resolved border-after of this grid unit, in the collapsing-border * model. * * @param which one of {@link ConditionalBorder#NORMAL}, * {@link ConditionalBorder#LEADING_TRAILING} or {@link ConditionalBorder#REST} * @return the corresponding border */ public BorderInfo getBorderAfter(int which) { switch (which) { case ConditionalBorder.NORMAL: return borderAfter.normal.getBorderInfo(); case ConditionalBorder.LEADING_TRAILING: return borderAfter.leadingTrailing.getBorderInfo(); case ConditionalBorder.REST: return borderAfter.rest.getBorderInfo(); default: assert false; return null; } } /** * Returns the resolved border-start of this grid unit, in the collapsing-border * model. * * @return the corresponding border */ public BorderInfo getBorderStart() { return borderStart.getBorderInfo(); } /** * Returns the resolved border-end of this grid unit, in the collapsing-border * model. * * @return the corresponding border */ public BorderInfo getBorderEnd() { return borderEnd.getBorderInfo(); } /** * Resolve collapsing borders for the given cell. Used in case of the collapsing * border model. * * @param other neighbouring grid unit * @param side the side to resolve (one of * CommonBorderPaddingBackground.BEFORE|AFTER|START|END) */ void resolveBorder(GridUnit other, int side) { switch (side) { case CommonBorderPaddingBackground.BEFORE: borderBefore.resolve(other.borderAfter, true, false, false); break; case CommonBorderPaddingBackground.AFTER: borderAfter.resolve(other.borderBefore, true, false, false); break; case CommonBorderPaddingBackground.START: BorderSpecification resolvedBorder = collapsingBorderModel.determineWinner( borderStart, other.borderEnd); if (resolvedBorder != null) { this.borderStart = resolvedBorder; other.borderEnd = resolvedBorder; } break; case CommonBorderPaddingBackground.END: resolvedBorder = collapsingBorderModel.determineWinner( borderEnd, other.borderStart); if (resolvedBorder != null) { this.borderEnd = resolvedBorder; other.borderStart = resolvedBorder; } break; default: assert false; } } /** * For the given side, integrates in the conflict resolution the border segment of the * given parent element. * * @param side the side to consider (either CommonBorderPaddingBackground.BEFORE or * AFTER) * @param parent a table element whose corresponding border coincides on the given * side */ void integrateBorderSegment(int side, TableFObj parent, boolean withNormal, boolean withLeadingTrailing, boolean withRest) { switch (side) { case CommonBorderPaddingBackground.BEFORE: borderBefore.integrateSegment(parent.borderBefore, withNormal, withLeadingTrailing, withRest); break; case CommonBorderPaddingBackground.AFTER: borderAfter.integrateSegment(parent.borderAfter, withNormal, withLeadingTrailing, withRest); break; default: assert false; } } /** * For the given side, integrates in the conflict resolution the border segment of the * given parent element. * * @param side the side to consider (one of * CommonBorderPaddingBackground.BEFORE|AFTER|START|END) * @param parent a table element whose corresponding border coincides on the given side */ void integrateBorderSegment(int side, TableFObj parent) { switch (side) { case CommonBorderPaddingBackground.BEFORE: case CommonBorderPaddingBackground.AFTER: integrateBorderSegment(side, parent, true, true, true); break; case CommonBorderPaddingBackground.START: borderStart = collapsingBorderModel.determineWinner(borderStart, parent.borderStart); break; case CommonBorderPaddingBackground.END: borderEnd = collapsingBorderModel.determineWinner(borderEnd, parent.borderEnd); break; default: assert false; } } /** * For the given side, integrates in the conflict resolution the given border segment. * * @param side the side to consider (one of CommonBorderPaddingBackground.START|END) * @param segment a border specification to integrate at the given side */ void integrateBorderSegment(int side, BorderSpecification segment) { switch(side) { case CommonBorderPaddingBackground.START: borderStart = collapsingBorderModel.determineWinner(borderStart, segment); break; case CommonBorderPaddingBackground.END: borderEnd = collapsingBorderModel.determineWinner(borderEnd, segment); break; default: assert false; } } void integrateCompetingBorder(int side, ConditionalBorder competitor, boolean withNormal, boolean withLeadingTrailing, boolean withRest) { switch (side) { case CommonBorderPaddingBackground.BEFORE: borderBefore.integrateCompetingSegment(competitor, withNormal, withLeadingTrailing, withRest); break; case CommonBorderPaddingBackground.AFTER: borderAfter.integrateCompetingSegment(competitor, withNormal, withLeadingTrailing, withRest); break; default: assert false; } } /** * Returns a flag for this GridUnit. * * @param which the requested flag * @return the value of the flag */ public boolean getFlag(int which) { return (flags & (1 << which)) != 0; } /** * Sets a flag on a GridUnit. * * @param which the flag to set * @param value the new value for the flag */ public void setFlag(int which, boolean value) { if (value) { flags |= (1 << which); // set flag } else { flags &= ~(1 << which); // clear flag } } /** * Sets the given flag on this grid unit. * * @param which the flag to set */ public void setFlag(int which) { setFlag(which, true); } /** {@inheritDoc} */ public String toString() { StringBuffer buffer = new StringBuffer(); if (isEmpty()) { buffer.append("EMPTY"); } else if (isPrimary()) { buffer.append("Primary"); } buffer.append("GridUnit:"); if (colSpanIndex > 0) { buffer.append(" colSpan=").append(colSpanIndex); if (isLastGridUnitColSpan()) { buffer.append("(last)"); } } if (rowSpanIndex > 0) { buffer.append(" rowSpan=").append(rowSpanIndex); if (isLastGridUnitRowSpan()) { buffer.append("(last)"); } } if (!isPrimary() && getPrimary() != null) { buffer.append(" primary=").append(getPrimary().getRowIndex()); buffer.append("/").append(getPrimary().getColIndex()); if (getPrimary().getCell() != null) { buffer.append(" id=" + getPrimary().getCell().getId()); } } buffer.append(" flags=").append(Integer.toBinaryString(flags)); return buffer.toString(); } }