<commit_before>/*
 * 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$ */

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.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;
    
    //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 = true;
        do {
            Iterator iter = lastRowInGroup.getGridUnits().iterator();
            while (iter.hasNext()) {
                GridUnit gu = (GridUnit)iter.next();
                if (!gu.isLastGridUnitRowSpan()) {
                    allFinished = false;
                    break;
                }
            }
            if (!allFinished) {
                lastIndex = lastRowInGroup.getIndex();
                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
                childInBodyIterator = null;
            }
        }
        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();
        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()) {<commit_msg>Skip markers (they are validated properly earlier).<commit_after>/*
 * 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$ */

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;
    
    //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 = true;
        do {
            Iterator iter = lastRowInGroup.getGridUnits().iterator();
            while (iter.hasNext()) {
                GridUnit gu = (GridUnit)iter.next();
                if (!gu.isLastGridUnitRowSpan()) {
                    allFinished = false;
                    break;
                }
            }
            if (!allFinished) {
                lastIndex = lastRowInGroup.getIndex();
                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
                childInBodyIterator = null;
            }
        }
        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()) {
{'fraction_non_alphanumeric': 0.1003943661971831, 'fraction_numerical': 0.002704225352112676, 'mean_word_length': 1.8064822134387353, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5042935', 'n_tokens_mistral': 4840, 'n_tokens_neox': 4697, 'n_words': 1398}