/**
   * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String,
   *     java.lang.String)
   */
  public void endElement(String namespaceURI, String sName, String qName) throws SAXException {

    if (canceled) throw new SAXException("Parsing canceled");

    // <NAME>
    if (qName.equals(PeakListElementName_2_3.PEAKLIST_NAME.getElementName())) {
      name = getTextOfElement();
      logger.info("Loading peak list: " + name);
      peakListName = name;
    }

    // <PEAKLIST_DATE>
    if (qName.equals(PeakListElementName_2_3.PEAKLIST_DATE.getElementName())) {
      dateCreated = getTextOfElement();
    }

    // <QUANTITY>
    if (qName.equals(PeakListElementName_2_3.QUANTITY.getElementName())) {
      String text = getTextOfElement();
      totalRows = Integer.parseInt(text);
    }

    // <RAW_FILE>
    if (qName.equals(PeakListElementName_2_3.RAWFILE.getElementName())) {
      rawDataFileID = getTextOfElement();
      RawDataFile dataFile = dataFilesIDMap.get(rawDataFileID);
      if (dataFile == null) {
        throw new SAXException(
            "Cannot open peak list, because raw data file " + rawDataFileID + " is missing.");
      }
      currentPeakListDataFiles.add(dataFile);
    }

    // <SCAN_ID>
    if (qName.equals(PeakListElementName_2_3.SCAN_ID.getElementName())) {

      byte[] bytes = Base64.decodeToBytes(getTextOfElement());
      // make a data input stream
      DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(bytes));
      scanNumbers = new int[numOfMZpeaks];
      for (int i = 0; i < numOfMZpeaks; i++) {
        try {
          scanNumbers[i] = dataInputStream.readInt();
        } catch (IOException ex) {
          throw new SAXException(ex);
        }
      }
    }

    // <REPRESENTATIVE_SCAN>
    if (qName.equals(PeakListElementName_2_3.REPRESENTATIVE_SCAN.getElementName())) {
      representativeScan = Integer.valueOf(getTextOfElement());
    }

    // <FRAGMENT_SCAN>

    if (qName.equals(PeakListElementName_2_3.FRAGMENT_SCAN.getElementName())) {
      fragmentScan = Integer.valueOf(getTextOfElement());
    }

    // <MASS>
    if (qName.equals(PeakListElementName_2_3.MZ.getElementName())) {

      byte[] bytes = Base64.decodeToBytes(getTextOfElement());
      // make a data input stream
      DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(bytes));
      masses = new double[numOfMZpeaks];
      for (int i = 0; i < numOfMZpeaks; i++) {
        try {
          masses[i] = (double) dataInputStream.readFloat();
        } catch (IOException ex) {
          throw new SAXException(ex);
        }
      }
    }

    // <HEIGHT>
    if (qName.equals(PeakListElementName_2_3.HEIGHT.getElementName())) {

      byte[] bytes = Base64.decodeToBytes(getTextOfElement());
      // make a data input stream
      DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(bytes));
      intensities = new double[numOfMZpeaks];
      for (int i = 0; i < numOfMZpeaks; i++) {
        try {
          intensities[i] = (double) dataInputStream.readFloat();
        } catch (IOException ex) {
          throw new SAXException(ex);
        }
      }
    }

    // <PEAK>
    if (qName.equals(PeakListElementName_2_3.PEAK.getElementName())) {

      DataPoint[] mzPeaks = new DataPoint[numOfMZpeaks];
      Range peakRTRange = null, peakMZRange = null, peakIntensityRange = null;
      RawDataFile dataFile = dataFilesIDMap.get(peakColumnID);

      if (dataFile == null)
        throw new SAXException("Error in project: data file " + peakColumnID + " not found");

      for (int i = 0; i < numOfMZpeaks; i++) {

        Scan sc = dataFile.getScan(scanNumbers[i]);
        double retentionTime = sc.getRetentionTime();

        double mz = masses[i];
        double intensity = intensities[i];

        if ((peakRTRange == null) || (peakIntensityRange == null)) {
          peakRTRange = new Range(retentionTime);
          peakIntensityRange = new Range(intensity);
        } else {
          peakRTRange.extendRange(retentionTime);
          peakIntensityRange.extendRange(intensity);
        }
        if (mz > 0.0) {
          mzPeaks[i] = new SimpleDataPoint(mz, intensity);
          if (peakMZRange == null) peakMZRange = new Range(mz);
          else peakMZRange.extendRange(mz);
        }
      }

      FeatureStatus status = FeatureStatus.valueOf(peakStatus);

      SimpleFeature peak =
          new SimpleFeature(
              dataFile,
              mass,
              rt,
              height,
              area,
              scanNumbers,
              mzPeaks,
              status,
              representativeScan,
              fragmentScan,
              peakRTRange,
              peakMZRange,
              peakIntensityRange);

      peak.setCharge(currentPeakCharge);

      if (currentIsotopes.size() > 0) {
        SimpleIsotopePattern newPattern =
            new SimpleIsotopePattern(
                currentIsotopes.toArray(new DataPoint[0]),
                currentIsotopePatternStatus,
                currentIsotopePatternDescription);
        peak.setIsotopePattern(newPattern);
        currentIsotopes.clear();
      }

      buildingRow.addPeak(dataFile, peak);
    }

    // <IDENTITY_PROPERTY>
    if (qName.equals(PeakListElementName_2_3.IDPROPERTY.getElementName())) {
      identityProperties.put(identityPropertyName, getTextOfElement());
    }

    // <PEAK_IDENTITY>
    if (qName.equals(PeakListElementName_2_3.PEAK_IDENTITY.getElementName())) {
      SimplePeakIdentity identity = new SimplePeakIdentity(identityProperties);
      buildingRow.addPeakIdentity(identity, preferred);
    }

    // <ROW>
    if (qName.equals(PeakListElementName_2_3.ROW.getElementName())) {
      buildingPeakList.addRow(buildingRow);
      buildingRow = null;
      parsedRows++;
    }

    // <ISOTOPE>
    if (qName.equals(PeakListElementName_2_3.ISOTOPE.getElementName())) {
      String text = getTextOfElement();
      String items[] = text.split(":");
      double mz = Double.valueOf(items[0]);
      double intensity = Double.valueOf(items[1]);
      DataPoint isotope = new SimpleDataPoint(mz, intensity);
      currentIsotopes.add(isotope);
    }

    if (qName.equals(PeakListElementName_2_3.METHOD_NAME.getElementName())) {
      String appliedMethod = getTextOfElement();
      appliedMethods.add(appliedMethod);
    }

    if (qName.equals(PeakListElementName_2_3.METHOD_PARAMETERS.getElementName())) {
      String appliedMethodParam = getTextOfElement();
      appliedMethodParameters.add(appliedMethodParam);
    }
  }
  /**
   * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String,
   *     java.lang.String, org.xml.sax.Attributes)
   */
  public void startElement(String namespaceURI, String lName, String qName, Attributes attrs)
      throws SAXException {

    if (canceled) throw new SAXException("Parsing canceled");

    // <ROW>
    if (qName.equals(PeakListElementName_2_3.ROW.getElementName())) {

      if (buildingPeakList == null) {
        initializePeakList();
      }
      int rowID = Integer.parseInt(attrs.getValue(PeakListElementName_2_3.ID.getElementName()));
      buildingRow = new SimplePeakListRow(rowID);
      String comment = attrs.getValue(PeakListElementName_2_3.COMMENT.getElementName());
      buildingRow.setComment(comment);
    }

    // <PEAK_IDENTITY>
    if (qName.equals(PeakListElementName_2_3.PEAK_IDENTITY.getElementName())) {
      identityProperties = new Hashtable<String, String>();
      preferred =
          Boolean.parseBoolean(attrs.getValue(PeakListElementName_2_3.PREFERRED.getElementName()));
    }

    // <IDENTITY_PROPERTY>
    if (qName.equals(PeakListElementName_2_3.IDPROPERTY.getElementName())) {
      identityPropertyName = attrs.getValue(PeakListElementName_2_3.NAME.getElementName());
    }

    // <PEAK>
    if (qName.equals(PeakListElementName_2_3.PEAK.getElementName())) {

      peakColumnID = attrs.getValue(PeakListElementName_2_3.COLUMN.getElementName());
      mass = Double.parseDouble(attrs.getValue(PeakListElementName_2_3.MZ.getElementName()));
      // Before MZmine 2.6 retention time was saved in seconds, but now we
      // use minutes, so we need to divide by 60
      rt = Double.parseDouble(attrs.getValue(PeakListElementName_2_3.RT.getElementName())) / 60d;
      height = Double.parseDouble(attrs.getValue(PeakListElementName_2_3.HEIGHT.getElementName()));
      area = Double.parseDouble(attrs.getValue(PeakListElementName_2_3.AREA.getElementName()));
      peakStatus = attrs.getValue(PeakListElementName_2_3.STATUS.getElementName());
      String chargeString = attrs.getValue(PeakListElementName_2_3.CHARGE.getElementName());
      if (chargeString != null) currentPeakCharge = Integer.valueOf(chargeString);
      else currentPeakCharge = 0;
    }

    // <MZPEAK>
    if (qName.equals(PeakListElementName_2_3.MZPEAKS.getElementName())) {
      numOfMZpeaks =
          Integer.parseInt(attrs.getValue(PeakListElementName_2_3.QUANTITY.getElementName()));
    }

    // <ISOTOPE_PATTERN>
    if (qName.equals(PeakListElementName_2_3.ISOTOPE_PATTERN.getElementName())) {
      currentIsotopes.clear();
      currentIsotopePatternStatus =
          IsotopePatternStatus.valueOf(
              attrs.getValue(PeakListElementName_2_3.STATUS.getElementName()));
      currentIsotopePatternDescription =
          attrs.getValue(PeakListElementName_2_3.DESCRIPTION.getElementName());
    }
  }