HistoricalData.java

package progen.output;

import java.util.HashMap;
import java.util.Map;

import progen.context.ProGenContext;
import progen.output.datacollectors.DataCollector;

/**
 * Representación de todos los datos históricos disponibles en la ejecución de
 * Progen. Se almacenan todos los datos históricos categorizados por tipo de
 * DataGenerator y generación de la evolución.
 * 
 * @author jirsis
 * @since 2.0
 * @see progen.output.datacollectors
 */
public final class HistoricalData {

  /** Identificador de los dator referentes a la totalidad del experimento. */
  public static final String EXPERIMENT = "-ALL";

  private static final String STRING_INTEGER_FORMATTER = "%s%d";
  
  /** Instancia única de la clase. */
  private static HistoricalData historical;

  /**
   * Colección de todos los recolectores de datos a lo largo de las
   * generaciónes.
   */
  private Map<String, DataCollector> collectors;
  /** Identifica la generación actual. */
  private int currentGeneration;
  /** Total de colectores disponibles en la ejecución. */
  private int totalCollectors;
  
  /**
   * Constructor genérico de la clase, en la que se inicializan los valores
   * iniciales.
   */
  private HistoricalData() {
    collectors = new HashMap<String, DataCollector>();
    totalCollectors = ProGenContext.getOptionalProperty("progen.datacollector.total", 0);
    this.addCollectors(HistoricalData.EXPERIMENT);
    this.newExperiment();
  }

  /**
   * Forma de obtener la única instancia que existirá a lo largo de la ejecución
   * de ProGen.
   * 
   * @return Instancia de la clase.
   */
  public static synchronized HistoricalData makeInstance() {
    if (historical == null) {
      historical = new HistoricalData();
    }
    return historical;
  }

  /**
   * Obtiene el DataColector de la última generación, identificada por el nombre
   * del colector.
   * 
   * @param name
   *          El nombre del colector a recuperar.
   * @return El colector deseado o uno que no continene plugins en caso de que
   *         no exista.
   */
  public DataCollector getCurrentDataCollector(String name) {
    return this.getDataCollector(String.format(STRING_INTEGER_FORMATTER, name, currentGeneration));
  }

  /**
   * Obtiene el DataCollector indicado en el nombre.
   * 
   * @param name
   *          El nombre del DataCollector.
   * @return El Datacollector solicitado.
   */
  private DataCollector getDataCollector(String name) {
    DataCollector dataCollector = collectors.get(name);
    if (dataCollector == null) {
      dataCollector = new DataCollector();
    }
    return dataCollector;
  }

  /**
   * Obtiene el DataColector de la generación especificada, identificada por el
   * nombre del colector.
   * 
   * @param name
   *          El nombre del colector a recuperar.
   * @param generation
   *          La generación de la que se quieren obtener los datos.
   * @return El colector deseado o <code>null</code> en caso de que no exista.
   */
  public DataCollector getDataCollector(String name, int generation) {
    return this.getDataCollector(String.format(STRING_INTEGER_FORMATTER, name, generation));
  }

  /**
   * Obtiene el DataCollector general del experimento, identificado por el
   * nombre del colector.
   * 
   * @param name
   *          EL nombre del colector a recuperar.
   * @return El colector deseado o <code>null</code> en caso de que no exista.
   */
  public DataCollector getDataCollectorExperiment(String name) {
    return this.getDataCollector(name + HistoricalData.EXPERIMENT);
  }

  /**
   * Actualiza el histórico para definir los colectores de la nueva generación.
   */
  public void newGeneration() {
    final int maxGeneration = ProGenContext.getOptionalProperty("progen.max-generation", 0);
    if (currentGeneration + 1 < maxGeneration) {
      currentGeneration++;
      this.addCollectors(String.valueOf(currentGeneration));
    }
  }

  /**
   * Añade la colección de DataCollectors disponibles al histórico, identifcados
   * por una etiqueta.
   * 
   * @param tag
   *          Etiqueta que identificará al DataCollector correspondiente.
   */
  private void addCollectors(String tag) {
    for (int i = 0; i < totalCollectors; i++) {
      final DataCollector collector = new DataCollector("progen.datacollector" + i);
      collectors.put(String.format("%s%s", collector.getName(), tag), collector);
    }
  }

  /**
   * Devuelve el número de la generación que se está ejecutando en un momento
   * determinado.
   * 
   * @return La generación que está ejecutando.
   */
  public int getCurrentGeneration() {
    return currentGeneration;
  }

  /**
   * Al comienzo de cada experimento, es necesario definir unos valores por
   * defecto.
   */
  public void newExperiment() {
    // default value is negative because newGeneration() increase this value
    // and really start in 0
    currentGeneration = -1;
    this.newGeneration();
    DataCollector collector;
    for (String keyCollector : collectors.keySet()) {
      collector = collectors.get(keyCollector);
      if (!collector.getName().endsWith(HistoricalData.EXPERIMENT)) {
        collectors.remove(collector.getName());
      }
    }
  }

}