/**
   * Receives notification that report generation has completed, the report footer was printed, no
   * more output is done. This is a helper event to shut down the output service.
   *
   * @param event The event.
   */
  public void reportDone(final ReportEvent event) {
    if (event.isDeepTraversing()) {
      return;
    }

    if (FunctionUtilities.isDefinedPrepareRunLevel(this, event)) {
      final Iterator iterator = dataStorage.entrySet().iterator();
      while (iterator.hasNext()) {
        final Map.Entry entry = (Map.Entry) iterator.next();
        final String key = (String) entry.getKey();
        final IndexDataHolder data = (IndexDataHolder) entry.getValue();
        model.addRow(
            new Object[] {
              data.getData(),
              data.getPagesText(indexSeparator, condensedStyle),
              data.getPages(),
              key
            });
      }
    } else if (FunctionUtilities.isLayoutLevel(event)) {
      final int rowCount = model.getRowCount();
      for (int i = 0; i < rowCount; i++) {
        final String key = (String) model.getValueAt(i, 3);
        final IndexDataHolder dataHolder = dataStorage.get(key);
        model.setValueAt(dataHolder.getPagesText(indexSeparator, condensedStyle), i, 1);
        model.setValueAt(dataHolder.getPages(), i, 2);
      }
    }
  }
  public void summaryRowSelection(final ReportEvent event) {
    if (FunctionUtilities.isDefinedGroup(getCrosstabFilterGroup(), event)) {
      final String targetName = getName().substring(1);
      final Object expressionValue = getDataRow().get(targetName);
      final Object tableModelValue = getDataRow().get("validate-" + targetName);

      currentDataItem = event.getState().getCurrentDataItem();
      long sequenceCounter = event.getState().getCrosstabColumnSequenceCounter(3);

      if (!equalNumeric(expressionValue, tableModelValue)) {
        logger.debug(
            String.format(
                "!*%12s %3d:%d# %s - %s",
                targetName, currentDataItem, sequenceCounter, expressionValue, tableModelValue));
        if (failHard) {
          Assert.assertEquals(tableModelValue, expressionValue);
        }
      } else {
        logger.debug(
            String.format(
                " *%12s %3d:%d# %s - %s",
                targetName, currentDataItem, sequenceCounter, expressionValue, tableModelValue));
      }
    }
  }
  /**
   * Receives notification that a row of data is being processed.
   *
   * @param event the event.
   */
  public void itemsAdvanced(final ReportEvent event) {
    if (event.isDeepTraversing()) {
      if ("index".equals(event.getOriginatingState().getReport().getMetaData().getName())) {
        return;
      }
    }

    final Object o = computeDataValue(event);
    if (o == null) {
      return;
    }

    if (FunctionUtilities.isDefinedPrepareRunLevel(this, event)) {
      dataStorage.put(String.valueOf(o), new IndexDataHolder(o));
    } else if (FunctionUtilities.isLayoutLevel(event)) {
      final IndexDataHolder o1 = dataStorage.get(String.valueOf(o));
      if (o1 == null) {
        throw new IllegalStateException(
            "Unable to compute index: Function values changed between prepare and layout run");
      }
      o1.addPage(pageFunction.getPage());
    }
  }
  protected void inspectExpression(
      final ReportDesignerContext designerContext,
      final ReportRenderContext reportRenderContext,
      final InspectionResultListener resultHandler,
      final String[] columnNames,
      final Expression expression,
      final ExpressionMetaData expressionMetaData) {
    if (expressionMetaData == null) {
      return;
    }

    try {
      final BeanUtility utility = new BeanUtility(expression);
      final ExpressionPropertyMetaData[] datas = expressionMetaData.getPropertyDescriptions();
      for (int i = 0; i < datas.length; i++) {
        final ExpressionPropertyMetaData metaData = datas[i];
        if (metaData.isHidden()) {
          continue;
        }
        if (WorkspaceSettings.getInstance().isShowExpertItems() == false && metaData.isExpert()) {
          continue;
        }
        if (WorkspaceSettings.getInstance().isShowDeprecatedItems() == false
            && metaData.isDeprecated()) {
          continue;
        }

        if (!"ElementName".equals(metaData.getPropertyRole())) // NON-NLS
        {
          continue;
        }

        final Object o = utility.getProperty(metaData.getName());
        final String[] elements = metaData.getReferencedElements(expression, o);
        for (int j = 0; j < elements.length; j++) {
          final String element = elements[j];
          final AbstractReportDefinition reportDefinition =
              reportRenderContext.getReportDefinition();
          final ReportElement e =
              FunctionUtilities.findElementByAttribute(
                  reportDefinition,
                  AttributeNames.Core.NAMESPACE,
                  AttributeNames.Core.NAME,
                  element);
          if (e == null) {
            resultHandler.notifyInspectionResult(
                new InspectionResult(
                    this,
                    InspectionResult.Severity.WARNING,
                    Messages.getString(
                        "InvalidElementReferenceInspection.ExpressionReferencesInvalidName",
                        expression.getName(),
                        metaData.getDisplayName(Locale.getDefault())),
                    new PropertyLocationInfo(expression, metaData.getName())));
          }
        }
      }
    } catch (Exception e) {
      resultHandler.notifyInspectionResult(
          new InspectionResult(
              this,
              InspectionResult.Severity.WARNING,
              e.getMessage(),
              new LocationInfo(expression)));
    }
  }