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;
  }
  /**
   * Same as {@link #createDefaultMultiValueCategoryDataset(ValueSource, PlotConfiguration)}, but
   * instead of storing only the values it stores pairs in the lists, where the first value is the
   * data value, and the second value holds the index of the value in the series in valueSource.
   *
   * <p>Must be used for the FormattedScatterRenderer.
   *
   * @throws ChartPlottimeException
   */
  public static DefaultMultiValueCategoryDataset createAnnotatedDefaultMultiValueCategoryDataset(
      ValueSource valueSource, PlotInstance plotInstance) throws ChartPlottimeException {
    ValueSourceData valueSourceData = plotInstance.getPlotData().getValueSourceData(valueSource);
    assertMaxValueCountNotExceededOrThrowException(valueSourceData);
    GroupCellSeriesData dataForAllGroupCells = valueSourceData.getSeriesDataForAllGroupCells();
    DefaultMultiValueCategoryDataset dataset = new DefaultMultiValueCategoryDataset();
    DefaultDimensionConfig domainConfig = valueSource.getDomainConfig();
    DimensionConfigData domainConfigData =
        plotInstance.getPlotData().getDimensionConfigData(domainConfig);

    // Loop all group cells and add data to dataset
    for (GroupCellKeyAndData groupCellKeyAndData : dataForAllGroupCells) {
      // the DefaultMultiValueCategoryDataset expects a List of values for each domain value. This
      // map
      // maps domain values to lists and is filled while we iterate through all data value below.
      Map<Double, List<Pair<Double, Integer>>> valueListsForDomainValues =
          new HashMap<Double, List<Pair<Double, Integer>>>();
      for (Double value : domainConfigData.getDistinctValues()) {
        valueListsForDomainValues.put(value, new LinkedList<Pair<Double, Integer>>());
      }

      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);
      int rowCount = xValues.length;

      // Loop all rows and add data into map
      double xValue;
      double yValue;
      String xString;
      for (int row = 0; row < rowCount; ++row) {
        xValue = xValues[row];
        yValue = yValues[row];
        Pair<Double, Integer> valueRowNumberPair = new Pair<Double, Integer>(yValue, row);
        valueListsForDomainValues.get(xValue).add(valueRowNumberPair);
      }

      for (Double value : domainConfigData.getDistinctValues()) {
        xString = domainConfigData.getStringForValue(value);
        dataset.add(valueListsForDomainValues.get(value), seriesName, xString);
      }
    }
    return dataset;
  }
  private static void configureBarRenderer(
      BarRenderer renderer, ValueSource valueSource, PlotInstance plotInstance) {
    StandardBarPainter barPainter = new StandardBarPainter();
    renderer.setBarPainter(barPainter);
    renderer.setGradientPaintTransformer(null);

    ValueSourceData valueSourceData = plotInstance.getPlotData().getValueSourceData(valueSource);
    int seriesCount = valueSourceData.getSeriesCount();
    DimensionConfig domainConfig = valueSource.getDomainConfig();
    DimensionConfig colorDimensionConfig =
        plotInstance.getCurrentPlotConfigurationClone().getDimensionConfig(PlotDimension.COLOR);
    SeriesFormat seriesFormat = valueSource.getSeriesFormat();

    // Loop all series and set series format.
    // Format based on dimension configs will be set later on in initFormatDelegate().
    for (int seriesIdx = 0; seriesIdx < seriesCount; ++seriesIdx) {
      // configure series paint if necessary
      if (!SeriesFormat.calculateIndividualFormatForEachItem(domainConfig, colorDimensionConfig)) {
        renderer.setSeriesPaint(seriesIdx, seriesFormat.getAreaFillPaint());
      }

      // configure general style of the bars
      renderer.setShadowVisible(false);
      renderer.setSeriesOutlinePaint(seriesIdx, PlotConfiguration.DEFAULT_SERIES_OUTLINE_PAINT);
    }
    renderer.setDrawBarOutline(true);
  }
  public static XYItemRenderer createStackedXYAreaRenderer2(
      ValueSource valueSource, PlotInstance plotInstance, boolean b) {
    FormattedStackedXYAreaRenderer2 renderer = new FormattedStackedXYAreaRenderer2();

    ValueSourceData valueSourceData = plotInstance.getPlotData().getValueSourceData(valueSource);
    configureXYAreaRenderer2(valueSourceData, renderer);
    initFormatDelegate(valueSource, renderer, plotInstance);
    return renderer;
  }
 private static void initFormatDelegate(
     ValueSource valueSource,
     List<Integer> seriesIndices,
     FormattedRenderer formattedRenderer,
     PlotInstance plotInstance) {
   ValueSourceData valueSourceData = plotInstance.getPlotData().getValueSourceData(valueSource);
   RenderFormatDelegate formatDelegate = formattedRenderer.getFormatDelegate();
   formatDelegate.setConfiguration(valueSourceData, plotInstance);
 }
  /**
   * @param valueSource
   * @param plotInstance
   * @param autoWidthFraction If this value is greater than 0, an auto width for the intervals is
   *     calculated such that the intervals nearest to each other touch. This value is then
   *     multiplied with the value of autoWidthFtraction. If unset, the intervals have width 0.
   * @param allowDuplicates
   * @param sortByDomain if true, the data is sorted by domain values (useful for bar and area
   *     charts)
   * @return
   * @throws ChartPlottimeException
   */
  public static XYSeriesCollection createXYSeriesCollection(
      ValueSource valueSource,
      PlotInstance plotInstance,
      double autoWidthFraction,
      boolean allowDuplicates,
      boolean sortByDomain)
      throws ChartPlottimeException {
    XYSeriesCollection xyDataset = new XYSeriesCollection();
    if (autoWidthFraction > 0) {
      xyDataset.setAutoWidth(true);
    } else {
      xyDataset.setAutoWidth(false);
      xyDataset.setIntervalWidth(0);
    }

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

    // Loop over 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());

      XYSeries series = new XYSeries(seriesName, sortByDomain, allowDuplicates);
      Map<PlotDimension, double[]> dataForUsageType =
          groupCellData.getDataForUsageType(SeriesUsageType.MAIN_SERIES);
      int rowCount = dataForUsageType.get(PlotDimension.DOMAIN).length;
      double[] xValues = dataForUsageType.get(PlotDimension.DOMAIN);
      double[] yValues = dataForUsageType.get(PlotDimension.VALUE);

      try {
        // Loop over rows and add data to series
        for (int row = 0; row < rowCount; ++row) {
          double x = xValues[row];
          double y = yValues[row];
          if (!Double.isNaN(x)) {
            series.add(x, y);
          }
        }
      } catch (SeriesException e) {
        throw new ChartPlottimeException(
            "duplicate_value", valueSource.toString(), PlotDimension.DOMAIN.getName());
      }

      xyDataset.addSeries(series);
    }
    // intervals should not touch each other, so decrease auto width.
    if (xyDataset.getIntervalWidth() > 0) {
      xyDataset.setIntervalWidth(xyDataset.getIntervalWidth() * autoWidthFraction);
    }
    return xyDataset;
  }
  public static XYDataset createDefaultXYDataset(ValueSource valueSource, PlotInstance plotInstace)
      throws ChartPlottimeException {
    DefaultXYDataset dataset = new DefaultXYDataset();
    ValueSourceData valueSourceData = plotInstace.getPlotData().getValueSourceData(valueSource);
    //		assertMaxValueCountNotExceededOrThrowException(valueSourceData);

    for (int seriesIdx = 0; seriesIdx < valueSourceData.getSeriesCount(); ++seriesIdx) {
      addSeriesToDefaultXYDataset(valueSource, seriesIdx, plotInstace, dataset);
    }
    return dataset;
  }
  private static void configureAreaRenderer(
      AreaRenderer renderer, ValueSource valueSource, PlotInstance plotInstance) {
    ValueSourceData valueSourceData = plotInstance.getPlotData().getValueSourceData(valueSource);
    int seriesCount = valueSourceData.getSeriesCount();
    SeriesFormat seriesFormat = valueSource.getSeriesFormat();

    // Loop all series and set series format.
    // Format based on dimension configs will be set later on in initFormatDelegate().
    for (int seriesIdx = 0; seriesIdx < seriesCount; ++seriesIdx) {
      renderer.setSeriesPaint(seriesIdx, seriesFormat.getAreaFillPaint());
    }
  }
 public static XYItemRenderer[] createXYDifferenceRenderers(
     ValueSource valueSource, PlotInstance plotInstance) {
   ValueSourceData valueSourceData = plotInstance.getPlotData().getValueSourceData(valueSource);
   FormattedXYDifferenceRenderer[] renderers =
       new FormattedXYDifferenceRenderer[valueSourceData.getSeriesCount()];
   for (int seriesIdx = 0; seriesIdx < valueSourceData.getSeriesCount(); ++seriesIdx) {
     FormattedXYDifferenceRenderer renderer = new FormattedXYDifferenceRenderer(seriesIdx);
     configureXYDifferenceRenderer(
         renderer, valueSource, plotInstance.getCurrentPlotConfigurationClone());
     initFormatDelegate(valueSource, seriesIdx, renderer, plotInstance);
     renderers[seriesIdx] = renderer;
   }
   return renderers;
 }
 private static void initFormatDelegate(
     ValueSource valueSource, FormattedRenderer formattedRenderer, PlotInstance plotInstance) {
   ValueSourceData valueSourceData = plotInstance.getPlotData().getValueSourceData(valueSource);
   List<Integer> seriesIndices = new LinkedList<Integer>();
   if (valueSourceData != null) {
     for (int i = 0; i < valueSourceData.getSeriesCount(); ++i) {
       seriesIndices.add(i);
     }
   } else {
     LogService.getRoot()
         .log(
             Level.WARNING,
             "com.rapidminer.gui.new_plotter.engine.jfreechart.ChartRendererFactory.null_value_source");
   }
   initFormatDelegate(valueSource, seriesIndices, formattedRenderer, plotInstance);
 }
  public static DefaultTableXYDataset createDefaultTableXYDataset(
      ValueSource valueSource, PlotInstance plotInstance) throws ChartPlottimeException {
    ValueSourceData valueSourceData = plotInstance.getPlotData().getValueSourceData(valueSource);
    assertMaxValueCountNotExceededOrThrowException(valueSourceData);
    GroupCellSeriesData dataForAllGroupCells = valueSourceData.getSeriesDataForAllGroupCells();
    DefaultTableXYDataset dataset = new DefaultTableXYDataset();

    // Loop all group cells and add data to dataset
    for (GroupCellKeyAndData groupCellKeyAndData : dataForAllGroupCells) {
      GroupCellKey groupCellKey = groupCellKeyAndData.getKey();
      GroupCellData groupCellData = groupCellKeyAndData.getData();

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

      String seriesName =
          generateSeriesName(
              valueSource, groupCellKey, plotInstance.getCurrentPlotConfigurationClone());

      XYSeries series = new XYSeries(seriesName, false, false);

      // Loop all rows and add data to series
      double x;
      double y;
      for (int row = 0; row < rowCount; ++row) {
        x = xValues[row];
        y = yValues[row];
        try {
          if (!Double.isNaN(x)) {
            series.add(
                x, y,
                false); // false means: do not notify. Since we are currently initializing, this is
                        // not necessary and saves us some fractions of a second
          }
        } catch (SeriesException e) {
          throw new ChartPlottimeException(
              "duplicate_value", valueSource.toString(), PlotDimension.DOMAIN.getName());
        }
      }
      dataset.addSeries(series);
    }
    dataset.setAutoWidth(true);
    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);
    }
  }
  private static void configureXYLineAndShapeRenderer(
      XYLineAndShapeRenderer renderer, ValueSource valueSource, PlotInstance plotInstance) {
    renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
    SeriesFormat seriesFormat = valueSource.getSeriesFormat();
    DimensionConfig domainConfig = valueSource.getDomainConfig();
    DimensionConfig colorDimensionConfig =
        plotInstance.getCurrentPlotConfigurationClone().getDimensionConfig(PlotDimension.COLOR);
    DimensionConfig shapeDimensionConfig =
        plotInstance.getCurrentPlotConfigurationClone().getDimensionConfig(PlotDimension.SHAPE);
    ValueSourceData valueSourceData = plotInstance.getPlotData().getValueSourceData(valueSource);

    int seriesCount = valueSourceData.getSeriesDataForAllGroupCells().groupCellCount();

    // Loop all series and set series format.
    // Format based on dimension configs will be set later on in initFormatDelegate().
    for (int seriesIdx = 0; seriesIdx < seriesCount; ++seriesIdx) {
      // configure linestyle
      if (seriesFormat.getLineStyle() == LineStyle.NONE) {
        renderer.setSeriesLinesVisible(seriesIdx, false);
      } else {
        renderer.setSeriesLinesVisible(seriesIdx, true);
        renderer.setSeriesStroke(seriesIdx, seriesFormat.getStroke(), false);
      }

      // configure series shape if necessary
      if (!SeriesFormat.calculateIndividualFormatForEachItem(domainConfig, shapeDimensionConfig)) {
        if (seriesFormat.getItemShape() != ItemShape.NONE) {
          renderer.setSeriesShapesVisible(seriesIdx, true);
          renderer.setSeriesShape(seriesIdx, seriesFormat.getItemShape().getShape());
        } else {
          renderer.setSeriesShapesVisible(seriesIdx, false);
        }
      }

      // configure series color if necessary
      if (!SeriesFormat.calculateIndividualFormatForEachItem(domainConfig, colorDimensionConfig)) {
        Color itemColor = seriesFormat.getItemColor();
        renderer.setSeriesPaint(seriesIdx, itemColor);
        renderer.setSeriesFillPaint(seriesIdx, itemColor);
      }
      renderer.setSeriesOutlinePaint(seriesIdx, PlotConfiguration.DEFAULT_SERIES_OUTLINE_PAINT);
      renderer.setUseOutlinePaint(true);
    }
  }
  private static void configureLineAndShapeRenderer(
      LineAndShapeRenderer renderer, ValueSource valueSource, PlotInstance plotInstance) {
    ValueSourceData valueSourceData = plotInstance.getPlotData().getValueSourceData(valueSource);
    int seriesCount = valueSourceData.getSeriesCount();
    SeriesFormat seriesFormat = valueSource.getSeriesFormat();
    DimensionConfig domainConfig = valueSource.getDomainConfig();
    DimensionConfig colorDimensionConfig =
        plotInstance.getCurrentPlotConfigurationClone().getDimensionConfig(PlotDimension.COLOR);
    DimensionConfig shapeDimensionConfig =
        plotInstance.getCurrentPlotConfigurationClone().getDimensionConfig(PlotDimension.SHAPE);

    renderer.setDefaultEntityRadius(4);

    // loop all series and set series format
    for (int seriesIdx = 0; seriesIdx < seriesCount; ++seriesIdx) {
      // configure linestyle
      if (seriesFormat.getLineStyle() != LineStyle.NONE) {
        renderer.setSeriesLinesVisible(seriesIdx, true);
        renderer.setSeriesStroke(seriesIdx, seriesFormat.getStroke(), false);
      } else {
        renderer.setSeriesLinesVisible(seriesIdx, false);
      }

      // configure series shape if necessary
      if (!SeriesFormat.calculateIndividualFormatForEachItem(domainConfig, shapeDimensionConfig)) {
        if (seriesFormat.getItemShape() != ItemShape.NONE) {
          renderer.setSeriesShapesVisible(seriesIdx, true);
          renderer.setSeriesShape(seriesIdx, seriesFormat.getItemShape().getShape());
        } else {
          renderer.setSeriesShapesVisible(seriesIdx, false);
        }
      }

      // configure series color if necessary
      if (!SeriesFormat.calculateIndividualFormatForEachItem(domainConfig, colorDimensionConfig)) {
        Color itemColor = seriesFormat.getItemColor();
        renderer.setSeriesPaint(seriesIdx, itemColor);
      }
      renderer.setSeriesOutlinePaint(seriesIdx, PlotConfiguration.DEFAULT_SERIES_OUTLINE_PAINT);
      renderer.setUseOutlinePaint(true);
    }
  }
  private static void configureScatterRenderer(
      ScatterRenderer renderer, ValueSource valueSource, PlotInstance plotInstance)
      throws ChartPlottimeException {
    ValueSourceData valueSourceData = plotInstance.getPlotData().getValueSourceData(valueSource);
    int seriesCount = valueSourceData.getSeriesCount();
    SeriesFormat seriesFormat = valueSource.getSeriesFormat();
    DimensionConfig domainConfig = valueSource.getDomainConfig();
    DimensionConfig colorDimensionConfig =
        plotInstance.getCurrentPlotConfigurationClone().getDimensionConfig(PlotDimension.COLOR);
    DimensionConfig shapeDimensionConfig =
        plotInstance.getCurrentPlotConfigurationClone().getDimensionConfig(PlotDimension.SHAPE);

    renderer.setDefaultEntityRadius(4);

    // loop all series and set series format
    for (int seriesIdx = 0; seriesIdx < seriesCount; ++seriesIdx) {
      // lines are not supported in a ScatterRenderer, but this is already checked in
      // JFreeChartPlotEngine.getWarnings().

      // configure series shape if necessary
      if (!SeriesFormat.calculateIndividualFormatForEachItem(domainConfig, shapeDimensionConfig)) {
        //				if(seriesFormat.getItemShape() != ItemShape.NONE) {
        renderer.setSeriesShape(seriesIdx, seriesFormat.getItemShape().getShape());
        //				} else {
        //					throw new ChartPlottimeException("unsupported_item_shape", valueSource.toString(),
        // seriesFormat.getItemShape());
        //				}
      }

      // configure series color if necessary
      if (!SeriesFormat.calculateIndividualFormatForEachItem(domainConfig, colorDimensionConfig)) {
        Color itemColor = seriesFormat.getItemColor();
        renderer.setSeriesPaint(seriesIdx, itemColor);
      }
      renderer.setSeriesOutlinePaint(seriesIdx, PlotConfiguration.DEFAULT_SERIES_OUTLINE_PAINT);
      renderer.setUseOutlinePaint(true);
      renderer.setDrawOutlines(true);
      renderer.setUseSeriesOffset(false);
    }
  }
  private void createDimensionConfigLegendItem(
      PlotInstance plotInstance,
      DefaultDimensionConfig dimensionConfig,
      Set<PlotDimension> dimensionSet,
      LegendItemCollection legendItemCollection) {
    PlotConfiguration plotConfiguration = plotInstance.getCurrentPlotConfigurationClone();
    DimensionConfigData dimensionConfigData =
        plotInstance.getPlotData().getDimensionConfigData(dimensionConfig);
    if (dimensionConfig.isGrouping()) {
      // create legend entry based on the grouping
      if (dimensionConfig.isNominal()) {
        // create categorical legend --> one item for each category
        createCategoricalLegendItems(
            plotInstance,
            dimensionSet,
            legendItemCollection,
            dimensionConfigData.getDistinctValues());
      } else if (dimensionConfig.isNumerical() || dimensionConfig.isDate()) {
        createDimensionTitleLegendItem(plotInstance, dimensionSet, legendItemCollection);

        // create one continuous legend item
        double minValue = dimensionConfigData.getMinValue();
        double maxValue = dimensionConfigData.getMaxValue();

        LegendItem legendItem =
            createContinuousLegendItem(
                plotInstance,
                dimensionSet,
                minValue,
                maxValue,
                dimensionConfig.isDate() ? dimensionConfig.getDateFormat() : null);
        if (legendItem != null) {
          legendItemCollection.add(legendItem);
        }
      } else {
        throw new RuntimeException(
            "unknown data type during legend creation - this should not happen");
      }
    } else {
      // dimension config not grouping --> create legend item only, if there exists
      // at least one non-aggregated value source (otherwise the dimension config is
      // not used at all in the plot and thus we also don't need a legend item for it).
      boolean createLegend = false;
      for (ValueSource valueSource : plotConfiguration.getAllValueSources()) {
        if (!valueSource.isUsingDomainGrouping()) {
          createLegend = true;
          break;
        }
      }
      if (createLegend) {
        // create legend based on the attribute values on the dimension config
        if (dimensionConfig.isNominal()) {
          // create one legend item for each nominal value
          List<Double> values = dimensionConfigData.getDistinctValues();
          createCategoricalLegendItems(plotInstance, dimensionSet, legendItemCollection, values);
        } else if (dimensionConfig.isNumerical() || dimensionConfig.isDate()) {
          createDimensionTitleLegendItem(plotInstance, dimensionSet, legendItemCollection);

          // create one continuous legend item for the value range
          double minValue = dimensionConfigData.getMinValue();
          double maxValue = dimensionConfigData.getMaxValue();

          LegendItem legendItem =
              createContinuousLegendItem(
                  plotInstance,
                  dimensionSet,
                  minValue,
                  maxValue,
                  dimensionConfig.isDate() ? dimensionConfig.getDateFormat() : null);
          if (legendItem != null) {
            legendItemCollection.add(legendItem);
          }
        } else {
          throw new RuntimeException(
              "unknown data type during legend creation - this should not happen");
        }
      }
    }
  }
  private void createCategoricalLegendItems(
      PlotInstance plotInstance,
      Set<PlotDimension> dimensionSet,
      LegendItemCollection legendItemCollection,
      Iterable<Double> values) {
    createDimensionTitleLegendItem(plotInstance, dimensionSet, legendItemCollection);

    PlotConfiguration plotConfig = plotInstance.getCurrentPlotConfigurationClone();

    Shape defaultShape = new Ellipse2D.Float(-5f, -5f, 10f, 10f);
    Color defaultOutlineColor = PlotConfiguration.DEFAULT_OUTLINE_COLOR;
    ColorProvider colorProvider = null;
    ShapeProvider shapeProvider = null;
    SizeProvider sizeProvider = null;

    DefaultDimensionConfig dimensionConfig =
        (DefaultDimensionConfig) plotConfig.getDimensionConfig(dimensionSet.iterator().next());
    DimensionConfigData dimensionConfigData =
        plotInstance.getPlotData().getDimensionConfigData(dimensionConfig);
    for (PlotDimension dimension : dimensionSet) {
      if (dimension == PlotDimension.COLOR) {
        colorProvider = dimensionConfigData.getColorProvider();
      } else if (dimension == PlotDimension.SHAPE) {
        shapeProvider = dimensionConfigData.getShapeProvider();
      } else if (dimension == PlotDimension.SIZE) {
        sizeProvider = dimensionConfigData.getSizeProvider();
      }
    }

    // initialize size scale for legend
    ContinuousSizeProvider legendSizeProvider = null;
    if (sizeProvider != null) {
      double minScalingFactor = sizeProvider.getMinScalingFactor();
      double maxScalingFactor = sizeProvider.getMaxScalingFactor();
      double minLegendScalingFactor = MIN_LEGEND_ITEM_SCALING_FACTOR;
      double maxLegendScalingFactor = MAX_LEGEND_ITEM_SCALING_FACTOR;
      if (minScalingFactor > maxScalingFactor) {
        double tmp = minScalingFactor;
        minScalingFactor = maxScalingFactor;
        maxScalingFactor = tmp;
        minLegendScalingFactor = MAX_LEGEND_ITEM_SCALING_FACTOR;
        maxLegendScalingFactor = MIN_LEGEND_ITEM_SCALING_FACTOR;
      }
      legendSizeProvider =
          new ContinuousSizeProvider(
              minScalingFactor,
              maxScalingFactor,
              minLegendScalingFactor,
              maxLegendScalingFactor,
              false);
    }

    for (Double value : values) {
      // configure shape and stroke
      Shape shape = defaultShape;
      BasicStroke outlineStroke;
      Color outlineColor = new Color(0, 0, 0, 0);
      if (shapeProvider != null) {
        shape = shapeProvider.getShapeForCategory(value);
        outlineStroke = DEFAULT_OUTLINE_STROKE;
        outlineColor = defaultOutlineColor;
      } else {
        outlineStroke = new BasicStroke();
        if (colorProvider != null) {
          shape = UNDEFINED_SHAPE;
        } else {
          shape = UNDEFINED_SHAPE_AND_COLOR;
        }
      }

      // configure fill paint
      Paint paint = UNDEFINED_COLOR_PAINT;
      if (colorProvider != null) {
        paint = colorProvider.getColorForValue(value);
      }

      double scalingFactor = 1;
      if (sizeProvider != null) {
        // scale shape according to sizeProvider
        scalingFactor = sizeProvider.getScalingFactorForValue(value);
        // scale shape to fit into legend
        scalingFactor = legendSizeProvider.getScalingFactorForValue(scalingFactor);
        AffineTransform transformation = new AffineTransform();
        transformation.scale(scalingFactor, scalingFactor);
        shape = transformation.createTransformedShape(shape);
      }

      String label = dimensionConfigData.getStringForValue(value);
      if (label == null) {
        label = "";
      }

      CustomLegendItem legendItem =
          new CustomLegendItem(label, null, null, null, shape, paint, outlineStroke, outlineColor);
      legendItemCollection.add(legendItem);
    }
  }
  public static CategoryDataset createDefaultCategoryDataset(
      ValueSource valueSource,
      PlotInstance plotInstance,
      boolean fillWithZero,
      boolean allowValuesLessThanZero)
      throws ChartPlottimeException {
    ValueSourceData valueSourceData = plotInstance.getPlotData().getValueSourceData(valueSource);
    assertMaxValueCountNotExceededOrThrowException(valueSourceData);
    GroupCellSeriesData dataForAllGroupCells = valueSourceData.getSeriesDataForAllGroupCells();
    DefaultDimensionConfig domainConfig = valueSource.getDomainConfig();
    DimensionConfigData domainConfigData =
        plotInstance.getPlotData().getDimensionConfigData(domainConfig);
    DefaultCategoryDataset dataset = new DefaultCategoryDataset();

    for (GroupCellKeyAndData groupCellKeyAndData : dataForAllGroupCells) {

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

      // create series name
      GroupCellKey groupCellKeyClone = (GroupCellKey) groupCellKey.clone();
      String seriesName =
          generateSeriesName(
              valueSource, groupCellKeyClone, plotInstance.getCurrentPlotConfigurationClone());

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

      int rowCount = xValues.length;
      for (int row = 0; row < rowCount; ++row) {
        double xValue = xValues[row];

        String xString = null;
        xString = domainConfigData.getStringForValue(xValue);
        Double y = yValues[row];
        if (!allowValuesLessThanZero && y < 0) {
          throw new ChartPlottimeException("illegal_zero_value", valueSource.toString());
        }

        if (xString != null) {
          dataset.addValue(y, seriesName, xString);
        }
      }
      if (fillWithZero) {
        for (int row = 0; row < rowCount; ++row) {
          double xValue = xValues[row];

          String xString = null;
          xString = domainConfigData.getStringForValue(xValue);
          Number value = dataset.getValue(seriesName, xString);
          if (value == null || Double.isNaN(value.doubleValue())) {
            dataset.addValue(0, seriesName, xString);
          }
        }
      }
    }

    return dataset;
  }
  /**
   * 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;
  }
  /**
   * Creates a continuous legend item for one item in dimensionSet, i.e. dimensionSet must be a set
   * containing exactly one value.
   *
   * @param dateFormat format used to format minValue and maxValue as dates, or null if they should
   *     be displayed numerically instead of as dates
   * @throws ChartPlottimeException
   */
  private LegendItem createContinuousLegendItem(
      PlotInstance plotInstance,
      Set<PlotDimension> dimensionSet,
      double minValue,
      double maxValue,
      DateFormat dateFormat) {
    PlotConfiguration plotConfiguration = plotInstance.getCurrentPlotConfigurationClone();
    PlotDimension dimension = dimensionSet.iterator().next();
    DefaultDimensionConfig dimensionConfig =
        (DefaultDimensionConfig) plotConfiguration.getDimensionConfig(dimension);
    DimensionConfigData dimensionConfigData =
        plotInstance.getPlotData().getDimensionConfigData(dimensionConfig);
    // String label = dimensionConfig.getLabel();
    // if(label == null) {
    // label = I18N.getGUILabel("plotter.unnamed_value_label");
    // }
    String label = "";

    if (dimension == PlotDimension.COLOR) {
      ColorProvider colorProvider = dimensionConfigData.getColorProvider();
      if (!colorProvider.supportsNumericalValues()) {
        throw new RuntimeException(
            "Color provider for continuous legend item does not support numerical values.");
      }

      // shape dimensions
      final int width = 50;
      final int height = 10;

      // create item paint

      // first disable logarithmic scale on color provider ( -> linear gradient in legend)
      // ContinuousColorProvider continuousColorProvider = null;
      // if (dimensionConfig.isLogarithmic() && colorProvider instanceof
      // ContinuousColorProvider) {
      // continuousColorProvider = (ContinuousColorProvider)colorProvider;
      // continuousColorProvider.setLogarithmic(false);
      // }

      // calculate gradient
      float fractions[] = new float[width];
      Color colors[] = new Color[width];
      for (int i = 0; i < width; ++i) {

        float fraction = i / (width - 1.0f);
        double fractionValue;
        if (colorProvider instanceof ContinuousColorProvider
            && ((ContinuousColorProvider) colorProvider)
                .isColorMinMaxValueDifferentFromOriginal(
                    ((ContinuousColorProvider) colorProvider).getMinValue(),
                    ((ContinuousColorProvider) colorProvider).getMaxValue())) {
          fractionValue =
              ((ContinuousColorProvider) colorProvider).getMinValue()
                  + fraction
                      * (((ContinuousColorProvider) colorProvider).getMaxValue()
                          - ((ContinuousColorProvider) colorProvider).getMinValue());
        } else {
          fractionValue = minValue + fraction * (maxValue - minValue);
        }
        colors[i] = colorProvider.getColorForValue(fractionValue);
        fractions[i] = fraction;
      }
      LinearGradientPaint shapeFillPaint =
          new LinearGradientPaint(
              new Point(0, 0), new Point(width, 0), fractions, colors, CycleMethod.REPEAT);

      // reset color provider to logarithmic if necessary
      // if (continuousColorProvider != null && dimensionConfig.isLogarithmic()) {
      // continuousColorProvider.setLogarithmic(true);
      // }

      // create item shape
      Rectangle itemShape = new Rectangle(width, height);

      if (colorProvider instanceof ContinuousColorProvider) {
        return createFlankedShapeLegendItem(
            label,
            ((ContinuousColorProvider) colorProvider).getMinValue(),
            ((ContinuousColorProvider) colorProvider).getMaxValue(),
            itemShape,
            shapeFillPaint,
            true,
            dateFormat);
      } else {
        return createFlankedShapeLegendItem(
            label, minValue, maxValue, itemShape, shapeFillPaint, true, dateFormat);
      }
    } else if (dimension == PlotDimension.SHAPE) {
      // shape provider probably never supports numerical values
      return null;
    } else if (dimension == PlotDimension.SIZE) {
      SizeProvider sizeProvider = dimensionConfigData.getSizeProvider();

      if (!sizeProvider.supportsNumericalValues()) {
        throw new RuntimeException(
            "Size provider for continuous legend item does not support numerical values.");
      }

      double minScalingFactor = sizeProvider.getMinScalingFactor();
      double maxScalingFactor = sizeProvider.getMaxScalingFactor();
      ContinuousSizeProvider legendSizeProvider =
          new ContinuousSizeProvider(
              minScalingFactor,
              maxScalingFactor,
              MIN_LEGEND_ITEM_SCALING_FACTOR,
              MAX_LEGEND_ITEM_SCALING_FACTOR,
              false);

      int legendItemCount = 4;
      Area composedShape = new Area();
      Shape originalShape = UNDEFINED_SHAPE;
      if (dimensionSet.contains(PlotDimension.SIZE) && dimensionSet.size() == 1) {
        originalShape = UNDEFINED_SHAPE_AND_COLOR;
      }
      double maxHeight = originalShape.getBounds().getHeight() * MAX_LEGEND_ITEM_SCALING_FACTOR;
      for (int i = 0; i < legendItemCount; ++i) {
        double fraction =
            minScalingFactor
                + ((double) i / legendItemCount * (maxScalingFactor - minScalingFactor));
        double legendScalingFactor = legendSizeProvider.getScalingFactorForValue(fraction);

        double composedWidth = composedShape.getBounds().getWidth();

        AffineTransform t = new AffineTransform();
        t.scale(legendScalingFactor, legendScalingFactor);
        Shape shape = t.createTransformedShape(originalShape);

        t = new AffineTransform();
        double shapeWidth = shape.getBounds().getWidth();
        double shapeHeight = shape.getBounds().getHeight();
        t.translate(composedWidth + shapeWidth * .1, (maxHeight - shapeHeight) / 2.0);
        t.translate(-shape.getBounds().getMinX(), -shape.getBounds().getMinY());
        shape = t.createTransformedShape(shape);
        composedShape.add(new Area(shape));
      }

      return createFlankedShapeLegendItem(
          label, minValue, maxValue, composedShape, UNDEFINED_COLOR_PAINT, false, dateFormat);

    } else {
      throw new RuntimeException(
          "Unsupported dimension. Execution path should never reach this line.");
    }
  }