com.icesoft.faces.component.dataexporter.DataExporter.java Source code

Java tutorial

Introduction

Here is the source code for com.icesoft.faces.component.dataexporter.DataExporter.java

Source

/*
 * Copyright 2004-2013 ICEsoft Technologies Canada Corp.
 *
 * 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.
 */

package com.icesoft.faces.component.dataexporter;

import com.icesoft.faces.component.CSS_DEFAULT;
import com.icesoft.faces.component.commandsortheader.CommandSortHeader;
import com.icesoft.faces.component.ext.RowSelector;
import com.icesoft.faces.component.ext.UIColumn;
import com.icesoft.faces.component.ext.UIColumns;
import com.icesoft.faces.component.ext.taglib.Util;
import com.icesoft.faces.component.outputresource.OutputResource;
import com.icesoft.faces.context.FileResource;
import com.icesoft.faces.context.effects.JavascriptContext;
import com.icesoft.faces.util.CoreUtils;
import com.icesoft.util.CoreComponentUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.icefaces.resources.BrowserType;
import org.icefaces.resources.ICEResourceDependencies;
import org.icefaces.resources.ICEResourceDependency;

import javax.faces.component.*;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.el.ValueBinding;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.FacesEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

@ICEResourceDependencies({
        @ICEResourceDependency(name = "icefaces-compat.js", library = "ice.compat", target = "head", browser = BrowserType.ALL, browserOverride = {}),
        @ICEResourceDependency(name = "compat.js", library = "ice.compat", target = "head", browser = BrowserType.ALL, browserOverride = {}) })
public class DataExporter extends OutputResource {
    public static final String COMPONENT_FAMILY = "com.icesoft.faces.DataExporter";
    public static final String COMPONENT_TYPE = "com.icesoft.faces.DataExporter";
    public static final String DEFAULT_RENDERER_TYPE = "com.icesoft.faces.DataExporterRenderer";
    private final Log log = LogFactory.getLog(DataExporter.class);

    private static final OutputTypeHandler NoopOutputHandler = new OutputTypeHandler("no-data") {
        public void writeHeaderCell(String text, int col) {
        }

        public void writeCell(Object output, int col, int row) {
        }

        public void flushFile() {
        }
    };

    private boolean readyToExport = false;

    private String _for;
    private String type;
    private String clickToCreateFileText;
    private String _origType;
    private transient OutputTypeHandler outputTypeHandler;
    private String _origFor;
    private transient OutputTypeHandler _origOutputTypeHandler;
    private transient int _origDataModelHash = 0;
    public final static String EXCEL_TYPE = "excel";
    public final static String CSV_TYPE = "csv";
    public final static String PDF_TYPE = "pdf";
    private Boolean ignorePagination;
    private Boolean renderLabelAsButton;
    private String styleClass;
    private String includeColumns;
    private int rows = Integer.MIN_VALUE;
    private int first = Integer.MIN_VALUE;
    private String popupBlockerLabel;

    public DataExporter() {
    }

    /*
    * (non-Javadoc)
    * 
    * @see javax.faces.component.UIComponent#getFamily()
    */
    public String getFamily() {
        return COMPONENT_FAMILY;
    }

    public String getComponentType() {
        return COMPONENT_TYPE;
    }

    /*
     * (non-Javadoc)
     * 
     * @see javax.faces.component.UIComponent#getRendererType()
     */
    public String getRendererType() {
        return DEFAULT_RENDERER_TYPE;
    }

    public UIData getUIData() {
        String forStr = getFor();
        UIData forComp = (UIData) CoreComponentUtils.findComponent(forStr, this);

        if (forComp == null) {
            throw new IllegalArgumentException(
                    "could not find UIData referenced by attribute @for = '" + forStr + "'");
        } else if (!(forComp instanceof UIData)) {
            throw new IllegalArgumentException("uiComponent referenced by attribute @for = '" + forStr
                    + "' must be of type " + UIData.class.getName() + ", not type " + forComp.getClass().getName());
        }
        //compare with cached DataModel to check for updates
        if (_origDataModelHash != 0 && _origDataModelHash != forComp.getValue().hashCode()) {
            reset();
        }
        if (!isIgnorePagination() && ((first != Integer.MIN_VALUE && first != forComp.getFirst())
                || (rows != Integer.MIN_VALUE && rows != forComp.getRows()))) {
            reset();
        }

        Object value = forComp.getValue();
        if (null != value) {
            _origDataModelHash = forComp.getValue().hashCode();
        }
        return forComp;
    }

    private void reset() {
        this.readyToExport = false;
        this.resource = null;
    }

    public String getFor() {
        if (_for != null) {
            if (!_for.equals(this._origFor))
                reset();
            this._origFor = _for;
            return _for;
        }
        ValueBinding vb = getValueBinding("for");
        String newFor = null;
        if (vb != null) {
            newFor = (String) vb.getValue(getFacesContext());
            if (newFor != null && !newFor.equals(this._origFor))
                reset();
            this._origFor = newFor;
        }

        return newFor;
    }

    public void setFor(String forValue) {
        if (forValue != null && !forValue.equals(_for))
            this.resource = null;
        _for = forValue;
    }

    public String getType() {
        if (type != null) {
            if (!type.equals(this._origType))
                reset();
            this._origType = type;
            return type;
        }
        ValueBinding vb = getValueBinding("type");
        String newType = null;
        if (vb != null) {
            newType = (String) vb.getValue(getFacesContext());
            if (newType != null && !newType.equals(this._origType))
                reset();
            this._origType = newType;
        }
        return newType;
    }

    public void setType(String type) {
        if (type != null && !type.equals(this.type))
            reset();
        this.type = type;
    }

    public boolean isReadyToExport() {
        return readyToExport;
    }

    public void setReadyToExport(boolean readyToExport) {
        this.readyToExport = readyToExport;
    }

    /**
     * @deprecated
     */
    public String getClickToCreateFileText() {
        if (this.clickToCreateFileText != null) {
            return clickToCreateFileText;
        }
        ValueBinding vb = getValueBinding("clickToCreateFileText");
        return vb != null ? (String) vb.getValue(getFacesContext()) : null;
    }

    /**
    * @deprecated
    */
    public void setClickToCreateFileText(String clickToCreateFileText) {
        this.clickToCreateFileText = clickToCreateFileText;
    }

    public OutputTypeHandler getOutputTypeHandler() {
        ValueBinding vb = getValueBinding("outputTypeHandler");
        OutputTypeHandler newOutputHandler = null;
        if (vb != null) {
            newOutputHandler = (OutputTypeHandler) vb.getValue(getFacesContext());
            if (newOutputHandler != null && newOutputHandler != this._origOutputTypeHandler)
                reset();
            this._origOutputTypeHandler = newOutputHandler;
        }
        return newOutputHandler;
    }

    public void setOutputTypeHandler(OutputTypeHandler outputTypeHandler) {
        if (outputTypeHandler != null && outputTypeHandler != this.outputTypeHandler)
            reset();
        this.outputTypeHandler = outputTypeHandler;
    }

    private transient Object values[];

    /*
     * (non-Javadoc)
     * 
     * @see javax.faces.component.StateHolder#saveState(javax.faces.context.FacesContext)
     */
    public Object saveState(FacesContext context) {

        if (values == null) {
            values = new Object[14];
        }
        values[0] = super.saveState(context);
        values[1] = _for;
        values[2] = type;
        values[3] = clickToCreateFileText;
        values[4] = readyToExport ? Boolean.TRUE : Boolean.FALSE;
        values[5] = _origType;
        values[6] = _origFor;
        values[7] = ignorePagination;
        values[8] = renderLabelAsButton;
        values[9] = styleClass;
        values[10] = includeColumns;
        values[11] = new Integer(rows);
        values[12] = new Integer(first);
        values[13] = popupBlockerLabel;
        return ((Object) (values));
    }

    /*
     * (non-Javadoc)
     * 
     * @see javax.faces.component.StateHolder#restoreState(javax.faces.context.FacesContext,
     *      java.lang.Object)
     */
    public void restoreState(FacesContext context, Object state) {
        Object values[] = (Object[]) state;
        super.restoreState(context, values[0]);
        _for = (String) values[1];
        type = (String) values[2];
        clickToCreateFileText = (String) values[3];
        readyToExport = ((Boolean) values[4]).booleanValue();
        _origType = (String) values[5];
        _origFor = (String) values[6];
        ignorePagination = (Boolean) values[7];
        renderLabelAsButton = (Boolean) values[8];
        styleClass = (String) values[9];
        includeColumns = (String) values[10];
        rows = ((Integer) values[11]).intValue();
        first = ((Integer) values[12]).intValue();
        popupBlockerLabel = (String) values[13];
    }

    public String getLabel() {
        String label = super.getLabel();
        if (label == null) {
            if (resource instanceof FileResource)
                label = ((FileResource) resource).getFile().getName();
        }
        return label;
    }

    public void broadcast(FacesEvent event) throws AbortProcessingException {
        super.broadcast(event);
        if (event != null) {
            FacesContext facesContext = FacesContext.getCurrentInstance();
            String type = getType();
            UIData uiData = getUIData();
            File output = createFile(facesContext, type, uiData);
            setResource(new FileResource(output));
            getResource();
            JavascriptContext.addJavascriptCall(facesContext,
                    "Ice.DataExporters['" + getClientId(facesContext) + "'].url('" + getPath() + "');");
        }
    }

    private File createFile(FacesContext fc, String type, UIData uiData) {
        OutputTypeHandler outputHandler = null;
        String path = CoreUtils.getRealPath(fc, "/export");
        File exportDir = new File(path);
        if (!exportDir.exists())
            exportDir.mkdirs();
        String pathWithoutExt = path + "/export_" + new Date().getTime();

        if (getOutputTypeHandler() != null)
            outputHandler = getOutputTypeHandler();
        else if (DataExporter.EXCEL_TYPE.equals(getType())) {
            outputHandler = new ExcelOutputHandler(pathWithoutExt + ".xls", fc, uiData.getId());
        } else if (DataExporter.CSV_TYPE.equals(getType())) {
            outputHandler = new CSVOutputHandler(pathWithoutExt + ".csv");
        } else if (DataExporter.PDF_TYPE.equals(getType())) {
            outputHandler = new PDFOutputHandler(pathWithoutExt + ".pdf", uiData.getId());
        } else {
            outputHandler = NoopOutputHandler;
        }
        renderToHandler(outputHandler, uiData, fc);
        setMimeType(outputHandler.getMimeType());

        return outputHandler.getFile();
    }

    private String encodeParentAndChildrenAsString(FacesContext fc, UIComponent uic) {
        StringBuffer str = new StringBuffer();
        if (uic instanceof CommandSortHeader) {
            if (uic.getChildCount() > 0) {
                Iterator iter = uic.getChildren().iterator();
                while (iter.hasNext()) {
                    UIComponent child = (UIComponent) iter.next();
                    str.append(encodeParentAndChildrenAsString(fc, child));
                }
            }
        }
        Object value = uic.getAttributes().get("value");
        if (value == null) {
            ValueBinding vb = uic.getValueBinding("value");
            if (vb != null) {
                value = vb.getValue(fc);
            }
        }
        if (value == null) {
            return str.toString();
        }
        Converter converter = null;
        if (uic instanceof ValueHolder) {
            converter = ((ValueHolder) uic).getConverter();
        }
        if (converter == null) {
            converter = FacesContext.getCurrentInstance().getApplication().createConverter(value.getClass());
        }
        if (converter != null) {
            str.append(converter.getAsString(FacesContext.getCurrentInstance(), uic, value));
        } else {
            str.append(value);
        }

        //don't process selectItems or f:param for uiCommand)
        if (uic instanceof UISelectBoolean || uic instanceof UISelectMany || uic instanceof UISelectOne
                || uic instanceof UICommand) {
            return str.toString();
        }

        if (uic.getChildCount() > 0) {
            Iterator iter = uic.getChildren().iterator();
            while (iter.hasNext()) {
                UIComponent child = (UIComponent) iter.next();
                str.append(encodeParentAndChildrenAsString(fc, child));
            }
        }
        return str.toString();
    }

    protected List getRenderedChildColumnsList(UIComponent component) {
        List results = new ArrayList();
        Iterator kids = component.getChildren().iterator();
        while (kids.hasNext()) {
            UIComponent kid = (UIComponent) kids.next();
            if ((kid instanceof UIColumn) && kid.isRendered()) {
                results.add(kid);
            } else if (kid instanceof UIColumns) {
                results.add(kid);
            }
        }
        return results;
    }

    private void renderToHandler(OutputTypeHandler outputHandler, UIData uiData, FacesContext fc) {

        try {
            int rowIndex = 0;
            int numberOfRowsToDisplay = 0;
            if (!isIgnorePagination()) {
                rowIndex = uiData.getFirst();
                numberOfRowsToDisplay = uiData.getRows();
                first = rowIndex;
                rows = numberOfRowsToDisplay;
            }

            int countOfRowsDisplayed = 0;
            uiData.setRowIndex(rowIndex);
            String[] includeColumnsArray = null;
            String includeColumns = getIncludeColumns();
            if (includeColumns != null)
                includeColumnsArray = includeColumns.split(",");
            List columns = getRenderedChildColumnsList(uiData);
            //System.out.println("DataExporter.renderToHandler()  columns.size: " + columns.size());

            // write header
            //System.out.println("DataExporter.renderToHandler()  HEADERS");
            processAllColumns(fc, outputHandler, columns, includeColumnsArray, -1, false);

            //System.out.println("DataExporter.renderToHandler()  ROWS");
            while (uiData.isRowAvailable()) {
                if (numberOfRowsToDisplay > 0 && countOfRowsDisplayed >= numberOfRowsToDisplay) {
                    break;
                }

                // render the child columns; each one in a td
                processAllColumns(fc, outputHandler, columns, includeColumnsArray, countOfRowsDisplayed, false);

                // keep track of rows displayed
                countOfRowsDisplayed++;
                // maintain the row index property on the underlying UIData
                // component
                rowIndex++;
                uiData.setRowIndex(rowIndex);

            }
            // reset the underlying UIData component
            uiData.setRowIndex(-1);

            // write footer
            //System.out.println("DataExporter.renderToHandler()  FOOTERS");
            processAllColumns(fc, outputHandler, columns, includeColumnsArray, countOfRowsDisplayed, true);

            outputHandler.flushFile();
        } catch (Exception e) {
            log.error("renderToHandler()", e);
        }
    }

    public void addInfo() {
    }

    /**
     * <p>Set the value of the <code>ignorePagination</code> property.</p>
     */
    public void setIgnorePagination(boolean ignorePagination) {
        this.ignorePagination = new Boolean(ignorePagination);
    }

    /**
     * <p>Return the value of the <code>ignorePagination</code> property.</p>
     */
    public boolean isIgnorePagination() {
        if (ignorePagination != null) {
            return ignorePagination.booleanValue();
        }
        ValueBinding vb = getValueBinding("ignorePagination");
        return vb != null ? ((Boolean) vb.getValue(getFacesContext())).booleanValue() : false;
    }

    /**
     * <p>Set the value of the <code>renderLabelAsButton</code> property.</p>
     */
    public void setRenderLabelAsButton(boolean renderLabelAsButton) {
        this.renderLabelAsButton = new Boolean(renderLabelAsButton);
    }

    /**
     * <p>Return the value of the <code>renderLabelAsButton</code> property.</p>
     */
    public boolean isRenderLabelAsButton() {
        if (renderLabelAsButton != null) {
            return renderLabelAsButton.booleanValue();
        }
        ValueBinding vb = getValueBinding("renderLabelAsButton");
        return vb != null ? ((Boolean) vb.getValue(getFacesContext())).booleanValue() : false;
    }

    public void setStyleClass(String styleClass) {
        this.styleClass = styleClass;
    }

    public String getStyleClass() {
        return Util.getQualifiedStyleClass(this, styleClass, CSS_DEFAULT.DATAEXPORTER_DEFAULT_STYLE_CLASS,
                "styleClass");
    }

    protected void processAllColumns(FacesContext fc, OutputTypeHandler outputHandler, List columns,
            String[] includeColumnsArray, int countOfRowsDisplayed, boolean footer) {
        if (includeColumnsArray != null) {
            //System.out.println("DataExporter.processAllColumns()  COLUMNS ARRAY");
            renderInUserDefinedOrder(fc, outputHandler, columns, includeColumnsArray, 0, countOfRowsDisplayed,
                    footer);
        } else {
            //System.out.println("DataExporter.processAllColumns()  COLUMNS ITERATING");
            int colIndex = 0;
            Iterator childColumns = columns.iterator();
            while (childColumns.hasNext()) {
                UIComponent nextColumn = (UIComponent) childColumns.next();
                if (nextColumn instanceof UIColumn) {
                    //System.out.println("DataExporter.processAllColumns()  UIColumn  colIndex: " + colIndex);
                    if (countOfRowsDisplayed == -1) {
                        //System.out.println("DataExporter.processAllColumns()    HEADER CELL");
                        processColumnHeader(fc, outputHandler, nextColumn, colIndex);
                    } else if (footer) {
                        //System.out.println("DataExporter.processAllColumns()    FOOTER CELL");
                        processColumnFooter(fc, outputHandler, nextColumn, colIndex, countOfRowsDisplayed);
                    } else {
                        //System.out.println("DataExporter.processAllColumns()    CELL");
                        processColumn(fc, outputHandler, nextColumn, colIndex, countOfRowsDisplayed);
                    }
                    colIndex++;
                } else if (nextColumn instanceof UIColumns) {
                    UIColumns nextColumns = (UIColumns) nextColumn;
                    int colsSpecificIndex = nextColumns.getFirst();
                    int count = nextColumns.getRows();
                    int colsSpecificLast = (count == 0) ? (Integer.MAX_VALUE - 1) : (colsSpecificIndex + count - 1);
                    //System.out.println("DataExporter.processAllColumns()  UIColumns  first: " + colsSpecificIndex + "  rows: " + count);
                    while (colsSpecificIndex <= colsSpecificLast) {
                        nextColumns.setRowIndex(colsSpecificIndex);
                        if (!nextColumns.isRowAvailable()) {
                            break;
                        }
                        if (countOfRowsDisplayed == -1) {
                            //System.out.println("DataExporter.processAllColumns()    HEADER CELL  colIndex: " + colIndex);
                            processColumnHeader(fc, outputHandler, nextColumn, colIndex);
                        } else if (footer) {
                            //System.out.println("DataExporter.processAllColumns()    FOOTER CELL  colIndex: " + colIndex);
                            processColumnFooter(fc, outputHandler, nextColumn, colIndex, countOfRowsDisplayed);
                        } else {
                            //System.out.println("DataExporter.processAllColumns()    CELL  colIndex: " + colIndex);
                            processColumn(fc, outputHandler, nextColumn, colIndex, countOfRowsDisplayed);
                        }
                        colsSpecificIndex++;
                        colIndex++;
                    }
                    nextColumns.setRowIndex(-1);
                }
            }
        }
    }

    protected void processColumnHeader(FacesContext fc, OutputTypeHandler outputHandler, UIComponent uiColumn,
            int colIndex) {
        UIComponent headerComp = uiColumn.getFacet("header");
        if (headerComp != null) {
            String headerText = encodeParentAndChildrenAsString(fc, headerComp);
            if (headerText != null) {
                outputHandler.writeHeaderCell(headerText, colIndex);
            }
        }
    }

    protected void processColumnFooter(FacesContext fc, OutputTypeHandler outputHandler, UIComponent uiColumn,
            int colIndex, int countOfRowsDisplayed) {
        UIComponent footerComp = uiColumn.getFacet("footer");
        if (footerComp != null) {
            Object output = encodeParentAndChildrenAsString(fc, footerComp);
            if (output != null) {
                outputHandler.writeFooterCell(output, colIndex, countOfRowsDisplayed);
            }
        }
    }

    protected void processColumn(FacesContext fc, OutputTypeHandler outputHandler, UIComponent uiColumn,
            int colIndex, int countOfRowsDisplayed) {
        StringBuffer stringOutput = new StringBuffer();

        Iterator childrenOfThisColumn = uiColumn.getChildren().iterator();
        while (childrenOfThisColumn.hasNext()) {

            UIComponent nextChild = (UIComponent) childrenOfThisColumn.next();
            if (nextChild.isRendered() && !(nextChild instanceof RowSelector)) {
                stringOutput.append(encodeParentAndChildrenAsString(fc, nextChild));
                //a blank to separate 
                if (childrenOfThisColumn.hasNext()) {
                    stringOutput.append(' ');
                }
            }

        }
        outputHandler.writeCell(stringOutput.toString(), colIndex, countOfRowsDisplayed);

    }

    /**
      */
    public String getIncludeColumns() {
        if (this.includeColumns != null) {
            return includeColumns;
        }
        ValueBinding vb = getValueBinding("includeColumns");
        return vb != null ? (String) vb.getValue(getFacesContext()) : null;
    }

    /**
    */
    public void setIncludeColumns(String includeColumns) {
        this.includeColumns = includeColumns;
    }

    protected void renderInUserDefinedOrder(FacesContext fc, OutputTypeHandler outputHandler, List columns,
            String[] includeColumnsArray, int colIndex, int countOfRowsDisplayed, boolean footer) {
        UIComponent previousColumn = null;
        for (int i = 0; i < includeColumnsArray.length; i++) {
            //System.out.println("DataExporter.renderInUserDefinedOrder()  includeColumnsArray["+i+"]: " + includeColumnsArray[i]);
            int userIndex = 0;
            try {
                userIndex = Integer.parseInt(includeColumnsArray[i].trim());
            } catch (Exception e) {
                log.error("renderInUserDefinedOrder() invalid column index ", e);
                continue;
            }
            //System.out.println("DataExporter.renderInUserDefinedOrder()  userIndex: " + userIndex);
            UIComponent nextColumn = null;
            int runningIndex = 0;
            for (int listIndex = 0; listIndex < columns.size(); listIndex++) {
                UIComponent runningComp = (UIComponent) columns.get(listIndex);
                if (runningComp instanceof UIColumn) {
                    //System.out.println("DataExporter.renderInUserDefinedOrder()    Check UIColumn  runningIndex: " + runningIndex);
                    if (runningIndex == userIndex) {
                        //System.out.println("DataExporter.renderInUserDefinedOrder()      MATCH");
                        nextColumn = runningComp;
                        break;
                    }
                    runningIndex++;
                } else if (runningComp instanceof UIColumns) {
                    //System.out.println("DataExporter.renderInUserDefinedOrder()    Check UIColumns");
                    UIColumns runningCols = (UIColumns) runningComp;
                    // If rows is specified, so it's possible to know if 
                    // userIndex is contained in this UIColumns or not.
                    int runningColsSize = runningCols.getRows();
                    if (runningColsSize != 0) {
                        //System.out.println("DataExporter.renderInUserDefinedOrder()      rows specified");
                        if (userIndex >= runningIndex && userIndex < runningIndex + runningColsSize) {
                            int colsSpecificIndex = userIndex - runningIndex + runningCols.getFirst();
                            runningIndex = userIndex;
                            runningCols.setRowIndex(colsSpecificIndex);
                            //System.out.println("DataExporter.renderInUserDefinedOrder()      MATCH  colsSpecificIndex: " + colsSpecificIndex);
                            nextColumn = runningCols;
                            break;
                        } else {
                            //System.out.println("DataExporter.renderInUserDefinedOrder()      beyond rows");
                            runningIndex += runningColsSize;
                        }
                    }
                    // Have to just iterate over the UIColumns to determine
                    // its size, to know if userIndex falls within it.
                    else {
                        //System.out.println("DataExporter.renderInUserDefinedOrder()      rows not specified");
                        int colsSpecificIndex = runningCols.getFirst();
                        while (true) {
                            runningCols.setRowIndex(colsSpecificIndex);
                            if (!runningCols.isRowAvailable()) {
                                break;
                            }
                            if (runningIndex == userIndex) {
                                //System.out.println("DataExporter.renderInUserDefinedOrder()      MATCH  colsSpecificIndex: " + colsSpecificIndex);
                                nextColumn = runningCols;
                                break;
                            }
                            runningIndex++;
                            colsSpecificIndex++;
                        }
                        if (nextColumn != null) {
                            break;
                        }

                        // If we just scanned through runningCols, and aren't 
                        // using it, clean it up, unless we're going to already
                        if (nextColumn == null && runningCols != previousColumn) {
                            runningCols.setRowIndex(-1);
                        }
                    }
                }
            }

            if (nextColumn == null) {
                log.info("[" + userIndex
                        + "] is invalid column index. Column index is 0 based and should be less then from "
                        + runningIndex);
                continue;
            }

            // If we're switching away from a UIColumns, clean it up
            if (previousColumn != null && nextColumn != previousColumn && previousColumn instanceof UIColumns) {
                ((UIColumns) previousColumn).setRowIndex(-1);
            }
            previousColumn = nextColumn;

            if (countOfRowsDisplayed == -1) {
                processColumnHeader(fc, outputHandler, nextColumn, colIndex);
            } else if (footer) {
                processColumnFooter(fc, outputHandler, nextColumn, colIndex, countOfRowsDisplayed);
            } else {
                processColumn(fc, outputHandler, nextColumn, colIndex, countOfRowsDisplayed);
            }
            colIndex++;
        }

        // Cleanup any trailing UIColumns
        if (previousColumn != null && previousColumn instanceof UIColumns) {
            ((UIColumns) previousColumn).setRowIndex(-1);
        }
    }

    public String getPopupBlockerLabel() {
        if (this.popupBlockerLabel != null) {
            return popupBlockerLabel;
        }
        ValueBinding vb = getValueBinding("popupBlockerLabel");
        return vb != null ? (String) vb.getValue(getFacesContext()) : null;
    }

    public void setPopupBlockerLabel(String popupBlockerLabel) {
        this.popupBlockerLabel = popupBlockerLabel;
    }
}