public static DefaultStatisticalCategoryDataset createDefaultStatisticalCategoryDataset(
      ValueSource valueSource, PlotInstance plotInstance) throws ChartPlottimeException {
    ValueSourceData valueSourceData = plotInstance.getPlotData().getValueSourceData(valueSource);
    assertMaxValueCountNotExceededOrThrowException(valueSourceData);
    GroupCellSeriesData dataForAllGroupCells = valueSourceData.getSeriesDataForAllGroupCells();
    DefaultStatisticalCategoryDataset dataset = new DefaultStatisticalCategoryDataset();
    DefaultDimensionConfig domainConfig = valueSource.getDomainConfig();
    DimensionConfigData domainConfigData =
        plotInstance.getPlotData().getDimensionConfigData(domainConfig);

    // Loop all group cells and add data to dataset
    for (GroupCellKeyAndData groupCellKeyAndData : dataForAllGroupCells) {
      GroupCellKey groupCellKey = groupCellKeyAndData.getKey();
      GroupCellData groupCellData = groupCellKeyAndData.getData();
      String seriesName =
          generateSeriesName(
              valueSource, groupCellKey, plotInstance.getCurrentPlotConfigurationClone());

      Map<PlotDimension, double[]> mainSeriesData =
          groupCellData.getDataForUsageType(SeriesUsageType.MAIN_SERIES);
      double[] xValues = mainSeriesData.get(PlotDimension.DOMAIN);
      double[] yValues = mainSeriesData.get(PlotDimension.VALUE);
      double[] yErrorValues = valueSourceData.getAbsoluteUtilityValues(groupCellKeyAndData, true);
      if (yErrorValues == null) {
        throw new ChartPlottimeException(
            "undefined_series", valueSource.toString(), SeriesUsageType.INDICATOR_1);
      }

      // this dataset does not support unsymmetric errors
      if (groupCellData.getDataForUsageType(SeriesUsageType.INDICATOR_2) != null) {
        throw new ChartPlottimeException(
            "unsymmetric_utility_not_supported", valueSource.toString());
      }

      int rowCount = xValues.length;

      // Loop all rows and add data to series
      double xValue;
      double yValue;
      double yErrorValue;
      String xString;
      for (int row = 0; row < rowCount; ++row) {
        xValue = xValues[row];
        xString = domainConfigData.getStringForValue(xValue);
        yValue = yValues[row];
        yErrorValue = yErrorValues[row] - yValue;

        dataset.add(yValue, yErrorValue, seriesName, xString);
      }
    }
    return dataset;
  }
  private static void addSeriesToDefaultXYDataset(
      ValueSource valueSource, int seriesIdx, PlotInstance plotInstance, DefaultXYDataset dataset)
      throws ChartPlottimeException {
    final int xIdx = 0;
    final int yIdx = 1;

    ValueSourceData valueSourceData = plotInstance.getPlotData().getValueSourceData(valueSource);
    assertMaxValueCountNotExceededOrThrowException(valueSourceData);

    GroupCellSeriesData dataForAllGroupCells = valueSourceData.getSeriesDataForAllGroupCells();
    GroupCellKeyAndData groupCellKeyAndData =
        dataForAllGroupCells.getGroupCellKeyAndData(seriesIdx);

    GroupCellKey groupCellKey = groupCellKeyAndData.getKey();
    GroupCellData groupCellData = groupCellKeyAndData.getData();

    // create series name
    GroupCellKey groupCellKeyClone = (GroupCellKey) groupCellKey.clone();
    String seriesName =
        generateSeriesName(
            valueSource, groupCellKeyClone, plotInstance.getCurrentPlotConfigurationClone());
    String differenceName = "__&%" + seriesName + "%&__";

    Map<PlotDimension, double[]> mainData =
        groupCellData.getDataForUsageType(SeriesUsageType.MAIN_SERIES);
    double[] xValues = mainData.get(PlotDimension.DOMAIN);
    double[] yValues = mainData.get(PlotDimension.VALUE);

    double[][] mainSeries = new double[2][xValues.length];
    mainSeries[xIdx] = xValues;
    mainSeries[yIdx] = yValues;
    dataset.addSeries(seriesName, mainSeries);

    if (valueSource.getSeriesFormat().getUtilityUsage() == IndicatorType.DIFFERENCE) {
      double[] differenceValues =
          valueSourceData.getAbsoluteUtilityValues(groupCellKeyAndData, true);
      if (differenceValues == null) {
        throw new ChartPlottimeException(
            "undefined_series", valueSource.toString(), SeriesUsageType.INDICATOR_1);
      }
      double[][] differenceSeries = new double[2][xValues.length];
      differenceSeries[xIdx] = xValues;
      differenceSeries[yIdx] = differenceValues;
      dataset.addSeries(differenceName, differenceSeries);
    }
  }
  /**
   * Creates a dataset which supports custom intervals on both axes.
   *
   * <p>Expects a grouping on the domain axis.
   *
   * @throws ChartPlottimeException
   */
  public static DefaultIntervalXYDataset createDefaultIntervalXYDataset(
      ValueSource valueSource, PlotInstance plotInstance, boolean createRangeIntervals)
      throws ChartPlottimeException {
    ValueSourceData valueSourceData = plotInstance.getPlotData().getValueSourceData(valueSource);
    assertMaxValueCountNotExceededOrThrowException(valueSourceData);

    GroupCellSeriesData dataForAllGroupCells = valueSourceData.getSeriesDataForAllGroupCells();

    DefaultIntervalXYDataset intervalDataset = new DefaultIntervalXYDataset();

    DefaultDimensionConfig domainConfig = valueSource.getDomainConfig();
    DimensionConfigData domainConfigData =
        plotInstance.getPlotData().getDimensionConfigData(domainConfig);

    // Loop all group cells and add data to dataset
    for (GroupCellKeyAndData groupCellKeyAndData : dataForAllGroupCells) {

      GroupCellKey groupCellKey = groupCellKeyAndData.getKey();
      GroupCellData groupCellData = groupCellKeyAndData.getData();

      // create series name
      GroupCellKey groupCellKeyClone = (GroupCellKey) groupCellKey.clone();
      groupCellKeyClone.removeRangeForDimension(
          PlotDimension.DOMAIN); // legend does not need X-group
      String seriesName =
          generateSeriesName(
              valueSource, groupCellKeyClone, plotInstance.getCurrentPlotConfigurationClone());

      List<ValueRange> domainValueGroups = domainConfigData.getGroupingModel();

      // Loop all rows and add data to series.
      // Remember that by definition one row in the groupCellData corresponds
      // to one group in xValueGroups (if the x-axis is grouped, which should
      // always be the case in this function).
      final int domainValueIdx = 0;
      final int domainLowerIdx = 1;
      final int domainUpperIdx = 2;
      final int rangeValueIdx = 3;
      final int rangeLowerIdx = 4;
      final int rangeUpperIdx = 5;
      Map<PlotDimension, double[]> dataForMainSeries =
          groupCellData.getDataForUsageType(SeriesUsageType.MAIN_SERIES);

      int rowCount = dataForMainSeries.get(PlotDimension.DOMAIN).length;
      double[] domainValues = dataForMainSeries.get(PlotDimension.DOMAIN);
      double[] rangeValues = dataForMainSeries.get(PlotDimension.VALUE);
      double[] upperErrorValues = null;
      double[] lowerErrorValues = null;

      upperErrorValues = valueSourceData.getAbsoluteUtilityValues(groupCellKeyAndData, true);
      lowerErrorValues = valueSourceData.getAbsoluteUtilityValues(groupCellKeyAndData, false);
      if (createRangeIntervals && upperErrorValues == null) {
        throw new ChartPlottimeException(
            "undefined_series", valueSource.toString(), SeriesUsageType.INDICATOR_1);
      }

      double[][] series = new double[6][rowCount];
      Iterator<ValueRange> domainGroupIterator = null;
      if (domainValueGroups != null) {
        domainGroupIterator = domainValueGroups.iterator();
      }

      double domainLower;
      double domainUpper;
      double domainValue;
      double rangeValue;
      double rangeUpper;
      double rangeLower;
      for (int row = 0; row < rowCount; ++row) {
        domainValue = domainValues[row];
        domainLower = domainValue;
        domainUpper = domainValue;
        if (domainGroupIterator != null) {
          ValueRange currentDomainGroup = domainGroupIterator.next();
          if (currentDomainGroup.definesUpperLowerBound()) {
            domainLower = currentDomainGroup.getLowerBound();
            domainUpper = currentDomainGroup.getUpperBound();
          }
        }
        rangeValue = rangeValues[row];
        rangeUpper = upperErrorValues != null ? upperErrorValues[row] : Double.NaN;
        rangeLower = lowerErrorValues != null ? lowerErrorValues[row] : Double.NaN;

        series[domainValueIdx][row] = domainValue;
        series[domainLowerIdx][row] = domainLower;
        series[domainUpperIdx][row] = domainUpper;
        series[rangeValueIdx][row] = rangeValue;
        series[rangeLowerIdx][row] = rangeLower;
        series[rangeUpperIdx][row] = rangeUpper;
      }

      intervalDataset.addSeries(seriesName, series);
    }
    return intervalDataset;
  }