Package org.apache.fop.layoutmgr.table

Source Code of org.apache.fop.layoutmgr.table.TableRowIterator

/*
* Copyright 2005 The Apache Software Foundation.
*
* Licensed 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: TableRowIterator.java 372504 2006-01-26 11:05:43Z jeremias $ */

package org.apache.fop.layoutmgr.table;

import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.fo.flow.Marker;
import org.apache.fop.fo.flow.Table;
import org.apache.fop.fo.flow.TableBody;
import org.apache.fop.fo.flow.TableCell;
import org.apache.fop.fo.flow.TableColumn;
import org.apache.fop.fo.flow.TableRow;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;


/**
* <p>Iterator that lets the table layout manager step over all rows of a table.
* </p>
* <p>Note: This class is not thread-safe.
* </p>
*/
public class TableRowIterator {

    /** Selects the list of table-body elements for iteration. */
    public static final int BODY = 0;
    /** Selects the table-header element for iteration. */
    public static final int HEADER = 1;
    /** Selects the table-footer element for iteration. */
    public static final int FOOTER = 2;
   
    /** Logger **/
    private static Log log = LogFactory.getLog(TableRowIterator.class);

    /** The table on with this instance operates. */
    protected Table table;
    private ColumnSetup columns;
    private int type;
   
    /** Holds the current row (TableCell instances) */
    private List currentRow = new java.util.ArrayList();
    /** Holds the grid units of cell from the last row while will span over the current row
     * (GridUnit instance) */
    private List lastRowsSpanningCells = new java.util.ArrayList();
    private int currentRowIndex = -1;
    //TODO rows should later be a Jakarta Commons LinkedList so concurrent modifications while
    //using a ListIterator are possible
    /** List of cache rows. */
    private List rows = new java.util.ArrayList();
    //private int indexOfFirstRowInList;
    private int currentIndex = -1;
    private int pendingRowSpans;
   
    //prefetch state
    private ListIterator bodyIterator = null;
    private ListIterator childInBodyIterator = null;
   
    /**
     * Creates a new TableRowIterator.
     * @param table the table to iterate over
     * @param columns the column setup for the table
     * @param what indicates what part of the table to iterate over (HEADER, FOOTER, BODY)
     */
    public TableRowIterator(Table table, ColumnSetup columns, int what) {
        this.table = table;
        this.columns = columns;
        this.type = what;
        switch(what) {
            case HEADER: {
                List bodyList = new java.util.ArrayList();
                bodyList.add(table.getTableHeader());
                this.bodyIterator = bodyList.listIterator();
                break;
            }
            case FOOTER: {
                List bodyList = new java.util.ArrayList();
                bodyList.add(table.getTableFooter());
                this.bodyIterator = bodyList.listIterator();
                break;
            }
            default: {
                this.bodyIterator = table.getChildNodes();
            }
        }
    }
   
    /**
     * <p>Preloads the whole table.
     * </p>
     * <p>Note:This is inefficient for large tables.
     * </p>
     */
    public void prefetchAll() {
        while (prefetchNext()) {
            log.trace("found row...");
        }
    }
   
    /**
     * Returns the next row group if any. A row group in this context is the minimum number of
     * consecutive rows which contains all spanned grid units of its cells.
     * @return the next row group, or null
     */
    public EffRow[] getNextRowGroup() {
        EffRow firstRowInGroup = getNextRow();
        if (firstRowInGroup == null) {
            return null;
        }
        EffRow lastRowInGroup = firstRowInGroup;
        int lastIndex = lastRowInGroup.getIndex();
        boolean allFinished;
        do {
            allFinished = true;
            Iterator iter = lastRowInGroup.getGridUnits().iterator();
            while (iter.hasNext()) {
                GridUnit gu = (GridUnit)iter.next();
                if (!gu.isLastGridUnitRowSpan()) {
                    allFinished = false;
                    break;
                }
            }
            lastIndex = lastRowInGroup.getIndex();
            if (!allFinished) {
                lastRowInGroup = getNextRow();
                if (lastRowInGroup == null) {
                    allFinished = true;
                }
            }
        } while (!allFinished);
        int rowCount = lastIndex - firstRowInGroup.getIndex() + 1;
        EffRow[] rowGroup = new EffRow[rowCount];
        for (int i = 0; i < rowCount; i++) {
            rowGroup[i] = getCachedRow(i + firstRowInGroup.getIndex());
        }
        return rowGroup;
    }
   
    /**
     * Retuns the next effective row.
     * @return the requested effective row.
     */
    public EffRow getNextRow() {
        currentIndex++;
        boolean moreRows = true;
        while (moreRows && rows.size() < currentIndex + 1) {
            moreRows = prefetchNext();
        }
        if (currentIndex < rows.size()) {
            return getCachedRow(currentIndex);
        } else {
            return null;
        }
    }
   
    /**
     * Sets the iterator to the previous row.
     */
    public void backToPreviousRow() {
        currentIndex--;
    }
   
    /**
     * Returns the first effective row.
     * @return the requested effective row.
     */
    public EffRow getFirstRow() {
        if (rows.size() == 0) {
            prefetchNext();
        }
        return getCachedRow(0);
    }
   
    /**
     * <p>Returns the last effective row.
     * </p>
     * <p>Note:This is inefficient for large tables because the whole table
     * if preloaded.
     * </p>
     * @return the requested effective row.
     */
    public EffRow getLastRow() {
        while (prefetchNext()) {
            //nop
        }
        return getCachedRow(rows.size() - 1);
    }
   
    /**
     * Returns a cached effective row.
     * @param index index of the row (zero-based)
     * @return the requested effective row
     */
    public EffRow getCachedRow(int index) {
        if (index < 0 || index >= rows.size()) {
            return null;
        } else {
            return (EffRow)rows.get(index);
        }
    }
   
    private boolean prefetchNext() {
        boolean firstInTable = false;
        boolean firstInBody = false;
        if (childInBodyIterator != null) {
            if (!childInBodyIterator.hasNext()) {
                //force skip on to next body
                if (pendingRowSpans > 0) {
                    this.currentRow.clear();
                    this.currentRowIndex++;
                    EffRow gridUnits = buildGridRow(this.currentRow, null);
                    log.debug(gridUnits);
                    rows.add(gridUnits);
                    return true;
                }
                childInBodyIterator = null;
                if (rows.size() > 0) {
                    getCachedRow(rows.size() - 1).setFlagForAllGridUnits(
                            GridUnit.LAST_IN_BODY, true);
                }
            }
        }
        if (childInBodyIterator == null) {
            if (bodyIterator.hasNext()) {
                childInBodyIterator = ((TableBody)bodyIterator.next()).getChildNodes();
                if (rows.size() == 0) {
                    firstInTable = true;
                }
                firstInBody = true;
            } else {
                //no more rows
                if (rows.size() > 0) {
                    getCachedRow(rows.size() - 1).setFlagForAllGridUnits(
                            GridUnit.LAST_IN_BODY, true);
                    if ((type == FOOTER || table.getTableFooter() == null)
                            && type != HEADER) {
                        getCachedRow(rows.size() - 1).setFlagForAllGridUnits(
                                GridUnit.LAST_IN_TABLE, true);
                    }
                }
                return false;
            }
        }
        Object node = childInBodyIterator.next();
        while (node instanceof Marker) {
            node = childInBodyIterator.next();
        }
        this.currentRow.clear();
        this.currentRowIndex++;
        TableRow rowFO = null;
        if (node instanceof TableRow) {
            rowFO = (TableRow)node;
            ListIterator cellIterator = rowFO.getChildNodes();
            while (cellIterator.hasNext()) {
                this.currentRow.add(cellIterator.next());
            }
        } else if (node instanceof TableCell) {
            this.currentRow.add(node);
            if (!((TableCell)node).endsRow()) {
                while (childInBodyIterator.hasNext()) {
                    TableCell cell = (TableCell)childInBodyIterator.next();
                    if (cell.startsRow()) {
                        //next row already starts here, one step back
                        childInBodyIterator.previous();
                        break;
                    }
                    this.currentRow.add(cell);
                    if (cell.endsRow()) {
                        break;
                    }
                }
            }
        } else {
            throw new IllegalStateException("Illegal class found: " + node.getClass().getName());
        }
        EffRow gridUnits = buildGridRow(this.currentRow, rowFO);
        if (firstInBody) {
            gridUnits.setFlagForAllGridUnits(GridUnit.FIRST_IN_BODY, true);
        }
        if (firstInTable && (type == HEADER || table.getTableHeader() == null)
                && type != FOOTER) {
            gridUnits.setFlagForAllGridUnits(GridUnit.FIRST_IN_TABLE, true);
        }
        log.debug(gridUnits);
        rows.add(gridUnits);
        return true;
    }

    private void safelySetListItem(List list, int position, Object obj) {
        while (position >= list.size()) {
            list.add(null);
        }
        list.set(position, obj);
    }
   
    private Object safelyGetListItem(List list, int position) {
        if (position >= list.size()) {
            return null;
        } else {
            return list.get(position);
        }
    }
   
    private EffRow buildGridRow(List cells, TableRow rowFO) {
        EffRow row = new EffRow(this.currentRowIndex, type);
        List gridUnits = row.getGridUnits();
       
        TableBody bodyFO = null;
       
        //Create all row-spanned grid units based on information from the last row
        int colnum = 1;
        GridUnit[] horzSpan = null;
        if (pendingRowSpans > 0) {
            ListIterator spanIter = lastRowsSpanningCells.listIterator();
            while (spanIter.hasNext()) {
                GridUnit gu = (GridUnit)spanIter.next();
                if (gu != null) {
                    if (gu.getColSpanIndex() == 0) {
                        horzSpan = new GridUnit[gu.getCell().getNumberColumnsSpanned()];
                    }
                    GridUnit newGU = gu.createNextRowSpanningGridUnit();
                    newGU.setRow(rowFO);
                    safelySetListItem(gridUnits, colnum - 1, newGU);
                    horzSpan[newGU.getColSpanIndex()] = newGU;
                    if (newGU.isLastGridUnitColSpan()) {
                        //Add the array of row-spanned grid units to the primary grid unit
                        newGU.getPrimary().addRow(horzSpan);
                        horzSpan = null;
                    }
                    if (newGU.isLastGridUnitRowSpan()) {
                        spanIter.set(null);
                        pendingRowSpans--;
                    } else {
                        spanIter.set(newGU);
                    }
                }
                colnum++;
            }
        }
        if (pendingRowSpans < 0) {
            throw new IllegalStateException("pendingRowSpans must not become negative!");
        }
       
        //Transfer available cells to their slots
        colnum = 1;
        ListIterator iter = cells.listIterator();
        while (iter.hasNext()) {
            TableCell cell = (TableCell)iter.next();
           
            colnum = cell.getColumnNumber();

            //TODO: remove the check below???
            //shouldn't happen here, since
            //overlapping cells already caught in
            //fo.flow.TableCell.bind()...
            GridUnit other = (GridUnit)safelyGetListItem(gridUnits, colnum - 1);
            if (other != null) {
                String err = "A table-cell ("
                        + cell.getContextInfo()
                        + ") is overlapping with another ("
                        + other.getCell().getContextInfo()
                        + ") in column " + colnum;
                throw new IllegalStateException(err
                        + " (this should have been catched by FO tree validation)");
            }
            TableColumn col = columns.getColumn(colnum);

            //Add grid unit for primary grid unit
            PrimaryGridUnit gu = new PrimaryGridUnit(cell, col, colnum - 1, this.currentRowIndex);
            safelySetListItem(gridUnits, colnum - 1, gu);
            boolean hasRowSpanningLeft = !gu.isLastGridUnitRowSpan();
            if (hasRowSpanningLeft) {
                pendingRowSpans++;
                safelySetListItem(lastRowsSpanningCells, colnum - 1, gu);
            }
           
            if (gu.hasSpanning()) {
                //Add grid units on spanned slots if any
                horzSpan = new GridUnit[cell.getNumberColumnsSpanned()];
                horzSpan[0] = gu;
                for (int j = 1; j < cell.getNumberColumnsSpanned(); j++) {
                    colnum++;
                    GridUnit guSpan = new GridUnit(gu, columns.getColumn(colnum), colnum - 1, j);
                    //TODO: remove the check below???
                    other = (GridUnit)safelyGetListItem(gridUnits, colnum - 1);
                    if (other != null) {
                        String err = "A table-cell ("
                            + cell.getContextInfo()
                            + ") is overlapping with another ("
                            + other.getCell().getContextInfo()
                            + ") in column " + colnum;
                        throw new IllegalStateException(err
                            + " (this should have been catched by FO tree validation)");
                    }
                    safelySetListItem(gridUnits, colnum - 1, guSpan);
                    if (hasRowSpanningLeft) {
                        pendingRowSpans++;
                        safelySetListItem(lastRowsSpanningCells, colnum - 1, gu);
                    }
                    horzSpan[j] = guSpan;
                }
                gu.addRow(horzSpan);
            }
           
            //Gather info for empty grid units (used later)
            if (bodyFO == null) {
                bodyFO = gu.getBody();
            }
           
            colnum++;
        }
       
        //Post-processing the list (looking for gaps and resolve start and end borders)
        fillEmptyGridUnits(gridUnits, rowFO, bodyFO);
        resolveStartEndBorders(gridUnits);
       
        return row;
    }
   
    private void fillEmptyGridUnits(List gridUnits, TableRow row, TableBody body) {
        for (int pos = 1; pos <= gridUnits.size(); pos++) {
            GridUnit gu = (GridUnit)gridUnits.get(pos - 1);
           
            //Empty grid units
            if (gu == null) {
                //Add grid unit
                gu = new EmptyGridUnit(row, columns.getColumn(pos), body,
                        pos - 1);
                gridUnits.set(pos - 1, gu);
            }
           
            //Set flags
            gu.setFlag(GridUnit.IN_FIRST_COLUMN, (pos == 1));
            gu.setFlag(GridUnit.IN_LAST_COLUMN, (pos == gridUnits.size()));
        }
    }
   
    private void resolveStartEndBorders(List gridUnits) {
        for (int pos = 1; pos <= gridUnits.size(); pos++) {
            GridUnit starting = (GridUnit)gridUnits.get(pos - 1);
        
            //Border resolution
            if (table.isSeparateBorderModel()) {
                starting.assignBorderForSeparateBorderModel();
            } else {
                //Neighbouring grid unit at start edge
                GridUnit start = null;
                int find = pos - 1;
                while (find >= 1) {
                    GridUnit candidate = (GridUnit)gridUnits.get(find - 1);
                    if (candidate.isLastGridUnitColSpan()) {
                        start = candidate;
                        break;
                    }
                    find--;
                }
               
                //Ending grid unit for current cell
                GridUnit ending = null;
                if (starting.getCell() != null) {
                    pos += starting.getCell().getNumberColumnsSpanned() - 1;
                }
                ending = (GridUnit)gridUnits.get(pos - 1);
               
                //Neighbouring grid unit at end edge
                GridUnit end = null;
                find = pos + 1;
                while (find <= gridUnits.size()) {
                    GridUnit candidate = (GridUnit)gridUnits.get(find - 1);
                    if (candidate.isPrimary()) {
                        end = candidate;
                        break;
                    }
                    find++;
                }
                starting.resolveBorder(start,
                        CommonBorderPaddingBackground.START);
                ending.resolveBorder(end,
                        CommonBorderPaddingBackground.END);
                //Only start and end borders here, before and after during layout
            }
        }
    }

}
TOP

Related Classes of org.apache.fop.layoutmgr.table.TableRowIterator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.