/** {@inheritDoc} */
  public void endElement(String uri, String localName, String qName) throws SAXException {
    if ("testcase".equals(qName)) {
      currentSuite.getTestCases().add(testCase);
    } else if ("failure".equals(qName)) {
      Map<String, Object> failure = testCase.getFailure();

      failure.put("detail", parseCause(currentElement.toString()));
    } else if ("error".equals(qName)) {
      Map<String, Object> error = testCase.getFailure();

      error.put("detail", parseCause(currentElement.toString()));
    } else if ("time".equals(qName)) {
      try {
        Number time = numberFormat.parse(currentElement.toString());
        defaultSuite.setTimeElapsed(time.floatValue());
      } catch (ParseException e) {
        throw new SAXException(e.getMessage(), e);
      }
    }
    // TODO extract real skipped reasons
  }
  public Collection<ReportTestSuite> parse(InputStreamReader stream)
      throws ParserConfigurationException, SAXException, IOException {
    SAXParserFactory factory = SAXParserFactory.newInstance();

    SAXParser saxParser = factory.newSAXParser();

    valid = true;

    classesToSuites = new HashMap<String, ReportTestSuite>();

    saxParser.parse(new InputSource(stream), this);

    if (currentSuite
        != defaultSuite) { // omit the defaultSuite if it's empty and there are alternatives
      if (defaultSuite.getNumberOfTests() == 0) {
        classesToSuites.remove(defaultSuite.getFullClassName());
      }
    }

    return classesToSuites.values();
  }
  /** {@inheritDoc} */
  public void startElement(String uri, String localName, String qName, Attributes attributes)
      throws SAXException {
    if (!valid) {
      return;
    }
    try {
      if ("testsuite".equals(qName)) {
        defaultSuite = new ReportTestSuite();
        currentSuite = defaultSuite;

        try {
          Number time = numberFormat.parse(attributes.getValue("time"));

          defaultSuite.setTimeElapsed(time.floatValue());
        } catch (NullPointerException npe) {
          System.err.println("WARNING: no time attribute found on testsuite element");
        }

        // check if group attribute is existing
        if (attributes.getValue("group") != null && !"".equals(attributes.getValue("group"))) {
          String packageName = attributes.getValue("group");
          String name = attributes.getValue("name");

          defaultSuite.setFullClassName(packageName + "." + name);
        } else {
          String fullClassName = attributes.getValue("name");
          defaultSuite.setFullClassName(fullClassName);
        }

        classesToSuites.put(defaultSuite.getFullClassName(), defaultSuite);
      } else if ("testcase".equals(qName)) {
        currentElement = new StringBuffer();

        testCase = new ReportTestCase();

        testCase.setName(attributes.getValue("name"));

        String fullClassName = attributes.getValue("classname");

        // if the testcase declares its own classname, it may need to belong to its own suite
        if (fullClassName != null) {
          currentSuite = classesToSuites.get(fullClassName);
          if (currentSuite == null) {
            currentSuite = new ReportTestSuite();
            currentSuite.setFullClassName(fullClassName);
            classesToSuites.put(fullClassName, currentSuite);
          }
        }

        testCase.setFullClassName(currentSuite.getFullClassName());
        testCase.setClassName(currentSuite.getName());
        testCase.setFullName(currentSuite.getFullClassName() + "." + testCase.getName());

        String timeAsString = attributes.getValue("time");

        Number time = 0;

        if (timeAsString != null) {
          time = numberFormat.parse(timeAsString);
        }

        testCase.setTime(time.floatValue());

        if (currentSuite != defaultSuite) {
          currentSuite.setTimeElapsed(time.floatValue() + currentSuite.getTimeElapsed());
        }
      } else if ("failure".equals(qName)) {
        testCase.addFailure(attributes.getValue("message"), attributes.getValue("type"));
        currentSuite.setNumberOfFailures(1 + currentSuite.getNumberOfFailures());
      } else if ("error".equals(qName)) {
        testCase.addFailure(attributes.getValue("message"), attributes.getValue("type"));
        currentSuite.setNumberOfErrors(1 + currentSuite.getNumberOfErrors());
      } else if ("skipped".equals(qName)) {
        final String message = attributes.getValue("message");
        testCase.addFailure(message != null ? message : "skipped", "skipped");
        currentSuite.setNumberOfSkipped(1 + currentSuite.getNumberOfSkipped());
      } else if ("failsafe-summary".equals(qName)) {
        valid = false;
      }
    } catch (ParseException e) {
      throw new SAXException(e.getMessage(), e);
    }
  }