Package net.sf.jasperreports.engine.fill

Source Code of net.sf.jasperreports.engine.fill.JRFillCrosstab$JRFillCrosstabDataset

/*
* JasperReports - Free Java Reporting Library.
* Copyright (C) 2001 - 2009 Jaspersoft Corporation. All rights reserved.
* http://www.jaspersoft.com
*
* Unless you have purchased a commercial license agreement from Jaspersoft,
* the following license terms apply:
*
* This program is part of JasperReports.
*
* JasperReports is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JasperReports is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with JasperReports. If not, see <http://www.gnu.org/licenses/>.
*/
package net.sf.jasperreports.engine.fill;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;

import net.sf.jasperreports.crosstabs.JRCellContents;
import net.sf.jasperreports.crosstabs.JRCrosstab;
import net.sf.jasperreports.crosstabs.JRCrosstabBucket;
import net.sf.jasperreports.crosstabs.JRCrosstabCell;
import net.sf.jasperreports.crosstabs.JRCrosstabColumnGroup;
import net.sf.jasperreports.crosstabs.JRCrosstabDataset;
import net.sf.jasperreports.crosstabs.JRCrosstabGroup;
import net.sf.jasperreports.crosstabs.JRCrosstabMeasure;
import net.sf.jasperreports.crosstabs.JRCrosstabParameter;
import net.sf.jasperreports.crosstabs.JRCrosstabRowGroup;
import net.sf.jasperreports.crosstabs.base.JRBaseCrosstab;
import net.sf.jasperreports.crosstabs.design.JRDesignCrosstab;
import net.sf.jasperreports.crosstabs.fill.JRCrosstabExpressionEvaluator;
import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabCell;
import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabColumnGroup;
import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabGroup;
import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabMeasure;
import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabObjectFactory;
import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabParameter;
import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabRowGroup;
import net.sf.jasperreports.crosstabs.fill.calculation.BucketDefinition;
import net.sf.jasperreports.crosstabs.fill.calculation.BucketingService;
import net.sf.jasperreports.crosstabs.fill.calculation.CrosstabCell;
import net.sf.jasperreports.crosstabs.fill.calculation.HeaderCell;
import net.sf.jasperreports.crosstabs.fill.calculation.MeasureDefinition;
import net.sf.jasperreports.crosstabs.fill.calculation.BucketDefinition.Bucket;
import net.sf.jasperreports.crosstabs.fill.calculation.MeasureDefinition.MeasureValue;
import net.sf.jasperreports.crosstabs.type.CrosstabColumnPositionEnum;
import net.sf.jasperreports.crosstabs.type.CrosstabPercentageEnum;
import net.sf.jasperreports.crosstabs.type.CrosstabRowPositionEnum;
import net.sf.jasperreports.engine.JRElement;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExpression;
import net.sf.jasperreports.engine.JRExpressionChunk;
import net.sf.jasperreports.engine.JRExpressionCollector;
import net.sf.jasperreports.engine.JROrigin;
import net.sf.jasperreports.engine.JRParameter;
import net.sf.jasperreports.engine.JRPrintElement;
import net.sf.jasperreports.engine.JRPrintFrame;
import net.sf.jasperreports.engine.JRPrintRectangle;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRVariable;
import net.sf.jasperreports.engine.JRVisitor;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.design.JRDesignRectangle;
import net.sf.jasperreports.engine.type.ModeEnum;
import net.sf.jasperreports.engine.type.RunDirectionEnum;
import net.sf.jasperreports.engine.util.JRProperties;
import net.sf.jasperreports.engine.util.JRStyleResolver;

import org.jfree.data.general.Dataset;

/**
* Fill-time implementation of a {@link net.sf.jasperreports.crosstabs.JRCrosstab crosstab}.
*
* @author Lucian Chirita (lucianc@users.sourceforge.net)
* @version $Id: JRFillCrosstab.java 4017 2010-10-27 12:10:17Z teodord $
*/
public class JRFillCrosstab extends JRFillElement implements JRCrosstab, JROriginProvider
{
  final protected JRCrosstab parentCrosstab;

  protected JRFillCrosstabDataset dataset;

  protected JRFillCrosstabRowGroup[] rowGroups;

  protected Map rowGroupsMap;

  protected JRFillCrosstabColumnGroup[] columnGroups;

  protected Map columnGroupsMap;

  protected JRFillCrosstabMeasure[] measures;

  protected BucketingService bucketingService;

  protected JRFillVariable[] variables;

  protected Map variablesMap;
 
  protected JRFillVariable[][][] totalVariables;
  protected boolean[][] retrieveTotal;

  protected JRFillCrosstabParameter[] parameters;

  protected Map parametersMap;
 
  protected boolean ignoreWidth;

  protected JRCrosstabExpressionEvaluator crosstabEvaluator;

  protected JRFillCrosstabCell[][] crossCells;
  protected JRFillCellContents headerCell;
  protected JRFillCellContents whenNoDataCell;

  protected boolean hasData;
  protected HeaderCell[][] columnHeadersData;
  protected HeaderCell[][] rowHeadersData;
  protected CrosstabCell[][] cellData;
  protected MeasureValue[] grandTotals;

  private boolean percentage;

  private CrosstabFiller crosstabFiller;
  private int overflowStartPage;
 
  private List fillElements;
 
  public JRFillCrosstab(JRBaseFiller filler, JRCrosstab crosstab, JRFillObjectFactory factory)
  {
    super(filler, crosstab, factory);

    parentCrosstab = crosstab;

    loadEvaluator(filler.getJasperReport());

    JRFillCrosstabObjectFactory crosstabFactory = new JRFillCrosstabObjectFactory(
        factory, crosstabEvaluator);
    crosstabFactory.setParentOriginProvider(this);
   
    headerCell = crosstabFactory.getCell(crosstab.getHeaderCell(),
        JRCellContents.TYPE_CROSSTAB_HEADER);

    copyRowGroups(crosstab, crosstabFactory);
    copyColumnGroups(crosstab, crosstabFactory);
   
    copyMeasures(crosstab, crosstabFactory);
    copyCells(crosstab, crosstabFactory);
    whenNoDataCell = crosstabFactory.getCell(crosstab.getWhenNoDataCell(),
        JRCellContents.TYPE_NO_DATA_CELL);
   
    dataset = factory.getCrosstabDataset(crosstab.getDataset(), this);

    copyParameters(crosstab, factory);
    copyVariables(crosstab, crosstabFactory);

    // default value from property
    this.ignoreWidth = JRProperties.getBooleanProperty(filler.jasperReport,
        PROPERTY_IGNORE_WIDTH, false);
    Boolean crosstabIgnoreWidth = crosstab.getIgnoreWidth();
    // crosstab attribute overrides property
    if (crosstabIgnoreWidth != null)
    {
      this.ignoreWidth = crosstabIgnoreWidth.booleanValue();
    }
   
    crosstabFiller = new CrosstabFiller();
  }

  /**
   *
   */
  public ModeEnum getModeValue()
  {
    return JRStyleResolver.getMode(this, ModeEnum.TRANSPARENT);
  }

  private void copyRowGroups(JRCrosstab crosstab, JRFillCrosstabObjectFactory factory)
  {
    JRCrosstabRowGroup[] groups = crosstab.getRowGroups();
    rowGroups = new JRFillCrosstabRowGroup[groups.length];
    rowGroupsMap = new HashMap();
    for (int i = 0; i < groups.length; ++i)
    {
      JRFillCrosstabRowGroup group = factory.getCrosstabRowGroup(groups[i]);
      group.getFillHeader().setVerticalPositionType(groups[i].getPositionValue());

      rowGroups[i] = group;
      rowGroupsMap.put(group.getName(), Integer.valueOf(i));
    }
  }

  private void copyColumnGroups(JRCrosstab crosstab, JRFillCrosstabObjectFactory factory)
  {
    JRCrosstabColumnGroup[] groups = crosstab.getColumnGroups();
    columnGroups = new JRFillCrosstabColumnGroup[groups.length];
    columnGroupsMap = new HashMap();
    for (int i = 0; i < groups.length; ++i)
    {
      JRFillCrosstabColumnGroup group = factory.getCrosstabColumnGroup(groups[i]);
      columnGroups[i] = group;
      columnGroupsMap.put(group.getName(), Integer.valueOf(i));
    }
  }

  private void copyMeasures(JRCrosstab crosstab, JRFillCrosstabObjectFactory factory)
  {
    JRCrosstabMeasure[] crossMeasures = crosstab.getMeasures();
    measures = new JRFillCrosstabMeasure[crossMeasures.length];
    for (int i = 0; i < crossMeasures.length; i++)
    {
      measures[i] = factory.getCrosstabMeasure(crossMeasures[i]);
    }
  }

  private void copyParameters(JRCrosstab crosstab, JRFillObjectFactory factory)
  {
    JRCrosstabParameter[] crossParams = crosstab.getParameters();
    parameters = new JRFillCrosstabParameter[crossParams.length];
    parametersMap = new HashMap();
    for (int i = 0; i < crossParams.length; i++)
    {
      parameters[i] = factory.getCrosstabParameter(crossParams[i]);
      parametersMap.put(parameters[i].getName(), parameters[i]);
    }
  }

  private void copyCells(JRCrosstab crosstab, JRFillCrosstabObjectFactory factory)
  {
    JRCrosstabCell[][] crosstabCells = crosstab.getCells();   
    crossCells = new JRFillCrosstabCell[rowGroups.length + 1][columnGroups.length + 1];
    for (int i = 0; i <= rowGroups.length; ++i)
    {
      for (int j = 0; j <= columnGroups.length; ++j)
      {
        if (crosstabCells[i][j] != null)
        {
          crossCells[i][j] = factory.getCrosstabCell(crosstabCells[i][j]);
        }
      }
    }
  }

  private void copyVariables(JRCrosstab crosstab, JRFillObjectFactory factory)
  {
    JRVariable[] vars = crosstab.getVariables();
    variables = new JRFillVariable[vars.length];
    variablesMap = new HashMap();
    for (int i = 0; i < variables.length; i++)
    {
      variables[i] = factory.getVariable(vars[i]);
      variablesMap.put(variables[i].getName(), variables[i]);
    }
   
    Map totalVarPos = new HashMap();
    totalVariables = new JRFillVariable[rowGroups.length + 1][columnGroups.length + 1][measures.length];
    for (int row = 0; row <= rowGroups.length; ++row)
    {
      JRCrosstabRowGroup rowGroup = row == rowGroups.length ? null : rowGroups[row];
      for (int col = 0; col <= columnGroups.length; ++col)
      {
        JRCrosstabColumnGroup colGroup = col == columnGroups.length ? null : columnGroups[col];
       
        if (row < rowGroups.length || col < columnGroups.length)
        {
          for (int m = 0; m < measures.length; m++)
          {
            String totalVariableName = JRDesignCrosstab.getTotalVariableName(measures[m], rowGroup, colGroup);
            totalVariables[row][col][m] = (JRFillVariable) variablesMap.get(totalVariableName);
            totalVarPos.put(totalVariableName, new int[]{row, col});
          }
        }
      }
    }

    retrieveTotal = new boolean[rowGroups.length + 1][columnGroups.length + 1];
   
    //FIXME avoid this
    List expressions = JRExpressionCollector.collectExpressions(filler.getJasperReport(), crosstab);
    for (Iterator iter = expressions.iterator(); iter.hasNext();)
    {
      JRExpression expression = (JRExpression) iter.next();
      JRExpressionChunk[] chunks = expression.getChunks();
      if (chunks != null)
      {
        for (int i = 0; i < chunks.length; i++)
        {
          JRExpressionChunk chunk = chunks[i];
          if (chunk.getType() == JRExpressionChunk.TYPE_VARIABLE)
          {
            String varName = chunk.getText();
            int[] pos = (int[]) totalVarPos.get(varName);
            if (pos != null)
            {
              retrieveTotal[pos[0]][pos[1]] = true;
            }
          }
        }
      }
    }
  }

  protected void loadEvaluator(JasperReport jasperReport)
  {
    try
    {
      JREvaluator evaluator = JasperCompileManager.loadEvaluator(jasperReport, parentCrosstab);
      crosstabEvaluator = new JRCrosstabExpressionEvaluator(evaluator);
    }
    catch (JRException e)
    {
      throw new JRRuntimeException("Could not load evaluator for crosstab.", e);
    }
  }

  private BucketingService createService(byte evaluation) throws JRException
  {
    boolean hasOrderByExpression = false;
    List rowBuckets = new ArrayList(rowGroups.length);
    for (int i = 0; i < rowGroups.length; ++i)
    {
      JRFillCrosstabRowGroup group = rowGroups[i];
      rowBuckets.add(createServiceBucket(group, evaluation));
      hasOrderByExpression |= group.getBucket().getOrderByExpression() != null;
    }

    List colBuckets = new ArrayList(columnGroups.length);
    for (int i = 0; i < columnGroups.length; ++i)
    {
      JRFillCrosstabColumnGroup group = columnGroups[i];
      colBuckets.add(createServiceBucket(group, evaluation));
      hasOrderByExpression |= group.getBucket().getOrderByExpression() != null;
    }

    percentage = false;
    List measureList = new ArrayList(measures.length);
    for (int i = 0; i < measures.length; ++i)
    {
      measureList.add(createServiceMeasure(measures[i]));
      percentage |= measures[i].getPercentageType() == CrosstabPercentageEnum.GRAND_TOTAL;
    }

    // if a group has order by expression, compute totals as they might be used
    // in the expression
    // TODO refine this
    if (percentage || hasOrderByExpression)
    {
      ((BucketDefinition) rowBuckets.get(0)).setComputeTotal();
      ((BucketDefinition) colBuckets.get(0)).setComputeTotal();
    }
   
    return new BucketingService(this, rowBuckets, colBuckets, measureList, dataset.isDataPreSorted(), retrieveTotal);
  }

  private BucketDefinition createServiceBucket(JRCrosstabGroup group, byte evaluation) throws JRException
  {
    JRCrosstabBucket bucket = group.getBucket();

    Comparator comparator = null;
    JRExpression comparatorExpression = bucket.getComparatorExpression();
    if (comparatorExpression != null)
    {
      comparator = (Comparator) evaluateExpression(comparatorExpression, evaluation);
    }

    return new BucketDefinition(bucket.getExpression().getValueClass(),
        bucket.getOrderByExpression(), comparator, bucket.getOrderValue(),
        group.getTotalPositionValue());
  }

  private MeasureDefinition createServiceMeasure(JRFillCrosstabMeasure measure)
  {
    return new MeasureDefinition(
        measure.getValueClass(),
        measure.getCalculationValue(),
        measure.getIncrementerFactory());
  }

  public Object evaluateExpression(JRExpression expression, MeasureValue[] measureValues)
      throws JRException
  {
    for (int i = 0; i < measures.length; i++)
    {
      Object value = measureValues[i].getValue();
      measures[i].getFillVariable().setValue(value);
    }
   
    return crosstabEvaluator.evaluate(expression, JRExpression.EVALUATION_DEFAULT);
  }
 
  protected void reset()
  {
    super.reset();

    for (int i = 0; i < variables.length; i++)
    {
      variables[i].setValue(null);
      variables[i].setInitialized(true);
    }
   
    fillElements = null;
  }

  protected void evaluate(byte evaluation) throws JRException
  {
    reset();

    evaluatePrintWhenExpression(evaluation);
    evaluateProperties(evaluation);

    if (isPrintWhenExpressionNull() || isPrintWhenTrue())
    {
      dataset.evaluateDatasetRun(evaluation);

      initEvaluator(evaluation);

      bucketingService.processData();

      hasData = bucketingService.hasData();
     
      if (hasData)
      {
        columnHeadersData = bucketingService.getColumnHeaders();
        rowHeadersData = bucketingService.getRowHeaders();
        cellData = bucketingService.getCrosstabCells();
        if (percentage)
        {
          grandTotals = bucketingService.getGrandTotals();
        }

        crosstabFiller.initCrosstab();
      }
     
      overflowStartPage = 0;
    }
  }

  protected void initEvaluator(byte evaluation) throws JRException
  {
    Map parameterValues =
      JRFillSubreport.getParameterValues(
        filler,
        getParametersMapExpression(),
        getParameters(),
        evaluation,
        true,
        false,//hasResourceBundle
        false//hasFormatFactory
        );
   
    ResourceBundle resBdl = (ResourceBundle) parameterValues.get(JRParameter.REPORT_RESOURCE_BUNDLE);
    if (resBdl == null)
    {
      JRFillParameter resourceBundleParam = (JRFillParameter) filler.getParametersMap().get(JRParameter.REPORT_RESOURCE_BUNDLE);
      parameterValues.put(JRParameter.REPORT_RESOURCE_BUNDLE, resourceBundleParam.getValue());
    }
   
    parameterValues.put(JRParameter.REPORT_PARAMETERS_MAP, parameterValues);

    for (int i = 0; i < parameters.length; i++)
    {
      Object value = parameterValues.get(parameters[i].getName());
      parameters[i].setValue(value);
    }

    crosstabEvaluator.init(parametersMap, variablesMap, filler.getWhenResourceMissingType());
  }

  protected void initBucketingService()
  {
    if (bucketingService == null)
    {
      try
      {
        bucketingService = createService(JRExpression.EVALUATION_DEFAULT);
      }
      catch (JRException e)
      {
        throw new JRRuntimeException("Could not create bucketing service", e);
      }
    }
    else
    {
      bucketingService.clear();
    }
  }

  protected boolean prepare(int availableHeight, boolean isOverflow) throws JRException
  {
    super.prepare(availableHeight, isOverflow);

    if (!isToPrint())
    {
      return false;
    }

    if (availableHeight < getRelativeY() + getHeight())
    {
      setToPrint(false);
      return true;
    }

    if (isOverflow && crosstabFiller.ended() && isAlreadyPrinted())
    {
      if (isPrintWhenDetailOverflows())
      {
        rewind();
        setReprinted(true);
      }
      else
      {
        setStretchHeight(getHeight());
        setToPrint(false);

        return false;
      }
    }

    if (isOverflow && isPrintWhenDetailOverflows())
    {
      setReprinted(true);
    }

    crosstabFiller.fill(availableHeight - getRelativeY());

    if (crosstabFiller.hasFilledRows())
    {
      // crosstab content has been filled, rest overflowPage
      overflowStartPage = 0;
    }
    else
    {
      int pageCount = filler.getCurrentPageCount();
      if (overflowStartPage == 0)
      {
        // first empty page
        overflowStartPage = pageCount;
      }
      else if (pageCount >= overflowStartPage + 2)
      {
        throw new JRRuntimeException("Crosstab has not printed anything on 3 consecutive pages, "
            + "likely infinite loop");
      }
    }
   
    boolean willOverflow = crosstabFiller.willOverflow();
    setStretchHeight(willOverflow ? availableHeight - getRelativeY() : crosstabFiller.getUsedHeight());
   
    return willOverflow;
  }

  protected JRPrintElement fill()
  {
    JRPrintRectangle printRectangle = null;
   
    printRectangle = new JRTemplatePrintRectangle(getJRTemplateRectangle());
    printRectangle.setX(getX());
    printRectangle.setY(getRelativeY());
    printRectangle.setWidth(getWidth());
    printRectangle.setHeight(getStretchHeight());

    fillElements = crosstabFiller.getPrintElements();

    if (ignoreWidth && !fillElements.isEmpty())
    {
      int xLimit = getXLimit(fillElements);
      // if the elements exceed the design crosstab width
      if (xLimit > getWidth())
      {
        // increase the rectangle width
        printRectangle.setWidth(xLimit);
        if (getRunDirectionValue() == RunDirectionEnum.RTL)
        {
          // if RTL filling, move the rectangle to the left
          printRectangle.setX(getX() + getWidth() - xLimit);
        }
      }
    }
   
    if (getRunDirectionValue() == RunDirectionEnum.RTL)
    {
      mirrorPrintElements(fillElements);
    }

    return printRectangle;
  }
 
  protected int getXLimit(List printElements)
  {
    int limit = Integer.MIN_VALUE;
    for (Iterator it = printElements.iterator(); it.hasNext();)
    {
      JRPrintElement element = (JRPrintElement) it.next();
      if (element.getX() + element.getWidth() > limit)
      {
        limit = element.getX() + element.getWidth();
      }
    }
    return limit;
  }

  protected JRTemplateRectangle getJRTemplateRectangle()
  {
    return (JRTemplateRectangle) getElementTemplate();
  }

  protected JRTemplateElement createElementTemplate()
  {
    JRDesignRectangle rectangle = new JRDesignRectangle();

    rectangle.setKey(getKey());
    rectangle.setPositionType(getPositionTypeValue());
    // rectangle.setPrintRepeatedValues(isPrintRepeatedValues());
    rectangle.setMode(getModeValue());
    rectangle.setX(getX());
    rectangle.setY(getY());
    rectangle.setWidth(getWidth());
    rectangle.setHeight(getHeight());
    rectangle.setRemoveLineWhenBlank(isRemoveLineWhenBlank());
    rectangle.setPrintInFirstWholeBand(isPrintInFirstWholeBand());
    rectangle.setPrintWhenDetailOverflows(isPrintWhenDetailOverflows());
    rectangle.setPrintWhenGroupChanges(getPrintWhenGroupChanges());
    rectangle.setForecolor(getForecolor());
    rectangle.setBackcolor(getBackcolor());
    rectangle.getLinePen().setLineWidth(0f);

    return new JRTemplateRectangle(getElementOrigin(),
        filler.getJasperPrint().getDefaultStyleProvider(), rectangle);
  }

  protected void rewind()
  {
    crosstabFiller.initCrosstab();
   
    overflowStartPage = 0;
  }

  protected List getPrintElements()
  {
    return fillElements;
  }

  protected void mirrorPrintElements(List printElements)
  {
    for (Iterator it = printElements.iterator(); it.hasNext();)
    {
      JRPrintElement element = (JRPrintElement) it.next();
      int mirrorX = getWidth() - element.getX() - element.getWidth();
      element.setX(mirrorX);
    }
  }

  protected void resolveElement(JRPrintElement element, byte evaluation)
  {
    // nothing
  }

  public void collectExpressions(JRExpressionCollector collector)
  {
    collector.collect(this);
  }

  /**
   *
   */
  public void visit(JRVisitor visitor)
  {
    visitor.visitCrosstab(this);
  }

  public int getId()
  {
    return parentCrosstab.getId();
  }

  public JRCrosstabDataset getDataset()
  {
    return dataset;
  }

  public JRCrosstabRowGroup[] getRowGroups()
  {
    return rowGroups;
  }

  public JRCrosstabColumnGroup[] getColumnGroups()
  {
    return columnGroups;
  }

  public JRCrosstabMeasure[] getMeasures()
  {
    return measures;
  }

 
  /**
   * Fill-time crosstab input dataset implementation.
   * 
   * @author Lucian Chirita (lucianc@users.sourceforge.net)
   */
  public class JRFillCrosstabDataset extends JRFillElementDataset implements JRCrosstabDataset
  {
    private Object[] bucketValues;

    private Object[] measureValues;

    public JRFillCrosstabDataset(JRCrosstabDataset dataset, JRFillObjectFactory factory)
    {
      super(dataset, factory);

      this.bucketValues = new Object[rowGroups.length + columnGroups.length];
      this.measureValues = new Object[measures.length];
    }

    protected void customInitialize()
    {
      initBucketingService();
    }

    protected void customEvaluate(JRCalculator calculator) throws JRExpressionEvalException
    {
      for (int i = 0; i < rowGroups.length; i++)
      {
        bucketValues[i] = calculator.evaluate(rowGroups[i].getBucket().getExpression());
      }

      for (int i = 0; i < columnGroups.length; ++i)
      {
        bucketValues[i + rowGroups.length] = calculator.evaluate(columnGroups[i].getBucket().getExpression());
      }

      for (int i = 0; i < measures.length; i++)
      {
        measureValues[i] = calculator.evaluate(measures[i].getValueExpression());
      }
    }

    protected void customIncrement()
    {
      try
      {
        bucketingService.addData(bucketValues, measureValues);
      }
      catch (JRException e)
      {
        throw new JRRuntimeException("Error incrementing crosstab dataset", e);
      }
    }

    protected Dataset getCustomDataset()
    {
      return null;
    }

    public void collectExpressions(JRExpressionCollector collector)
    {
    }

    public boolean isDataPreSorted()
    {
      return ((JRCrosstabDataset) parent).isDataPreSorted();
    }
  }
 
  /**
   * Crosstab filler class.
   * 
   * @author Lucian Chirita (lucianc@users.sourceforge.net)
   */
  protected class CrosstabFiller
  {
    private int yOffset;
    private boolean willOverflow;

    private int[] rowHeadersXOffsets;
   
    private boolean[] columnBreakable;
    private boolean[] rowBreakable;
    private int[] columnCount;
    private int[] rowCount;
    private int[] columnXOffsets;
   
    private boolean noDataCellPrinted;
   
    private int startRowIndex;
    private int startColumnIndex;
    private int lastColumnIndex;
    private List columnHeaders;

    private List printRows;

    private HeaderCell[] spanHeaders;
    private int[] spanHeadersStart;

    private List rowYs = new ArrayList();
    private int rowIdx;
   
    private List preparedRow = new ArrayList();
    private int preparedRowHeight;
   
    private boolean printRowHeaders;
    private boolean printColumnHeaders;
   
    private JRFillVariable rowCountVar;
    private JRFillVariable colCountVar;

    protected CrosstabFiller()
    {
      setRowHeadersXOffsets();

      printRows = new ArrayList();
     
      rowCountVar = (JRFillVariable) variablesMap.get(JRCrosstab.VARIABLE_ROW_COUNT);
      colCountVar = (JRFillVariable) variablesMap.get(JRCrosstab.VARIABLE_COLUMN_COUNT);
    }
   
    protected void initCrosstab()
    {
      columnXOffsets = computeOffsets(columnHeadersData, columnGroups, true);
      columnBreakable = computeBreakableHeaders(columnHeadersData, columnGroups, columnXOffsets, true, true);
      columnCount = computeCounts(columnHeadersData);
     
      int[] rowYOffsets = computeOffsets(rowHeadersData, rowGroups, false);
      rowBreakable = computeBreakableHeaders(rowHeadersData, rowGroups, rowYOffsets, false, false);
      rowCount = computeCounts(rowHeadersData);
     
      spanHeaders = new HeaderCell[rowGroups.length - 1];
      spanHeadersStart = new int[rowGroups.length - 1];
     
      startRowIndex = 0;
      startColumnIndex = 0;
      lastColumnIndex = 0;
      noDataCellPrinted = false;
    }

    protected void setRowHeadersXOffsets()
    {
      rowHeadersXOffsets = new int[rowGroups.length + 1];
      rowHeadersXOffsets[0] = 0;
      for (int i = 0; i < rowGroups.length; i++)
      {
        rowHeadersXOffsets[i + 1] = rowHeadersXOffsets[i] + rowGroups[i].getWidth();
      }
    }

    protected int[] computeOffsets(HeaderCell[][] headersData, JRFillCrosstabGroup[] groups, boolean width)
    {
      int[] offsets = new int[headersData[0].length + 1];
      offsets[0] = 0;
      for (int i = 0; i < headersData[0].length; i++)
      {
        int size = 0;
        for (int j = groups.length - 1; j >= 0; --j)
        {
          if (headersData[j][i] != null)
          {
            JRFillCellContents cell = headersData[j][i].isTotal() ? groups[j].getFillTotalHeader() : groups[j].getFillHeader();
            size = cell == null ? 0 : (width ? cell.getWidth() : cell.getHeight());
            break;
          }
        }

        offsets[i + 1] = offsets[i] + size;
      }

      return offsets;
    }

    protected boolean[] computeBreakableHeaders(HeaderCell[][] headersData, JRFillCrosstabGroup[] groups, int[] offsets, boolean width, boolean startHeaders)
    {
      boolean[] breakable = new boolean[headersData[0].length];
      for (int i = 0; i < breakable.length; i++)
      {
        breakable[i] = true;
      }

      for (int j = 0; j < groups.length; ++j)
      {
        JRFillCellContents fillHeader = groups[j].getFillHeader();
       
        if (fillHeader != null)
        {
          int size = width ? fillHeader.getWidth() : fillHeader.getHeight();
         
          for (int i = 0; i < headersData[0].length; i++)
          {
            HeaderCell header = headersData[j][i];
            if (header != null && !header.isTotal() && header.getLevelSpan() > 1)
            {
              int span = header.getLevelSpan();
             
              if (startHeaders)
              {
                for (int k = i + 1; k < i + span && offsets[k] - offsets[i] < size; ++k)
                {
                  breakable[k] = false;
                }
              }
             
              for (int k = i + span - 1; k > i && offsets[i + span] - offsets[k] < size; --k)
              {
                breakable[k] = false;
              }
            }
          }
        }
      }

      return breakable;
    }

    private int[] computeCounts(HeaderCell[][] headersData)
    {
      int[] counts = new int[headersData[0].length];
     
      HeaderCell[] lastHeaders = headersData[headersData.length - 1];
      for (int i = 0, c = 0; i < counts.length; ++i)
      {
        HeaderCell lastHeader = lastHeaders[i];
        if (lastHeader != null && !lastHeader.isTotal())
        {
          ++c;
        }
       
        counts[i] = c;
      }
     
      return counts;
    }

    protected void fill(int availableHeight) throws JRException
    {
      printRows.clear();

      yOffset = 0;
      willOverflow = false;
     
      fillVerticalCrosstab(availableHeight);
    }
   
    protected boolean hasFilledRows()
    {
      return !printRows.isEmpty();
    }
   
    protected boolean willOverflow()
    {
      return willOverflow;
    }
   
    protected int getUsedHeight()
    {
      return yOffset;
    }
   
    protected boolean ended()
    {
      return hasData ? (startRowIndex >= rowHeadersData[0].length && startColumnIndex >= columnHeadersData[0].length) : noDataCellPrinted;
    }
   
    protected void fillVerticalCrosstab(int availableHeight) throws JRException
    {
      if (!hasData)
      {
        fillNoDataCell(availableHeight);     
        return;
      }
     
      printRowHeaders = startColumnIndex == 0 || isRepeatRowHeaders();
      int rowHeadersXOffset = printRowHeaders ? rowHeadersXOffsets[rowGroups.length] : 0;

      if (startColumnIndex == lastColumnIndex)
      {
        int availableWidth = getWidth();

        columnHeaders = getGroupHeaders(availableWidth - rowHeadersXOffset, columnXOffsets, columnBreakable, startColumnIndex, columnHeadersData, columnGroups);
        lastColumnIndex = startColumnIndex + columnHeaders.size();

        if (startColumnIndex == lastColumnIndex)
        {
          throw new JRRuntimeException("Not enough space to render the crosstab.");
        }
      }
     
      printColumnHeaders = startRowIndex == 0 || isRepeatColumnHeaders();
      List columnHeaderRows = null;
      if (printColumnHeaders)
      {
        columnHeaderRows = fillColumnHeaders(rowHeadersXOffset, availableHeight - yOffset);
        if (willOverflow)
        {
          return;
        }
      }

      int lastRowIndex = fillRows(rowHeadersXOffset, availableHeight - yOffset);

      if (lastRowIndex == startRowIndex)
      {
        willOverflow = true;
        return;
      }

      if (columnHeaderRows != null)
      {
        printRows.addAll(columnHeaderRows);
      }
     
      if (lastRowIndex >= rowHeadersData[0].length)
      {
        startColumnIndex = lastColumnIndex;

        if (startColumnIndex < columnHeadersData[0].length)
        {
          startRowIndex = lastRowIndex = 0;

          yOffset += getColumnBreakOffset();
          fillVerticalCrosstab(availableHeight);
          return;
        }
      }

      boolean fillEnded = lastRowIndex >= rowHeadersData[0].length && lastColumnIndex >= columnHeadersData[0].length;
      if (fillEnded)
      {
        setStretchHeight(yOffset);
      }
      else
      {
        setStretchHeight(availableHeight);
      }

      startRowIndex = lastRowIndex;

      willOverflow = !fillEnded;
    }

   
    protected List getGroupHeaders(int available, int[] offsets, boolean[] breakable, int firstIndex, HeaderCell[][] headersData, JRFillCrosstabGroup[] groups)
    {
      List headers = new ArrayList();

      int maxOffset = available + offsets[firstIndex];
      int lastIndex;
      for (lastIndex = firstIndex;
        lastIndex < headersData[0].length
          && (ignoreWidth || offsets[lastIndex + 1] <= maxOffset);
        ++lastIndex)
      {
        HeaderCell[] groupHeaders = new HeaderCell[groups.length];

        for (int j = 0; j < groups.length; ++j)
        {
          groupHeaders[j] = headersData[j][lastIndex];
        }

        headers.add(groupHeaders);
      }

     
      if (lastIndex < headersData[0].length)
      {
        while(lastIndex > firstIndex && !breakable[lastIndex])
        {
          --lastIndex;
          headers.remove(headers.size() - 1);
        }
      }
     
      if (lastIndex > firstIndex)
      {
        if (firstIndex > 0)
        {
          HeaderCell[] firstHeaders = (HeaderCell[]) headers.get(0);

          for (int j = 0; j < groups.length; ++j)
          {
            HeaderCell header = headersData[j][firstIndex];

            if (header == null)
            {
              int spanIndex = getSpanIndex(firstIndex, j, headersData);
              if (spanIndex >= 0)
              {
                HeaderCell spanCell = headersData[j][spanIndex];
                int headerEndIdx = spanCell.getLevelSpan() + spanIndex;
                if (headerEndIdx > lastIndex)
                {
                  headerEndIdx = lastIndex;
                }
                firstHeaders[j] = HeaderCell.createLevelSpanCopy(spanCell, headerEndIdx - firstIndex);
              }
            }
          }
        }

        if (lastIndex < headersData[0].length)
        {
          for (int j = 0; j < groups.length; ++j)
          {
            HeaderCell header = headersData[j][lastIndex];

            if (header == null)
            {
              int spanIndex = getSpanIndex(lastIndex, j, headersData);
              if (spanIndex >= firstIndex)
              {
                HeaderCell spanCell = headersData[j][spanIndex];
                HeaderCell[] headerCells = (HeaderCell[]) headers.get(spanIndex - firstIndex);
                headerCells[j] = HeaderCell.createLevelSpanCopy(spanCell, lastIndex - spanIndex);
              }
            }
          }
        }
      }

      return headers;
    }

   
    protected int getSpanIndex(int i, int j, HeaderCell[][] headersData)
    {
      int spanIndex = i - 1;
      while (spanIndex >= 0 && headersData[j][spanIndex] == null)
      {
        --spanIndex;
      }

      if (spanIndex >= 0)
      {
        HeaderCell spanCell = headersData[j][spanIndex];
        int span = spanCell.getLevelSpan();

        if (span > i - spanIndex)
        {
          return spanIndex;
        }
      }

      return -1;
    }
   
   
    protected void fillNoDataCell(int availableHeight) throws JRException
    {
      if (whenNoDataCell == null)
      {
        noDataCellPrinted = true;
      }
      else
      {
        if (availableHeight < whenNoDataCell.getHeight())
        {
          willOverflow = true;
        }
        else
        {
          whenNoDataCell.evaluate(JRExpression.EVALUATION_DEFAULT);
          whenNoDataCell.prepare(availableHeight);
         
          willOverflow = whenNoDataCell.willOverflow();
         
          if (!willOverflow)
          {
            whenNoDataCell.setX(0);
            whenNoDataCell.setY(0);
           
            JRPrintFrame printCell = whenNoDataCell.fill();
            List noDataRow = new ArrayList(1);
            noDataRow.add(printCell);
            addPrintRow(noDataRow);
           
            yOffset += whenNoDataCell.getPrintHeight();
            noDataCellPrinted = true;
          }
        }
      }
    }
   

    protected List fillColumnHeaders(int rowHeadersXOffset, int availableHeight) throws JRException
    {
      JRFillCellContents[][] columnHeaderRows = new JRFillCellContents[columnGroups.length][lastColumnIndex - startColumnIndex + 1];
     
      rowYs.clear();
      rowYs.add(Integer.valueOf(0));
     
      if (printRowHeaders && headerCell != null)
      {
        JRFillCellContents contents = fillHeader(availableHeight);

        if (willOverflow)
        {
          return null;
        }

        columnHeaderRows[columnGroups.length - 1][0] = contents;
      }
     
      rows:
      for (rowIdx = 0; rowIdx < columnGroups.length; rowIdx++)
      {
        for (int columnIdx = startColumnIndex; columnIdx < lastColumnIndex; ++columnIdx)
        {
          HeaderCell[] headers = (HeaderCell[]) columnHeaders.get(columnIdx - startColumnIndex);
          HeaderCell cell = headers[rowIdx];
         
          if (cell != null)
          {
            JRFillCellContents contents = prepareColumnHeader(cell, columnIdx, rowHeadersXOffset, availableHeight);
            columnHeaderRows[rowIdx + cell.getDepthSpan() - 1][columnIdx - startColumnIndex + 1] = contents;
           
            if (willOverflow)
            {
              break rows;
            }
          }
        }

        int rowStretchHeight = stretchColumnHeadersRow(columnHeaderRows[rowIdx]);
        rowYs.add(Integer.valueOf(((Integer) rowYs.get(rowIdx)).intValue() + rowStretchHeight));
      }
     
      List headerRows;
      if (willOverflow)
      {
        headerRows = null;       
        releaseColumnHeaderCells(columnHeaderRows);
      }
      else
      {
        headerRows = fillColumnHeaders(columnHeaderRows);
        yOffset += ((Integer) rowYs.get(columnGroups.length)).intValue();
      }

      resetVariables();
     
      return headerRows;
    }

   
    private void setCountVars(int rowIdx, int colIdx)
    {
      if (rowIdx == -1)
      {
        rowCountVar.setValue(null);
      }
      else
      {
        rowCountVar.setValue(Integer.valueOf(rowCount[rowIdx]));
      }
     
      if (colIdx == -1)
      {
        colCountVar.setValue(null);
      }
      else
      {
        colCountVar.setValue(Integer.valueOf(columnCount[colIdx]));
      }
    }
   
    private JRFillCellContents fillHeader(int availableHeight) throws JRException
    {
      setCountVars(-1, -1);
     
      JRFillCellContents contents = headerCell.getWorkingClone();
      contents.evaluate(JRExpression.EVALUATION_DEFAULT);
      contents.prepare(availableHeight);
     
      willOverflow = contents.willOverflow();
     
      if (!willOverflow)
      {
        contents.setX(0);
        contents.setY(yOffset);
        contents.setVerticalSpan(columnGroups.length);
        contents.setHorizontalSpan(rowGroups.length);
      }
      return contents;
    }
   
    private JRFillCellContents prepareColumnHeader(HeaderCell cell, int columnIdx, int xOffset, int availableHeight) throws JRException
    {
      JRFillCrosstabColumnGroup group = columnGroups[rowIdx];
      JRFillCellContents contents = cell.isTotal() ? group.getFillTotalHeader() : group.getFillHeader();

      int width = columnXOffsets[columnIdx + cell.getLevelSpan()] - columnXOffsets[columnIdx];
      int height = contents.getHeight();
     
      if (width <= 0 || height <= 0)
      {
        return null;
      }
     
      JRFillCellContents preparedContents = null;
     
      int rowY = ((Integer) rowYs.get(rowIdx)).intValue();
     
      if (availableHeight >=  rowY + height)
      {
        setCountVars(-1, columnIdx);
        setGroupVariables(columnGroups, cell.getBucketValues());
       
        contents = contents.getTransformedContents(width, height, group.getPositionValue(), CrosstabRowPositionEnum.TOP);
        boolean firstOnRow = columnIdx == startColumnIndex && (!printRowHeaders || headerCell == null);
        contents = contents.getBoxContents(
            firstOnRow && getRunDirectionValue() == RunDirectionEnum.LTR,
            firstOnRow && getRunDirectionValue() == RunDirectionEnum.RTL,
            false);
        contents = contents.getWorkingClone();

        contents.evaluate(JRExpression.EVALUATION_DEFAULT);
        contents.prepare(availableHeight - rowY);

        if (contents.willOverflow())
        {
          willOverflow = true;
        }
        else
        {
          contents.setX(columnXOffsets[columnIdx] - columnXOffsets[startColumnIndex] + xOffset);
          contents.setY(rowY + yOffset);
          contents.setVerticalSpan(cell.getDepthSpan());
          contents.setHorizontalSpan(cell.getLevelSpan());
         
          preparedContents = contents;
        }
      }
      else
      {
        willOverflow = true;
      }
     
      return preparedContents;
    }

   
    private int stretchColumnHeadersRow(JRFillCellContents[] headers)
    {
      int rowY = ((Integer) rowYs.get(rowIdx)).intValue();
     
      int rowStretchHeight = 0;
      for (int j = 0; j < headers.length; j++)
      {
        JRFillCellContents contents = headers[j];
       
        if (contents != null)
        {
          int startRowY = rowY;
          if (contents.getVerticalSpan() > 1)
          {
            startRowY = ((Integer) rowYs.get(rowIdx - contents.getVerticalSpan() + 1)).intValue();
          }
         
          int height = contents.getPrintHeight() - rowY + startRowY;
         
          if (height > rowStretchHeight)
          {
            rowStretchHeight = height;
          }
        }
      }
     
      for (int j = 0; j < headers.length; j++)
      {
        JRFillCellContents contents = headers[j];
       
        if (contents != null)
        {
          int startRowY = rowY;
          if (contents.getVerticalSpan() > 1)
          {
            startRowY = ((Integer) rowYs.get(rowIdx - contents.getVerticalSpan() + 1)).intValue();
          }
         
          contents.stretchTo(rowStretchHeight + rowY - startRowY);
        }
      }
     
      return rowStretchHeight;
    }

   
    private List fillColumnHeaders(JRFillCellContents[][] columnHeaderRows) throws JRException
    {
      List headerRows = new ArrayList(columnGroups.length);
     
      for (int i = 0; i < columnHeaderRows.length; ++i)
      {
        List headerRow = new ArrayList(lastColumnIndex - startColumnIndex);
        headerRows.add(headerRow);
       
        for (int j = 0; j < columnHeaderRows[i].length; j++)
        {
          JRFillCellContents contents = columnHeaderRows[i][j];
         
          if (contents != null)
          {
            headerRow.add(contents.fill());
            contents.releaseWorkingClone();
          }
        }
      }
     
      return headerRows;
    }

    private void releaseColumnHeaderCells(JRFillCellContents[][] columnHeaderRows) throws JRException
    {
      for (int i = 0; i < columnHeaderRows.length; ++i)
      {
        for (int j = 0; j < columnHeaderRows[i].length; j++)
        {
          JRFillCellContents contents = columnHeaderRows[i][j];
         
          if (contents != null)
          {
            contents.rewind();
            contents.releaseWorkingClone();
          }
        }
      }
    }
   
    protected int fillRows(int xOffset, int availableHeight) throws JRException
    {
      rowYs.clear();     
      rowYs.add(Integer.valueOf(0));

      for (rowIdx = 0; rowIdx < cellData.length - startRowIndex; ++rowIdx)
      {
        initPreparedRow();
       
        prepareRow(xOffset, availableHeight);

        if (willOverflow)
        {
          break;
        }
       
        fillRow();
       
        rowYs.add(Integer.valueOf(((Integer) rowYs.get(rowIdx)).intValue() + preparedRowHeight));
      }
     
      if (rowIdx < cellData.length - startRowIndex)//overflow
      {
        releasePreparedRow();
       
        if (printRowHeaders)
        {
          fillContinuingRowHeaders(xOffset, availableHeight);
        }
      }
     
      yOffset += ((Integer) rowYs.get(rowIdx)).intValue();

      return rowIdx + startRowIndex;
    }

    private void initPreparedRow()
    {
      preparedRow.clear();
      preparedRowHeight = 0;
    }

    private void removeFilledRows(int rowsToRemove)
    {
      if (rowsToRemove > 0)
      {
        for (int i = 0; i < rowsToRemove; ++i)
        {
          printRows.remove(printRows.size() - 1);
          rowYs.remove(rowYs.size() - 1);
        }
       
        rowIdx -= rowsToRemove;
      }
    }

    private void releasePreparedRow() throws JRException
    {
      for (Iterator it = preparedRow.iterator(); it.hasNext();)
      {
        JRFillCellContents cell = (JRFillCellContents) it.next();
        cell.rewind();
        cell.releaseWorkingClone();
      }
     
      preparedRow.clear();
    }

    private void fillRow() throws JRException
    {
      int rowY = ((Integer) rowYs.get(rowIdx)).intValue();
     
      List rowPrints = new ArrayList(preparedRow.size());
      for (Iterator it = preparedRow.iterator(); it.hasNext();)
      {
        JRFillCellContents cell = (JRFillCellContents) it.next();
       
        int spanHeight = 0;
        if (cell.getVerticalSpan() > 1)
        {
          spanHeight = rowY - ((Integer) rowYs.get(rowIdx - cell.getVerticalSpan() + 1)).intValue();
        }
       
        cell.stretchTo(preparedRowHeight + spanHeight);
        rowPrints.add(cell.fill());
       
        cell.releaseWorkingClone();
      }
     
      addPrintRow(rowPrints);
    }
   
    private void prepareRow(int xOffset, int availableHeight) throws JRException
    {
      for (int col = startColumnIndex; col < lastColumnIndex; ++col)
      {
        CrosstabCell data = cellData[rowIdx + startRowIndex][col];
        boolean overflow = prepareDataCell(data, col, availableHeight, xOffset);
       
        if (overflow)
        {
          willOverflow = true;
          return;
        }
      }
     
      resetVariables();
     
      if (printRowHeaders)
      {
        for (int j = 0; j < rowGroups.length; j++)
        {
          HeaderCell cell = rowHeadersData[j][rowIdx + startRowIndex];
          int vSpan = 0;
          if (cell == null)
          {
            // if we have a span header
            if (toCloseRowHeader(j))
            {
              cell = spanHeaders[j];
              vSpan = cell.getLevelSpan();
              if (spanHeadersStart[j] < startRowIndex)//continuing from the prev page
              {
                vSpan += spanHeadersStart[j] - startRowIndex;
              }
            }
          }
          else
          {
            if (cell.getLevelSpan() > 1)
            {
              spanHeaders[j] = cell;
              spanHeadersStart[j] = rowIdx + startRowIndex;
              continue;
            }
           
            vSpan = 1;
          }
         
          if (cell != null)
          {
            boolean overflow = prepareRowHeader(j, cell, vSpan, availableHeight);           
            if (overflow)
            {
              willOverflow = true;
              return;
            }
          }
        }
       
        // successfully prepared a row, reset the span headers
        for (int j = 0; j < rowGroups.length; j++)
        {
          // if we have a span header
          if (rowHeadersData[j][rowIdx + startRowIndex] == null
              && toCloseRowHeader(j))
          {
            // reset it
            spanHeaders[j] = null;
          }
        }
       
        resetVariables();
      }
    }
   
    private boolean prepareDataCell(CrosstabCell data, int column, int availableHeight, int xOffset) throws JRException
    {
      int rowY = ((Integer) rowYs.get(rowIdx)).intValue();
     
      JRFillCrosstabCell cell = crossCells[data.getRowTotalGroupIndex()][data.getColumnTotalGroupIndex()];
      JRFillCellContents contents = cell == null ? null : cell.getFillContents();
      if (contents == null || contents.getWidth() <= 0 || contents.getHeight() <= 0)
      {
        return false;
      }
     
      boolean overflow = availableHeight < rowY + contents.getHeight();
      if (!overflow)
      {
        boolean leftEmpty = startColumnIndex != 0 && !isRepeatRowHeaders();
        boolean topEmpty = startRowIndex != 0 && !isRepeatColumnHeaders();
       
        setCountVars(rowIdx + startRowIndex, column);
        setGroupVariables(rowGroups, data.getRowBucketValues());
        setGroupVariables(columnGroups, data.getColumnBucketValues());
        setMeasureVariables(data);
       
        boolean firstOnRow = leftEmpty && column == startColumnIndex;
        contents = contents.getBoxContents(
            firstOnRow && getRunDirectionValue() == RunDirectionEnum.LTR,
            firstOnRow && getRunDirectionValue() == RunDirectionEnum.RTL,
            topEmpty && rowIdx == 0);
        contents = contents.getWorkingClone();
       
        contents.evaluate(JRExpression.EVALUATION_DEFAULT);
        contents.prepare(availableHeight - rowY);
               
        preparedRow.add(contents);
       
        overflow = contents.willOverflow();
       
        if (!overflow)
        {
          contents.setX(columnXOffsets[column] - columnXOffsets[startColumnIndex] + xOffset);
          contents.setY(rowY + yOffset);

          int rowCellHeight = contents.getPrintHeight();
          if (rowCellHeight > preparedRowHeight)
          {
            preparedRowHeight = rowCellHeight;
          }
        }
      }
     
      return overflow;
    }

    private boolean prepareRowHeader(int rowGroup, HeaderCell cell, int vSpan, int availableHeight) throws JRException
    {
      JRFillCrosstabRowGroup group = rowGroups[rowGroup];
      JRFillCellContents contents = cell.isTotal() ? group.getFillTotalHeader() : group.getFillHeader();

      if (contents.getWidth() <= 0 || contents.getHeight() <= 0)
      {
        return false;
      }
     
      int spanHeight = 0;
      int headerY = ((Integer) rowYs.get(rowIdx - vSpan + 1)).intValue();
      if (vSpan > 1)
      {
        spanHeight += ((Integer) rowYs.get(rowIdx)).intValue() - headerY;
      }
      int rowHeight = spanHeight + preparedRowHeight;
     
      boolean stretchContents = group.getPositionValue() == CrosstabRowPositionEnum.STRETCH;
      int contentsHeight = stretchContents ? rowHeight : contents.getHeight();
     
      boolean headerOverflow = availableHeight <  headerY + contentsHeight || rowHeight < contents.getHeight();
     
      if (!headerOverflow)
      {
        setCountVars(rowIdx + startRowIndex - vSpan + 1, -1);
        setGroupVariables(rowGroups, cell.getBucketValues());

        if (stretchContents)
        {
          contents = contents.getTransformedContents(contents.getWidth(), rowHeight, CrosstabColumnPositionEnum.LEFT, CrosstabRowPositionEnum.STRETCH);
        }
        contents = contents.getBoxContents(false, false, rowIdx + 1 == vSpan && (!printColumnHeaders || headerCell == null));
        contents.getWorkingClone();

        contents.evaluate(JRExpression.EVALUATION_DEFAULT);
        contents.prepare(availableHeight - headerY);
       
        preparedRow.add(contents);

        headerOverflow = contents.willOverflow();
       
        if (!headerOverflow)
        {
          contents.setX(rowHeadersXOffsets[rowGroup]);
          contents.setY(headerY + yOffset);
          contents.setVerticalSpan(vSpan);
          contents.setHorizontalSpan(cell.getDepthSpan());
         
          int rowCellHeight = contents.getPrintHeight() - spanHeight;
          if (rowCellHeight > preparedRowHeight)
          {
            preparedRowHeight = rowCellHeight;
          }
        }
      }
     
      if (headerOverflow)
      {
        removeFilledRows(vSpan - 1);
      }
     
      return headerOverflow;
    }

    protected boolean toCloseRowHeader(int rowGroup)
    {
      return rowGroup < rowGroups.length - 1 &&
          spanHeaders[rowGroup] != null &&
          spanHeaders[rowGroup].getLevelSpan() + spanHeadersStart[rowGroup] ==
            rowIdx + startRowIndex + 1;
    }

    private void removeExceedingSpanHeaders()
    {
      for (int j = rowGroups.length - 2; j >= 0; --j)
      {
        if (spanHeaders[j] != null && spanHeadersStart[j] >= rowIdx + startRowIndex)
        {
          spanHeaders[j] = null;
        }
      }
    }

    private void setBackSpanHeaders()
    {
      for (int j = rowGroups.length - 2; j >= 0 && spanHeaders[j] == null; --j)
      {
        int spanIndex = getSpanIndex(rowIdx + startRowIndex, j, rowHeadersData);
       
        if (spanIndex >= 0)
        {
          spanHeaders[j] = rowHeadersData[j][spanIndex];
          spanHeadersStart[j] = spanIndex;
        }
      }
    }

    private void fillContinuingRowHeaders(int xOffset, int availableHeight) throws JRException
    {
      boolean done = false;
      breakCrosstab:
      do
      {
        removeExceedingSpanHeaders();
       
        if (!rowBreakable[rowIdx + startRowIndex])
        {
          removeFilledRows(1);
          setBackSpanHeaders();
          continue;
        }

        initPreparedRow();
       
        //fill continuing headers
        for (int j = 0; j < rowGroups.length - 1; ++j)
        {
          if (spanHeaders[j] != null)
          {
            boolean headerOverflow = prepareContinuingRowHeader(j, availableHeight);
           
            if (headerOverflow)
            {
              releasePreparedRow();
              continue breakCrosstab;
            }
          }
        }

        if (!preparedRow.isEmpty())
        {
          int lastRowHeight = ((Integer) rowYs.get(rowIdx)).intValue() - ((Integer) rowYs.get(rowIdx - 1)).intValue();
         
          if (preparedRowHeight > lastRowHeight)//need to stretch already filled row by refilling
          {
            refillLastRow(xOffset, availableHeight);
          }
          else
          {
            fillContinuingHeaders(lastRowHeight);
          }
        }
       
        done = true;
      }
      while (!done && rowIdx > 0);
    }

    private void fillContinuingHeaders(int lastRowHeight) throws JRException
    {
      int nextToLastHeaderY = ((Integer) rowYs.get(rowIdx - 1)).intValue();
      List lastPrintRow = getLastPrintRow();
     
      for (int j = 0; j < preparedRow.size(); ++j)
      {
        JRFillCellContents contents = (JRFillCellContents) preparedRow.get(j);
       
        int headerY = ((Integer) rowYs.get(rowIdx - contents.getVerticalSpan())).intValue();
       
        contents.stretchTo(nextToLastHeaderY - headerY + lastRowHeight);
        lastPrintRow.add(contents.fill());
        contents.releaseWorkingClone();
      }
    }

    private void refillLastRow(int xOffset, int availableHeight) throws JRException
    {
      removeFilledRows(1);
      setBackSpanHeaders();
     
      prepareRow(xOffset, availableHeight);
      fillRow();
     
      rowYs.add(Integer.valueOf(((Integer) rowYs.get(rowIdx)).intValue() + preparedRowHeight));
      ++rowIdx;
    }

    private boolean prepareContinuingRowHeader(int rowGroup, int availableHeight) throws JRException
    {
      HeaderCell cell = spanHeaders[rowGroup];
      int vSpan = rowIdx + startRowIndex - spanHeadersStart[rowGroup];

      if (spanHeadersStart[rowGroup] < startRowIndex)//continuing from the prev page
      {
        vSpan += spanHeadersStart[rowGroup] - startRowIndex;
      }

      int headerY = ((Integer) rowYs.get(rowIdx - vSpan)).intValue();
      int lastHeaderY = ((Integer) rowYs.get(rowIdx)).intValue();
      int headerHeight = lastHeaderY - headerY;
      int nextToLastHeaderY = ((Integer) rowYs.get(rowIdx - 1)).intValue();
      int stretchHeight = nextToLastHeaderY - headerY;
     
      JRFillCrosstabRowGroup group = rowGroups[rowGroup];
      JRFillCellContents contents = cell.isTotal() ? group.getFillTotalHeader() : group.getFillHeader();
     
      boolean stretchContents = group.getPositionValue() == CrosstabRowPositionEnum.STRETCH;
      int contentsHeight = stretchContents ? headerHeight : contents.getHeight();
     
      boolean headerOverflow = availableHeight < headerY + contentsHeight || headerHeight < contents.getHeight();
      if (!headerOverflow)
      {
        setCountVars(rowIdx + startRowIndex - vSpan, -1);
        setGroupVariables(rowGroups, cell.getBucketValues());

        if (stretchContents)
        {
          contents = contents.getTransformedContents(contents.getWidth(), headerHeight, CrosstabColumnPositionEnum.LEFT, CrosstabRowPositionEnum.STRETCH);
        }
       
        contents = contents.getBoxContents(false, false, rowIdx == vSpan && (!printColumnHeaders || headerCell == null));
        contents.getWorkingClone();

        contents.evaluate(JRExpression.EVALUATION_DEFAULT);
        contents.prepare(availableHeight - headerY);
       
        preparedRow.add(contents);

        headerOverflow = contents.willOverflow();

        if (!headerOverflow)
        {
          contents.setX(rowHeadersXOffsets[rowGroup]);
          contents.setY(headerY + yOffset);
          contents.setVerticalSpan(vSpan);
          contents.setHorizontalSpan(cell.getDepthSpan());
         
          int rowHeight = contents.getPrintHeight() - stretchHeight;
          if (rowHeight > preparedRowHeight)
          {
            preparedRowHeight = rowHeight;
          }
        }
      }

      if (headerOverflow)
      {
        removeFilledRows(vSpan);
      }
     
      return headerOverflow;
    }
   
    protected void addPrintRow(List printRow)
    {
      printRows.add(printRow);
    }
   
    protected List getLastPrintRow()
    {
      return (List) printRows.get(printRows.size() - 1);
    }

    protected List getPrintElements()
    {
      List prints = new ArrayList();
     
      for (Iterator it = printRows.iterator(); it.hasNext();)
      {
        List rowPrints = (List) it.next();
        prints.addAll(rowPrints);
      }
     
      Collections.sort(prints, new JRYXComparator());//FIXME make singleton comparator; same for older comparator
     
      return prints;
    }

    protected void setGroupVariables(JRFillCrosstabGroup[] groups, Bucket[] bucketValues)
    {
      for (int i = 0; i < groups.length; i++)
      {
        Object value = null;
        if (bucketValues[i] != null && !bucketValues[i].isTotal())
        {
          value = bucketValues[i].getValue();
        }
        groups[i].getFillVariable().setValue(value);
      }
    }

    protected void setMeasureVariables(CrosstabCell cell)
    {
      MeasureValue[] values = cell.getMesureValues();
      for (int i = 0; i < measures.length; i++)
      {
        Object value = measureValue(values, i);
        measures[i].getFillVariable().setValue(value);
      }
     
      MeasureValue[][][] totals = cell.getTotals();
      for (int row = 0; row <= rowGroups.length; row++)
      {
        for (int col = 0; col <= columnGroups.length; col++)
        {
          MeasureValue[] vals = totals[row][col];
          if (retrieveTotal[row][col])
          {
            for (int m = 0; m < measures.length; m++)
            {
              JRFillVariable totalVar = totalVariables[row][col][m];
              Object value = measureValue(vals, m);
              totalVar.setValue(value);
            }
          }
        }
      }
    }

   
    protected Object measureValue(MeasureValue[] values, int measureIdx)
    {
      Object value;
      if (measures[measureIdx].getPercentageType() == CrosstabPercentageEnum.GRAND_TOTAL)
      {
        if (values[measureIdx].isInitialized())
        {
          value = values[measureIdx].getValue();
        }
        else
        {
          value = measures[measureIdx].getPercentageCalculator().calculatePercentage(values[measureIdx], grandTotals[measureIdx]);
        }
      }
      else
      {
        value = values[measureIdx].getValue();
      }
      return value;
    }

   
    protected void resetVariables()
    {
      for (int i = 0; i < rowGroups.length; i++)
      {
        rowGroups[i].getFillVariable().setValue(null);
      }
     
      for (int i = 0; i < columnGroups.length; i++)
      {
        columnGroups[i].getFillVariable().setValue(null);
      }
     
      for (int i = 0; i < measures.length; i++)
      {
        measures[i].getFillVariable().setValue(null);
      }
     
      for (int row = 0; row <= rowGroups.length; ++row)
      {
        for (int col = 0; col <= columnGroups.length; ++col)
        {
          if (retrieveTotal[row][col])
          {
            for (int i = 0; i < measures.length; i++)
            {
              totalVariables[row][col][i].setValue(null);
            }
          }
        }
      }
    }
  }

  public int getColumnBreakOffset()
  {
    return parentCrosstab.getColumnBreakOffset();
  }

  public boolean isRepeatColumnHeaders()
  {
    return parentCrosstab.isRepeatColumnHeaders();
  }

  public boolean isRepeatRowHeaders()
  {
    return parentCrosstab.isRepeatRowHeaders();
  }

  public JRCrosstabCell[][] getCells()
  {
    return crossCells;
  }

  public JRCellContents getWhenNoDataCell()
  {
    return whenNoDataCell;
  }

  public JRCrosstabParameter[] getParameters()
  {
    return parameters;
  }

  public JRExpression getParametersMapExpression()
  {
    return parentCrosstab.getParametersMapExpression();
  }

 
  public JRElement getElementByKey(String elementKey)
  {
    return JRBaseCrosstab.getElementByKey(this, elementKey);
  }

  public JRFillCloneable createClone(JRFillCloneFactory factory)
  {
    //not needed
    return null;
  }

  public JRCellContents getHeaderCell()
  {
    return headerCell;
  }

  public JRVariable[] getVariables()
  {
    return variables;
  }

  /**
   * @deprecated Replaced by {@link #getRunDirectionValue()}.
   */
  public byte getRunDirection()
  {
    return getRunDirectionValue() == null ? null : getRunDirectionValue().getValue();
  }

  /**
   * @deprecated Replaced by {@link #setRunDirection(RunDirectionEnum)}.
   */
  public void setRunDirection(byte direction)
  {
    throw new UnsupportedOperationException();
  }

  /**
   *
   */
  public RunDirectionEnum getRunDirectionValue()
  {
    return parentCrosstab.getRunDirectionValue();
  }

  /**
   *
   */
  public void setRunDirection(RunDirectionEnum runDirection)
  {
    throw new UnsupportedOperationException();
  }

  public JROrigin getOrigin()
  {
    return getElementOrigin();
  }

  public Boolean getIgnoreWidth()
  {
    return Boolean.valueOf(ignoreWidth);
  }

  public void setIgnoreWidth(Boolean ignoreWidth)
  {
    if (ignoreWidth != null)
    {
      setIgnoreWidth(ignoreWidth.booleanValue());
    }
  }

  public void setIgnoreWidth(boolean ignoreWidth)
  {
    this.ignoreWidth = ignoreWidth;
  }

}
TOP

Related Classes of net.sf.jasperreports.engine.fill.JRFillCrosstab$JRFillCrosstabDataset

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.