/**
   * Writes the histogram's data to tab-delimited text.
   *
   * @param jOut java.io.FileWriter The file to write to.
   * @throws java.io.IOException if there's a problem writing the file.
   */
  protected void writeChartDataToFile(java.io.FileWriter jOut) throws java.io.IOException {

    int i, j, iNumSeries, iNumItems;

    // jOut.write("Values are in number per hectare.\n");

    // Get the number of series - this will be the number of species, with
    // a possible totals series
    iNumSeries = m_oDataset.getSeriesCount();
    if (iNumSeries <= 0) {
      jOut.write("This histogram has no data.\n");
      return;
    }

    // Write out the header columns
    jOut.write("Species");
    for (i = 1; i <= m_iNumBins; i++) {
      jOut.write(
          "\t" + String.valueOf((i - 1) * m_fBinSize) + " - " + String.valueOf(i * m_fBinSize));
    }
    jOut.write("\n");

    // Write out the series
    for (i = 0; i < iNumSeries; i++) {
      jOut.write((String) m_oDataset.getSeriesKey(i));
      iNumItems = m_oDataset.getItemCount(i);
      for (j = 0; j < iNumItems; j++) {
        jOut.write("\t" + String.valueOf(m_oDataset.getYValue(i, j)));
      }
      jOut.write("\n");
    }
    jOut.write("\n");
  }
  /**
   * Updates the histogram.
   *
   * @param oLegend Legend for this chart.
   * @throws ModelException Passing through an underlying exception.
   */
  void updateChart(Legend oLegend) throws ModelException {

    // If there are no series in the dataset, add them now (in case the
    // file was re-parsed while this chart was open)
    if (m_oDataset.getSeriesCount() == 0) {
      addSeriesToDataset();
    }

    ModelHistogramDataset oAdjustedDataset = updateForVisible(oLegend);

    DataGrapher.updateHistogram(oAdjustedDataset, m_oChartFrame, oLegend, this);

    // Update bin information, if we're updating
    if (m_bRecalcBinsOnUpdate) {
      JTextField jNumBins =
          (JTextField)
              DataGrapher.findNamedComponent(m_oChartFrame.getContentPane(), NUMBER_BINS_NAME);
      JTextField jBinSize =
          (JTextField)
              DataGrapher.findNamedComponent(m_oChartFrame.getContentPane(), BIN_SIZE_NAME);
      jNumBins.setText(String.valueOf(m_iNumBins));
      jBinSize.setText(m_jFormat.format(m_fBinSize));
    }

    if (m_bIsInt) m_jMinDatasetValue.setText(String.valueOf((int) m_fDatasetMin));
    else m_jMinDatasetValue.setText(String.valueOf(m_jFormat.format(m_fDatasetMin)));
    if (m_bIsInt) m_jMaxDatasetValue.setText(String.valueOf((int) m_fDatasetMax));
    else m_jMaxDatasetValue.setText(String.valueOf(m_jFormat.format(m_fDatasetMax)));
  }
 /**
  * Adds the series to the dataset and makes values per-hectare.
  *
  * @param bBatchMode Whether or not this is batch mode.
  */
 public void timestepParseFinished(boolean bBatchMode) throws ModelException {
   int iNumSeries, iNumItems, i, j;
   float fValue;
   // Add series to dataset
   if (m_oDataset.getSeriesCount() == 0) {
     addSeriesToDataset();
     // Adjust values to amounts per hectare
     iNumSeries = m_oDataset.getSeriesCount();
     for (i = 0; i < iNumSeries; i++) {
       iNumItems = m_oDataset.getItemCount(i);
       for (j = 0; j < iNumItems; j++) {
         fValue = m_oDataset.getY(i, j).intValue();
         // fValue /= m_fPlotAreaInHectares;
         m_oDataset.setYValue(i, j, fValue);
       }
     }
   }
 }
  /**
   * Copies our dataset and includes only species marked as visible in the legend.
   *
   * @param oLegend Legend for this chart.
   * @return The copied dataset.
   * @throws ModelException Passing through an underlying exception.
   */
  protected ModelHistogramDataset updateForVisible(Legend oLegend) throws ModelException {
    ModelHistogramDataset oNewDataset = (ModelHistogramDataset) m_oDataset.clone();
    String sName;
    int i;

    for (i = 0; i < oNewDataset.getSeriesCount(); i++) {
      sName = (String) oNewDataset.getSeriesKey(i);

      if (sName.equalsIgnoreCase("total") == false) {

        // If it's enabled, add the series to the copy
        if (!oLegend.getIsSpeciesSelected(m_oManager.getSpeciesCodeFromName(sName))) {
          oNewDataset.removeSeries(i);
          i--;
        }
      }
    }

    return oNewDataset;
  }
  /**
   * This will add all series to the dataset.
   *
   * @throws ModelException Passing through from underlying
   */
  protected void addSeriesToDataset() throws ModelException {
    float[] p_oNewSeries;
    float fMax = 0;
    int i, j;

    // If our binsize is 0, figure it out from the data.  Get the maximum value
    // in the dataset.  We'll want to refigure this at every update because we
    // don't know the final limits of whatever we're graphing.
    if (m_bRecalcBinsOnUpdate) {
      // Get the maximum value of all series
      for (i = 0; i < mp_oData.size(); i++) {
        for (j = 0; j < mp_oData.get(i).size(); j++) {
          Number oNumber = (Number) mp_oData.get(i).get(j);
          if (oNumber.floatValue() > fMax) {
            fMax = oNumber.floatValue();
          }
        }
      }
      m_fBinSize = (fMax + 1) / m_iNumBins;
    }

    // Add all accumulated data to the dataset
    for (i = 0; i < mp_oData.size(); i++) {
      p_oNewSeries = new float[mp_oData.get(i).size()];

      for (j = 0; j < mp_oData.get(i).size(); j++) {
        Number fVal = (Number) mp_oData.get(i).get(j);
        p_oNewSeries[j] = fVal.floatValue();
      }

      if (p_oNewSeries.length > 0) {
        m_oDataset.addSeries(
            m_oManager.getSpeciesNameFromCode(i), p_oNewSeries, m_iNumBins, m_fBinSize);
      }
    }

    // If totals are desired, add them
    if (m_bShowTotal) {
      m_oDataset.addTotalSeries();
    }
  }
  /**
   * Performs actions for the controls in the Histogram window.
   *
   * @param oEvent Event triggered.
   */
  public void actionPerformed(ActionEvent oEvent) {
    try {
      if (oEvent.getSource() instanceof JCheckBox) {
        JCheckBox jBox = (JCheckBox) oEvent.getSource();
        if (jBox.getName().equals("log_checkbox")) {
          if (jBox.isSelected() != m_bUseLogarithmicAxis) {
            m_bUseLogarithmicAxis = jBox.isSelected();
            updateChart(m_oManager.getLegend());
          }
        } else if (jBox.getName().equals("total_checkbox")) {
          if (jBox.isSelected() != m_bShowTotal) {
            m_bShowTotal = jBox.isSelected();

            // If the dataset already exists, simply calling an update will
            // have no effect; so either remove or add the series, as
            // requested
            if (m_oDataset != null && m_oDataset.getSeriesCount() > 0) {
              if (m_bShowTotal == true) {
                m_oDataset.addTotalSeries();
              } else {
                for (int i = 0; i < m_oDataset.getSeriesCount(); i++) {
                  if (((String) m_oDataset.getSeriesKey(i)).equalsIgnoreCase("total")) {
                    m_oDataset.removeSeries(i);
                    break;
                  }
                }
              }
            }
            updateChart(m_oManager.getLegend());
          }
        }
      } else if (oEvent.getActionCommand().equals("UpdateBins")) {

        // Make sure the values in the bins are recognizable, greater-than-zero numbers
        int iNumBins = 0;
        float fBinSize = 0;
        JTextField jNumBins =
            (JTextField)
                DataGrapher.findNamedComponent(m_oChartFrame.getContentPane(), NUMBER_BINS_NAME);
        JTextField jBinSize =
            (JTextField)
                DataGrapher.findNamedComponent(m_oChartFrame.getContentPane(), BIN_SIZE_NAME);
        try {
          Integer oNumBins = new Integer(jNumBins.getText());
          Float oBinSize = new Float(jBinSize.getText());
          iNumBins = oNumBins.intValue();
          fBinSize = oBinSize.floatValue();
        } catch (java.lang.NumberFormatException oErr) {
          JOptionPane.showMessageDialog(
              m_oChartFrame,
              "The value for number of bins or bin size is not a recognized number.");
          jNumBins.setText(String.valueOf(m_iNumBins));
          jBinSize.setText(String.valueOf(m_fBinSize));
          return;
        }

        if (iNumBins <= 0 || fBinSize <= 0) {
          JOptionPane.showMessageDialog(
              m_oChartFrame,
              "The values for number of bins and bin size must be greater than zero.");
          jNumBins.setText(String.valueOf(m_iNumBins));
          jBinSize.setText(String.valueOf(m_fBinSize));
          return;
        }

        if (iNumBins != m_iNumBins || fBinSize != m_fBinSize) {
          m_iNumBins = iNumBins;
          m_fBinSize = fBinSize;

          // Clear the dataset to force a reconstruction
          m_oDataset = null;
          m_oDataset = new ModelHistogramDataset();

          m_bRecalcBinsOnUpdate = false;
          updateChart(m_oManager.getLegend());
        }
      }
      super.actionPerformed(oEvent);
    } catch (sortie.data.simpletypes.ModelException oErr) {
      ErrorGUI oHandler = new ErrorGUI(m_oChartFrame);
      oHandler.writeErrorMessage(oErr);
    }
  }