text: #include "cmParseCoberturaCoverage.h"
  
  #include <cstdlib>
  #include <cstring>
  
  #include "cmsys/FStream.hxx"
  
  #include "cmCTest.h"
  #include "cmCTestCoverageHandler.h"
  #include "cmStringAlgorithms.h"
  #include "cmSystemTools.h"
  #include "cmXMLParser.h"
  
  class cmParseCoberturaCoverage::XMLParser : public cmXMLParser
  {
  public:
    XMLParser(cmCTest* ctest, cmCTestCoverageHandlerContainer& cont)
      : FilePaths{ cont.SourceDir, cont.BinaryDir }
      , CTest(ctest)
      , Coverage(cont)
    {
    }
  
  protected:
    void EndElement(const std::string& name) override
    {
      if (name == "source") {
        this->InSource = false;
      } else if (name == "sources") {
        this->InSources = false;
      } else if (name == "class") {
        this->SkipThisClass = false;
      }
    }
  
    void CharacterDataHandler(const char* data, int length) override
    {
      std::string tmp;
      tmp.insert(0, data, length);
      if (this->InSources && this->InSource) {
        this->FilePaths.push_back(tmp);
        cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                           "Adding Source: " << tmp << std::endl,
                           this->Coverage.Quiet);
      }
    }
  
    void StartElement(const std::string& name, const char** atts) override
    {
      std::string FoundSource;
      std::string finalpath;
      if (name == "source") {
        this->InSource = true;
      } else if (name == "sources") {
        this->InSources = true;
      } else if (name == "class") {
        int tagCount = 0;
        while (true) {
          if (strcmp(atts[tagCount], "filename") == 0) {
            cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                               "Reading file: " << atts[tagCount + 1]
                                                << std::endl,
                               this->Coverage.Quiet);
            std::string filename = atts[tagCount + 1];
            this->CurFileName.clear();
  
            // Check if this is an absolute path that falls within our
            // source or binary directories.
            for (std::string const& filePath : this->FilePaths) {
              if (cmHasPrefix(filename, filePath)) {
                this->CurFileName = filename;
                break;
              }
            }
  
            if (this->CurFileName.empty()) {
              // Check if this is a path that is relative to our source or
              // binary directories.
              for (std::string const& filePath : this->FilePaths) {
                finalpath = cmStrCat(filePath, "/", filename);
                if (cmSystemTools::FileExists(finalpath)) {
                  this->CurFileName = finalpath;
                  break;
                }
              }
            }
  
            cmsys::ifstream fin(this->CurFileName.c_str());
            if (this->CurFileName.empty() || !fin) {
              this->CurFileName =
                cmStrCat(this->Coverage.BinaryDir, "/", atts[tagCount + 1]);
              fin.open(this->CurFileName.c_str());
              if (!fin) {
                cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                                   "Skipping system file " << filename
                                                           << std::endl,
                                   this->Coverage.Quiet);
  
                this->SkipThisClass = true;
                break;
              }
            }
            std::string line;
            FileLinesType& curFileLines =
              this->Coverage.TotalCoverage[this->CurFileName];
            while (cmSystemTools::GetLineFromStream(fin, line)) {
              curFileLines.push_back(-1);
            }
  
            break;
          }
          ++tagCount;
        }
      } else if (name == "line") {
        int tagCount = 0;
        int curNumber = -1;
        int curHits = -1;
        while (true) {
          if (this->SkipThisClass) {
            break;
          }
          if (strcmp(atts[tagCount], "hits") == 0) {
            curHits = atoi(atts[tagCount + 1]);
          } else if (strcmp(atts[tagCount], "number") == 0) {
            curNumber = atoi(atts[tagCount + 1]);
          }
  
          if (curHits > -1 && curNumber > 0) {
            FileLinesType& curFileLines =
              this->Coverage.TotalCoverage[this->CurFileName];
            {
              curFileLines[curNumber - 1] = curHits;
            }
            break;
          }
          ++tagCount;
        }
      }
    }
  
  private:
    bool InSources = false;
    bool InSource = false;
    bool SkipThisClass = false;
    std::vector<std::string> FilePaths;
    using FileLinesType =
      cmCTestCoverageHandlerContainer::SingleFileCoverageVector;
    cmCTest* CTest;
    cmCTestCoverageHandlerContainer& Coverage;
    std::string CurFileName;
  };
  
  cmParseCoberturaCoverage::cmParseCoberturaCoverage(
    cmCTestCoverageHandlerContainer& cont, cmCTest* ctest)
    : Coverage(cont)
    , CTest(ctest)
  {
  }
  
  bool cmParseCoberturaCoverage::ReadCoverageXML(const char* xmlFile)
  {
    cmParseCoberturaCoverage::XMLParser parser(this->CTest, this->Coverage);
    parser.ParseFile(xmlFile);
    return true;
  }
  