@Override
  public void parameterValueChanged(IAlgoInstance ai, String parameterId, Object novelValue) {

    if (DEBUG_DURATIONS) Timers.SINGLETON.startTask(DEBUG_KEY_UPDATE_DATA);

    loadDataFromTable();

    if (DEBUG_DURATIONS) Timers.SINGLETON.endTask(DEBUG_KEY_UPDATE_DATA, 5);
  }
  public void loadDataFromTable() {

    if (lastVersionDataToDisplay == null) return;

    if (compositeCharts.isDisposed()) return;

    try {
      showBusy(true);

      if (DEBUG_DURATIONS) Timers.SINGLETON.startTask(DEBUG_KEY_LOAD_DATA);

      chart.setNotify(false);
      dataset.clear();

      // decode metadata
      final Map<String, Map<String, String>> metadata = getMetadataFromTable();
      if (metadata == null) return;
      final String columnIteration =
          (String)
              lastVersionDataToDisplay.getTableMetaData(
                  GeneticExplorationAlgoConstants.TABLE_METADATA_KEY_COLTITLE_ITERATION);

      // define what is the last iteration
      final Integer iterationToDisplay =
          (Integer)
              lastVersionDataToDisplay.getValue(
                  lastVersionDataToDisplay.getRowsCount() - 1, columnIteration);

      // search for the first line to display
      int currentRow = lastVersionDataToDisplay.getRowsCount();
      Integer currentRowIteration = null;
      do {
        currentRow--;
        currentRowIteration =
            (Integer) lastVersionDataToDisplay.getValue(currentRow, columnIteration);
      } while (currentRowIteration != iterationToDisplay);

      final int rowFirst = currentRow;

      // first iterate to find
      // * the row end
      // * the min and max for each goal
      Map<String, Number> key2min = new HashMap<String, Number>(metadata.keySet().size());
      Map<String, Number> key2max = new HashMap<String, Number>(metadata.keySet().size());
      do {
        // for each goal, measure min and max
        for (String goal : metadata.keySet()) {

          final Map<String, String> metadataGoal = metadata.get(goal);

          String colGoalValue =
              metadataGoal.get(
                  GeneticExplorationAlgoConstants.TABLE_COLUMN_GOAL_METADATA_VALUE_VALUE);
          Number goalValue = (Number) lastVersionDataToDisplay.getValue(currentRow, colGoalValue);
          if (goalValue == null) {
            // data not available
            continue;
          }

          Number minBefore = key2min.get(colGoalValue);
          if ((minBefore == null) || (minBefore.doubleValue() > goalValue.doubleValue())) {
            key2min.put(colGoalValue, goalValue);
          }

          Number maxBefore = key2max.get(colGoalValue);
          if ((maxBefore == null) || (maxBefore.doubleValue() < goalValue.doubleValue())) {
            key2max.put(colGoalValue, goalValue);
          }
        }
        currentRowIteration =
            (Integer) lastVersionDataToDisplay.getValue(currentRow, columnIteration);
        currentRow--;
      } while ((currentRowIteration == iterationToDisplay) && (currentRow >= 0));

      if (DEBUG_DURATIONS) Timers.SINGLETON.endTask(DEBUG_KEY_LOAD_DATA, 5);

      // display all the individuals for this iteration ID
      final int rowEnd = currentRow + 1;

      labelIteration.setText(
          "results for iteration "
              + iterationToDisplay
              + " ("
              + (rowFirst - rowEnd)
              + " Pareto efficient solutions)");

      // for each individual...
      int individualDisplayId = 1;
      for (currentRow = rowFirst; currentRow > rowEnd; currentRow--) {

        // for each goal...
        for (String goal : metadata.keySet()) {

          // retrieve information about this goal
          final Map<String, String> metadataGoal = metadata.get(goal);
          String colGoalTarget =
              metadataGoal.get(
                  GeneticExplorationAlgoConstants.TABLE_COLUMN_GOAL_METADATA_VALUE_TARGET);
          String colGoalValue =
              metadataGoal.get(
                  GeneticExplorationAlgoConstants.TABLE_COLUMN_GOAL_METADATA_VALUE_VALUE);
          final Object goalValueObject =
              lastVersionDataToDisplay.getValue(currentRow, colGoalValue);

          if (goalValueObject == null) {
            // data not available
            continue;
          }

          double goalTarget =
              ((Number) lastVersionDataToDisplay.getValue(rowFirst, colGoalTarget)).doubleValue();
          double goalValue = ((Number) goalValueObject).doubleValue();

          double valueMin = Math.min(key2min.get(colGoalValue).doubleValue(), goalTarget);
          double valueMax = Math.max(key2max.get(colGoalValue).doubleValue(), goalTarget);
          valueMin -= valueMin / 10;

          double ratio = (valueMax - valueMin);
          double b = 1; // ratio/(goalTarget-valueMin)*0.5;

          // for the first individual, let's display the goal first
          if (currentRow == rowFirst) {
            double propTarget = (goalTarget - valueMin) / ratio * b;
            final String axisTarget = "target";
            dataset.addValue(propTarget, axisTarget, goal);
          }

          // for all the individuals, also display the value !
          double propValue = (goalValue - valueMin) / ratio * b;
          dataset.addValue(propValue, "solution " + individualDisplayId, goal);
        }

        individualDisplayId++;
      }

      chart.setNotify(true);

      // hide all the other ones
      if (DEBUG_DURATIONS) Timers.SINGLETON.startTask(DEBUG_KEY_HIDE_USELESS);

      if (DEBUG_DURATIONS) Timers.SINGLETON.endTask(DEBUG_KEY_HIDE_USELESS, 5);

    } catch (RuntimeException e) {
      e.printStackTrace();

    } finally {

      showBusy(false);
    }
  }