Package org.pentaho.reporting.engine.classic.extensions.toc

Source Code of org.pentaho.reporting.engine.classic.extensions.toc.TocDataGeneratorFunction

package org.pentaho.reporting.engine.classic.extensions.toc;

import java.util.ArrayList;
import java.util.Arrays;

import org.pentaho.reporting.engine.classic.core.DataRow;
import org.pentaho.reporting.engine.classic.core.Group;
import org.pentaho.reporting.engine.classic.core.RelationalGroup;
import org.pentaho.reporting.engine.classic.core.event.PageEventListener;
import org.pentaho.reporting.engine.classic.core.event.ReportEvent;
import org.pentaho.reporting.engine.classic.core.function.AbstractFunction;
import org.pentaho.reporting.engine.classic.core.function.Expression;
import org.pentaho.reporting.engine.classic.core.function.FormulaExpression;
import org.pentaho.reporting.engine.classic.core.function.FunctionUtilities;
import org.pentaho.reporting.engine.classic.core.function.PageFunction;
import org.pentaho.reporting.engine.classic.core.function.WrapperExpressionRuntime;
import org.pentaho.reporting.engine.classic.core.states.LayoutProcess;
import org.pentaho.reporting.engine.classic.core.util.IntegerCache;
import org.pentaho.reporting.engine.classic.core.util.TypedTableModel;
import org.pentaho.reporting.libraries.base.util.StringUtils;

/**
* A data-collector that collects table-of-contents items at group-starts. The function
* collects these items accross subreport boundaries.
*
* @author Thomas Morgner.
*/
public class TocDataGeneratorFunction extends AbstractFunction implements PageEventListener
{
  private PageFunction pageFunction;
  private TypedTableModel model;
  private ArrayList groups;
  private ArrayList groupCount;
  private ArrayList groupValues;
  private String titleField;
  private FormulaExpression titleFormula;
  private String indexSeparator;
  private boolean collectDetails;

  private transient int groupIndex;
  private transient boolean rowAdded;
  private transient int rowCounter;
  private transient boolean initialized;
  private int dependencyLevel;

  /**
   * Creates an unnamed function. Make sure the name of the function is set using {@link #setName} before the function
   * is added to the report's function collection.
   */
  public TocDataGeneratorFunction()
  {
    this.groups = new ArrayList();
    this.pageFunction = new PageFunction();
    this.indexSeparator = ".";
    this.model = new TypedTableModel();
    this.titleFormula = new FormulaExpression();
    this.dependencyLevel = LayoutProcess.LEVEL_COLLECT;
  }

  public void setDependencyLevel(final int dependencyLevel)
  {
    this.dependencyLevel = dependencyLevel;
  }

  /**
   * Returns the dependency level for the expression (controls evaluation order for expressions and functions).
   *
   * @return the level.
   */
  public int getDependencyLevel()
  {
    return dependencyLevel;
  }

  public String getIndexSeparator()
  {
    return indexSeparator;
  }

  public void setIndexSeparator(final String indexSeparator)
  {
    this.indexSeparator = indexSeparator;
  }

  public String getTitleFormula()
  {
    return titleFormula.getFormula();
  }

  public void setTitleFormula(final String titleFormula)
  {
    this.titleFormula.setFormula(titleFormula);
  }

  public String getTitleField()
  {
    return titleField;
  }

  public void setTitleField(final String titleField)
  {
    this.titleField = titleField;
  }

  public boolean isCollectDetails()
  {
    return collectDetails;
  }

  public void setCollectDetails(final boolean collectDetails)
  {
    this.collectDetails = collectDetails;
  }

  /**
   * Defines the field in the field-list at the given index.
   *
   * @param index the position in the list, where the field should be defined.
   * @param field the name of the field.
   */
  public void setGroup(final int index, final String field)
  {
    if (groups.size() == index)
    {
      groups.add(field);
    }
    else
    {
      groups.set(index, field);
    }
    groupCount = null;
  }

  /**
   * Returns the defined field at the given index-position.
   *
   * @param index the position of the field name that should be queried.
   * @return the field name at the given position.
   */
  public String getGroup(final int index)
  {
    return (String) groups.get(index);
  }

  /**
   * Returns the number of groups defined in this expression.
   *
   * @return the number of groups.
   */
  public int getGroupCount()
  {
    return groups.size();
  }

  /**
   * Returns all defined groups as array of strings.
   *
   * @return all the groups.
   */
  public String[] getGroup()
  {
    return (String[]) groups.toArray(new String[groups.size()]);
  }

  /**
   * Defines all groups as array. This completely replaces any previously defined groups.
   *
   * @param fields the new list of groups.
   */
  public void setGroup(final String[] fields)
  {
    this.groups.clear();
    this.groups.addAll(Arrays.asList(fields));
    groupCount = null;
  }

  /**
   * Receives notification that report generation initializes the current run. <P> The event carries a
   * ReportState.Started state.  Use this to initialize the report.
   *
   * @param event The event.
   */
  public void reportInitialized(final ReportEvent event)
  {
    if (event.isDeepTraversing())
    {
      return;
    }

    if (initialized == false)
    {
      initialized = true;
      model.addColumn("item-title", Object.class);
      model.addColumn("item-page", Number.class);
      model.addColumn("item-index", String.class);
      model.addColumn("item-index-array", Integer[].class);

      if (groups.size() == 0)
      {
        // we cannot alter the structure of the model after the toc subreport report has been started,
        // so we have to reserve a reasonable amount of groupings here. If you have reports with more
        // than 40 groups, please contact me. I will be delighted to hear about your usage scenario.
        for (int i = 0; i < 40; i++)
        {
          model.addColumn("group-value-" + i, Object.class);
        }
      }
      else
      {
        for (int i = 0; i < groups.size(); i++)
        {
          model.addColumn("group-value-" + i, Object.class);
        }
      }

      groupCount = new ArrayList(groups.size());
      groupValues = new ArrayList(groups.size());
    }

    pageFunction.reportInitialized(event);
  }

  /**
   * Receives notification that the report has started.
   *
   * @param event the event.
   */
  public void reportStarted(final ReportEvent event)
  {
    if (event.isDeepTraversing())
    {
      return;
    }

    pageFunction.reportStarted(event);
    rowAdded = false;
    groupIndex = -1;
    rowCounter = 0;
  }

  public void groupStarted(final ReportEvent event)
  {
    if (event.isDeepTraversing())
    {
      if ("toc".equals(event.getOriginatingState().getReport().getMetaData().getName()))
      {
        return;
      }
    }

    final Group group = FunctionUtilities.getCurrentDeepTraverseGroup(event);
    if (group instanceof RelationalGroup == false)
    {
      return;
    }
    final RelationalGroup relationalGroup = (RelationalGroup) group;

    groupIndex += 1;
    if (groupIndex < getGroupCount() ||
        getGroupCount() == 0)
    {
      rowAdded = false;

      final DataRow dataRow = extractDataRow(event);
      final Object groupValue;
      if (groupIndex < getGroupCount())
      {
        final String fieldName = getGroup(groupIndex);
        if (fieldName != null)
        {
          groupValue = dataRow.get(fieldName);
        }
        else
        {
          groupValue = extractFieldFromGroup(relationalGroup, dataRow);
        }
      }
      else // group-count is zero .. means, collect all groups ..
      {
        groupValue = extractFieldFromGroup(relationalGroup, dataRow);
      }

      addOrUpdateValue(groupValue);
    }
  }

  private DataRow extractDataRow(final ReportEvent event)
  {
    if (event.isDeepTraversing() == false)
    {
      return getDataRow();
    }

    return event.getOriginatingState().getDataRow();
  }

  private Object extractFieldFromGroup(final RelationalGroup relationalGroup,
                                       final DataRow dataRow)
  {
    final Object groupValue;
    final String[] fields = relationalGroup.getFieldsArray();
    if (fields.length == 0)
    {
      groupValue = null;
    }
    else if (fields.length == 1)
    {
      groupValue = dataRow.get(fields[0]);
    }
    else
    {
      final Object[] localGroupValues = new Object[fields.length];
      for (int i = 0; i < localGroupValues.length; i++)
      {
        localGroupValues[i] = dataRow.get(fields[i]);
      }
      groupValue = localGroupValues;
    }
    return groupValue;
  }

  /**
   * Receives notification that a group of item bands is about to be processed. <P> The next events will be
   * itemsAdvanced events until the itemsFinished event is raised.
   *
   * @param event The event.
   */
  public void itemsStarted(final ReportEvent event)
  {
    if (event.isDeepTraversing())
    {
      if ("toc".equals(event.getOriginatingState().getReport().getMetaData().getName()))
      {
        return;
      }
    }

    if (collectDetails)
    {
      groupIndex += 1;
    }
  }

  /**
   * Receives notification that a row of data is being processed.
   *
   * @param event the event.
   */
  public void itemsAdvanced(final ReportEvent event)
  {
    if (event.isDeepTraversing())
    {
      if ("toc".equals(event.getOriginatingState().getReport().getMetaData().getName()))
      {
        return;
      }
    }

    if (collectDetails)
    {
      if (groupIndex < getGroupCount() || getGroupCount() == 0)
      {
        rowAdded = false;
        final Object groupValue;
        if (groupIndex < getGroupCount())
        {
          final String fieldName = getGroup(groupIndex);
          if (fieldName != null)
          {
            final DataRow dataRow = extractDataRow(event);
            groupValue = dataRow.get(fieldName);
          }
          else
          {
            groupValue = null;
          }
        }
        else
        {
          groupValue = null;
        }

        addOrUpdateValue(groupValue);
      }
    }

    collectOrUpdate(event);
  }

  private void collectOrUpdate(final ReportEvent event)
  {
    if (FunctionUtilities.isDefinedPrepareRunLevel(this, event))
    {
      if (rowAdded == false)
      {
        if (isValidGroupValues() == false)
        {
          return;
        }

        final DataRow dataRow = extractDataRow(event);
        final Object titleValue = computeTitleValue(dataRow);
        final StringBuffer indexText = new StringBuffer();
        final Integer[] indexValues = new Integer[groupCount.size()];
        for (int i = 0; i < groupCount.size(); i++)
        {
          if (i != 0)
          {
            indexText.append(this.indexSeparator);
          }
          final Integer o = (Integer) groupCount.get(i);
          indexValues[i] = o;
          indexText.append(o);
        }

        model.setValueAt(titleValue, rowCounter, 0);
        model.setValueAt(new Integer(9999), rowCounter, 1);
        model.setValueAt(indexText.toString(), rowCounter, 2);
        model.setValueAt(indexValues, rowCounter, 3);

        for (int i = 0; i < groupValues.size(); i++)
        {
          final Object groupValue = groupValues.get(i);
          model.setValueAt(groupValue, rowCounter, 4 + i);
        }
        rowCounter += 1;
        rowAdded = true;
      }
    }
    else if (FunctionUtilities.isLayoutLevel(event))
    {
      if (rowAdded == false)
      {
        if (isValidGroupValues() == false)
        {
          return;
        }
        model.setValueAt(pageFunction.getValue(), rowCounter, 1);
        rowAdded = true;
        rowCounter += 1;
      }
    }
  }

  private boolean isValidGroupValues()
  {
    for (int i = 0; i < groupValues.size(); i++)
    {
      final String s = (String) groupValues.get(i);
      if (StringUtils.isEmpty(s) == false)
      {
        return true;
      }
    }
    return false;
  }

  private void addOrUpdateValue(final Object groupValue)
  {
    if (groupCount.size() == groupIndex)
    {
      // new level entered
      groupCount.add(IntegerCache.getInteger(1));
      groupValues.add(groupValue);
    }
    else
    {
      final int lastIndex = groupCount.size() - 1;
      if (lastIndex == groupIndex)
      {
        // existing level increased
        final Integer o = (Integer) groupCount.get(lastIndex);
        if (o == null)
        {
          throw new IllegalStateException();
        }
        groupCount.set(lastIndex, IntegerCache.getInteger(o.intValue() + 1));
        groupValues.set(lastIndex, groupValue);
      }
      else
      {
        throw new IllegalStateException("Out of index error: " + groupIndex + " " + groupCount.size());
      }
    }
  }

  private Object computeTitleValue(final DataRow dataRow)
  {
    if (StringUtils.isEmpty(titleField) == false)
    {
      return dataRow.get(titleField);
    }
    try
    {
      this.titleFormula.setRuntime(new WrapperExpressionRuntime(dataRow, getRuntime()));
      return titleFormula.getValue();
    }
    finally
    {
      this.titleFormula.setRuntime(null);
    }
  }

  /**
   * Receives notification that a group of item bands has been completed. <P> The itemBand is finished, the report
   * starts to close open groups.
   *
   * @param event The event.
   */
  public void itemsFinished(final ReportEvent event)
  {
    if (event.isDeepTraversing())
    {
      if ("toc".equals(event.getOriginatingState().getReport().getMetaData().getName()))
      {
        return;
      }
    }

    // just to make sure that even a empty subreport leaves its mark ..
    collectOrUpdate(event);

    if (collectDetails)
    {
      if ((groupIndex + 2) == groupCount.size())
      {
        groupCount.remove(groupCount.size() - 1);
        groupValues.remove(groupValues.size() - 1);
      }
      groupIndex -= 1;
    }
  }

  /**
   * Receives notification that a group has finished.
   *
   * @param event the event.
   */
  public void groupFinished(final ReportEvent event)
  {
    if (event.isDeepTraversing())
    {
      if ("toc".equals(event.getOriginatingState().getReport().getMetaData().getName()))
      {
        return;
      }
    }

    final Group group = FunctionUtilities.getCurrentDeepTraverseGroup(event);
    if (group instanceof RelationalGroup == false)
    {
      return;
    }

    if ((groupIndex + 2) == groupCount.size())
    {
      groupCount.remove(groupCount.size() - 1);
      groupValues.remove(groupValues.size() - 1);
    }
    groupIndex -= 1;
  }

  /**
   * Receives notification that a new page is being started.
   *
   * @param event The event.
   */
  public void pageStarted(final ReportEvent event)
  {
    pageFunction.pageStarted(event);
  }

  /**
   * Receives notification that a page is completed.
   *
   * @param event The event.
   */
  public void pageFinished(final ReportEvent event)
  {
    pageFunction.pageFinished(event);
  }

  /**
   * Return the current expression value.
   * <p/>
   * The value depends (obviously) on the expression implementation.
   *
   * @return the value of the function.
   */
  public Object getValue()
  {
    return model;
  }

  /**
   * Checks whether this expression is a deep-traversing expression. Deep-traversing expressions receive events from all
   * sub-reports. This returns false by default, as ordinary expressions have no need to be deep-traversing.
   *
   * @return false.
   */
  public boolean isDeepTraversing()
  {
    return true;
  }

  /**
   * Clones the expression.  The expression should be reinitialized after the cloning. <P> Expressions maintain no
   * state, cloning is done at the beginning of the report processing to disconnect the expression from any other object
   * space.
   *
   * @return a clone of this expression.
   * @throws CloneNotSupportedException this should never happen.
   */
  public Object clone() throws CloneNotSupportedException
  {
    final TocDataGeneratorFunction o = (TocDataGeneratorFunction) super.clone();
    o.titleFormula = (FormulaExpression) titleFormula.clone();
    o.pageFunction = (PageFunction) pageFunction.clone();
    if (groupCount != null)
    {
      o.groupCount = (ArrayList) groupCount.clone();
      o.groupValues = (ArrayList) groupValues.clone();
    }
    return o;
  }

  /**
   * Return a completly separated copy of this function. The copy does no longer share any changeable objects with the
   * original function.
   *
   * @return a copy of this function.
   */
  public Expression getInstance()
  {
    final TocDataGeneratorFunction instance = (TocDataGeneratorFunction) super.getInstance();
    instance.model = new TypedTableModel();
    instance.titleFormula = (FormulaExpression) titleFormula.getInstance();
    instance.pageFunction = (PageFunction) pageFunction.getInstance();
    instance.groups = (ArrayList) groups.clone();
    instance.groupCount = null;
    instance.groupValues = null;
    return instance;
  }
}
TOP

Related Classes of org.pentaho.reporting.engine.classic.extensions.toc.TocDataGeneratorFunction

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.