diff --git a/Analysis.cpp b/Analysis.cpp index 20030eed6f11cb6aed7e6af487ae9ee018708524..06345af459f8410b48846367f95169aeb75706ab 100644 --- a/Analysis.cpp +++ b/Analysis.cpp @@ -2,15 +2,14 @@ #include "Analysis.h" #include "geometry/Room.h" -#include "general/xmlParser.h" - -//#include "VoronoiDiagramGenerator.h" -//#include "VoronoiPolygons.h" +#include "tinyxml/tinyxml.h" #include "VoronoiDiagram.h" #include <iostream> #include <fstream> +#include <direct.h> + using namespace std; /************************************************ @@ -19,66 +18,65 @@ using namespace std; Analysis::Analysis() { - _building = NULL; - _iod = new IODispatcher(); - _numFrames = 10; - - _tIn = NULL; - _tOut = NULL; - _lengthMeasurementarea = 200; // the length of the measurement area - _maxNumofPed =0; //the maximum index of the pedestrian in the trajectory data - _deltaF=5; // half of the time interval that used to calculate instantaneous velocity of ped i. - // here v_i = (X(t+deltaF) - X(t+deltaF))/(2*deltaF). X is location. - _xCor = NULL; - _yCor = NULL; - _firstFrame = NULL; // Record the first frame of each pedestrian - _lastFrame = NULL; // Record the last frame of each pedestrian - _deltaT =160; // the time interval to calculate the classic flow - _lineStartX = 0; //the coordinate of the line used to calculate the flow and velocity - _lineStartY =10; - _lineEndX = 0; - _lineEndY =100; - _flowVelocity = false; // Method A (Zhang2011a) - _fundamentalTinTout = false; // Method B (Zhang2011a) - _classicMethod = false; // Method C //calculate and save results of classic in separate file - _voronoiMethod = false; // Method D--Voronoi method - _cutByCircle = false; //Adjust whether cut each original voronoi cell by a circle - _getProfile = false; // Whether make field analysis or not - _outputGraph = false; // Whether output the data for plot the fundamental diagram each frame - _calcIndividualFD = false; //Adjust whether analyze the individual density and velocity of each pedestrian in stationary state (ALWAYS VORONOI-BASED) - _vComponent = 'B'; // to mark whether x, y or x and y coordinate are used when calculating the velocity - //AccumPedsPassLine = NULL; // the accumulative pedestrians pass a line with time - //AccumVPassLine = NULL; // the accumulative instantaneous velocity of the pedestrians pass a line - _fps = 16; // Frame rate of data - _fClassicRhoV = NULL; - _fVoronoiRhoV = NULL; - _individualFD = NULL; - _fN_t = NULL; - - _scaleX = 10; // the size of the grid - _scaleY = 10; - _lowVertexX = 0;// LOWest vertex of the geometry (x coordinate) - _lowVertexY = 0; // LOWest vertex of the geometry (y coordinate) - _highVertexX = 10; // Highest vertex of the geometry - _highVertexY = 10; - + _building = NULL; + _iod = new IODispatcher(); + _numFrames = 10; + + _tIn = NULL; + _tOut = NULL; + _lengthMeasurementarea = 200; // the length of the measurement area + _maxNumofPed =0; //the maximum index of the pedestrian in the trajectory data + _deltaF=5; // half of the time interval that used to calculate instantaneous velocity of ped i. + // here v_i = (X(t+deltaF) - X(t+deltaF))/(2*deltaF). X is location. + _xCor = NULL; + _yCor = NULL; + _firstFrame = NULL; // Record the first frame of each pedestrian + _lastFrame = NULL; // Record the last frame of each pedestrian + _deltaT =160; // the time interval to calculate the classic flow + _lineStartX = 0; //the coordinate of the line used to calculate the flow and velocity + _lineStartY =10; + _lineEndX = 0; + _lineEndY =100; + _flowVelocity = false; // Method A (Zhang2011a) + _fundamentalTinTout = false; // Method B (Zhang2011a) + _classicMethod = false; // Method C //calculate and save results of classic in separate file + _voronoiMethod = false; // Method D--Voronoi method + _cutByCircle = false; //Adjust whether cut each original voronoi cell by a circle + _getProfile = false; // Whether make field analysis or not + _outputGraph = false; // Whether output the data for plot the fundamental diagram each frame + _calcIndividualFD = false; //Adjust whether analyze the individual density and velocity of each pedestrian in stationary state (ALWAYS VORONOI-BASED) + _vComponent = 'B'; // to mark whether x, y or x and y coordinate are used when calculating the velocity + //AccumPedsPassLine = NULL; // the accumulative pedestrians pass a line with time + //AccumVPassLine = NULL; // the accumulative instantaneous velocity of the pedestrians pass a line + _fps = 16; // Frame rate of data + _fClassicRhoV = NULL; + _fVoronoiRhoV = NULL; + _individualFD = NULL; + _fN_t = NULL; + + _scaleX = 10; // the size of the grid + _scaleY = 10; + _lowVertexX = 0;// LOWest vertex of the geometry (x coordinate) + _lowVertexY = 0; // LOWest vertex of the geometry (y coordinate) + _highVertexX = 10; // Highest vertex of the geometry + _highVertexY = 10; } Analysis::~Analysis() { - delete _building; - delete _iod; - delete _firstFrame; - delete _lastFrame; - delete _tIn; - delete _tOut; - - for (int i=0; i<_maxNumofPed; i++) - { - delete _xCor[i]; - delete _yCor[i]; - } - delete []_xCor; - delete []_yCor; + delete _building; + delete _iod; + delete _firstFrame; + delete _lastFrame; + delete _tIn; + delete _tOut; + + for (int i=0; i<_maxNumofPed; i++) + { + delete _xCor[i]; + delete _yCor[i]; + } + delete []_xCor; + delete []_yCor; } /************************************************ @@ -87,7 +85,7 @@ Analysis::~Analysis() { Building * Analysis::GetBuilding() const { - return _building; + return _building; } /************************************************ @@ -98,470 +96,500 @@ Building * Analysis::GetBuilding() const { * und setzt die entsprechenden Parameter in der Simulation * */ void Analysis::InitArgs(ArgumentParser* args) { - char tmp[CLENGTH]; - string s = "Parameter:\n"; - - switch (args->GetLog()) { - case 0: - // no log file - //Log = new OutputHandler(); - break; - case 1: - if(Log) delete Log; - Log = new STDIOHandler(); - break; - case 2: - { - char name[CLENGTH]=""; - sprintf(name,"%s.P0.dat",args->GetErrorLogFile().c_str()); - if(Log) delete Log; - Log = new FileHandler(name); - } - break; - default: - printf("Wrong option for Logfile!\n\n"); - exit(0); - } + char tmp[CLENGTH]; + string s = "Parameter:\n"; + + switch (args->GetLog()) { + case 0: + // no log file + //Log = new OutputHandler(); + break; + case 1: + if(Log) delete Log; + Log = new STDIOHandler(); + break; + case 2: + { + char name[CLENGTH]=""; + sprintf(name,"%s.P0.dat",args->GetErrorLogFile().c_str()); + if(Log) delete Log; + Log = new FileHandler(name); + } + break; + default: + printf("Wrong option for Logfile!\n\n"); + exit(0); + } - Log->Write("INFO: \tOptionen an Simulation geben\n"); + Log->Write("INFO: \tOptionen an Simulation geben\n"); - _measureZone = args->GetMeasureArea(); + _measureZone = args->GetMeasureArea(); - _flowVelocity = args->GetIsMethodA(); - _fundamentalTinTout = args->GetIsMethodB(); - _classicMethod = args ->GetIsMethodC(); - if(_fundamentalTinTout) - { - _classicMethod = true; - } - _voronoiMethod = args ->GetIsMethodD(); - _lengthMeasurementarea = args->GetLengthMeasurementArea(); - _deltaF = args->GetDelatT_Vins(); - _deltaT = args->GetTimeIntervalA(); - _lineStartX = args->GetLineStartX(); - _lineStartY = args->GetLineStartY(); - _lineEndX = args->GetLineEndX(); - _lineEndY = args->GetLineEndY(); - _cutByCircle = args->GetIsCutByCircle(); - _getProfile = args->GetIsGetProfile(); - _outputGraph = args->GetIsOutputGraph(); - _calcIndividualFD = args->GetIsIndividualFD(); - _vComponent = args->GetVComponent(); - _scaleX = args->GetScaleX(); - _scaleY = args->GetScaleY(); - - // IMPORTANT: do not change the order in the following.. - sprintf(tmp, "\tGeometrie: [%s]\n", args->GetGeometryFilename().c_str()); - s.append(tmp); - Log->Write("INFO: \t" + s); - _geoPoly = ReadGeometry(args->GetGeometryFilename()); - //pBuilding->WriteToErrorLog(); - - _trajectoryName = args->GetTrajectoryName(); - _trajectoryFile = args->GetTrajectoriesFile(); - std::cout<<_trajectoryFile.c_str(); - Log->Write(string("INFO: \t") + _trajectoryFile); - ReadTrajetories(_trajectoryFile); - - if(_classicMethod) - { -#ifdef WIN32 - string results_C= "Output\\Fundamental_Diagram\\Classical_Voronoi\\rho_v_Classic_"+_trajectoryName+".dat"; -#else - string results_C= "Output/Fundamental_Diagram/Classical_Voronoi/rho_v_Classic_"+_trajectoryName+".dat"; -#endif - - if((_fClassicRhoV=fopen(results_C.c_str(),"w"))==NULL) + _flowVelocity = args->GetIsMethodA(); + _fundamentalTinTout = args->GetIsMethodB(); + _classicMethod = args ->GetIsMethodC(); + if(_fundamentalTinTout) { - Log->Write("cannot open file %s to write classical density and velocity\n", results_C.c_str()); - exit(0); + _classicMethod = true; } - fprintf(_fClassicRhoV,"#Frame \tclassical density(m^(-2))\t classical velocity(m/s)\n"); - } - if(_voronoiMethod) - { -#ifdef WIN32 - string results_V= "Output\\Fundamental_Diagram\\Classical_Voronoi\\rho_v_Voronoi_"+_trajectoryName+".dat"; - //string results_V= "rho_v_Voronoi_"+_trajectoryName+".dat"; -#else - string results_V= "Output/Fundamental_Diagram/Classical_Voronoi/rho_v_Voronoi_"+_trajectoryName+".dat"; -#endif - if((_fVoronoiRhoV=fopen(results_V.c_str(),"w"))==NULL) + _voronoiMethod = args ->GetIsMethodD(); + _lengthMeasurementarea = args->GetLengthMeasurementArea(); + _deltaF = args->GetDelatT_Vins(); + _deltaT = args->GetTimeIntervalA(); + _lineStartX = args->GetLineStartX(); + _lineStartY = args->GetLineStartY(); + _lineEndX = args->GetLineEndX(); + _lineEndY = args->GetLineEndY(); + _cutByCircle = args->GetIsCutByCircle(); + _getProfile = args->GetIsGetProfile(); + _outputGraph = args->GetIsOutputGraph(); + _calcIndividualFD = args->GetIsIndividualFD(); + _vComponent = args->GetVComponent(); + _scaleX = args->GetScaleX(); + _scaleY = args->GetScaleY(); + + sprintf(tmp, "\tGeometrie: [%s]\n", args->GetGeometryFilename().c_str()); + s.append(tmp); + Log->Write("INFO: \t" + s); + _geoPoly = ReadGeometry(args->GetGeometryFilename()); + //pBuilding->WriteToErrorLog(); + + _trajectoryName = args->GetTrajectoriesFilename(); + _trajectoriesLocation= args->GetTrajectoriesLocation(); + + + ReadTrajetories(_trajectoriesLocation+"/"+_trajectoryName); + + if(_classicMethod) { - Log->Write("cannot open the file to write Voronoi density and velocity\n"); - exit(0); + string results_C= "Output/Fundamental_Diagram/Classical_Voronoi/rho_v_Classic_"+_trajectoryName+".dat"; + if((_fClassicRhoV=CreateFile(results_C))==NULL) + { + Log->Write("cannot open file %s to write classical density and velocity\n", results_C.c_str()); + exit(EXIT_FAILURE); + } + fprintf(_fClassicRhoV,"#Frame \tclassical density(m^(-2))\t classical velocity(m/s)\n"); } - fprintf(_fVoronoiRhoV,"#Frame \t Voronoi density(m^(-2))\t Voronoi velocity(m/s)\n"); - } - if(_calcIndividualFD) - { -#ifdef WIN32 - string Individualfundment="Output\\Fundamental_Diagram\\Individual_FD\\IndividualFD"+_trajectoryName+".dat"; -#else - string Individualfundment="Output/Fundamental_Diagram/Individual_FD/IndividualFD"+_trajectoryName+".dat"; -#endif - if((_individualFD=fopen(Individualfundment.c_str(),"w"))==NULL) + if(_voronoiMethod) { - Log->Write("cannot open the file individual\n"); - exit(0); + string results_V= "Output/Fundamental_Diagram/Classical_Voronoi/rho_v_Voronoi_"+_trajectoryName+".dat"; + if((_fVoronoiRhoV=CreateFile(results_V))==NULL) + { + Log->Write("cannot open the file to write Voronoi density and velocity\n"); + exit(EXIT_FAILURE); + } + fprintf(_fVoronoiRhoV,"#Frame \t Voronoi density(m^(-2))\t Voronoi velocity(m/s)\n"); } - fprintf(_individualFD,"#Individual density(m^(-2))\t Individual velocity(m/s)\n"); - } - if(_flowVelocity) - { -#ifdef WIN32 - string N_t= "Output\\Fundamental_Diagram\\FlowVelocity\\Flow_NT_"+_trajectoryName+"_Out.dat"; -#else - string N_t= "Output/Fundamental_Diagram/FlowVelocity/Flow_NT_"+_trajectoryName+"_Out.dat"; -#endif - if((_fN_t=fopen(N_t.c_str(),"w"))==NULL) + if(_calcIndividualFD) { - Log->Write("cannot open the file %s t\n", N_t.c_str() ); - exit(0); + string Individualfundment="Output/Fundamental_Diagram/Individual_FD/IndividualFD"+_trajectoryName+".dat"; + if((_individualFD=CreateFile(Individualfundment))==NULL) + { + Log->Write("cannot open the file individual\n"); + exit(EXIT_FAILURE); + } + fprintf(_individualFD,"#Individual density(m^(-2))\t Individual velocity(m/s)\n"); + } + + if(_flowVelocity) + { + + string N_t= "Output/Fundamental_Diagram/FlowVelocity/Flow_NT_"+_trajectoryName+"_Out.dat"; + if((_fN_t=CreateFile(N_t))==NULL) + { + Log->Write("cannot open the file %s t\n", N_t.c_str() ); + exit(EXIT_FAILURE); + } + else + cout << "can open \n"; + + fprintf(_fN_t,"#Frame\t Cumulative pedestrians\n"); } - else - cout << "can open \n"; - - fprintf(_fN_t,"#Frame\t Cumulative pedestrians\n"); - } } polygon_2d Analysis::ReadGeometry(const string& geometryFile){ - _building = new Building(); - _building->LoadBuilding(geometryFile); - double geo_minX = FLT_MAX; - double geo_minY = FLT_MAX; - double geo_maxX = -FLT_MAX; - double geo_maxY = -FLT_MAX; - polygon_2d geoPoly; - // create the polygons - _building->InitGeometry(); - vector<Obstacle*> GeoObst; - for(int i=0;i<_building->GetNumberOfRooms();i++){ - Room* room=_building->GetRoom(i); - - for( int j=0;j<room->GetNumberOfSubRooms();j++){ - SubRoom* subroom = room->GetSubRoom(i); - const vector<Point>& temp_GeoPoly = subroom->GetPolygon(); - for (unsigned int j = 0; j< temp_GeoPoly.size(); j++){ - append(geoPoly, make<point_2d>(temp_GeoPoly[j]._x, temp_GeoPoly[j]._y)); - geo_minX = (temp_GeoPoly[j]._x<=geo_minX) ? temp_GeoPoly[j]._x : geo_minX; - geo_minY = (temp_GeoPoly[j]._y<=geo_minY) ? temp_GeoPoly[j]._y : geo_minY; - geo_maxX = (temp_GeoPoly[j]._x>=geo_maxX) ? temp_GeoPoly[j]._x : geo_maxX; - geo_maxY = (temp_GeoPoly[j]._y>=geo_maxY) ? temp_GeoPoly[j]._y : geo_maxY; - - } - correct(geoPoly); - //std::cout << "poly without hole: " << boost::geometry::dsv(geoPoly) << std::endl; - GeoObst = subroom->GetAllObstacles(); - } - for (unsigned int k = 0; k < GeoObst.size(); k++) { - //std::cout<< GeoObst.size()<<std::endl; - //std::cout<< pBuilding->GetAnzRooms() <<'\t'<<room->GetAnzSubRooms()<<std::endl; - const vector<Point>& temp_obst = GeoObst[k]->GetPolygon(); - - geoPoly.inners().resize(k+1); - geoPoly.inners().back(); - model::ring<point_2d>& inner = geoPoly.inners().back(); - for (unsigned int j = 0; j< temp_obst.size();j++){ - append(inner, make<point_2d>(temp_obst[j]._x, temp_obst[j]._y)); - } - correct(geoPoly); - } - } + _building = new Building(); + _building->LoadBuilding(geometryFile); + double geo_minX = FLT_MAX; + double geo_minY = FLT_MAX; + double geo_maxX = -FLT_MAX; + double geo_maxY = -FLT_MAX; + polygon_2d geoPoly; + // create the polygons + _building->InitGeometry(); + vector<Obstacle*> GeoObst; + for(int i=0;i<_building->GetNumberOfRooms();i++){ + Room* room=_building->GetRoom(i); + + for( int j=0;j<room->GetNumberOfSubRooms();j++){ + SubRoom* subroom = room->GetSubRoom(i); + const vector<Point>& temp_GeoPoly = subroom->GetPolygon(); + for (unsigned int j = 0; j< temp_GeoPoly.size(); j++){ + append(geoPoly, make<point_2d>(temp_GeoPoly[j]._x, temp_GeoPoly[j]._y)); + geo_minX = (temp_GeoPoly[j]._x<=geo_minX) ? temp_GeoPoly[j]._x : geo_minX; + geo_minY = (temp_GeoPoly[j]._y<=geo_minY) ? temp_GeoPoly[j]._y : geo_minY; + geo_maxX = (temp_GeoPoly[j]._x>=geo_maxX) ? temp_GeoPoly[j]._x : geo_maxX; + geo_maxY = (temp_GeoPoly[j]._y>=geo_maxY) ? temp_GeoPoly[j]._y : geo_maxY; - _highVertexX = geo_maxX; - _highVertexY = geo_maxY; - _lowVertexX = geo_minX; - _lowVertexY = geo_minY; - cout <<"_highVertexX: \t"<<_highVertexX<< endl; - cout <<"_highVertexY: \t"<<_highVertexY<< endl; - cout <<"_lowVertexX: \t"<<_lowVertexX<< endl; - cout <<"_lowVertexY: \t"<<_lowVertexY<< endl; + } + correct(geoPoly); + //std::cout << "poly without hole: " << boost::geometry::dsv(geoPoly) << std::endl; + GeoObst = subroom->GetAllObstacles(); + } + for (unsigned int k = 0; k < GeoObst.size(); k++) { + //std::cout<< GeoObst.size()<<std::endl; + //std::cout<< pBuilding->GetAnzRooms() <<'\t'<<room->GetAnzSubRooms()<<std::endl; + const vector<Point>& temp_obst = GeoObst[k]->GetPolygon(); + + geoPoly.inners().resize(k+1); + geoPoly.inners().back(); + model::ring<point_2d>& inner = geoPoly.inners().back(); + for (unsigned int j = 0; j< temp_obst.size();j++){ + append(inner, make<point_2d>(temp_obst[j]._x, temp_obst[j]._y)); + } + correct(geoPoly); + } + } + + _highVertexX = geo_maxX; + _highVertexY = geo_maxY; + _lowVertexX = geo_minX; + _lowVertexY = geo_minY; - return geoPoly; + return geoPoly; } void Analysis::ReadTrajetories(const string& trajectoriesFile){ - //---------read the trajectory data from file----------- - ////////////////////////////--------------Parse the xmlFile---------------/////////////////////////////////////////////////////// + //---------read the trajectory data from file----------- + + Log->Write("INFO:\t input file <%s>\n",trajectoriesFile.c_str()); + TiXmlDocument docGeo(trajectoriesFile); + if (!docGeo.LoadFile()){ + Log->Write("ERROR: \t%s", docGeo.ErrorDesc()); + Log->Write("ERROR: \t could not parse the trajectories file"); + exit(EXIT_FAILURE); + } - Log->Write(">>> input file <%s>\n",trajectoriesFile.c_str()); - XMLNode xMainNode = XMLNode::openFileHelper(trajectoriesFile.c_str(),"trajectoriesDataset"); - XMLNode xHeader = xMainNode.getChildNode("header"); // header - XMLNode xFrame = xMainNode.getChildNode("frame"); // frame - _numFrames = xMainNode.nChildNode("frame"); // how much frames + TiXmlElement* xRootNode = docGeo.RootElement(); + if( ! xRootNode ) { + Log->Write("ERROR:\tRoot element does not exist"); + exit(EXIT_FAILURE); + } - Log->Write("numFrames = %d\n",_numFrames); - if(!xHeader.getChildNode("agents").isEmpty()){ - const char* N = xHeader.getChildNode("agents").getText(); - _maxNumofPed = atoi(N); - Log->Write("N=%d\n", _maxNumofPed); - } - if(!xHeader.getChildNode("frameRate").isEmpty()){ - const char* NF = xHeader.getChildNode("frameRate").getText(); - _fps = atoi(NF); - Log->Write("fps=%d\n", _fps); - } - _xCor = new double* [_maxNumofPed]; - _yCor = new double* [_maxNumofPed]; - for (int i=0; i<_maxNumofPed; i++) - { - _xCor[i] = new double [_numFrames]; - _yCor[i] = new double [_numFrames]; - } - _firstFrame = new int[_maxNumofPed]; // Record the first frame of each pedestrian - _lastFrame = new int[_maxNumofPed]; // Record the last frame of each pedestrian - _tIn = new int[_maxNumofPed]; // Record the time of each pedestrian entering measurement area - _tOut = new int[_maxNumofPed]; // Record the time of each pedestrian exiting measurement area - bool IsinMeasurezone[_maxNumofPed]; // Record whether pedestrian i is in measurement area or not - - for(int i = 0; i <_maxNumofPed; i++) - { - for (int j = 0; j < _numFrames; j++) - { - _xCor[i][j] = 0; - _yCor[i][j] = 0; + if( xRootNode->ValueStr () != "trajectoriesDataset" ) { + Log->Write("ERROR:\tRoot element value is not 'geometry'."); + exit(EXIT_FAILURE); } - _firstFrame[i] = INT_MAX; - _lastFrame[i] = INT_MIN; - _tIn[i] = 0; - _tOut[i] = 0; - IsinMeasurezone[i] = false; - } + + //fixme in JPSGCFM + double version = xmltof(xRootNode->Attribute("version"), -1); + if (version != JPS_MAJOR_VERSION) { + Log->Write("ERROR: \tOnly version [%f] > 0.4 supported",version); + Log->Write("ERROR: \tparsing trajectories file failed!"); + //exit(EXIT_FAILURE); + } + + + //counting the number of frames + _numFrames=0; + for(TiXmlElement* xFrame = xRootNode->FirstChildElement("frame"); xFrame; + xFrame = xFrame->NextSiblingElement("frame")) { + _numFrames++; + } + Log->Write("numFrames = %d\n",_numFrames); - if(!xFrame.isEmpty()) - { - for (int f=0; f<_numFrames; f++) //read the data frame by frame + TiXmlNode* xHeader = xRootNode->FirstChild("header"); // header + //Number of agents + if(xHeader->FirstChild("agents")){ + _maxNumofPed=atoi(xHeader->FirstChild("agents")->FirstChild()->Value()); + Log->Write("N=%d\n", _maxNumofPed); + } + + //framerate + if(xHeader->FirstChild("frameRate")){ + _fps=atoi(xHeader->FirstChild("frameRate")->FirstChild()->Value()); + Log->Write("fps=%d\n", _fps); + } + + _xCor = new double* [_maxNumofPed]; + _yCor = new double* [_maxNumofPed]; + for (int i=0; i<_maxNumofPed; i++) { - xFrame = xMainNode.getChildNode("frame", f); // frame j - int numPedsInFrame = xFrame.nChildNode("agent"); // how much agents in this frame - //printf("This frame has %d peds\n",numPedsInFrame); - for (int i=0; i<numPedsInFrame; i++) //read pedestrians in frame j - { - if(!xFrame.getChildNode("agent", i).isEmpty()) + _xCor[i] = new double [_numFrames]; + _yCor[i] = new double [_numFrames]; + } + _firstFrame = new int[_maxNumofPed]; // Record the first frame of each pedestrian + _lastFrame = new int[_maxNumofPed]; // Record the last frame of each pedestrian + _tIn = new int[_maxNumofPed]; // Record the time of each pedestrian entering measurement area + _tOut = new int[_maxNumofPed]; // Record the time of each pedestrian exiting measurement area + bool IsinMeasurezone[_maxNumofPed]; // Record whether pedestrian i is in measurement area or not + + for(int i = 0; i <_maxNumofPed; i++) + { + for (int j = 0; j < _numFrames; j++) { - //get agent id, x, y - const char* x = xFrame.getChildNode("agent", i).getAttribute("xPos"); - const char* y = xFrame.getChildNode("agent", i).getAttribute("yPos"); - const char* id = xFrame.getChildNode("agent", i).getAttribute("ID"); - int ID = atoi(id)-1; - _xCor[ID][f] = atof(x); - _yCor[ID][f] = atof(y); - if(f < _firstFrame[ID]) - { - _firstFrame[ID] = f; - } - if(f > _lastFrame[ID]) - { - _lastFrame[ID] = f; - } - if(_fundamentalTinTout==true) - { - if(within(make<point_2d>(atof(x),atof(y)), _measureZone)&&!(IsinMeasurezone[ID])) + _xCor[i][j] = 0; + _yCor[i][j] = 0; + } + _firstFrame[i] = INT_MAX; + _lastFrame[i] = INT_MIN; + _tIn[i] = 0; + _tOut[i] = 0; + IsinMeasurezone[i] = false; + } + + + //processing the frames node + TiXmlNode* xFramesNode = xRootNode->FirstChild("frame"); + if (!xFramesNode){ + Log->Write("ERROR: \tThe geometry should have at least one frame"); + exit(EXIT_FAILURE); + } + + int frameNr=0; + for(TiXmlElement* xFrame = xRootNode->FirstChildElement("frame"); xFrame; + xFrame = xFrame->NextSiblingElement("frame")) { + + for(TiXmlElement* xAgent = xFrame->FirstChildElement("agent"); xAgent; + xAgent = xAgent->NextSiblingElement("agent")) { + + //get agent id, x, y + double x= atof(xAgent->Attribute("xPos")); + double y= atof(xAgent->Attribute("yPos")); + int ID= atoi(xAgent->Attribute("ID"))-1; + + _xCor[ID][frameNr] = x; + _yCor[ID][frameNr] = y; + if(frameNr < _firstFrame[ID]) { - _tIn[ID]=f; - IsinMeasurezone[ID] = true; + _firstFrame[ID] = frameNr; } - if((!within(make<point_2d>(atof(x),atof(y)), _measureZone))&&IsinMeasurezone[ID]) + if(frameNr > _lastFrame[ID]) { - _tOut[ID]=f; - IsinMeasurezone[ID] = false; + _lastFrame[ID] = frameNr; + } + if(_fundamentalTinTout==true) + { + if(within(make<point_2d>( (x), (y)), _measureZone)&&!(IsinMeasurezone[ID])) + { + _tIn[ID]=frameNr; + IsinMeasurezone[ID] = true; + } + if((!within(make<point_2d>( (x), (y)), _measureZone))&&IsinMeasurezone[ID]) + { + _tOut[ID]=frameNr; + IsinMeasurezone[ID] = false; + } } - } } - } //for i - }// getFrame number j - } - Log->Write("finished test of xmlParser.\n"); + frameNr++; + } + Log->Write("INFO:\tDone reading the trajectories"); } int Analysis::RunAnalysis() { - XMLNode xMainNode = XMLNode::openFileHelper(_trajectoryFile.c_str(),"trajectoriesDataset"); - XMLNode xFrame = xMainNode.getChildNode("frame"); // frame - int ClassicFlow=0; // the number of pedestrians pass a line in a certain time - double V_deltaT=0; // define this is to measure cumulative velocity each pedestrian pass a measure line each time step to calculate the <v>delat T=sum<vi>/N - double *DensityPerFrame; - DensityPerFrame = new double[_numFrames]; - for(int i=0;i<_numFrames;i++) - { - DensityPerFrame[i]=0; - } - bool *PassLine = new bool[_maxNumofPed]; - for(int i=0; i<_maxNumofPed; i++) - { - PassLine[i] = false; - } - string N_t="Flow_NT_"+_trajectoryName+"_Out.dat"; - ofstream flowNTs(N_t.c_str()); + string fullTrajectoriesPathName= _trajectoriesLocation+"/"+_trajectoryName; + + TiXmlDocument docGeo(fullTrajectoriesPathName); + if (!docGeo.LoadFile()){ + Log->Write("ERROR: \t%s", docGeo.ErrorDesc()); + Log->Write("ERROR: \t could not parse the trajectories file <%s>",fullTrajectoriesPathName.c_str()); + exit(EXIT_FAILURE); + } - if(!xFrame.isEmpty()) - { - for (int f=0; f<_numFrames; f++) //read the data frame by frame + TiXmlElement* xRootNode = docGeo.RootElement(); + if( ! xRootNode ) { + Log->Write("ERROR:\tRoot element does not exist"); + exit(EXIT_FAILURE); + } + + if( xRootNode->ValueStr () != "trajectoriesDataset" ) { + Log->Write("ERROR:\tRoot element value is not 'geometry'."); + exit(EXIT_FAILURE); + } + + int ClassicFlow=0; // the number of pedestrians pass a line in a certain time + double V_deltaT=0; // define this is to measure cumulative velocity each pedestrian pass a measure line each time step to calculate the <v>delat T=sum<vi>/N + double *DensityPerFrame; + DensityPerFrame = new double[_numFrames]; + for(int i=0;i<_numFrames;i++) { - xFrame = xMainNode.getChildNode("frame", f); // frame j - const char* frid = xFrame.getAttribute("ID"); + DensityPerFrame[i]=0; + } + bool *PassLine = new bool[_maxNumofPed]; + for(int i=0; i<_maxNumofPed; i++) + { + PassLine[i] = false; + } + + string N_t="Flow_NT_"+_trajectoryName+"_Out.dat"; + ofstream flowNTs(N_t.c_str()); + + int frameNr=0; + for(TiXmlElement* xFrame = xRootNode->FirstChildElement("frame"); xFrame; + xFrame = xFrame->NextSiblingElement("frame")) { + + int frid = atoi(xFrame->Attribute("ID")); + frameNr++; + + //counting the agents in the frame + int numPedsInFrame=0; + for(TiXmlElement* xAgent = xFrame->FirstChildElement("agent"); xAgent; + xAgent = xAgent->NextSiblingElement("agent")) numPedsInFrame++; + + + Log->Write("frame ID = %d\n",(frid)); + + //printf("This frame has %d peds\n",numPedsInFrame); + double *XInFrame = new double[numPedsInFrame]; // save the X coordinates of pedestrian in the geometry in this frame + double *YInFrame = new double[numPedsInFrame]; // save the Y coordinates of pedestrian in the geometry in this frame + double *VInFrame = new double[numPedsInFrame]; // save the instantaneous velocity of pedestrians in the geometry in this frame + + int agentCnt=0; + for(TiXmlElement* xAgent = xFrame->FirstChildElement("agent"); xAgent; + xAgent = xAgent->NextSiblingElement("agent")) { + + //get agent id, x, y + double x= atof(xAgent->Attribute("xPos")); + double y= atof(xAgent->Attribute("yPos")); + int ID= atoi(xAgent->Attribute("ID"))-1; - Log->Write("frame ID = %d\n",atoi(frid)); - int numPedsInFrame = xFrame.nChildNode("agent"); // how many agents in this frame - //printf("This frame has %d peds\n",numPedsInFrame); - double *XInFrame = new double[numPedsInFrame]; // save the X coordinates of pedestrian in the geometry in this frame - double *YInFrame = new double[numPedsInFrame]; // save the Y coordinates of pedestrian in the geometry in this frame - double *VInFrame = new double[numPedsInFrame]; // save the instantaneous velocity of pedestrians in the geometry in this frame + XInFrame[agentCnt] = (x); + YInFrame[agentCnt] = (y); + int Tpast = frameNr - _deltaF; + int Tfuture = frameNr + _deltaF; + VInFrame[agentCnt] = GetVinFrame(frameNr, Tpast, Tfuture, ID, _firstFrame, _lastFrame, _xCor, _yCor, _vComponent); + bool IspassLine=false; + if(frameNr >_firstFrame[ID]&&!PassLine[ID]) + { + IspassLine = IsPassLine(_lineStartX,_lineStartY, _lineEndX, _lineEndY,_xCor[ID][frameNr-1],_yCor[ID][frameNr-1],_xCor[ID][frameNr],_yCor[ID][frameNr]); + } + if(IspassLine==true) + { + PassLine[ID] = true; + ClassicFlow++; + V_deltaT+=VInFrame[agentCnt]; + //cout<<_xCor[ID][f-1]<< "\t"<<_yCor[ID][f-1]<< "\t"<<_xCor[ID][f]<< "\t"<<_yCor[ID][f]<< endl; + } + } + agentCnt++; + //for agentCnt - for (int i=0; i<numPedsInFrame; i++) //read pedestrians in frame j - { - if(!xFrame.getChildNode("agent", i).isEmpty()) + if(_flowVelocity) { - //get agent id, x, y - const char* x = xFrame.getChildNode("agent", i).getAttribute("xPos"); - const char* y = xFrame.getChildNode("agent", i).getAttribute("yPos"); - const char* id = xFrame.getChildNode("agent", i).getAttribute("ID"); - int ID = atoi(id)-1; - XInFrame[i] = atof(x); - YInFrame[i] = atof(y); - int Tpast = f - _deltaF; - int Tfuture = f + _deltaF; - VInFrame[i] = GetVinFrame(f, Tpast, Tfuture, ID, _firstFrame, _lastFrame, _xCor, _yCor, _vComponent); - bool IspassLine=false; - if(f >_firstFrame[ID]&&!PassLine[ID]) - { - IspassLine = IsPassLine(_lineStartX,_lineStartY, _lineEndX, _lineEndY,_xCor[ID][f-1],_yCor[ID][f-1],_xCor[ID][f],_yCor[ID][f]); - } - if(IspassLine==true) - { - PassLine[ID] = true; - ClassicFlow++; - V_deltaT+=VInFrame[i]; - //cout<<_xCor[ID][f-1]<< "\t"<<_yCor[ID][f-1]<< "\t"<<_xCor[ID][f]<< "\t"<<_yCor[ID][f]<< endl; - } + _accumPedsPassLine.push_back(ClassicFlow); + _accumVPassLine.push_back(V_deltaT); + fprintf(_fN_t,"%d\t%d\n",frid, ClassicFlow); } - } //for i - - if(_flowVelocity) - { - _accumPedsPassLine.push_back(ClassicFlow); - _accumVPassLine.push_back(V_deltaT); - fprintf(_fN_t,"%d\t%d\n",atoi(frid), ClassicFlow); - } - - if(_classicMethod) - { - double ClassicDensity = GetClassicalDensity(XInFrame, YInFrame, numPedsInFrame, _measureZone); - double ClassicVelocity = GetClassicalVelocity(XInFrame, YInFrame, VInFrame, numPedsInFrame, _measureZone); - DensityPerFrame[f]=ClassicDensity; - fprintf(_fClassicRhoV,"%d\t%.3f\t%.3f\n",atoi(frid), ClassicDensity,ClassicVelocity); - } - - //------------------Voronoi Method--------------------------------- - if(_voronoiMethod) - { - - vector<polygon_2d> polygons; - VoronoiDiagram vd; - if(numPedsInFrame>2) + + if(_classicMethod) + { + double ClassicDensity = GetClassicalDensity(XInFrame, YInFrame, numPedsInFrame, _measureZone); + double ClassicVelocity = GetClassicalVelocity(XInFrame, YInFrame, VInFrame, numPedsInFrame, _measureZone); + DensityPerFrame[frameNr]=ClassicDensity; + fprintf(_fClassicRhoV,"%d\t%.3f\t%.3f\n", frid, ClassicDensity,ClassicVelocity); + } + + //------------------Voronoi Method--------------------------------- + if(_voronoiMethod) { - polygons = vd.getVoronoiPolygons(XInFrame, YInFrame, VInFrame, numPedsInFrame); - if(_cutByCircle) - { - //polygons = cutPolygonsWithCircle(polygons, XInFrame, YInFrame, 50); - } - polygons = vd.cutPolygonsWithGeometry(polygons, _geoPoly, XInFrame, YInFrame); - double VoronoiVelocity; - if(numPedsInFrame>0) - { - VoronoiVelocity=GetVoronoiVelocity(polygons,VInFrame,_measureZone); - } - else - { - VoronoiVelocity=0; - } - double VoronoiDensity=GetVoronoiDensity(polygons, _measureZone); - fprintf(_fVoronoiRhoV,"%d\t%.3f\t%.3f\n",atoi(frid),VoronoiDensity, VoronoiVelocity); - - if(_calcIndividualFD) - { - if(numPedsInFrame>0) + vector<polygon_2d> polygons; + VoronoiDiagram vd; + if(numPedsInFrame>2) { - // if(i>beginstationary&&i<endstationary) - { - GetIndividualFD(polygons,VInFrame,_measureZone); - } + polygons = vd.getVoronoiPolygons(XInFrame, YInFrame, VInFrame, numPedsInFrame); + if(_cutByCircle) + { + //polygons = cutPolygonsWithCircle(polygons, XInFrame, YInFrame, 50); + } + polygons = vd.cutPolygonsWithGeometry(polygons, _geoPoly, XInFrame, YInFrame); + double VoronoiVelocity; + if(numPedsInFrame>0) + { + VoronoiVelocity=GetVoronoiVelocity(polygons,VInFrame,_measureZone); + } + else + { + VoronoiVelocity=0; + } + double VoronoiDensity=GetVoronoiDensity(polygons, _measureZone); + fprintf(_fVoronoiRhoV,"%d\t%.3f\t%.3f\n",frid,VoronoiDensity, VoronoiVelocity); + + if(_calcIndividualFD) + { + if(numPedsInFrame>0) + { + // if(i>beginstationary&&i<endstationary) + { + GetIndividualFD(polygons,VInFrame,_measureZone); + } + } + } + //------------------field analysis---------------------------------------------------------- + if(_getProfile) + { + if(numPedsInFrame>0) + { + GetProfiles(boost::lexical_cast<string>(frid), polygons, VInFrame); + // GetProfiles(lexical_cast<string>(frid)string(frid_str), polygons, VInFrame); + } + } + //------------the following codes is written to output the Voronoi polygons of a frame----------- + if(_outputGraph) + { + cout<<"output polygons"<<endl; + OutputVoroGraph(boost::lexical_cast<string>(frid), polygons, numPedsInFrame,XInFrame, YInFrame,VInFrame); + } } - } - //------------------field analysis---------------------------------------------------------- - if(_getProfile) - { - if(numPedsInFrame>0) + else { - GetProfiles(string(frid), polygons, VInFrame); + cout<<" the number of the pedestrians is less than 2 !!"<< endl; } - } - //------------the following codes is written to output the Voronoi polygons of a frame----------- - if(_outputGraph) - { - cout<<"output polygons"<<endl; - OutputVoroGraph(string(frid), polygons, numPedsInFrame,XInFrame, YInFrame,VInFrame); - } } - else - { - cout<<" the number of the pedestrians is less than 2 !!"<< endl; - } - } - - delete []XInFrame; - - delete []YInFrame; - - delete []VInFrame; - + + delete []XInFrame; + + delete []YInFrame; + + delete []VInFrame; + }// getFrame number j - } - - if(_classicMethod) - fclose(_fClassicRhoV); - - if(_voronoiMethod) - fclose(_fVoronoiRhoV); - - if(_flowVelocity) - fclose(_fN_t);// - - delete []PassLine; - - //--------------------Fundamental diagram based on Tin and Tout---------------------------------------------------------------------- - if(_fundamentalTinTout) - { -#ifdef WIN32 - string FD_TinTout= "Output\\Fundamental_Diagram\\TinTout\\FDTinTout_"+_trajectoryName+".dat"; -#else - string FD_TinTout= "Output/Fundamental_Diagram/TinTout/FDTinTout_"+_trajectoryName+".dat"; -#endif - Log->Write("Fundamental diagram based on Tin and Tout will be calculated!"); - GetFundamentalTinTout(_tIn,_tOut,DensityPerFrame, _fps, _lengthMeasurementarea,_maxNumofPed, FD_TinTout); //MC. 15.8.12. replaced "datafile" by results - } - //----------------------------------------------------------------------------------------------------------------------------------- - if(_flowVelocity) - { -#ifdef WIN32 - string FD_FlowVelocity= "Output\\Fundamental_Diagram\\FlowVelocity\\FDFlowVelocity_"+_trajectoryName+".dat"; -#else - string FD_FlowVelocity= "Output/Fundamental_Diagram/FlowVelocity/FDFlowVelocity_"+_trajectoryName+".dat"; -#endif - FlowRate_Velocity(_deltaT,_fps, _accumPedsPassLine,_accumVPassLine,FD_FlowVelocity); - } - delete []DensityPerFrame; - return 0; + + if(_classicMethod) + fclose(_fClassicRhoV); + + if(_voronoiMethod) + fclose(_fVoronoiRhoV); + + if(_flowVelocity) + fclose(_fN_t);// + + delete []PassLine; + + //--------------------Fundamental diagram based on Tin and Tout---------------------------------------------------------------------- + if(_fundamentalTinTout) + { + string FD_TinTout= "Output/Fundamental_Diagram/TinTout/FDTinTout_"+_trajectoryName+".dat"; + Log->Write("Fundamental diagram based on Tin and Tout will be calculated!"); + GetFundamentalTinTout(_tIn,_tOut,DensityPerFrame, _fps, _lengthMeasurementarea,_maxNumofPed, FD_TinTout); //MC. 15.8.12. replaced "datafile" by results + } + //----------------------------------------------------------------------------------------------------------------------------------- + if(_flowVelocity) + { + string FD_FlowVelocity= "Output/Fundamental_Diagram/FlowVelocity/FDFlowVelocity_"+_trajectoryName+".dat"; + FlowRate_Velocity(_deltaT,_fps, _accumPedsPassLine,_accumVPassLine,FD_FlowVelocity); + } + delete []DensityPerFrame; + return 0; } /* @@ -570,53 +598,53 @@ int Analysis::RunAnalysis() */ bool Analysis::IsPassLine(double Line_startX,double Line_startY, double Line_endX, double Line_endY,double pt1_X, double pt1_Y,double pt2_X, double pt2_Y) { - double x1=Line_startX; - double y1=Line_startY; - double x2=Line_endX; - double y2=Line_endY; - double x3=pt1_X; - double y3=pt1_Y; - double x4=pt2_X; - double y4=pt2_Y; - - double d=(y2-y1)*(x4-x3)-(y4-y3)*(x2-x1); - if(d==0) - { - return false; - } - else - { - double x0=((x2-x1)*(x4-x3)*(y3-y1)+(y2-y1)*(x4-x3)*x1-(y4-y3)*(x2-x1)*x3)/d; - double y0=((y2-y1)*(y4-y3)*(x3-x1)+(x2-x1)*(y4-y3)*y1-(x4-x3)*(y2-y1)*y3)/(-1.0*d); - double temp1=(x0-x1)*(x0-x2); - double temp2=(x0-x3)*(x0-x4); - double temp3=(y0-y1)*(y0-y2); - double temp4=(y0-y3)*(y0-y4); - if(temp1<10.0E-16) + double x1=Line_startX; + double y1=Line_startY; + double x2=Line_endX; + double y2=Line_endY; + double x3=pt1_X; + double y3=pt1_Y; + double x4=pt2_X; + double y4=pt2_Y; + + double d=(y2-y1)*(x4-x3)-(y4-y3)*(x2-x1); + if(d==0) { - temp1=0.0; + return false; } - if(temp2<10.0E-16) + else { - temp2=0.0; - } - if(temp3<10.0E-16) - { - temp3=0.0; - } - if(temp4<10.0E-16) - { - temp4=0.0; - } - if(temp1<=0.0&&temp2<=0.0&&temp3<=0.0&&temp4<=0.0) - { - return true; - } - else - { - return false; + double x0=((x2-x1)*(x4-x3)*(y3-y1)+(y2-y1)*(x4-x3)*x1-(y4-y3)*(x2-x1)*x3)/d; + double y0=((y2-y1)*(y4-y3)*(x3-x1)+(x2-x1)*(y4-y3)*y1-(x4-x3)*(y2-y1)*y3)/(-1.0*d); + double temp1=(x0-x1)*(x0-x2); + double temp2=(x0-x3)*(x0-x4); + double temp3=(y0-y1)*(y0-y2); + double temp4=(y0-y3)*(y0-y4); + if(temp1<10.0E-16) + { + temp1=0.0; + } + if(temp2<10.0E-16) + { + temp2=0.0; + } + if(temp3<10.0E-16) + { + temp3=0.0; + } + if(temp4<10.0E-16) + { + temp4=0.0; + } + if(temp1<=0.0&&temp2<=0.0&&temp3<=0.0&&temp4<=0.0) + { + return true; + } + else + { + return false; + } } - } } //----------------------------------------------------------------------------------------------------------------------------- @@ -627,26 +655,26 @@ bool Analysis::IsPassLine(double Line_startX,double Line_startY, double Line_end */ void Analysis::GetFundamentalTinTout(int *Tin, int *Tout, double *DensityPerFrame, int fps, double LengthMeasurementarea,int Nped, string ofile) { - FILE *fFD_TinTout; - string fdTinTout=ofile; - if((fFD_TinTout=fopen(fdTinTout.c_str(),"w"))==NULL) - { - Log->Write("cannot open the file to write the TinTout data\n"); - exit(0); - } - fprintf(fFD_TinTout,"#person Index\t density_i(m^(-2))\t velocity_i(m/s)\n"); - for(int i=0;i<Nped;i++) - { - double velocity_temp=fps*CMtoM*LengthMeasurementarea/(Tout[i]-Tin[i]); - double density_temp=0; - for(int j=Tin[i];j<Tout[i];j++) + FILE *fFD_TinTout; + string fdTinTout=ofile; + if((fFD_TinTout=CreateFile(fdTinTout))==NULL) { - density_temp+=DensityPerFrame[j]; + Log->Write("cannot open the file to write the TinTout data\n"); + exit(0); } - density_temp/=(Tout[i]-Tin[i]); - fprintf(fFD_TinTout,"%d\t%f\t%f\n",i+1,density_temp,velocity_temp); - } - fclose(fFD_TinTout); + fprintf(fFD_TinTout,"#person Index\t density_i(m^(-2))\t velocity_i(m/s)\n"); + for(int i=0;i<Nped;i++) + { + double velocity_temp=fps*CMtoM*LengthMeasurementarea/(Tout[i]-Tin[i]); + double density_temp=0; + for(int j=Tin[i];j<Tout[i];j++) + { + density_temp+=DensityPerFrame[j]; + } + density_temp/=(Tout[i]-Tin[i]); + fprintf(fFD_TinTout,"%d\t%f\t%f\n",i+1,density_temp,velocity_temp); + } + fclose(fFD_TinTout); } //---------------------------------------------------------------------------------------------------------------------------- @@ -660,43 +688,43 @@ void Analysis::GetFundamentalTinTout(int *Tin, int *Tout, double *DensityPerFram void Analysis::FlowRate_Velocity(int DeltaT, int fps, vector<int> AccumPeds, vector<double> AccumVelocity, string ofile) { - FILE *fFD_FlowVelocity; - string fdFlowVelocity = ofile; //"Fundamental Diagram\\FlowVelocity\\FundamentalFlowVelocity"+datafile+".dat"; - if((fFD_FlowVelocity=fopen(fdFlowVelocity.c_str(),"w"))==NULL) - { - Log->Write("cannot open the file to write the Flow-Velocity data\n"); - exit(0); - } - fprintf(fFD_FlowVelocity,"#Flow rate(1/s) Mean velocity(m/s)\n"); - int TotalTime=AccumPeds.size(); // the total Frame of in the data file - int TotalPeds=AccumPeds[TotalTime-1]; //the total pedestrians in the data file - int firstPassT=-1; // the first time that there are pedestrians pass the line - int *pedspassT=new int[TotalPeds+1]; // the time for certain pedestrian passing the line - for(int i=0; i<=TotalPeds; i++) - { - pedspassT[i]=-1; - } - - for(int ix=0; ix<TotalTime; ix++) - { - if(AccumPeds[ix]>0 && firstPassT<0) + FILE *fFD_FlowVelocity; + string fdFlowVelocity = ofile; //"Fundamental Diagram\\FlowVelocity\\FundamentalFlowVelocity"+datafile+".dat"; + if((fFD_FlowVelocity=CreateFile(fdFlowVelocity))==NULL) { - firstPassT=ix; + Log->Write("cannot open the file to write the Flow-Velocity data\n"); + exit(0); } - if(pedspassT[AccumPeds[ix]]<0) + fprintf(fFD_FlowVelocity,"#Flow rate(1/s) Mean velocity(m/s)\n"); + int TotalTime=AccumPeds.size(); // the total Frame of in the data file + int TotalPeds=AccumPeds[TotalTime-1]; //the total pedestrians in the data file + int firstPassT=-1; // the first time that there are pedestrians pass the line + int *pedspassT=new int[TotalPeds+1]; // the time for certain pedestrian passing the line + for(int i=0; i<=TotalPeds; i++) { - pedspassT[AccumPeds[ix]]=ix; + pedspassT[i]=-1; } - } - for(int i=firstPassT+DeltaT; i<TotalTime; i+=DeltaT) - { - double flow_rate=fps*(AccumPeds[i]-AccumPeds[i-DeltaT])*1.00/(pedspassT[AccumPeds[i]]-pedspassT[AccumPeds[i-DeltaT]]); - double MeanV=(AccumVelocity[i]-AccumVelocity[i-DeltaT])/(AccumPeds[i]-AccumPeds[i-DeltaT]); - fprintf(fFD_FlowVelocity,"%.3f\t%.3f\n",flow_rate,MeanV); - } - fclose(fFD_FlowVelocity); - delete []pedspassT; + for(int ix=0; ix<TotalTime; ix++) + { + if(AccumPeds[ix]>0 && firstPassT<0) + { + firstPassT=ix; + } + if(pedspassT[AccumPeds[ix]]<0) + { + pedspassT[AccumPeds[ix]]=ix; + } + } + for(int i=firstPassT+DeltaT; i<TotalTime; i+=DeltaT) + { + double flow_rate=fps*(AccumPeds[i]-AccumPeds[i-DeltaT])*1.00/(pedspassT[AccumPeds[i]]-pedspassT[AccumPeds[i-DeltaT]]); + double MeanV=(AccumVelocity[i]-AccumVelocity[i-DeltaT])/(AccumPeds[i]-AccumPeds[i-DeltaT]); + fprintf(fFD_FlowVelocity,"%.3f\t%.3f\n",flow_rate,MeanV); + + } + fclose(fFD_FlowVelocity); + delete []pedspassT; } //----------------------------------------------------------------------------------------------------------------- @@ -708,29 +736,29 @@ void Analysis::FlowRate_Velocity(int DeltaT, int fps, vector<int> AccumPeds, vec void Analysis::GetIndividualFD(vector<polygon_2d> polygon, double* Velocity, polygon_2d measureArea) { - vector<polygon_2d>::iterator polygon_iterator; - double uniquedensity=0; - double uniquevelocity=0; - int temp=0; - for(polygon_iterator = polygon.begin(); polygon_iterator!=polygon.end();polygon_iterator++) - { - typedef std::vector<polygon_2d> polygon_list; - polygon_list v; - intersection(measureArea, *polygon_iterator, v); - if(!v.empty()) + vector<polygon_2d>::iterator polygon_iterator; + double uniquedensity=0; + double uniquevelocity=0; + int temp=0; + for(polygon_iterator = polygon.begin(); polygon_iterator!=polygon.end();polygon_iterator++) { - uniquedensity=1/(area(*polygon_iterator)*CMtoM*CMtoM); - uniquevelocity=Velocity[temp]; - fprintf(_individualFD,"%.3f\t%.3f\n",uniquedensity,uniquevelocity); + typedef std::vector<polygon_2d> polygon_list; + polygon_list v; + intersection(measureArea, *polygon_iterator, v); + if(!v.empty()) + { + uniquedensity=1/(area(*polygon_iterator)*CMtoM*CMtoM); + uniquevelocity=Velocity[temp]; + fprintf(_individualFD,"%.3f\t%.3f\n",uniquedensity,uniquevelocity); + } + temp++; } - temp++; - } } double Analysis::Distance(double x1, double y1, double x2, double y2) { - double distance=sqrt(pow((x1-x2),2)+pow((y1-y2),2)); - return distance; + double distance=sqrt(pow((x1-x2),2)+pow((y1-y2),2)); + return distance; } ///--------------------------------------------------------------------------------------------------------------------- /*this function is to obtain the frequency distribution of the pedestrian movement through a line from (Line_startX,Line_startY) @@ -740,41 +768,41 @@ double Analysis::Distance(double x1, double y1, double x2, double y2) */ void Analysis::DistributionOnLine(int *frequency,int fraction, double Line_startX,double Line_startY, double Line_endX, double Line_endY,double pt1_X, double pt1_Y,double pt2_X, double pt2_Y) { - /* in this function, we firstly calculate the intersection point between the trajectory (the line from point (pt1_X,pt1_Y) - * to (pt2_X,pt2_Y)) of a pedestrian and the Line. then, we determine which part of the line he or she belongs to - */ - double sumdistance=Distance(Line_startX,Line_startY,Line_endX, Line_endY); - double A=Line_endY-Line_startY; - double B=Line_startX-Line_endX; - double C=Line_endX*Line_startY-Line_startX*Line_endY; - double d1=A*pt1_X+B*pt1_Y+C; - double d2=A*pt2_X+B*pt2_Y+C; - if(d1*d2<0) - { - double x1=Line_startX; - double y1=Line_startY; - double x2=Line_endX; - double y2=Line_endY; - double x3=pt1_X; - double y3=pt1_Y; - double x4=pt2_X; - double y4=pt2_Y; - double x =((x1 - x2) * (x3 * y4 - x4 * y3) - (x3 - x4) * (x1 * y2 - x2 * y1)) - /((x3 - x4) * (y1 - y2) - (x1 - x2) * (y3 - y4)); - - double y =((y1 - y2) * (x3 * y4 - x4 * y3) - (x1 * y2 - x2 * y1) * (y3 - y4)) - /((y1 - y2) * (x3 - x4) - (x1 - x2) * (y3 - y4)); - int index=(int)floor(Distance(x,y,x1,y1)*fraction/sumdistance); - frequency[index]++; - } - else if(d1*d2==0) - { - if(d1==0) + /* in this function, we firstly calculate the intersection point between the trajectory (the line from point (pt1_X,pt1_Y) + * to (pt2_X,pt2_Y)) of a pedestrian and the Line. then, we determine which part of the line he or she belongs to + */ + double sumdistance=Distance(Line_startX,Line_startY,Line_endX, Line_endY); + double A=Line_endY-Line_startY; + double B=Line_startX-Line_endX; + double C=Line_endX*Line_startY-Line_startX*Line_endY; + double d1=A*pt1_X+B*pt1_Y+C; + double d2=A*pt2_X+B*pt2_Y+C; + if(d1*d2<0) { - int index=(int)floor(Distance(Line_startX,Line_startY,pt1_X,pt1_Y)*fraction/sumdistance); - frequency[index]++; + double x1=Line_startX; + double y1=Line_startY; + double x2=Line_endX; + double y2=Line_endY; + double x3=pt1_X; + double y3=pt1_Y; + double x4=pt2_X; + double y4=pt2_Y; + double x =((x1 - x2) * (x3 * y4 - x4 * y3) - (x3 - x4) * (x1 * y2 - x2 * y1)) + /((x3 - x4) * (y1 - y2) - (x1 - x2) * (y3 - y4)); + + double y =((y1 - y2) * (x3 * y4 - x4 * y3) - (x1 * y2 - x2 * y1) * (y3 - y4)) + /((y1 - y2) * (x3 - x4) - (x1 - x2) * (y3 - y4)); + int index=(int)floor(Distance(x,y,x1,y1)*fraction/sumdistance); + frequency[index]++; + } + else if(d1*d2==0) + { + if(d1==0) + { + int index=(int)floor(Distance(Line_startX,Line_startY,pt1_X,pt1_Y)*fraction/sumdistance); + frequency[index]++; + } } - } } @@ -786,24 +814,24 @@ void Analysis::DistributionOnLine(int *frequency,int fraction, double Line_start */ double Analysis::GetVoronoiDensity(vector<polygon_2d> polygon, polygon_2d measureArea) { - vector<polygon_2d>::iterator polygon_iterator; - double density=0; - for(polygon_iterator = polygon.begin(); polygon_iterator!=polygon.end();polygon_iterator++) - { - typedef std::vector<polygon_2d > polygon_list; - polygon_list v; - intersection(measureArea, *polygon_iterator, v); - if(!v.empty()) + vector<polygon_2d>::iterator polygon_iterator; + double density=0; + for(polygon_iterator = polygon.begin(); polygon_iterator!=polygon.end();polygon_iterator++) { - density+=area(v[0])/area(*polygon_iterator); - if((area(v[0])/area(*polygon_iterator))>1.00001) - { - std::cout<<"this is a wrong result "<<area(v[0])<<'\t'<<area(*polygon_iterator)<<"\n"; - } + typedef std::vector<polygon_2d > polygon_list; + polygon_list v; + intersection(measureArea, *polygon_iterator, v); + if(!v.empty()) + { + density+=area(v[0])/area(*polygon_iterator); + if((area(v[0])/area(*polygon_iterator))>1.00001) + { + std::cout<<"this is a wrong result "<<area(v[0])<<'\t'<<area(*polygon_iterator)<<"\n"; + } + } } - } - density=density/(area(measureArea)*CMtoM*CMtoM); - return density; + density=density/(area(measureArea)*CMtoM*CMtoM); + return density; } //--------------------------------------------------------------------------------------------- @@ -818,40 +846,40 @@ double Analysis::GetVoronoiDensity(vector<polygon_2d> polygon, polygon_2d measur */ double Analysis::GetClassicalDensity(double *xs, double *ys, int pednum, polygon_2d measureArea) { - int pedsinMeasureArea=0; - for(int i=0;i<pednum;i++) - { - if(within(make<point_2d>(xs[i], ys[i]), measureArea)) + int pedsinMeasureArea=0; + for(int i=0;i<pednum;i++) { - pedsinMeasureArea++; + if(within(make<point_2d>(xs[i], ys[i]), measureArea)) + { + pedsinMeasureArea++; + } } - } - double density=pedsinMeasureArea/(area(measureArea)*CMtoM*CMtoM); - return density; + double density=pedsinMeasureArea/(area(measureArea)*CMtoM*CMtoM); + return density; } double Analysis::GetClassicalVelocity(double *xs, double *ys, double *VInFrame, int pednum, polygon_2d measureArea){ - int pedsinMeasureArea=0; - double velocity = 0; - for(int i=0;i<pednum;i++) - { - if(within(make<point_2d>(xs[i], ys[i]), measureArea)) + int pedsinMeasureArea=0; + double velocity = 0; + for(int i=0;i<pednum;i++) { - velocity+=VInFrame[i]; - pedsinMeasureArea++; + if(within(make<point_2d>(xs[i], ys[i]), measureArea)) + { + velocity+=VInFrame[i]; + pedsinMeasureArea++; + } } - } - if(pedsinMeasureArea!=0) - { - velocity /= (1.0*pedsinMeasureArea); - } - else - { - velocity = 0; - } - return velocity; + if(pedsinMeasureArea!=0) + { + velocity /= (1.0*pedsinMeasureArea); + } + else + { + velocity = 0; + } + return velocity; } //--------------------------------------------------------------------------------------------- @@ -863,30 +891,30 @@ double Analysis::GetClassicalVelocity(double *xs, double *ys, double *VInFrame, */ double Analysis::GetVoronoiVelocity(vector<polygon_2d> polygon, double* Velocity, polygon_2d measureArea) { - vector<polygon_2d>::iterator polygon_iterator; - double meanV=0; - int temp=0; - - for(polygon_iterator = polygon.begin(); polygon_iterator!=polygon.end();polygon_iterator++) - { - typedef std::vector<polygon_2d > polygon_list; - - polygon_list v; - //intersection_inserter<polygon_2d>(measureArea, *polygon_iterator, std::back_inserter(v)); - intersection(measureArea, *polygon_iterator, v); - if(!v.empty()) + vector<polygon_2d>::iterator polygon_iterator; + double meanV=0; + int temp=0; + + for(polygon_iterator = polygon.begin(); polygon_iterator!=polygon.end();polygon_iterator++) { - meanV+=(Velocity[temp]*area(v[0])/area(measureArea)); - //std::cout<<"the velocity and areas:"<<Velocity[temp]<<'\t'<<area(v[0])<<'\t'<<meanV<<'\t'<<temp<<'\n'; - if((area(v[0])/area(*polygon_iterator))>1.00001) - { - std::cout<<"this is a wrong result"<<area(v[0])<<'\t'<<area(*polygon_iterator); - } + typedef std::vector<polygon_2d > polygon_list; + + polygon_list v; + //intersection_inserter<polygon_2d>(measureArea, *polygon_iterator, std::back_inserter(v)); + intersection(measureArea, *polygon_iterator, v); + if(!v.empty()) + { + meanV+=(Velocity[temp]*area(v[0])/area(measureArea)); + //std::cout<<"the velocity and areas:"<<Velocity[temp]<<'\t'<<area(v[0])<<'\t'<<meanV<<'\t'<<temp<<'\n'; + if((area(v[0])/area(*polygon_iterator))>1.00001) + { + std::cout<<"this is a wrong result"<<area(v[0])<<'\t'<<area(*polygon_iterator); + } + } + temp++; } - temp++; - } - return meanV; + return meanV; } /* @@ -896,138 +924,163 @@ double Analysis::GetVoronoiVelocity(vector<polygon_2d> polygon, double* Velocity //double Analysis::GetVinFrame(int Tnow,int Tpast, int Tfuture, int Tfirst, int Tlast, double Xcor_past, double Xcor_now, double Xcor_future,double Ycor_past, double Ycor_now, double Ycor_future, char VComponent){ double Analysis::GetVinFrame(int Tnow,int Tpast, int Tfuture, int ID, int *Tfirst, int *Tlast, double **Xcor, double **Ycor, char VComponent){ - double v=0; - //VInFrame[i] = GetVinFrame(f, Tpast, Tfuture, _firstFrame[ID], _lastFrame[ID], _xCor[ID][Tpast], _xCor[ID][f], _xCor[ID][Tfuture], _yCor[ID][Tpast], _yCor[ID][f], _yCor[ID][Tfuture], _vComponent); + double v=0; + //VInFrame[i] = GetVinFrame(f, Tpast, Tfuture, _firstFrame[ID], _lastFrame[ID], _xCor[ID][Tpast], _xCor[ID][f], _xCor[ID][Tfuture], _yCor[ID][Tpast], _yCor[ID][f], _yCor[ID][Tfuture], _vComponent); - if(VComponent == 'X') - { - if((Tpast >=Tfirst[ID])&&(Tfuture <= Tlast[ID])) + if(VComponent == 'X') { - v = _fps*CMtoM*(Xcor[ID][Tfuture] - Xcor[ID][Tpast])/(2.0 * _deltaF); //one dimensional velocity - } - else if((Tpast <Tfirst[ID])&&(Tfuture <= Tlast[ID])) - { - v = _fps*CMtoM*(Xcor[ID][Tfuture] - Xcor[ID][Tnow])/(_deltaF); //one dimensional velocity + if((Tpast >=Tfirst[ID])&&(Tfuture <= Tlast[ID])) + { + v = _fps*CMtoM*(Xcor[ID][Tfuture] - Xcor[ID][Tpast])/(2.0 * _deltaF); //one dimensional velocity + } + else if((Tpast <Tfirst[ID])&&(Tfuture <= Tlast[ID])) + { + v = _fps*CMtoM*(Xcor[ID][Tfuture] - Xcor[ID][Tnow])/(_deltaF); //one dimensional velocity + } + else if((Tpast >=Tfirst[ID])&&(Tfuture > Tlast[ID])) + { + v = _fps*CMtoM*(Xcor[ID][Tnow] - Xcor[ID][Tpast])/( _deltaF); //one dimensional velocity + } } - else if((Tpast >=Tfirst[ID])&&(Tfuture > Tlast[ID])) + if(VComponent == 'Y') { - v = _fps*CMtoM*(Xcor[ID][Tnow] - Xcor[ID][Tpast])/( _deltaF); //one dimensional velocity + if((Tpast >=Tfirst[ID])&&(Tfuture <= Tlast[ID])) + { + v = _fps*CMtoM*(Ycor[ID][Tfuture] - Ycor[ID][Tpast])/(2.0 * _deltaF); //one dimensional velocity + } + else if((Tpast <Tfirst[ID])&&(Tfuture <= Tlast[ID])) + { + v = _fps*CMtoM*(Ycor[ID][Tfuture] - Ycor[ID][Tnow])/(_deltaF); //one dimensional velocity + } + else if((Tpast >=Tfirst[ID])&&(Tfuture > Tlast[ID])) + { + v = _fps*CMtoM*(Ycor[ID][Tnow] - Ycor[ID][Tpast])/( _deltaF); //one dimensional velocity + } } - } - if(VComponent == 'Y') - { - if((Tpast >=Tfirst[ID])&&(Tfuture <= Tlast[ID])) + if(VComponent == 'B') { - v = _fps*CMtoM*(Ycor[ID][Tfuture] - Ycor[ID][Tpast])/(2.0 * _deltaF); //one dimensional velocity + if((Tpast >=Tfirst[ID])&&(Tfuture <= Tlast[ID])) + { + v = _fps*CMtoM*sqrt(pow((Xcor[ID][Tfuture] - Xcor[ID][Tpast]),2)+pow((Ycor[ID][Tfuture] - Ycor[ID][Tpast]),2))/(2.0 * _deltaF); //two dimensional velocity + } + else if((Tpast <Tfirst[ID])&&(Tfuture <= Tlast[ID])) + { + v = _fps*CMtoM*sqrt(pow((Xcor[ID][Tfuture] - Xcor[ID][Tnow]),2)+pow((Ycor[ID][Tfuture] - Ycor[ID][Tnow]),2))/(_deltaF); + } + else if((Tpast >=Tfirst[ID])&&(Tfuture > Tlast[ID])) + { + v = _fps*CMtoM*sqrt(pow((Xcor[ID][Tnow] - Xcor[ID][Tpast]),2)+pow((Ycor[ID][Tnow] - Ycor[ID][Tpast]),2))/(_deltaF); //two dimensional velocity + } } - else if((Tpast <Tfirst[ID])&&(Tfuture <= Tlast[ID])) + + v = fabs(v); + return v; +} + +void Analysis::GetProfiles(string frameId, vector<polygon_2d> polygons, double * velocity) +{ + string Prfvelocity="Fundamental_Diagram/Classical_Voronoi/field/velocity/Prf_v_"+_trajectoryName+"_"+frameId+".dat"; + string Prfdensity="Fundamental_Diagram/Classical_Voronoi/field/density/Prf_d_"+_trajectoryName+"_"+frameId+".dat"; + + FILE *Prf_velocity; + if((Prf_velocity=CreateFile(Prfvelocity))==NULL) { - v = _fps*CMtoM*(Ycor[ID][Tfuture] - Ycor[ID][Tnow])/(_deltaF); //one dimensional velocity + Log->Write("cannot open the file <%s> to write the field data\n",Prfvelocity.c_str()); + exit(0); } - else if((Tpast >=Tfirst[ID])&&(Tfuture > Tlast[ID])) + FILE *Prf_density; + if((Prf_density=CreateFile(Prfdensity))==NULL) { - v = _fps*CMtoM*(Ycor[ID][Tnow] - Ycor[ID][Tpast])/( _deltaF); //one dimensional velocity + Log->Write("cannot open the file to write the field density\n"); + exit(0); } - } - if(VComponent == 'B') - { - if((Tpast >=Tfirst[ID])&&(Tfuture <= Tlast[ID])) + + int NRow = (int)ceil((_highVertexY-_lowVertexY)/_scaleY); // the number of rows that the geometry will be discretized for field analysis + int NColumn = (int)ceil((_highVertexX-_lowVertexX)/_scaleX); //the number of columns that the geometry will be discretized for field analysis + for(int row_i=0;row_i<NRow;row_i++) // { - v = _fps*CMtoM*sqrt(pow((Xcor[ID][Tfuture] - Xcor[ID][Tpast]),2)+pow((Ycor[ID][Tfuture] - Ycor[ID][Tpast]),2))/(2.0 * _deltaF); //two dimensional velocity + for(int colum_j=0;colum_j<NColumn;colum_j++) + { + polygon_2d measurezoneXY; + { + const double coor[][2] = { + {_lowVertexX+colum_j*_scaleX,_lowVertexY+row_i*_scaleY}, {_lowVertexX+colum_j*_scaleX+_scaleX,_lowVertexY+row_i*_scaleY}, {_lowVertexX+colum_j*_scaleX+_scaleX, _lowVertexY+row_i*_scaleY+_scaleY}, + {_lowVertexX+colum_j*_scaleX, _lowVertexY+row_i*_scaleY+_scaleY}, + {_lowVertexX+colum_j*_scaleX,_lowVertexY+row_i*_scaleY} // closing point is opening point + }; + assign_points(measurezoneXY, coor); + } + correct(measurezoneXY); // Polygons should be closed, and directed clockwise. If you're not sure if that is the case, call this function + double densityXY=GetVoronoiDensity(polygons,measurezoneXY); + fprintf(Prf_density,"%.3f\t",densityXY); + double velocityXY = GetVoronoiVelocity(polygons,velocity,measurezoneXY); + fprintf(Prf_velocity,"%.3f\t",velocityXY); + } + fprintf(Prf_density,"\n"); + fprintf(Prf_velocity,"\n"); } - else if((Tpast <Tfirst[ID])&&(Tfuture <= Tlast[ID])) + fclose(Prf_velocity); + fclose(Prf_density); +} + +void Analysis::OutputVoroGraph(string frameId, vector<polygon_2d> polygons, int numPedsInFrame, double* XInFrame, double* YInFrame,double* VInFrame) +{ + string point="Fundamental_Diagram/Classical_Voronoi/VoronoiCell/points"+_trajectoryName+"_"+frameId+".dat"; + string polygon="Fundamental_Diagram/Classical_Voronoi/VoronoiCell/polygon"+_trajectoryName+"_"+frameId+".dat"; + string v_individual="Fundamental_Diagram/Classical_Voronoi/VoronoiCell/speed"+_trajectoryName+"_"+frameId+".dat"; + + ofstream points (point.c_str()); + ofstream polys (polygon.c_str()); + ofstream velo (v_individual.c_str()); + + for(vector<polygon_2d>::iterator polygon_iterator=polygons.begin(); polygon_iterator!=polygons.end();polygon_iterator++) { - v = _fps*CMtoM*sqrt(pow((Xcor[ID][Tfuture] - Xcor[ID][Tnow]),2)+pow((Ycor[ID][Tfuture] - Ycor[ID][Tnow]),2))/(_deltaF); + polys << dsv(*polygon_iterator) << endl; } - else if((Tpast >=Tfirst[ID])&&(Tfuture > Tlast[ID])) + for(int pts=0;pts<numPedsInFrame;pts++) { - v = _fps*CMtoM*sqrt(pow((Xcor[ID][Tnow] - Xcor[ID][Tpast]),2)+pow((Ycor[ID][Tnow] - Ycor[ID][Tpast]),2))/(_deltaF); //two dimensional velocity + points << XInFrame[pts] << "\t" << YInFrame[pts] << endl; + velo << fabs(VInFrame[pts]) << endl; } - } - - v = fabs(v); - return v; + points.close(); + polys.close(); + velo.close(); } -void Analysis::GetProfiles(string frameId, vector<polygon_2d> polygons, double * velocity) -{ - //std::string buffer(frameId); -#ifdef WIN32 - string Prfvelocity="Fundamental_Diagram\\Classical_Voronoi\\Profile\\velocity\\Prf_v_"+_trajectoryName+"_"+frameId+".dat"; - string Prfdensity= "Fundamental_Diagram\\Classical_Voronoi\\Profile\\density\\Prf_d_"+_trajectoryName+"_"+frameId+".dat"; -#else - string Prfvelocity="Fundamental_Diagram/Classical_Voronoi/field/velocity/Prf_v_"+_trajectoryName+"_"+frameId+".dat"; - string Prfdensity="Fundamental_Diagram/Classical_Voronoi/field/density/Prf_d_"+_trajectoryName+"_"+frameId+".dat"; -#endif - FILE *Prf_velocity; - if((Prf_velocity=fopen(Prfvelocity.c_str(),"w"))==NULL) - { - Log->Write("cannot open the file <%s> to write the field data\n",Prfvelocity.c_str()); - exit(0); - } - FILE *Prf_density; - if((Prf_density=fopen(Prfdensity.c_str(),"w"))==NULL) - { - Log->Write("cannot open the file to write the field density\n"); - exit(0); - } +FILE* Analysis::CreateFile(const string& filename){ + //first try to create the file + FILE* fHandle= fopen(filename.c_str(),"w"); + if(fHandle) return fHandle; - int NRow = (int)ceil((_highVertexY-_lowVertexY)/_scaleY); // the number of rows that the geometry will be discretized for field analysis - int NColumn = (int)ceil((_highVertexX-_lowVertexX)/_scaleX); //the number of columns that the geometry will be discretized for field analysis - for(int row_i=0;row_i<NRow;row_i++) // - { - for(int colum_j=0;colum_j<NColumn;colum_j++) - { - polygon_2d measurezoneXY; - { - const double coor[][2] = { - {_lowVertexX+colum_j*_scaleX,_lowVertexY+row_i*_scaleY}, {_lowVertexX+colum_j*_scaleX+_scaleX,_lowVertexY+row_i*_scaleY}, {_lowVertexX+colum_j*_scaleX+_scaleX, _lowVertexY+row_i*_scaleY+_scaleY}, - {_lowVertexX+colum_j*_scaleX, _lowVertexY+row_i*_scaleY+_scaleY}, - {_lowVertexX+colum_j*_scaleX,_lowVertexY+row_i*_scaleY} // closing point is opening point - }; - assign_points(measurezoneXY, coor); - } - correct(measurezoneXY); // Polygons should be closed, and directed clockwise. If you're not sure if that is the case, call this function - double densityXY=GetVoronoiDensity(polygons,measurezoneXY); - fprintf(Prf_density,"%.3f\t",densityXY); - double velocityXY = GetVoronoiVelocity(polygons,velocity,measurezoneXY); - fprintf(Prf_velocity,"%.3f\t",velocityXY); + unsigned int found=filename.find_last_of("/\\"); + string dir = filename.substr(0,found)+"/"; + string file= filename.substr(found+1); + + // the directories are probably missing, create it + if (mkpath(dir.c_str())==-1) { + Log->Write("ERROR:\tcannot create the directory <%s>",dir.c_str()); + return NULL; } - fprintf(Prf_density,"\n"); - fprintf(Prf_velocity,"\n"); - } - fclose(Prf_velocity); - fclose(Prf_density); + //second and last attempt + return fopen(filename.c_str(),"w"); } -void Analysis::OutputVoroGraph(string frameId, vector<polygon_2d> polygons, int numPedsInFrame, double* XInFrame, double* YInFrame,double* VInFrame) -{ -#ifdef WIN32 - string point="Output\\Fundamental_Diagram\\Classical_Voronoi\\VoronoiCell\\points"+_trajectoryName+"_"+frameId+".dat"; - string polygon="Output\\Fundamental_Diagram\\Classical_Voronoi\\VoronoiCell\\polygon"+_trajectoryName+"_"+frameId+".dat"; - //string point="points"+_trajectoryName+"_"+frameId+".dat"; - //string polygon="polygon"+_trajectoryName+"_"+frameId+".dat"; - string v_individual="Output\\Fundamental_Diagram\\Classical_Voronoi\\VoronoiCell\\speed"+_trajectoryName+"_"+frameId+".dat"; + +int Analysis::mkpath(const char* file_path, mode_t mode) { + assert(file_path && *file_path); + char* p; + for (p=strchr(file_path+1, '/'); p; p=strchr(p+1, '/')) { + *p='\0'; + +#ifdef __linux__ + if (_mkdir(file_path, mode)==-1) { #else - string point="Fundamental_Diagram/Classical_Voronoi/VoronoiCell/points"+_trajectoryName+"_"+frameId+".dat"; - string polygon="Fundamental_Diagram/Classical_Voronoi/VoronoiCell/polygon"+_trajectoryName+"_"+frameId+".dat"; - string v_individual="Fundamental_Diagram/Classical_Voronoi/VoronoiCell/speed"+_trajectoryName+"_"+frameId+".dat"; + if (_mkdir(file_path)==-1) { #endif - ofstream points (point.c_str()); - ofstream polys (polygon.c_str()); - ofstream velo (v_individual.c_str()); - - for(vector<polygon_2d>::iterator polygon_iterator=polygons.begin(); polygon_iterator!=polygons.end();polygon_iterator++) - { - polys << dsv(*polygon_iterator) << endl; - } - for(int pts=0;pts<numPedsInFrame;pts++) - { - points << XInFrame[pts] << "\t" << YInFrame[pts] << endl; - velo << fabs(VInFrame[pts]) << endl; + if (errno!=EEXIST) { *p='/'; return -1; } } - points.close(); - polys.close(); - velo.close(); + *p='/'; + } + return 0; } diff --git a/Analysis.h b/Analysis.h index 21643b5ecdc2ccbee95fcff6facac600238d797a..319c1f4ea76e2e01a7116df9f3a62ad0bee4c3e7 100644 --- a/Analysis.h +++ b/Analysis.h @@ -1,24 +1,3 @@ -/*Simulation.h: - The Simulation class represents a simulation of pedestrians - based on a certain model in a specific scenario. A simulation is defined by - various parameters and functions. - Copyright (C) <2009-2010> <Jonas Mehlich and Mohcine Chraibi> - - This file is part of OpenPedSim. - - OpenPedSim is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - any later version. - - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - OpenPedSim is distributed in the hope that it will be useful, - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Foobar. If not, see <http://www.gnu.org/licenses/>. - */ #ifndef ANALYSIS_H_ #define ANALYSIS_H_ @@ -55,7 +34,7 @@ private: polygon_2d _geoPoly; polygon_2d _measureZone; string _trajectoryName; - string _trajectoryFile; + string _trajectoriesLocation; int _numFrames; // how much frames int *_tIn; //the time for each pedestrian enter the measurement area @@ -106,6 +85,11 @@ private: void GetProfiles(string frameId, vector<polygon_2d> polygons, double * velocity); void OutputVoroGraph(string frameId, vector<polygon_2d> polygons, int numPedsInFrame, double* XInFrame, double* YInFrame,double* VInFrame); void DistributionOnLine(int *frequency,int fraction, double Line_startX,double Line_startY, double Line_endX, double Line_endY,double pt1_X, double pt1_Y,double pt2_X, double pt2_Y); + + //create a file and the directory structure if needed. + FILE* CreateFile(const string& filename); + int mkpath(const char* file_path, mode_t mode=0755); + public: Analysis(); diff --git a/CMakeLists.txt b/CMakeLists.txt index 062685c7e910fa27579bf68c70d112a4144582e0..929668982a10b0b559b67f3cc956db8f49ef4d88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,18 +15,18 @@ INCLUDE_DIRECTORIES("./") file( GLOB_RECURSE source_files - Analysis.cpp - main.cpp - VoronoiDiagram.cpp - VoronoiDiagramGenerator.cpp - VoronoiPolygons.cpp - + Analysis.cpp + main.cpp + VoronoiDiagram.cpp + VoronoiDiagramGenerator.cpp + VoronoiPolygons.cpp IO/IODispatcher.cpp IO/OutputHandler.cpp - general/ArgumentParser.cpp - general/xmlParser.cpp - + tinyxml/tinystr.cpp + tinyxml/tinyxml.cpp + tinyxml/tinyxmlerror.cpp + tinyxml/tinyxmlparser.cpp geometry/Building.cpp geometry/Line.cpp geometry/Point.cpp @@ -39,21 +39,17 @@ file( geometry/Obstacle.cpp geometry/SubRoom.cpp - - header_files: - Analysis.h - VoronoiDiagramGenerator.h - VoronoiDiagram.h - VoronoiPolygons.h - + Analysis.h + VoronoiDiagramGenerator.h + VoronoiDiagram.h + VoronoiPolygons.h IO/IODispatcher.h IO/OutputHandler.h - general/ArgumentParser.h - general/xmlParser.h + tinyxml/tinyxml.h + tinyxml/tinystr.h general/Macros.h - geometry/Building.h geometry/Line.h geometry/Point.h diff --git a/general/ArgumentParser.cpp b/general/ArgumentParser.cpp index a9fc4067d06e402aa0ed34014a3ecbe5f988bfac..cbaf4c71316242597bef1dfc6f34ffe01af08b4d 100644 --- a/general/ArgumentParser.cpp +++ b/general/ArgumentParser.cpp @@ -11,7 +11,7 @@ #include <omp.h> #endif -#include "../general/xmlParser.h" +#include "../tinyxml/tinyxml.h" #include "../IO/OutputHandler.h" #include "ArgumentParser.h" @@ -21,7 +21,7 @@ void ArgumentParser::Usage() { Log->Write("Usage: \n"); Log->Write("\tJPSreport.exe --inifile=input.xml\n"); -/* + /* fprintf(stderr, "Usage: program options\n\n" "With the following options (default values in parenthesis):\n\n" @@ -114,7 +114,7 @@ void ArgumentParser::Usage() { "\n"); -*/ + */ exit(EXIT_SUCCESS); } @@ -146,6 +146,8 @@ ArgumentParser::ArgumentParser() { _errorLogFile="./Logfile.dat"; _log=1; //no output wanted pFormat=FORMAT_XML_PLAIN; + _trajectoriesLocation=""; + _trajectoriesFilename=""; } @@ -169,25 +171,25 @@ void ArgumentParser::ParseArgs(int argc, char **argv) { switch (c) { - case 'q': - { - string inifile="input.xml"; - if (optarg) - inifile=optarg; - Log->Write("INFO: \t Loading initialization file <"+inifile+">"); - ParseIniFile(inifile); - } - break; + case 'q': + { + string inifile="input.xml"; + if (optarg) + inifile=optarg; + Log->Write("INFO: \t Loading initialization file <"+inifile+">"); + ParseIniFile(inifile); + } + break; - case 'h': - Usage(); - break; - default: - { - Log->Write("ERROR: \tin ArgumentParser::ParseArgs() " - "wrong program options!!!\n"); - Usage(); - } + case 'h': + Usage(); + break; + default: + { + Log->Write("ERROR: \tin ArgumentParser::ParseArgs() " + "wrong program options!!!\n"); + Usage(); + } } } } @@ -195,119 +197,130 @@ void ArgumentParser::ParseArgs(int argc, char **argv) { void ArgumentParser::ParseIniFile(string inifile){ - XMLNode xMainNode=XMLNode::openFileHelper(inifile.c_str(),"JPSreport"); - Log->Write("INFO: \tParsing the ini file"); //I just assume all parameters are present + TiXmlDocument doc(inifile); + if (!doc.LoadFile()){ + Log->Write("ERROR: \t%s", doc.ErrorDesc()); + Log->Write("ERROR: \t could not parse the ini file"); + exit(EXIT_FAILURE); + } + + // everything is fine. proceed with parsing + + TiXmlElement* xMainNode = doc.RootElement(); + if( ! xMainNode ) { + Log->Write("ERROR:\tRoot element does not exist"); + exit(EXIT_FAILURE); + } + + if( xMainNode->ValueStr () != "JPSreport" ) { + Log->Write("ERROR:\tRoot element value is not 'JPSreport'."); + exit(EXIT_FAILURE); + } + //geometry - XMLNode xGeometry = xMainNode.getChildNode("Geometry"); - if(!xGeometry.isEmpty()){ - string geometry = xmltoa(xGeometry.getAttribute("file")); - _geometryFileName =geometry.c_str(); - Log->Write("INFO: \tgeometry <"+geometry+">"); + if(xMainNode->FirstChild("geometry")){ + _geometryFileName=xMainNode->FirstChildElement("geometry")->Attribute("file"); + Log->Write("INFO: \tgeometry <"+_geometryFileName+">"); } //trajectories - XMLNode xTrajectories=xMainNode.getChildNode("Trajectories"); - if(!xTrajectories.isEmpty()){ + TiXmlNode* xTrajectories=xMainNode->FirstChild("trajectories"); + if(xTrajectories){ + //a file descriptor was given + if(xTrajectories->FirstChild("file")){ + if(xTrajectories->FirstChildElement("file")) + _trajectoriesFilename = xTrajectories->FirstChildElement("file")->Attribute("name"); -/* pfps = xmltof(xTrajectories.getAttribute("fps"),pfps); - string format=xmltoa(xTrajectories.getAttribute("format"),"xml-plain"); - if(format=="xml-plain") pFormat=FORMAT_XML_PLAIN; - if(format=="xml-bin") pFormat=FORMAT_XML_BIN; - if(format=="plain") pFormat=FORMAT_PLAIN; - if(format=="vtk") pFormat=FORMAT_VTK;*/ + if(xTrajectories->FirstChildElement("path")) + _trajectoriesLocation = xTrajectories->FirstChildElement("path")->Attribute("location"); - //a file descriptor was given - _trajectoryName = - xmltoa( - xTrajectories.getChildNode( - "file").getAttribute("name"), _trajectoryName.c_str()); - string trajectoriesfile = - xmltoa( - xTrajectories.getChildNode( - "path").getAttribute("location"))+ string(_trajectoryName)+".xml"; - _trajectoryFile = trajectoriesfile.c_str(); - - Log->Write("INFO: \ttrajectory file <" + _trajectoryFile+">"); + Log->Write("INFO: \tinput file <"+ (_trajectoriesFilename)+">"); + Log->Write("INFO: \tinput dir <"+ (_trajectoriesLocation)+">"); + } } //measurement area - XMLNode xMeasurementArea=xMainNode.getChildNode("MeasurementAreas"); - if(!xMeasurementArea.isEmpty()){ - if(!xMeasurementArea.getChildNode("Area_B").isEmpty()) + if(xMainNode->FirstChild("measurementAreas")){ + + string unit = xMainNode->FirstChildElement("measurementAreas")->Attribute("unit"); + + if(unit!="cm"){ + Log->Write("WARNING: \tonly <cm> unit is supported. Convert your units."); + exit(EXIT_FAILURE); + } + + TiXmlNode* xMeasurementArea_B=xMainNode->FirstChild("measurementAreas")->FirstChild("area_B"); + if(xMeasurementArea_B) { - _measureAreaId = - xmltoa( - xMeasurementArea.getChildNode( - "Area_B").getAttribute("id")); - string pMeasureAreaType = - xmltoa( - xMeasurementArea.getChildNode( - "Area_B").getAttribute("type")); - XMLNode xBox = xMeasurementArea.getChildNode("Area_B"); - const char* box_p1x = xmltoa(xBox.getChildNode("p1").getAttribute("x")); - const char* box_p1y = xmltoa(xBox.getChildNode("p1").getAttribute("y")); - const char* box_p2x = xmltoa(xBox.getChildNode("p2").getAttribute("x")); - const char* box_p2y = xmltoa(xBox.getChildNode("p2").getAttribute("y")); - const char* box_p3x = xmltoa(xBox.getChildNode("p3").getAttribute("x")); - const char* box_p3y = xmltoa(xBox.getChildNode("p3").getAttribute("y")); - const char* box_p4x = xmltoa(xBox.getChildNode("p4").getAttribute("x")); - const char* box_p4y = xmltoa(xBox.getChildNode("p4").getAttribute("y")); - //-------------the following codes define measurement area------------------------------------- - polygon_2d poly; - { - const double coor[][2] = { - {atof(box_p1x),atof(box_p1y)}, {atof(box_p2x),atof(box_p2y)}, {atof(box_p3x),atof(box_p3y)}, {atof(box_p4x),atof(box_p4y)}, - {atof(box_p1x),atof(box_p1y)} // closing point is opening point - }; - assign_points(poly, coor); - correct(poly); // Polygons should be closed, and directed clockwise. If you're not sure if that is the case, call this function - } - _measureArea = poly; - - - string MovingDire_start = xmltoa(xBox.getChildNode("MovingDirection").getAttribute("start")); - string MovingDire_end = xmltoa(xBox.getChildNode("MovingDirection").getAttribute("end")); - double start_x = atof(xmltoa(xBox.getChildNode(MovingDire_start.c_str()).getAttribute("x"))); - double start_y = atof(xmltoa(xBox.getChildNode(MovingDire_start.c_str()).getAttribute("y"))); - double end_x = atof(xmltoa(xBox.getChildNode(MovingDire_end.c_str()).getAttribute("x"))); - double end_y = atof(xmltoa(xBox.getChildNode(MovingDire_end.c_str()).getAttribute("y"))); + _measureAreaId =xMeasurementArea_B->ToElement()->Attribute("id"); + //FIXME: you never used this variable + string pMeasureAreaType = xMeasurementArea_B->ToElement()->Attribute("type"); + + double box_p1x = xmltof(xMeasurementArea_B->FirstChildElement("p1")->Attribute("x")); + double box_p1y = xmltof(xMeasurementArea_B->FirstChildElement("p1")->Attribute("y")); + double box_p2x = xmltof(xMeasurementArea_B->FirstChildElement("p2")->Attribute("x")); + double box_p2y = xmltof(xMeasurementArea_B->FirstChildElement("p2")->Attribute("y")); + double box_p3x = xmltof(xMeasurementArea_B->FirstChildElement("p3")->Attribute("x")); + double box_p3y = xmltof(xMeasurementArea_B->FirstChildElement("p3")->Attribute("y")); + double box_p4x = xmltof(xMeasurementArea_B->FirstChildElement("p4")->Attribute("x")); + double box_p4y = xmltof(xMeasurementArea_B->FirstChildElement("p4")->Attribute("y")); + + //-------------the following codes define measurement area--------------------------- + // Polygons should be closed, and directed clockwise. + // If you're not sure if that is the case, call the function correct + polygon_2d poly; + const double coor[][2] = { + {box_p1x,box_p1y}, {box_p2x,box_p2y}, {box_p3x,box_p3y}, {box_p4x,box_p4y}, + {box_p1x,box_p1y} // closing point is opening point + }; + + assign_points(poly, coor); + correct(poly); // in the case the Polygone is not closed + + _measureArea = poly; + + string MovingDire_start = xMeasurementArea_B->FirstChildElement("movingDirection")->Attribute("start"); + string MovingDire_end = xMeasurementArea_B->FirstChildElement("movingDirection")->Attribute("end"); + double start_x = xmltof(xMeasurementArea_B->FirstChildElement(MovingDire_start.c_str())->Attribute("x")); + double start_y = xmltof(xMeasurementArea_B->FirstChildElement(MovingDire_start.c_str())->Attribute("y")); + double end_x = xmltof(xMeasurementArea_B->FirstChildElement(MovingDire_end.c_str())->Attribute("x")); + double end_y = xmltof(xMeasurementArea_B->FirstChildElement(MovingDire_end.c_str())->Attribute("y")); _lengthMeasureArea = sqrt(pow((start_x-end_x),2)+pow((start_y-end_y),2)); Log->Write("INFO: \tmeasure area id <"+_measureAreaId+">"); Log->Write("INFO: \tmeasure area type <"+pMeasureAreaType+">"); - Log->Write("INFO: \tp1 of Box <"+string(box_p1x)+','+string(box_p1y)+">"); - cout<<"INFO: \tlength of measurement area is: <" << _lengthMeasureArea<<">"<<endl; + Log->Write("INFO: \tp1 of Box < %f, %f>",box_p1x,box_p1y); + Log->Write("INFO: \tlength of measurement area is: < %f>", _lengthMeasureArea); } - if(!xMeasurementArea.getChildNode("Area_L").isEmpty()) + + TiXmlNode* xMeasurementArea_L=xMainNode->FirstChild("measurementAreas")->FirstChild("area_L"); + if(xMeasurementArea_L) { - _measureAreaId = xmltoa(xMeasurementArea.getChildNode("Area_L").getAttribute("id")); - string pMeasureAreaType = xmltoa(xMeasurementArea.getChildNode("Area_L").getAttribute("type")); - XMLNode xLine = xMeasurementArea.getChildNode("Area_L"); - string line_startx = xmltoa(xLine.getChildNode("Start").getAttribute("x")); - string line_starty = xmltoa(xLine.getChildNode("Start").getAttribute("y")); - string line_endx = xmltoa(xLine.getChildNode("End").getAttribute("x")); - string line_endy = xmltoa(xLine.getChildNode("End").getAttribute("y")); - - _lineStartX = atof(line_startx.c_str()); - _lineStartY = atof(line_starty.c_str()); - _lineEndX = atof(line_endx.c_str()); - _lineEndY = atof(line_endy.c_str()); + //fixme: this value overwrite the previous one vom area_B + _measureAreaId =xMeasurementArea_L->ToElement()->Attribute("id"); + string pMeasureAreaType = xMeasurementArea_L->ToElement()->Attribute("type"); + + _lineStartX = xmltof(xMeasurementArea_L->FirstChildElement("start")->Attribute("x")); + _lineStartY =xmltof(xMeasurementArea_L->FirstChildElement("start")->Attribute("y")); + _lineEndX = xmltof(xMeasurementArea_L->FirstChildElement("end")->Attribute("x")); + _lineEndY =xmltof(xMeasurementArea_L->FirstChildElement("end")->Attribute("y")); Log->Write("INFO: \tmeasure area id <"+_measureAreaId+">" + "\t<"+pMeasureAreaType+">"); - Log->Write("INFO: \treference line starts from <"+line_startx+","+line_starty+">" +" to <"+line_endx+","+line_endy+">" ); + Log->Write("INFO: \treference line starts from <%f, %f> to <%f, %f>",_lineStartX,_lineStartY,_lineEndX,_lineEndY); } } //instantaneous velocity - XMLNode xVelocity=xMainNode.getChildNode("Velocity"); - if(!xVelocity.isEmpty()){ - string UseXComponent = string(xVelocity.getChildNode("UseXComponent").getText()); - string UseYComponent = string(xVelocity.getChildNode("UseYComponent").getText()); - string HalfFrameNumberToUse = string(xVelocity.getChildNode("HalfFrameNumberToUse").getText()); + TiXmlNode* xVelocity=xMainNode->FirstChild("velocity"); + if(xVelocity){ + string UseXComponent = xVelocity->FirstChildElement("useXComponent")->GetText(); + string UseYComponent = xVelocity->FirstChildElement("useYComponent")->GetText(); + string HalfFrameNumberToUse = xVelocity->FirstChildElement("halfFrameNumberToUse")->GetText(); + _delatTVInst = atof(HalfFrameNumberToUse.c_str()); if(UseXComponent == "true"&&UseYComponent == "false"){ _vComponent = 'X'; @@ -323,73 +336,72 @@ void ArgumentParser::ParseIniFile(string inifile){ } else{ Log->Write("INFO: \ttype of velocity is not selected, please check it !!! " ); - exit(0) ; + exit(EXIT_FAILURE) ; } } // method A - XMLNode xMethod_A=xMainNode.getChildNode("Method_A"); - if(string(xMethod_A.getAttribute("enabled"))=="true"){ - const char* TimeInterval_A = xMethod_A.getChildNode("TimeInterval").getText(); - _measureAreaId = xmltoa(xMeasurementArea.getChildNode("MeasurementArea").getAttribute("id")); - _isMethodA = true; - _timeIntervalA = atoi(TimeInterval_A); - Log->Write("INFO: \tMethod A is selected" ); - Log->Write("INFO: \ttime interval used in Method A is <"+string(TimeInterval_A)+" frames>" ); + TiXmlElement* xMethod_A=xMainNode->FirstChildElement("method_A"); + if(xMethod_A){ + if(string(xMethod_A->Attribute("enabled"))=="true"){ + _timeIntervalA = xmltoi(xMethod_A->FirstChildElement("timeInterval")->GetText()); + _measureAreaId = xMethod_A->FirstChildElement("measurementArea")->Attribute("id"); + _isMethodA = true; + Log->Write("INFO: \tMethod A is selected" ); + Log->Write("INFO: \ttime interval used in Method A is < %d>",_timeIntervalA); + } } // method B - XMLNode xMethod_B=xMainNode.getChildNode("Method_B"); - if(string(xMethod_B.getAttribute("enabled"))=="true"){ + TiXmlElement* xMethod_B=xMainNode->FirstChildElement("method_B"); + if(string(xMethod_B->Attribute("enabled"))=="true"){ _isMethodB = true; - _measureAreaId = xmltoa(xMeasurementArea.getChildNode("MeasurementArea").getAttribute("id")); + //FIXME> this value is overwritten + _measureAreaId = xMethod_A->FirstChildElement("measurementArea")->Attribute("id"); Log->Write("INFO: \tMethod B is selected" ); } // method C - XMLNode xMethod_C=xMainNode.getChildNode("Method_C"); - if(string(xMethod_C.getAttribute("enabled"))=="true"){ + TiXmlElement* xMethod_C=xMainNode->FirstChildElement("method_C"); + if(string(xMethod_C->Attribute("enabled"))=="true"){ _isMethodC = true; - _measureAreaId = xmltoa(xMeasurementArea.getChildNode("MeasurementArea").getAttribute("id")); + _measureAreaId = xMethod_C->FirstChildElement("measurementArea")->Attribute("id"); Log->Write("INFO: \tMethod C is selected" ); } - // method D - XMLNode xMethod_D=xMainNode.getChildNode("Method_D"); - if(string(xMethod_D.getAttribute("enabled"))=="true"){ + TiXmlElement* xMethod_D=xMainNode->FirstChildElement("method_D"); + if(string(xMethod_D->Attribute("enabled"))=="true"){ _isMethodD = true; - string xIsCutByCircle = string(xMethod_D.getAttribute("cutbycircle")) ; - _isCutByCircle = (xIsCutByCircle == "true") ? true: false; - string xIsOutputGraph = string(xMethod_D.getAttribute("IsOutputGraph")); - _isOutputGraph = (xIsOutputGraph == "true") ? true: false; + _isCutByCircle = (string(xMethod_D->Attribute("cutByCircle")) == "true"); + _isOutputGraph = (string(xMethod_D->Attribute("outputGraph")) == "true"); if(_isOutputGraph) { Log->Write("INFO: \tVoronoi graph is asked to output!" ); } - string xIsIndividualFD = string(xMethod_D.getAttribute("IndividualFDdata")); - _isIndividualFD = (xIsIndividualFD== "true") ? true: false; - _measureAreaId = xmltoa(xMeasurementArea.getChildNode("MeasurementArea").getAttribute("id")); - - if(!xMethod_D.getChildNode("SteadyState").isEmpty()){ - const char* steady_start = xMethod_D.getChildNode("SteadyState").getAttribute("start"); - const char* steady_end = xMethod_D.getChildNode("SteadyState").getAttribute("end"); - _steadyStart = atof(steady_start); - _steadyEnd = atof(steady_end); - Log->Write("INFO: \tthe steady state is from <" + string(steady_start) + "> to <"+string(steady_end) +"> frame" ); + _isIndividualFD = (string(xMethod_D->Attribute("individualFDdata")) == "true"); + _measureAreaId = xMethod_D->FirstChildElement("measurementArea")->Attribute("id"); + + if ( xMethod_D->FirstChildElement("steadyState")) + { + _steadyStart =xmltof(xMethod_D->FirstChildElement("steadyState")->Attribute("start")); + _steadyEnd =xmltof(xMethod_D->FirstChildElement("steadyState")->Attribute("end")); + Log->Write("INFO: \tthe steady state is from <%f> to <%f> frames", _steadyStart, _steadyEnd); } - if(string(xMethod_D.getChildNode("GetProfile").getAttribute("enabled")) == "true"){ + if(xMethod_D->FirstChildElement("getProfile")) + if ( string(xMethod_D->FirstChildElement("getProfile")->Attribute("enabled"))=="true") + { _isGetProfile = true; - const char* scale_x = xMethod_D.getChildNode("GetProfile").getAttribute("scale_x"); - _scaleX = atoi(scale_x); - const char* scale_y = xMethod_D.getChildNode("GetProfile").getAttribute("scale_y"); - _scaleY = atoi(scale_y); + _scaleX =xmltoi(xMethod_D->FirstChildElement("getProfile")->Attribute("scale_x")); + _scaleY =xmltoi(xMethod_D->FirstChildElement("getProfile")->Attribute("scale_y")); Log->Write("INFO: \tprofiles will be calculated" ); - Log->Write("INFO: \tthe scale of the discretized cell in x, y direction are: <" + string(scale_x) + "> and <"+string(scale_y) +">" ); + Log->Write("INFO: \tthe scale of the discretized cell in x, y direction are: < %d > and < %d >",_scaleX, _scaleY); } + Log->Write("INFO: \tMethod D is selected" ); } + Log->Write("INFO: \tdone parsing ini"); } @@ -411,16 +423,12 @@ const string& ArgumentParser::GetGeometryFilename() const { return _geometryFileName; } -const string& ArgumentParser::GetTrajectoriesFile() const { - return _trajectoryFile; -} - -const string& ArgumentParser::GetTrajectoryName() const { - return _trajectoryName; +const string& ArgumentParser::GetTrajectoriesLocation() const { + return _trajectoriesLocation; } -void ArgumentParser::SetTrajectoriesFile(const string& trajectoriesFile) { - _trajectoryFile = trajectoriesFile; +const string& ArgumentParser::GetTrajectoriesFilename() const { + return _trajectoriesFilename; } const string& ArgumentParser::GetMeasureAreaId() const { diff --git a/general/ArgumentParser.h b/general/ArgumentParser.h index fbfe6905fdfa804fc3e725bacd2c4d8bb7453642..caca45f60a0448832c925e0746e2e9843b1cc27d 100644 --- a/general/ArgumentParser.h +++ b/general/ArgumentParser.h @@ -56,8 +56,8 @@ private: FileFormat pFormat; string _geometryFileName; string _errorLogFile; - string _trajectoryFile; - string _trajectoryName; + string _trajectoriesLocation; + string _trajectoriesFilename; string _measureAreaId; double _lengthMeasureArea; polygon_2d _measureArea; @@ -91,8 +91,8 @@ public: ArgumentParser(); // gibt die Programmoptionen aus // Getter-Funktionen - const string& GetTrajectoriesFile() const; - const string& GetTrajectoryName() const; + const string& GetTrajectoriesFilename() const; + const string& GetTrajectoriesLocation() const; const string& GetMeasureAreaId() const; const FileFormat& GetFileFormat() const; const string& GetGeometryFilename() const; @@ -121,8 +121,6 @@ public: int GetLog() const; void SetHostname(const string& hostname); void SetPort(int port); - void SetTrajectoriesFile(const string& trajectoriesFile); - void SetTrajectoriesPath(const string& trajectoriesPath); void ParseArgs(int argc, char **argv); /** diff --git a/general/Macros.h b/general/Macros.h index f050a36e9fc800bc2984f90ebb2c1fd07d949618..292811a34a5f047ee2f0f489bb23aab6758fb58e 100644 --- a/general/Macros.h +++ b/general/Macros.h @@ -25,11 +25,16 @@ */ #ifndef _MACROS_H -#define _MACROS_H +#define _MACROS_H +#include <cstdlib> +#include <vector> +#include <string.h> //#define _SIMULATOR 1 +//#undef _OPENMP + // Genauigkeit #define J_EPS 0.001 #define J_EPS_DIST 0.05// [m] @@ -41,13 +46,14 @@ // zur Versionskontrolle beim Geometrieformat #define VERSION 0.40 #define JPS_VERSION "0.4" +#define JPS_MAJOR_VERSION 0 +#define JPS_MINOR_VERSION 4 // Länge von char vectoren zur Ausgabe #define CLENGTH 1000 // Faktor für TraVisTo (cm <-> m) -//#define FAKTOR 100 #define FAKTOR 100 @@ -85,5 +91,16 @@ enum RoutingStrategy { ROUTING_UNDEFINED =-1 }; + +//global functions for convenience +// convenience functions + +inline char xmltob(const char * t,char v=0){ if (t&&(*t)) return (char)atoi(t); return v; } +inline int xmltoi(const char * t,int v=0){ if (t&&(*t)) return atoi(t); return v; } +inline long xmltol(const char * t,long v=0){ if (t&&(*t)) return atol(t); return v; } +inline double xmltof(const char * t,double v=0.0){ if (t&&(*t)) return atof(t); return v; } +inline const char * xmltoa(const char * t, const char * v=""){ if (t) return t; return v; } +inline char xmltoc(const char * t,const char v='\0'){ if (t&&(*t)) return *t; return v; } + #endif /* _MACROS_H */ diff --git a/general/xmlParser.cpp b/general/xmlParser.cpp deleted file mode 100644 index 82046476a5ed93516a475e2b4c0324af288b2a86..0000000000000000000000000000000000000000 --- a/general/xmlParser.cpp +++ /dev/null @@ -1,2887 +0,0 @@ -/** - **************************************************************************** - * <P> XML.c - implementation file for basic XML parser written in ANSI C++ - * for portability. It works by using recursion and a node tree for breaking - * down the elements of an XML document. </P> - * - * @version V2.41 - * @author Frank Vanden Berghen - * - * NOTE: - * - * If you add "#define STRICT_PARSING", on the first line of this file - * the parser will see the following XML-stream: - * <a><b>some text</b><b>other text </a> - * as an error. Otherwise, this tring will be equivalent to: - * <a><b>some text</b><b>other text</b></a> - * - * NOTE: - * - * If you add "#define APPROXIMATE_PARSING" on the first line of this file - * the parser will see the following XML-stream: - * <data name="n1"> - * <data name="n2"> - * <data name="n3" /> - * as equivalent to the following XML-stream: - * <data name="n1" /> - * <data name="n2" /> - * <data name="n3" /> - * This can be useful for badly-formed XML-streams but prevent the use - * of the following XML-stream (problem is: tags at contiguous levels - * have the same names): - * <data name="n1"> - * <data name="n2"> - * <data name="n3" /> - * </data> - * </data> - * - * NOTE: - * - * If you add "#define _XMLPARSER_NO_MESSAGEBOX_" on the first line of this file - * the "openFileHelper" function will always display error messages inside the - * console instead of inside a message-box-window. Message-box-windows are - * available on windows 9x/NT/2000/XP/Vista only. - * - * Copyright (c) 2002, Business-Insight - * <a href="http://www.Business-Insight.com">Business-Insight</a> - * All rights reserved. - * See the file "AFPL-license.txt" about the licensing terms - * - **************************************************************************** - */ -#ifndef _CRT_SECURE_NO_DEPRECATE -#define _CRT_SECURE_NO_DEPRECATE -#endif -#include "xmlParser.h" -#ifdef _XMLWINDOWS -//#ifdef _DEBUG -//#define _CRTDBG_MAP_ALLOC -//#include <crtdbg.h> -//#endif -#define WIN32_LEAN_AND_MEAN -#include <Windows.h> // to have IsTextUnicode, MultiByteToWideChar, WideCharToMultiByte to handle unicode files - // to have "MessageBoxA" to display error messages for openFilHelper -#endif - -#include <memory.h> -#include <assert.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> - -XMLCSTR XMLNode::getVersion() { return _CXML("v2.41"); } -void freeXMLString(XMLSTR t){if(t)free(t);} - -static XMLNode::XMLCharEncoding characterEncoding=XMLNode::char_encoding_UTF8; -static char guessWideCharChars=1, dropWhiteSpace=1, removeCommentsInMiddleOfText=1; - -inline int mmin( const int t1, const int t2 ) { return t1 < t2 ? t1 : t2; } - -// You can modify the initialization of the variable "XMLClearTags" below -// to change the clearTags that are currently recognized by the library. -// The number on the second columns is the length of the string inside the -// first column. The "<!DOCTYPE" declaration must be the second in the list. -// The "<!--" declaration must be the third in the list. -typedef struct { XMLCSTR lpszOpen; int openTagLen; XMLCSTR lpszClose;} ALLXMLClearTag; -static ALLXMLClearTag XMLClearTags[] = -{ - { _CXML("<![CDATA["),9, _CXML("]]>") }, - { _CXML("<!DOCTYPE"),9, _CXML(">") }, - { _CXML("<!--") ,4, _CXML("-->") }, - { _CXML("<PRE>") ,5, _CXML("</PRE>") }, -// { _CXML("<Script>") ,8, _CXML("</Script>")}, - { NULL ,0, NULL } -}; - -// You can modify the initialization of the variable "XMLEntities" below -// to change the character entities that are currently recognized by the library. -// The number on the second columns is the length of the string inside the -// first column. Additionally, the syntaxes " " and " " are recognized. -typedef struct { XMLCSTR s; int l; XMLCHAR c;} XMLCharacterEntity; -static XMLCharacterEntity XMLEntities[] = -{ - { _CXML("&" ), 5, _CXML('&' )}, - { _CXML("<" ), 4, _CXML('<' )}, - { _CXML(">" ), 4, _CXML('>' )}, - { _CXML("""), 6, _CXML('\"')}, - { _CXML("'"), 6, _CXML('\'')}, - { NULL , 0, '\0' } -}; - -// When rendering the XMLNode to a string (using the "createXMLString" function), -// you can ask for a beautiful formatting. This formatting is using the -// following indentation character: -#define INDENTCHAR _CXML('\t') - -// The following function parses the XML errors into a user friendly string. -// You can edit this to change the output language of the library to something else. -XMLCSTR XMLNode::getError(XMLError xerror) -{ - switch (xerror) - { - case eXMLErrorNone: return _CXML("No error"); - case eXMLErrorMissingEndTag: return _CXML("Warning: Unmatched end tag"); - case eXMLErrorNoXMLTagFound: return _CXML("Warning: No XML tag found"); - case eXMLErrorEmpty: return _CXML("Error: No XML data"); - case eXMLErrorMissingTagName: return _CXML("Error: Missing start tag name"); - case eXMLErrorMissingEndTagName: return _CXML("Error: Missing end tag name"); - case eXMLErrorUnmatchedEndTag: return _CXML("Error: Unmatched end tag"); - case eXMLErrorUnmatchedEndClearTag: return _CXML("Error: Unmatched clear tag end"); - case eXMLErrorUnexpectedToken: return _CXML("Error: Unexpected token found"); - case eXMLErrorNoElements: return _CXML("Error: No elements found"); - case eXMLErrorFileNotFound: return _CXML("Error: File not found"); - case eXMLErrorFirstTagNotFound: return _CXML("Error: First Tag not found"); - case eXMLErrorUnknownCharacterEntity:return _CXML("Error: Unknown character entity"); - case eXMLErrorCharacterCodeAbove255: return _CXML("Error: Character code above 255 is forbidden in MultiByte char mode."); - case eXMLErrorCharConversionError: return _CXML("Error: unable to convert between WideChar and MultiByte chars"); - case eXMLErrorCannotOpenWriteFile: return _CXML("Error: unable to open file for writing"); - case eXMLErrorCannotWriteFile: return _CXML("Error: cannot write into file"); - - case eXMLErrorBase64DataSizeIsNotMultipleOf4: return _CXML("Warning: Base64-string length is not a multiple of 4"); - case eXMLErrorBase64DecodeTruncatedData: return _CXML("Warning: Base64-string is truncated"); - case eXMLErrorBase64DecodeIllegalCharacter: return _CXML("Error: Base64-string contains an illegal character"); - case eXMLErrorBase64DecodeBufferTooSmall: return _CXML("Error: Base64 decode output buffer is too small"); - }; - return _CXML("Unknown"); -} - -///////////////////////////////////////////////////////////////////////// -// Here start the abstraction layer to be OS-independent // -///////////////////////////////////////////////////////////////////////// - -// Here is an abstraction layer to access some common string manipulation functions. -// The abstraction layer is currently working for gcc, Microsoft Visual Studio 6.0, -// Microsoft Visual Studio .NET, CC (sun compiler) and Borland C++. -// If you plan to "port" the library to a new system/compiler, all you have to do is -// to edit the following lines. -#ifdef XML_NO_WIDE_CHAR -char myIsTextWideChar(const void *b, int len) { return FALSE; } -#else - #if defined (UNDER_CE) || !defined(_XMLWINDOWS) - char myIsTextWideChar(const void *b, int len) // inspired by the Wine API: RtlIsTextUnicode - { -#ifdef sun - // for SPARC processors: wchar_t* buffers must always be alligned, otherwise it's a char* buffer. - if ((((unsigned long)b)%sizeof(wchar_t))!=0) return FALSE; -#endif - const wchar_t *s=(const wchar_t*)b; - - // buffer too small: - if (len<(int)sizeof(wchar_t)) return FALSE; - - // odd length test - if (len&1) return FALSE; - - /* only checks the first 256 characters */ - len=mmin(256,len/sizeof(wchar_t)); - - // Check for the special byte order: - if (*((unsigned short*)s) == 0xFFFE) return TRUE; // IS_TEXT_UNICODE_REVERSE_SIGNATURE; - if (*((unsigned short*)s) == 0xFEFF) return TRUE; // IS_TEXT_UNICODE_SIGNATURE - - // checks for ASCII characters in the UNICODE stream - int i,stats=0; - for (i=0; i<len; i++) if (s[i]<=(unsigned short)255) stats++; - if (stats>len/2) return TRUE; - - // Check for UNICODE NULL chars - for (i=0; i<len; i++) if (!s[i]) return TRUE; - - return FALSE; - } - #else - char myIsTextWideChar(const void *b,int l) { return (char)IsTextUnicode((CONST LPVOID)b,l,NULL); }; - #endif -#endif - -#ifdef _XMLWINDOWS -// for Microsoft Visual Studio 6.0 and Microsoft Visual Studio .NET and Borland C++ Builder 6.0 - #ifdef _XMLWIDECHAR - wchar_t *myMultiByteToWideChar(const char *s, XMLNode::XMLCharEncoding ce) - { - int i; - if (ce==XMLNode::char_encoding_UTF8) i=(int)MultiByteToWideChar(CP_UTF8,0 ,s,-1,NULL,0); - else i=(int)MultiByteToWideChar(CP_ACP ,MB_PRECOMPOSED,s,-1,NULL,0); - if (i<0) return NULL; - wchar_t *d=(wchar_t *)malloc((i+1)*sizeof(XMLCHAR)); - if (ce==XMLNode::char_encoding_UTF8) i=(int)MultiByteToWideChar(CP_UTF8,0 ,s,-1,d,i); - else i=(int)MultiByteToWideChar(CP_ACP ,MB_PRECOMPOSED,s,-1,d,i); - d[i]=0; - return d; - } - static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode) { return _wfopen(filename,mode); } - static inline int xstrlen(XMLCSTR c) { return (int)wcslen(c); } - static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return _wcsnicmp(c1,c2,l);} - static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return wcsncmp(c1,c2,l);} - static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return _wcsicmp(c1,c2); } - static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)wcsstr(c1,c2); } - static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)wcscpy(c1,c2); } - #else - char *myWideCharToMultiByte(const wchar_t *s) - { - UINT codePage=CP_ACP; if (characterEncoding==XMLNode::char_encoding_UTF8) codePage=CP_UTF8; - int i=(int)WideCharToMultiByte(codePage, // code page - 0, // performance and mapping flags - s, // wide-character string - -1, // number of chars in string - NULL, // buffer for new string - 0, // size of buffer - NULL, // default for unmappable chars - NULL // set when default char used - ); - if (i<0) return NULL; - char *d=(char*)malloc(i+1); - WideCharToMultiByte(codePage, // code page - 0, // performance and mapping flags - s, // wide-character string - -1, // number of chars in string - d, // buffer for new string - i, // size of buffer - NULL, // default for unmappable chars - NULL // set when default char used - ); - d[i]=0; - return d; - } - static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode) { return fopen(filename,mode); } - static inline int xstrlen(XMLCSTR c) { return (int)strlen(c); } - #ifdef __BORLANDC__ - static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return strnicmp(c1,c2,l);} - static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return stricmp(c1,c2); } - #else - static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return _strnicmp(c1,c2,l);} - static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return _stricmp(c1,c2); } - #endif - static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return strncmp(c1,c2,l);} - static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)strstr(c1,c2); } - static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)strcpy(c1,c2); } - #endif -#else -// for gcc and CC - #ifdef XML_NO_WIDE_CHAR - char *myWideCharToMultiByte(const wchar_t *s) { return NULL; } - #else - char *myWideCharToMultiByte(const wchar_t *s) - { - const wchar_t *ss=s; - int i=(int)wcsrtombs(NULL,&ss,0,NULL); - if (i<0) return NULL; - char *d=(char *)malloc(i+1); - wcsrtombs(d,&s,i,NULL); - d[i]=0; - return d; - } - #endif - #ifdef _XMLWIDECHAR - wchar_t *myMultiByteToWideChar(const char *s, XMLNode::XMLCharEncoding ce) - { - const char *ss=s; - int i=(int)mbsrtowcs(NULL,&ss,0,NULL); - if (i<0) return NULL; - wchar_t *d=(wchar_t *)malloc((i+1)*sizeof(wchar_t)); - mbsrtowcs(d,&s,i,NULL); - d[i]=0; - return d; - } - int xstrlen(XMLCSTR c) { return wcslen(c); } - #ifdef sun - // for CC - #include <widec.h> - static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return wsncasecmp(c1,c2,l);} - static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return wsncmp(c1,c2,l);} - static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return wscasecmp(c1,c2); } - #else - static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return wcsncmp(c1,c2,l);} - #ifdef __linux__ - // for gcc/linux - static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return wcsncasecmp(c1,c2,l);} - static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return wcscasecmp(c1,c2); } - #else - #include <wctype.h> - // for gcc/non-linux (MacOS X 10.3, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, AIX 4.3.2, HP-UX 11, IRIX 6.5, OSF/1 5.1, Cygwin, mingw) - static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) - { - wchar_t left,right; - do - { - left=towlower(*c1++); right=towlower(*c2++); - } while (left&&(left==right)); - return (int)left-(int)right; - } - static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) - { - wchar_t left,right; - while(l--) - { - left=towlower(*c1++); right=towlower(*c2++); - if ((!left)||(left!=right)) return (int)left-(int)right; - } - return 0; - } - #endif - #endif - static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)wcsstr(c1,c2); } - static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)wcscpy(c1,c2); } - static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode) - { - char *filenameAscii=myWideCharToMultiByte(filename); - FILE *f; - if (mode[0]==_CXML('r')) f=fopen(filenameAscii,"rb"); - else f=fopen(filenameAscii,"wb"); - free(filenameAscii); - return f; - } - #else - static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode) { return fopen(filename,mode); } - static inline int xstrlen(XMLCSTR c) { return strlen(c); } - static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return strncasecmp(c1,c2,l);} - static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return strncmp(c1,c2,l);} - static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return strcasecmp(c1,c2); } - static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)strstr(c1,c2); } - static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)strcpy(c1,c2); } - #endif - static inline int _strnicmp(const char *c1,const char *c2, int l) { return strncasecmp(c1,c2,l);} -#endif - - -/////////////////////////////////////////////////////////////////////////////// -// the "xmltoc,xmltob,xmltoi,xmltol,xmltof,xmltoa" functions // -/////////////////////////////////////////////////////////////////////////////// -// These 6 functions are not used inside the XMLparser. -// There are only here as "convenience" functions for the user. -// If you don't need them, you can delete them without any trouble. -#ifdef _XMLWIDECHAR - #ifdef _XMLWINDOWS - // for Microsoft Visual Studio 6.0 and Microsoft Visual Studio .NET and Borland C++ Builder 6.0 - char xmltob(XMLCSTR t,int v){ if (t&&(*t)) return (char)_wtoi(t); return v; } - int xmltoi(XMLCSTR t,int v){ if (t&&(*t)) return _wtoi(t); return v; } - long xmltol(XMLCSTR t,long v){ if (t&&(*t)) return _wtol(t); return v; } - double xmltof(XMLCSTR t,double v){ if (t&&(*t)) wscanf(t, "%f", &v); /*v=_wtof(t);*/ return v; } - #else - #ifdef sun - // for CC - #include <widec.h> - char xmltob(XMLCSTR t,int v){ if (t) return (char)wstol(t,NULL,10); return v; } - int xmltoi(XMLCSTR t,int v){ if (t) return (int)wstol(t,NULL,10); return v; } - long xmltol(XMLCSTR t,long v){ if (t) return wstol(t,NULL,10); return v; } - #else - // for gcc - char xmltob(XMLCSTR t,int v){ if (t) return (char)wcstol(t,NULL,10); return v; } - int xmltoi(XMLCSTR t,int v){ if (t) return (int)wcstol(t,NULL,10); return v; } - long xmltol(XMLCSTR t,long v){ if (t) return wcstol(t,NULL,10); return v; } - #endif - double xmltof(XMLCSTR t,double v){ if (t&&(*t)) wscanf(t, "%f", &v); /*v=_wtof(t);*/ return v; } - #endif -#else - char xmltob(XMLCSTR t,char v){ if (t&&(*t)) return (char)atoi(t); return v; } - int xmltoi(XMLCSTR t,int v){ if (t&&(*t)) return atoi(t); return v; } - long xmltol(XMLCSTR t,long v){ if (t&&(*t)) return atol(t); return v; } - double xmltof(XMLCSTR t,double v){ if (t&&(*t)) return atof(t); return v; } -#endif -XMLCSTR xmltoa(XMLCSTR t, XMLCSTR v){ if (t) return t; return v; } -XMLCHAR xmltoc(XMLCSTR t,const XMLCHAR v){ if (t&&(*t)) return *t; return v; } - -///////////////////////////////////////////////////////////////////////// -// the "openFileHelper" function // -///////////////////////////////////////////////////////////////////////// - -// Since each application has its own way to report and deal with errors, you should modify & rewrite -// the following "openFileHelper" function to get an "error reporting mechanism" tailored to your needs. -XMLNode XMLNode::openFileHelper(XMLCSTR filename, XMLCSTR tag) -{ - // guess the value of the global parameter "characterEncoding" - // (the guess is based on the first 200 bytes of the file). - FILE *f=xfopen(filename,_CXML("rb")); - if (f) - { - char bb[205]; - int l=(int)fread(bb,1,200,f); - setGlobalOptions(guessCharEncoding(bb,l),guessWideCharChars,dropWhiteSpace,removeCommentsInMiddleOfText); - fclose(f); - } - - // parse the file - XMLResults pResults; - XMLNode xnode=XMLNode::parseFile(filename,tag,&pResults); - - // display error message (if any) - if (pResults.error != eXMLErrorNone) - { - // create message - char message[2000],*s1=(char*)"",*s3=(char*)""; XMLCSTR s2=_CXML(""); - if (pResults.error==eXMLErrorFirstTagNotFound) { s1=(char*)"First Tag should be '"; s2=tag; s3=(char*)"'.\n"; } - sprintf(message, -#ifdef _XMLWIDECHAR - "XML Parsing error inside file '%S'.\n%S\nAt line %i, column %i.\n%s%S%s" -#else - "XML Parsing error inside file '%s'.\n%s\nAt line %i, column %i.\n%s%s%s" -#endif - ,filename,XMLNode::getError(pResults.error),pResults.nLine,pResults.nColumn,s1,s2,s3); - - // display message -#if defined(_XMLWINDOWS) && !defined(UNDER_CE) && !defined(_XMLPARSER_NO_MESSAGEBOX_) - MessageBoxA(NULL,message,"XML Parsing error",MB_OK|MB_ICONERROR|MB_TOPMOST); -#else - printf("%s",message); -#endif - exit(255); - } - return xnode; -} - -///////////////////////////////////////////////////////////////////////// -// Here start the core implementation of the XMLParser library // -///////////////////////////////////////////////////////////////////////// - -// You should normally not change anything below this point. - -#ifndef _XMLWIDECHAR -// If "characterEncoding=ascii" then we assume that all characters have the same length of 1 byte. -// If "characterEncoding=UTF8" then the characters have different lengths (from 1 byte to 4 bytes). -// If "characterEncoding=ShiftJIS" then the characters have different lengths (from 1 byte to 2 bytes). -// This table is used as lookup-table to know the length of a character (in byte) based on the -// content of the first byte of the character. -// (note: if you modify this, you must always have XML_utf8ByteTable[0]=0 ). -static const char XML_utf8ByteTable[256] = -{ - // 0 1 2 3 4 5 6 7 8 9 a b c d e f - 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70 End of ASCII range - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x80 0x80 to 0xc1 invalid - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x90 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xa0 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xb0 - 1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xc0 0xc2 to 0xdf 2 byte - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xd0 - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,// 0xe0 0xe0 to 0xef 3 byte - 4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid -}; -static const char XML_legacyByteTable[256] = -{ - 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 -}; -static const char XML_sjisByteTable[256] = -{ - // 0 1 2 3 4 5 6 7 8 9 a b c d e f - 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70 - 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x80 0x81 to 0x9F 2 bytes - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x90 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xa0 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xb0 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xc0 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xd0 - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xe0 0xe0 to 0xef 2 bytes - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 // 0xf0 -}; -static const char XML_gb2312ByteTable[256] = -{ -// 0 1 2 3 4 5 6 7 8 9 a b c d e f - 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x80 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x90 - 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xa0 0xa1 to 0xf7 2 bytes - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xb0 - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xc0 - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xd0 - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xe0 - 2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1 // 0xf0 -}; -static const char XML_gbk_big5_ByteTable[256] = -{ - // 0 1 2 3 4 5 6 7 8 9 a b c d e f - 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60 - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70 - 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x80 0x81 to 0xfe 2 bytes - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x90 - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xa0 - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xb0 - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xc0 - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xd0 - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xe0 - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1 // 0xf0 -}; -static const char *XML_ByteTable=(const char *)XML_utf8ByteTable; // the default is "characterEncoding=XMLNode::encoding_UTF8" -#endif - - -XMLNode XMLNode::emptyXMLNode; -XMLClear XMLNode::emptyXMLClear={ NULL, NULL, NULL}; -XMLAttribute XMLNode::emptyXMLAttribute={ NULL, NULL}; - -// Enumeration used to decipher what type a token is -typedef enum XMLTokenTypeTag -{ - eTokenText = 0, - eTokenQuotedText, - eTokenTagStart, /* "<" */ - eTokenTagEnd, /* "</" */ - eTokenCloseTag, /* ">" */ - eTokenEquals, /* "=" */ - eTokenDeclaration, /* "<?" */ - eTokenShortHandClose, /* "/>" */ - eTokenClear, - eTokenError -} XMLTokenType; - -// Main structure used for parsing XML -typedef struct XML -{ - XMLCSTR lpXML; - XMLCSTR lpszText; - int nIndex,nIndexMissigEndTag; - enum XMLError error; - XMLCSTR lpEndTag; - int cbEndTag; - XMLCSTR lpNewElement; - int cbNewElement; - int nFirst; -} XML; - -typedef struct -{ - ALLXMLClearTag *pClr; - XMLCSTR pStr; -} NextToken; - -// Enumeration used when parsing attributes -typedef enum Attrib -{ - eAttribName = 0, - eAttribEquals, - eAttribValue -} Attrib; - -// Enumeration used when parsing elements to dictate whether we are currently -// inside a tag -typedef enum Status -{ - eInsideTag = 0, - eOutsideTag -} Status; - -XMLError XMLNode::writeToFile(XMLCSTR filename, const char *encoding, char nFormat) const -{ - if (!d) return eXMLErrorNone; - FILE *f=xfopen(filename,_CXML("wb")); - if (!f) return eXMLErrorCannotOpenWriteFile; -#ifdef _XMLWIDECHAR - unsigned char h[2]={ 0xFF, 0xFE }; - if (!fwrite(h,2,1,f)) return eXMLErrorCannotWriteFile; - if ((!isDeclaration())&&((d->lpszName)||(!getChildNode().isDeclaration()))) - { - if (!fwrite(L"<?xml version=\"1.0\" encoding=\"utf-16\"?>\n",sizeof(wchar_t)*40,1,f)) - return eXMLErrorCannotWriteFile; - } -#else - if ((!isDeclaration())&&((d->lpszName)||(!getChildNode().isDeclaration()))) - { - if (characterEncoding==char_encoding_UTF8) - { - // header so that windows recognize the file as UTF-8: - unsigned char h[3]={0xEF,0xBB,0xBF}; if (!fwrite(h,3,1,f)) return eXMLErrorCannotWriteFile; - encoding="utf-8"; - } else if (characterEncoding==char_encoding_ShiftJIS) encoding="SHIFT-JIS"; - - if (!encoding) encoding="ISO-8859-1"; - if (fprintf(f,"<?xml version=\"1.0\" encoding=\"%s\"?>\n",encoding)<0) return eXMLErrorCannotWriteFile; - } else - { - if (characterEncoding==char_encoding_UTF8) - { - unsigned char h[3]={0xEF,0xBB,0xBF}; if (!fwrite(h,3,1,f)) return eXMLErrorCannotWriteFile; - } - } -#endif - int i; - XMLSTR t=createXMLString(nFormat,&i); - if (!fwrite(t,sizeof(XMLCHAR)*i,1,f)) return eXMLErrorCannotWriteFile; - if (fclose(f)!=0) return eXMLErrorCannotWriteFile; - free(t); - return eXMLErrorNone; -} - -// Duplicate a given string. -XMLSTR stringDup(XMLCSTR lpszData, int cbData) -{ - if (lpszData==NULL) return NULL; - - XMLSTR lpszNew; - if (cbData==-1) cbData=(int)xstrlen(lpszData); - lpszNew = (XMLSTR)malloc((cbData+1) * sizeof(XMLCHAR)); - if (lpszNew) - { - memcpy(lpszNew, lpszData, (cbData) * sizeof(XMLCHAR)); - lpszNew[cbData] = (XMLCHAR)NULL; - } - return lpszNew; -} - -XMLSTR ToXMLStringTool::toXMLUnSafe(XMLSTR dest,XMLCSTR source) -{ - XMLSTR dd=dest; - XMLCHAR ch; - XMLCharacterEntity *entity; - while ((ch=*source)) - { - entity=XMLEntities; - do - { - if (ch==entity->c) {xstrcpy(dest,entity->s); dest+=entity->l; source++; goto out_of_loop1; } - entity++; - } while(entity->s); -#ifdef _XMLWIDECHAR - *(dest++)=*(source++); -#else - switch(XML_ByteTable[(unsigned char)ch]) - { - case 4: *(dest++)=*(source++); - case 3: *(dest++)=*(source++); - case 2: *(dest++)=*(source++); - case 1: *(dest++)=*(source++); - } -#endif -out_of_loop1: - ; - } - *dest=0; - return dd; -} - -// private (used while rendering): -int ToXMLStringTool::lengthXMLString(XMLCSTR source) -{ - int r=0; - XMLCharacterEntity *entity; - XMLCHAR ch; - while ((ch=*source)) - { - entity=XMLEntities; - do - { - if (ch==entity->c) { r+=entity->l; source++; goto out_of_loop1; } - entity++; - } while(entity->s); -#ifdef _XMLWIDECHAR - r++; source++; -#else - ch=XML_ByteTable[(unsigned char)ch]; r+=ch; source+=ch; -#endif -out_of_loop1: - ; - } - return r; -} - -ToXMLStringTool::~ToXMLStringTool(){ freeBuffer(); } -void ToXMLStringTool::freeBuffer(){ if (buf) free(buf); buf=NULL; buflen=0; } -XMLSTR ToXMLStringTool::toXML(XMLCSTR source) -{ - if (!source) return _CXML((XMLSTR)""); - int l=lengthXMLString(source)+1; - if (l>buflen) { buflen=l; buf=(XMLSTR)realloc(buf,l*sizeof(XMLCHAR)); } - return toXMLUnSafe(buf,source); -} - -// private: -XMLSTR fromXMLString(XMLCSTR s, int lo, XML *pXML) -{ - // This function is the opposite of the function "toXMLString". It decodes the escape - // sequences &, ", ', <, > and replace them by the characters - // &,",',<,>. This function is used internally by the XML Parser. All the calls to - // the XML library will always gives you back "decoded" strings. - // - // in: string (s) and length (lo) of string - // out: new allocated string converted from xml - if (!s) return NULL; - - int ll=0,j; - XMLSTR d; - XMLCSTR ss=s; - XMLCharacterEntity *entity; - while ((lo>0)&&(*s)) - { - if (*s==_CXML('&')) - { - if ((lo>2)&&(s[1]==_CXML('#'))) - { - s+=2; lo-=2; - if ((*s==_CXML('X'))||(*s==_CXML('x'))) { s++; lo--; } - while ((*s)&&(*s!=_CXML(';'))&&((lo--)>0)) s++; - if (*s!=_CXML(';')) - { - pXML->error=eXMLErrorUnknownCharacterEntity; - return NULL; - } - s++; lo--; - } else - { - entity=XMLEntities; - do - { - if ((lo>=entity->l)&&(xstrnicmp(s,entity->s,entity->l)==0)) { s+=entity->l; lo-=entity->l; break; } - entity++; - } while(entity->s); - if (!entity->s) - { - pXML->error=eXMLErrorUnknownCharacterEntity; - return NULL; - } - } - } else - { -#ifdef _XMLWIDECHAR - s++; lo--; -#else - j=XML_ByteTable[(unsigned char)*s]; s+=j; lo-=j; ll+=j-1; -#endif - } - ll++; - } - - d=(XMLSTR)malloc((ll+1)*sizeof(XMLCHAR)); - s=d; - while (ll-->0) - { - if (*ss==_CXML('&')) - { - if (ss[1]==_CXML('#')) - { - ss+=2; j=0; - if ((*ss==_CXML('X'))||(*ss==_CXML('x'))) - { - ss++; - while (*ss!=_CXML(';')) - { - if ((*ss>=_CXML('0'))&&(*ss<=_CXML('9'))) j=(j<<4)+*ss-_CXML('0'); - else if ((*ss>=_CXML('A'))&&(*ss<=_CXML('F'))) j=(j<<4)+*ss-_CXML('A')+10; - else if ((*ss>=_CXML('a'))&&(*ss<=_CXML('f'))) j=(j<<4)+*ss-_CXML('a')+10; - else { free((void*)s); pXML->error=eXMLErrorUnknownCharacterEntity;return NULL;} - ss++; - } - } else - { - while (*ss!=_CXML(';')) - { - if ((*ss>=_CXML('0'))&&(*ss<=_CXML('9'))) j=(j*10)+*ss-_CXML('0'); - else { free((void*)s); pXML->error=eXMLErrorUnknownCharacterEntity;return NULL;} - ss++; - } - } -#ifndef _XMLWIDECHAR - if (j>255) { free((void*)s); pXML->error=eXMLErrorCharacterCodeAbove255;return NULL;} -#endif - (*d++)=(XMLCHAR)j; ss++; - } else - { - entity=XMLEntities; - do - { - if (xstrnicmp(ss,entity->s,entity->l)==0) { *(d++)=entity->c; ss+=entity->l; break; } - entity++; - } while(entity->s); - } - } else - { -#ifdef _XMLWIDECHAR - *(d++)=*(ss++); -#else - switch(XML_ByteTable[(unsigned char)*ss]) - { - case 4: *(d++)=*(ss++); ll--; - case 3: *(d++)=*(ss++); ll--; - case 2: *(d++)=*(ss++); ll--; - case 1: *(d++)=*(ss++); - } -#endif - } - } - *d=0; - return (XMLSTR)s; -} - -#define XML_isSPACECHAR(ch) ((ch==_CXML('\n'))||(ch==_CXML(' '))||(ch== _CXML('\t'))||(ch==_CXML('\r'))) - -// private: -char myTagCompare(XMLCSTR cclose, XMLCSTR copen) -// !!!! WARNING strange convention&: -// return 0 if equals -// return 1 if different -{ - if (!cclose) return 1; - int l=(int)xstrlen(cclose); - if (xstrnicmp(cclose, copen, l)!=0) return 1; - const XMLCHAR c=copen[l]; - if (XML_isSPACECHAR(c)|| - (c==_CXML('/' ))|| - (c==_CXML('<' ))|| - (c==_CXML('>' ))|| - (c==_CXML('=' ))) return 0; - return 1; -} - -// Obtain the next character from the string. -static inline XMLCHAR getNextChar(XML *pXML) -{ - XMLCHAR ch = pXML->lpXML[pXML->nIndex]; -#ifdef _XMLWIDECHAR - if (ch!=0) pXML->nIndex++; -#else - pXML->nIndex+=XML_ByteTable[(unsigned char)ch]; -#endif - return ch; -} - -// Find the next token in a string. -// pcbToken contains the number of characters that have been read. -static NextToken GetNextToken(XML *pXML, int *pcbToken, enum XMLTokenTypeTag *pType) -{ - NextToken result; - XMLCHAR ch; - XMLCHAR chTemp; - int indexStart,nFoundMatch,nIsText=FALSE; - result.pClr=NULL; // prevent warning - - // Find next non-white space character - do { indexStart=pXML->nIndex; ch=getNextChar(pXML); } while XML_isSPACECHAR(ch); - - if (ch) - { - // Cache the current string pointer - result.pStr = &pXML->lpXML[indexStart]; - - // First check whether the token is in the clear tag list (meaning it - // does not need formatting). - ALLXMLClearTag *ctag=XMLClearTags; - do - { - if (xstrncmp(ctag->lpszOpen, result.pStr, ctag->openTagLen)==0) - { - result.pClr=ctag; - pXML->nIndex+=ctag->openTagLen-1; - *pType=eTokenClear; - return result; - } - ctag++; - } while(ctag->lpszOpen); - - // If we didn't find a clear tag then check for standard tokens - switch(ch) - { - // Check for quotes - case _CXML('\''): - case _CXML('\"'): - // Type of token - *pType = eTokenQuotedText; - chTemp = ch; - - // Set the size - nFoundMatch = FALSE; - - // Search through the string to find a matching quote - while((ch = getNextChar(pXML))) - { - if (ch==chTemp) { nFoundMatch = TRUE; break; } - if (ch==_CXML('<')) break; - } - - // If we failed to find a matching quote - if (nFoundMatch == FALSE) - { - pXML->nIndex=indexStart+1; - nIsText=TRUE; - break; - } - -// 4.02.2002 -// if (FindNonWhiteSpace(pXML)) pXML->nIndex--; - - break; - - // Equals (used with attribute values) - case _CXML('='): - *pType = eTokenEquals; - break; - - // Close tag - case _CXML('>'): - *pType = eTokenCloseTag; - break; - - // Check for tag start and tag end - case _CXML('<'): - - // Peek at the next character to see if we have an end tag '</', - // or an xml declaration '<?' - chTemp = pXML->lpXML[pXML->nIndex]; - - // If we have a tag end... - if (chTemp == _CXML('/')) - { - // Set the type and ensure we point at the next character - getNextChar(pXML); - *pType = eTokenTagEnd; - } - - // If we have an XML declaration tag - else if (chTemp == _CXML('?')) - { - - // Set the type and ensure we point at the next character - getNextChar(pXML); - *pType = eTokenDeclaration; - } - - // Otherwise we must have a start tag - else - { - *pType = eTokenTagStart; - } - break; - - // Check to see if we have a short hand type end tag ('/>'). - case _CXML('/'): - - // Peek at the next character to see if we have a short end tag '/>' - chTemp = pXML->lpXML[pXML->nIndex]; - - // If we have a short hand end tag... - if (chTemp == _CXML('>')) - { - // Set the type and ensure we point at the next character - getNextChar(pXML); - *pType = eTokenShortHandClose; - break; - } - - // If we haven't found a short hand closing tag then drop into the - // text process - - // Other characters - default: - nIsText = TRUE; - } - - // If this is a TEXT node - if (nIsText) - { - // Indicate we are dealing with text - *pType = eTokenText; - while((ch = getNextChar(pXML))) - { - if XML_isSPACECHAR(ch) - { - indexStart++; break; - - } else if (ch==_CXML('/')) - { - // If we find a slash then this maybe text or a short hand end tag - // Peek at the next character to see it we have short hand end tag - ch=pXML->lpXML[pXML->nIndex]; - // If we found a short hand end tag then we need to exit the loop - if (ch==_CXML('>')) { pXML->nIndex--; break; } - - } else if ((ch==_CXML('<'))||(ch==_CXML('>'))||(ch==_CXML('='))) - { - pXML->nIndex--; break; - } - } - } - *pcbToken = pXML->nIndex-indexStart; - } else - { - // If we failed to obtain a valid character - *pcbToken = 0; - *pType = eTokenError; - result.pStr=NULL; - } - - return result; -} - -XMLCSTR XMLNode::updateName_WOSD(XMLSTR lpszName) -{ - if (!d) { free(lpszName); return NULL; } - if (d->lpszName&&(lpszName!=d->lpszName)) free((void*)d->lpszName); - d->lpszName=lpszName; - return lpszName; -} - -// private: -XMLNode::XMLNode(struct XMLNodeDataTag *p){ d=p; (p->ref_count)++; } -XMLNode::XMLNode(XMLNodeData *pParent, XMLSTR lpszName, char isDeclaration) -{ - d=(XMLNodeData*)malloc(sizeof(XMLNodeData)); - d->ref_count=1; - - d->lpszName=NULL; - d->nChild= 0; - d->nText = 0; - d->nClear = 0; - d->nAttribute = 0; - - d->isDeclaration = isDeclaration; - - d->pParent = pParent; - d->pChild= NULL; - d->pText= NULL; - d->pClear= NULL; - d->pAttribute= NULL; - d->pOrder= NULL; - - updateName_WOSD(lpszName); -} - -XMLNode XMLNode::createXMLTopNode_WOSD(XMLSTR lpszName, char isDeclaration) { return XMLNode(NULL,lpszName,isDeclaration); } -XMLNode XMLNode::createXMLTopNode(XMLCSTR lpszName, char isDeclaration) { return XMLNode(NULL,stringDup(lpszName),isDeclaration); } - -#define MEMORYINCREASE 50 - -static inline void myFree(void *p) { if (p) free(p); } -static inline void *myRealloc(void *p, int newsize, int memInc, int sizeofElem) -{ - if (p==NULL) { if (memInc) return malloc(memInc*sizeofElem); return malloc(sizeofElem); } - if ((memInc==0)||((newsize%memInc)==0)) p=realloc(p,(newsize+memInc)*sizeofElem); -// if (!p) -// { -// printf("XMLParser Error: Not enough memory! Aborting...\n"); exit(220); -// } - return p; -} - -// private: -XMLElementPosition XMLNode::findPosition(XMLNodeData *d, int index, XMLElementType xxtype) -{ - if (index<0) return -1; - int i=0,j=(int)((index<<2)+xxtype),*o=d->pOrder; while (o[i]!=j) i++; return i; -} - -// private: -// update "order" information when deleting a content of a XMLNode -int XMLNode::removeOrderElement(XMLNodeData *d, XMLElementType t, int index) -{ - int n=d->nChild+d->nText+d->nClear, *o=d->pOrder,i=findPosition(d,index,t); - memmove(o+i, o+i+1, (n-i)*sizeof(int)); - for (;i<n;i++) - if ((o[i]&3)==(int)t) o[i]-=4; - // We should normally do: - // d->pOrder=(int)realloc(d->pOrder,n*sizeof(int)); - // but we skip reallocation because it's too time consuming. - // Anyway, at the end, it will be free'd completely at once. - return i; -} - -void *XMLNode::addToOrder(int memoryIncrease,int *_pos, int nc, void *p, int size, XMLElementType xtype) -{ - // in: *_pos is the position inside d->pOrder ("-1" means "EndOf") - // out: *_pos is the index inside p - p=myRealloc(p,(nc+1),memoryIncrease,size); - int n=d->nChild+d->nText+d->nClear; - d->pOrder=(int*)myRealloc(d->pOrder,n+1,memoryIncrease*3,sizeof(int)); - int pos=*_pos,*o=d->pOrder; - - if ((pos<0)||(pos>=n)) { *_pos=nc; o[n]=(int)((nc<<2)+xtype); return p; } - - int i=pos; - memmove(o+i+1, o+i, (n-i)*sizeof(int)); - - while ((pos<n)&&((o[pos]&3)!=(int)xtype)) pos++; - if (pos==n) { *_pos=nc; o[n]=(int)((nc<<2)+xtype); return p; } - - o[i]=o[pos]; - for (i=pos+1;i<=n;i++) if ((o[i]&3)==(int)xtype) o[i]+=4; - - *_pos=pos=o[pos]>>2; - memmove(((char*)p)+(pos+1)*size,((char*)p)+pos*size,(nc-pos)*size); - - return p; -} - -// Add a child node to the given element. -XMLNode XMLNode::addChild_priv(int memoryIncrease, XMLSTR lpszName, char isDeclaration, int pos) -{ - if (!lpszName) return emptyXMLNode; - d->pChild=(XMLNode*)addToOrder(memoryIncrease,&pos,d->nChild,d->pChild,sizeof(XMLNode),eNodeChild); - d->pChild[pos].d=NULL; - d->pChild[pos]=XMLNode(d,lpszName,isDeclaration); - d->nChild++; - return d->pChild[pos]; -} - -// Add an attribute to an element. -XMLAttribute *XMLNode::addAttribute_priv(int memoryIncrease,XMLSTR lpszName, XMLSTR lpszValuev) -{ - if (!lpszName) return &emptyXMLAttribute; - if (!d) { myFree(lpszName); myFree(lpszValuev); return &emptyXMLAttribute; } - int nc=d->nAttribute; - d->pAttribute=(XMLAttribute*)myRealloc(d->pAttribute,(nc+1),memoryIncrease,sizeof(XMLAttribute)); - XMLAttribute *pAttr=d->pAttribute+nc; - pAttr->lpszName = lpszName; - pAttr->lpszValue = lpszValuev; - d->nAttribute++; - return pAttr; -} - -// Add text to the element. -XMLCSTR XMLNode::addText_priv(int memoryIncrease, XMLSTR lpszValue, int pos) -{ - if (!lpszValue) return NULL; - if (!d) { myFree(lpszValue); return NULL; } - d->pText=(XMLCSTR*)addToOrder(memoryIncrease,&pos,d->nText,d->pText,sizeof(XMLSTR),eNodeText); - d->pText[pos]=lpszValue; - d->nText++; - return lpszValue; -} - -// Add clear (unformatted) text to the element. -XMLClear *XMLNode::addClear_priv(int memoryIncrease, XMLSTR lpszValue, XMLCSTR lpszOpen, XMLCSTR lpszClose, int pos) -{ - if (!lpszValue) return &emptyXMLClear; - if (!d) { myFree(lpszValue); return &emptyXMLClear; } - d->pClear=(XMLClear *)addToOrder(memoryIncrease,&pos,d->nClear,d->pClear,sizeof(XMLClear),eNodeClear); - XMLClear *pNewClear=d->pClear+pos; - pNewClear->lpszValue = lpszValue; - if (!lpszOpen) lpszOpen=XMLClearTags->lpszOpen; - if (!lpszClose) lpszClose=XMLClearTags->lpszClose; - pNewClear->lpszOpenTag = lpszOpen; - pNewClear->lpszCloseTag = lpszClose; - d->nClear++; - return pNewClear; -} - -// private: -// Parse a clear (unformatted) type node. -char XMLNode::parseClearTag(void *px, void *_pClear) -{ - XML *pXML=(XML *)px; - ALLXMLClearTag pClear=*((ALLXMLClearTag*)_pClear); - int cbTemp=0; - XMLCSTR lpszTemp=NULL; - XMLCSTR lpXML=&pXML->lpXML[pXML->nIndex]; - static XMLCSTR docTypeEnd=_CXML("]>"); - - // Find the closing tag - // Seems the <!DOCTYPE need a better treatment so lets handle it - if (pClear.lpszOpen==XMLClearTags[1].lpszOpen) - { - XMLCSTR pCh=lpXML; - while (*pCh) - { - if (*pCh==_CXML('<')) { pClear.lpszClose=docTypeEnd; lpszTemp=xstrstr(lpXML,docTypeEnd); break; } - else if (*pCh==_CXML('>')) { lpszTemp=pCh; break; } -#ifdef _XMLWIDECHAR - pCh++; -#else - pCh+=XML_ByteTable[(unsigned char)(*pCh)]; -#endif - } - } else lpszTemp=xstrstr(lpXML, pClear.lpszClose); - - if (lpszTemp) - { - // Cache the size and increment the index - cbTemp = (int)(lpszTemp - lpXML); - - pXML->nIndex += cbTemp+(int)xstrlen(pClear.lpszClose); - - // Add the clear node to the current element - addClear_priv(MEMORYINCREASE,stringDup(lpXML,cbTemp), pClear.lpszOpen, pClear.lpszClose,-1); - return 0; - } - - // If we failed to find the end tag - pXML->error = eXMLErrorUnmatchedEndClearTag; - return 1; -} - -void XMLNode::exactMemory(XMLNodeData *d) -{ - if (d->pOrder) d->pOrder=(int*)realloc(d->pOrder,(d->nChild+d->nText+d->nClear)*sizeof(int)); - if (d->pChild) d->pChild=(XMLNode*)realloc(d->pChild,d->nChild*sizeof(XMLNode)); - if (d->pAttribute) d->pAttribute=(XMLAttribute*)realloc(d->pAttribute,d->nAttribute*sizeof(XMLAttribute)); - if (d->pText) d->pText=(XMLCSTR*)realloc(d->pText,d->nText*sizeof(XMLSTR)); - if (d->pClear) d->pClear=(XMLClear *)realloc(d->pClear,d->nClear*sizeof(XMLClear)); -} - -char XMLNode::maybeAddTxT(void *pa, XMLCSTR tokenPStr) -{ - XML *pXML=(XML *)pa; - XMLCSTR lpszText=pXML->lpszText; - if (!lpszText) return 0; - if (dropWhiteSpace) while (XML_isSPACECHAR(*lpszText)&&(lpszText!=tokenPStr)) lpszText++; - int cbText = (int)(tokenPStr - lpszText); - if (!cbText) { pXML->lpszText=NULL; return 0; } - if (dropWhiteSpace) { cbText--; while ((cbText)&&XML_isSPACECHAR(lpszText[cbText])) cbText--; cbText++; } - if (!cbText) { pXML->lpszText=NULL; return 0; } - XMLSTR lpt=fromXMLString(lpszText,cbText,pXML); - if (!lpt) return 1; - pXML->lpszText=NULL; - if (removeCommentsInMiddleOfText && d->nText && d->nClear) - { - // if the previous insertion was a comment (<!-- -->) AND - // if the previous previous insertion was a text then, delete the comment and append the text - int n=d->nChild+d->nText+d->nClear-1,*o=d->pOrder; - if (((o[n]&3)==eNodeClear)&&((o[n-1]&3)==eNodeText)) - { - int i=o[n]>>2; - if (d->pClear[i].lpszOpenTag==XMLClearTags[2].lpszOpen) - { - deleteClear(i); - i=o[n-1]>>2; - n=xstrlen(d->pText[i]); - int n2=xstrlen(lpt)+1; - d->pText[i]=(XMLSTR)realloc((void*)d->pText[i],(n+n2)*sizeof(XMLCHAR)); - if (!d->pText[i]) return 1; - memcpy((void*)(d->pText[i]+n),lpt,n2*sizeof(XMLCHAR)); - free(lpt); - return 0; - } - } - } - addText_priv(MEMORYINCREASE,lpt,-1); - return 0; -} -// private: -// Recursively parse an XML element. -int XMLNode::ParseXMLElement(void *pa) -{ - XML *pXML=(XML *)pa; - int cbToken; - enum XMLTokenTypeTag xtype; - NextToken token; - XMLCSTR lpszTemp=NULL; - int cbTemp=0; - char nDeclaration; - XMLNode pNew; - enum Status status; // inside or outside a tag - enum Attrib attrib = eAttribName; - - assert(pXML); - - // If this is the first call to the function - if (pXML->nFirst) - { - // Assume we are outside of a tag definition - pXML->nFirst = FALSE; - status = eOutsideTag; - } else - { - // If this is not the first call then we should only be called when inside a tag. - status = eInsideTag; - } - - // Iterate through the tokens in the document - for(;;) - { - // Obtain the next token - token = GetNextToken(pXML, &cbToken, &xtype); - - if (xtype != eTokenError) - { - // Check the current status - switch(status) - { - - // If we are outside of a tag definition - case eOutsideTag: - - // Check what type of token we obtained - switch(xtype) - { - // If we have found text or quoted text - case eTokenText: - case eTokenCloseTag: /* '>' */ - case eTokenShortHandClose: /* '/>' */ - case eTokenQuotedText: - case eTokenEquals: - break; - - // If we found a start tag '<' and declarations '<?' - case eTokenTagStart: - case eTokenDeclaration: - - // Cache whether this new element is a declaration or not - nDeclaration = (xtype == eTokenDeclaration); - - // If we have node text then add this to the element - if (maybeAddTxT(pXML,token.pStr)) return FALSE; - - // Find the name of the tag - token = GetNextToken(pXML, &cbToken, &xtype); - - // Return an error if we couldn't obtain the next token or - // it wasnt text - if (xtype != eTokenText) - { - pXML->error = eXMLErrorMissingTagName; - return FALSE; - } - - // If we found a new element which is the same as this - // element then we need to pass this back to the caller.. - -#ifdef APPROXIMATE_PARSING - if (d->lpszName && - myTagCompare(d->lpszName, token.pStr) == 0) - { - // Indicate to the caller that it needs to create a - // new element. - pXML->lpNewElement = token.pStr; - pXML->cbNewElement = cbToken; - return TRUE; - } else -#endif - { - // If the name of the new element differs from the name of - // the current element we need to add the new element to - // the current one and recurse - pNew = addChild_priv(MEMORYINCREASE,stringDup(token.pStr,cbToken), nDeclaration,-1); - - while (!pNew.isEmpty()) - { - // Callself to process the new node. If we return - // FALSE this means we dont have any more - // processing to do... - - if (!pNew.ParseXMLElement(pXML)) return FALSE; - else - { - // If the call to recurse this function - // evented in a end tag specified in XML then - // we need to unwind the calls to this - // function until we find the appropriate node - // (the element name and end tag name must - // match) - if (pXML->cbEndTag) - { - // If we are back at the root node then we - // have an unmatched end tag - if (!d->lpszName) - { - pXML->error=eXMLErrorUnmatchedEndTag; - return FALSE; - } - - // If the end tag matches the name of this - // element then we only need to unwind - // once more... - - if (myTagCompare(d->lpszName, pXML->lpEndTag)==0) - { - pXML->cbEndTag = 0; - } - - return TRUE; - } else - if (pXML->cbNewElement) - { - // If the call indicated a new element is to - // be created on THIS element. - - // If the name of this element matches the - // name of the element we need to create - // then we need to return to the caller - // and let it process the element. - - if (myTagCompare(d->lpszName, pXML->lpNewElement)==0) - { - return TRUE; - } - - // Add the new element and recurse - pNew = addChild_priv(MEMORYINCREASE,stringDup(pXML->lpNewElement,pXML->cbNewElement),0,-1); - pXML->cbNewElement = 0; - } - else - { - // If we didn't have a new element to create - pNew = emptyXMLNode; - - } - } - } - } - break; - - // If we found an end tag - case eTokenTagEnd: - - // If we have node text then add this to the element - if (maybeAddTxT(pXML,token.pStr)) return FALSE; - - // Find the name of the end tag - token = GetNextToken(pXML, &cbTemp, &xtype); - - // The end tag should be text - if (xtype != eTokenText) - { - pXML->error = eXMLErrorMissingEndTagName; - return FALSE; - } - lpszTemp = token.pStr; - - // After the end tag we should find a closing tag - token = GetNextToken(pXML, &cbToken, &xtype); - if (xtype != eTokenCloseTag) - { - pXML->error = eXMLErrorMissingEndTagName; - return FALSE; - } - pXML->lpszText=pXML->lpXML+pXML->nIndex; - - // We need to return to the previous caller. If the name - // of the tag cannot be found we need to keep returning to - // caller until we find a match - if (myTagCompare(d->lpszName, lpszTemp) != 0) -#ifdef STRICT_PARSING - { - pXML->error=eXMLErrorUnmatchedEndTag; - pXML->nIndexMissigEndTag=pXML->nIndex; - return FALSE; - } -#else - { - pXML->error=eXMLErrorMissingEndTag; - pXML->nIndexMissigEndTag=pXML->nIndex; - pXML->lpEndTag = lpszTemp; - pXML->cbEndTag = cbTemp; - } -#endif - - // Return to the caller - exactMemory(d); - return TRUE; - - // If we found a clear (unformatted) token - case eTokenClear: - // If we have node text then add this to the element - if (maybeAddTxT(pXML,token.pStr)) return FALSE; - if (parseClearTag(pXML, token.pClr)) return FALSE; - pXML->lpszText=pXML->lpXML+pXML->nIndex; - break; - - default: - break; - } - break; - - // If we are inside a tag definition we need to search for attributes - case eInsideTag: - - // Check what part of the attribute (name, equals, value) we - // are looking for. - switch(attrib) - { - // If we are looking for a new attribute - case eAttribName: - - // Check what the current token type is - switch(xtype) - { - // If the current type is text... - // Eg. 'attribute' - case eTokenText: - // Cache the token then indicate that we are next to - // look for the equals - lpszTemp = token.pStr; - cbTemp = cbToken; - attrib = eAttribEquals; - break; - - // If we found a closing tag... - // Eg. '>' - case eTokenCloseTag: - // We are now outside the tag - status = eOutsideTag; - pXML->lpszText=pXML->lpXML+pXML->nIndex; - break; - - // If we found a short hand '/>' closing tag then we can - // return to the caller - case eTokenShortHandClose: - exactMemory(d); - pXML->lpszText=pXML->lpXML+pXML->nIndex; - return TRUE; - - // Errors... - case eTokenQuotedText: /* '"SomeText"' */ - case eTokenTagStart: /* '<' */ - case eTokenTagEnd: /* '</' */ - case eTokenEquals: /* '=' */ - case eTokenDeclaration: /* '<?' */ - case eTokenClear: - pXML->error = eXMLErrorUnexpectedToken; - return FALSE; - default: break; - } - break; - - // If we are looking for an equals - case eAttribEquals: - // Check what the current token type is - switch(xtype) - { - // If the current type is text... - // Eg. 'Attribute AnotherAttribute' - case eTokenText: - // Add the unvalued attribute to the list - addAttribute_priv(MEMORYINCREASE,stringDup(lpszTemp,cbTemp), NULL); - // Cache the token then indicate. We are next to - // look for the equals attribute - lpszTemp = token.pStr; - cbTemp = cbToken; - break; - - // If we found a closing tag 'Attribute >' or a short hand - // closing tag 'Attribute />' - case eTokenShortHandClose: - case eTokenCloseTag: - // If we are a declaration element '<?' then we need - // to remove extra closing '?' if it exists - pXML->lpszText=pXML->lpXML+pXML->nIndex; - - if (d->isDeclaration && - (lpszTemp[cbTemp-1]) == _CXML('?')) - { - cbTemp--; - if (d->pParent && d->pParent->pParent) xtype = eTokenShortHandClose; - } - - if (cbTemp) - { - // Add the unvalued attribute to the list - addAttribute_priv(MEMORYINCREASE,stringDup(lpszTemp,cbTemp), NULL); - } - - // If this is the end of the tag then return to the caller - if (xtype == eTokenShortHandClose) - { - exactMemory(d); - return TRUE; - } - - // We are now outside the tag - status = eOutsideTag; - break; - - // If we found the equals token... - // Eg. 'Attribute =' - case eTokenEquals: - // Indicate that we next need to search for the value - // for the attribute - attrib = eAttribValue; - break; - - // Errors... - case eTokenQuotedText: /* 'Attribute "InvalidAttr"'*/ - case eTokenTagStart: /* 'Attribute <' */ - case eTokenTagEnd: /* 'Attribute </' */ - case eTokenDeclaration: /* 'Attribute <?' */ - case eTokenClear: - pXML->error = eXMLErrorUnexpectedToken; - return FALSE; - default: break; - } - break; - - // If we are looking for an attribute value - case eAttribValue: - // Check what the current token type is - switch(xtype) - { - // If the current type is text or quoted text... - // Eg. 'Attribute = "Value"' or 'Attribute = Value' or - // 'Attribute = 'Value''. - case eTokenText: - case eTokenQuotedText: - // If we are a declaration element '<?' then we need - // to remove extra closing '?' if it exists - if (d->isDeclaration && - (token.pStr[cbToken-1]) == _CXML('?')) - { - cbToken--; - } - - if (cbTemp) - { - // Add the valued attribute to the list - if (xtype==eTokenQuotedText) { token.pStr++; cbToken-=2; } - XMLSTR attrVal=(XMLSTR)token.pStr; - if (attrVal) - { - attrVal=fromXMLString(attrVal,cbToken,pXML); - if (!attrVal) return FALSE; - } - addAttribute_priv(MEMORYINCREASE,stringDup(lpszTemp,cbTemp),attrVal); - } - - // Indicate we are searching for a new attribute - attrib = eAttribName; - break; - - // Errors... - case eTokenTagStart: /* 'Attr = <' */ - case eTokenTagEnd: /* 'Attr = </' */ - case eTokenCloseTag: /* 'Attr = >' */ - case eTokenShortHandClose: /* "Attr = />" */ - case eTokenEquals: /* 'Attr = =' */ - case eTokenDeclaration: /* 'Attr = <?' */ - case eTokenClear: - pXML->error = eXMLErrorUnexpectedToken; - return FALSE; - break; - default: break; - } - } - } - } - // If we failed to obtain the next token - else - { - if ((!d->isDeclaration)&&(d->pParent)) - { -#ifdef STRICT_PARSING - pXML->error=eXMLErrorUnmatchedEndTag; -#else - pXML->error=eXMLErrorMissingEndTag; -#endif - pXML->nIndexMissigEndTag=pXML->nIndex; - } - maybeAddTxT(pXML,pXML->lpXML+pXML->nIndex); - return FALSE; - } - } -} - -// Count the number of lines and columns in an XML string. -static void CountLinesAndColumns(XMLCSTR lpXML, int nUpto, XMLResults *pResults) -{ - XMLCHAR ch; - assert(lpXML); - assert(pResults); - - struct XML xml={ lpXML,lpXML, 0, 0, eXMLErrorNone, NULL, 0, NULL, 0, TRUE }; - - pResults->nLine = 1; - pResults->nColumn = 1; - while (xml.nIndex<nUpto) - { - ch = getNextChar(&xml); - if (ch != _CXML('\n')) pResults->nColumn++; - else - { - pResults->nLine++; - pResults->nColumn=1; - } - } -} - -// Parse XML and return the root element. -XMLNode XMLNode::parseString(XMLCSTR lpszXML, XMLCSTR tag, XMLResults *pResults) -{ - if (!lpszXML) - { - if (pResults) - { - pResults->error=eXMLErrorNoElements; - pResults->nLine=0; - pResults->nColumn=0; - } - return emptyXMLNode; - } - - XMLNode xnode(NULL,NULL,FALSE); - struct XML xml={ lpszXML, lpszXML, 0, 0, eXMLErrorNone, NULL, 0, NULL, 0, TRUE }; - - // Create header element - xnode.ParseXMLElement(&xml); - enum XMLError error = xml.error; - if (!xnode.nChildNode()) error=eXMLErrorNoXMLTagFound; - if ((xnode.nChildNode()==1)&&(xnode.nElement()==1)) xnode=xnode.getChildNode(); // skip the empty node - - // If no error occurred - if ((error==eXMLErrorNone)||(error==eXMLErrorMissingEndTag)||(error==eXMLErrorNoXMLTagFound)) - { - XMLCSTR name=xnode.getName(); - if (tag&&(*tag)&&((!name)||(xstricmp(name,tag)))) - { - xnode=xnode.getChildNode(tag); - if (xnode.isEmpty()) - { - if (pResults) - { - pResults->error=eXMLErrorFirstTagNotFound; - pResults->nLine=0; - pResults->nColumn=0; - } - return emptyXMLNode; - } - } - } else - { - // Cleanup: this will destroy all the nodes - xnode = emptyXMLNode; - } - - - // If we have been given somewhere to place results - if (pResults) - { - pResults->error = error; - - // If we have an error - if (error!=eXMLErrorNone) - { - if (error==eXMLErrorMissingEndTag) xml.nIndex=xml.nIndexMissigEndTag; - // Find which line and column it starts on. - CountLinesAndColumns(xml.lpXML, xml.nIndex, pResults); - } - } - return xnode; -} - -XMLNode XMLNode::parseFile(XMLCSTR filename, XMLCSTR tag, XMLResults *pResults) -{ - if (pResults) { pResults->nLine=0; pResults->nColumn=0; } - FILE *f=xfopen(filename,_CXML("rb")); - if (f==NULL) { if (pResults) pResults->error=eXMLErrorFileNotFound; return emptyXMLNode; } - fseek(f,0,SEEK_END); - int l=(int)ftell(f),headerSz=0; - if (!l) { if (pResults) pResults->error=eXMLErrorEmpty; fclose(f); return emptyXMLNode; } - fseek(f,0,SEEK_SET); - unsigned char *buf=(unsigned char*)malloc(l+4); - l=(int)fread(buf,1,l,f); - fclose(f); - buf[l]=0;buf[l+1]=0;buf[l+2]=0;buf[l+3]=0; -#ifdef _XMLWIDECHAR - if (guessWideCharChars) - { - if (!myIsTextWideChar(buf,l)) - { - XMLNode::XMLCharEncoding ce=XMLNode::char_encoding_legacy; - if ((buf[0]==0xef)&&(buf[1]==0xbb)&&(buf[2]==0xbf)) { headerSz=3; ce=XMLNode::char_encoding_UTF8; } - XMLSTR b2=myMultiByteToWideChar((const char*)(buf+headerSz),ce); - free(buf); buf=(unsigned char*)b2; headerSz=0; - } else - { - if ((buf[0]==0xef)&&(buf[1]==0xff)) headerSz=2; - if ((buf[0]==0xff)&&(buf[1]==0xfe)) headerSz=2; - } - } -#else - if (guessWideCharChars) - { - if (myIsTextWideChar(buf,l)) - { - if ((buf[0]==0xef)&&(buf[1]==0xff)) headerSz=2; - if ((buf[0]==0xff)&&(buf[1]==0xfe)) headerSz=2; - char *b2=myWideCharToMultiByte((const wchar_t*)(buf+headerSz)); - free(buf); buf=(unsigned char*)b2; headerSz=0; - } else - { - if ((buf[0]==0xef)&&(buf[1]==0xbb)&&(buf[2]==0xbf)) headerSz=3; - } - } -#endif - - if (!buf) { if (pResults) pResults->error=eXMLErrorCharConversionError; return emptyXMLNode; } - XMLNode x=parseString((XMLSTR)(buf+headerSz),tag,pResults); - free(buf); - return x; -} - -static inline void charmemset(XMLSTR dest,XMLCHAR c,int l) { while (l--) *(dest++)=c; } -// private: -// Creates an user friendly XML string from a given element with -// appropriate white space and carriage returns. -// -// This recurses through all subnodes then adds contents of the nodes to the -// string. -int XMLNode::CreateXMLStringR(XMLNodeData *pEntry, XMLSTR lpszMarker, int nFormat) -{ - int nResult = 0; - int cb=nFormat<0?0:nFormat; - int cbElement; - int nChildFormat=-1; - int nElementI=pEntry->nChild+pEntry->nText+pEntry->nClear; - int i,j; - if ((nFormat>=0)&&(nElementI==1)&&(pEntry->nText==1)&&(!pEntry->isDeclaration)) nFormat=-2; - - assert(pEntry); - -#define LENSTR(lpsz) (lpsz ? xstrlen(lpsz) : 0) - - // If the element has no name then assume this is the head node. - cbElement = (int)LENSTR(pEntry->lpszName); - - if (cbElement) - { - // "<elementname " - if (lpszMarker) - { - if (cb) charmemset(lpszMarker, INDENTCHAR, cb); - nResult = cb; - lpszMarker[nResult++]=_CXML('<'); - if (pEntry->isDeclaration) lpszMarker[nResult++]=_CXML('?'); - xstrcpy(&lpszMarker[nResult], pEntry->lpszName); - nResult+=cbElement; - lpszMarker[nResult++]=_CXML(' '); - - } else - { - nResult+=cbElement+2+cb; - if (pEntry->isDeclaration) nResult++; - } - - // Enumerate attributes and add them to the string - XMLAttribute *pAttr=pEntry->pAttribute; - for (i=0; i<pEntry->nAttribute; i++) - { - // "Attrib - cb = (int)LENSTR(pAttr->lpszName); - if (cb) - { - if (lpszMarker) xstrcpy(&lpszMarker[nResult], pAttr->lpszName); - nResult += cb; - // "Attrib=Value " - if (pAttr->lpszValue) - { - cb=(int)ToXMLStringTool::lengthXMLString(pAttr->lpszValue); - if (lpszMarker) - { - lpszMarker[nResult]=_CXML('='); - lpszMarker[nResult+1]=_CXML('"'); - if (cb) ToXMLStringTool::toXMLUnSafe(&lpszMarker[nResult+2],pAttr->lpszValue); - lpszMarker[nResult+cb+2]=_CXML('"'); - } - nResult+=cb+3; - } - if (lpszMarker) lpszMarker[nResult] = _CXML(' '); - nResult++; - } - pAttr++; - } - - if (pEntry->isDeclaration) - { - if (lpszMarker) - { - lpszMarker[nResult-1]=_CXML('?'); - lpszMarker[nResult]=_CXML('>'); - } - nResult++; - if (nFormat!=-1) - { - if (lpszMarker) lpszMarker[nResult]=_CXML('\n'); - nResult++; - } - } else - // If there are child nodes we need to terminate the start tag - if (nElementI) - { - if (lpszMarker) lpszMarker[nResult-1]=_CXML('>'); - if (nFormat>=0) - { - if (lpszMarker) lpszMarker[nResult]=_CXML('\n'); - nResult++; - } - } else nResult--; - } - - // Calculate the child format for when we recurse. This is used to - // determine the number of spaces used for prefixes. - if (nFormat!=-1) - { - if (cbElement&&(!pEntry->isDeclaration)) nChildFormat=nFormat+1; - else nChildFormat=nFormat; - } - - // Enumerate through remaining children - for (i=0; i<nElementI; i++) - { - j=pEntry->pOrder[i]; - switch((XMLElementType)(j&3)) - { - // Text nodes - case eNodeText: - { - // "Text" - XMLCSTR pChild=pEntry->pText[j>>2]; - cb = (int)ToXMLStringTool::lengthXMLString(pChild); - if (cb) - { - if (nFormat>=0) - { - if (lpszMarker) - { - charmemset(&lpszMarker[nResult],INDENTCHAR,nFormat+1); - ToXMLStringTool::toXMLUnSafe(&lpszMarker[nResult+nFormat+1],pChild); - lpszMarker[nResult+nFormat+1+cb]=_CXML('\n'); - } - nResult+=cb+nFormat+2; - } else - { - if (lpszMarker) ToXMLStringTool::toXMLUnSafe(&lpszMarker[nResult], pChild); - nResult += cb; - } - } - break; - } - - // Clear type nodes - case eNodeClear: - { - XMLClear *pChild=pEntry->pClear+(j>>2); - // "OpenTag" - cb = (int)LENSTR(pChild->lpszOpenTag); - if (cb) - { - if (nFormat!=-1) - { - if (lpszMarker) - { - charmemset(&lpszMarker[nResult], INDENTCHAR, nFormat+1); - xstrcpy(&lpszMarker[nResult+nFormat+1], pChild->lpszOpenTag); - } - nResult+=cb+nFormat+1; - } - else - { - if (lpszMarker)xstrcpy(&lpszMarker[nResult], pChild->lpszOpenTag); - nResult += cb; - } - } - - // "OpenTag Value" - cb = (int)LENSTR(pChild->lpszValue); - if (cb) - { - if (lpszMarker) xstrcpy(&lpszMarker[nResult], pChild->lpszValue); - nResult += cb; - } - - // "OpenTag Value CloseTag" - cb = (int)LENSTR(pChild->lpszCloseTag); - if (cb) - { - if (lpszMarker) xstrcpy(&lpszMarker[nResult], pChild->lpszCloseTag); - nResult += cb; - } - - if (nFormat!=-1) - { - if (lpszMarker) lpszMarker[nResult] = _CXML('\n'); - nResult++; - } - break; - } - - // Element nodes - case eNodeChild: - { - // Recursively add child nodes - nResult += CreateXMLStringR(pEntry->pChild[j>>2].d, lpszMarker ? lpszMarker + nResult : 0, nChildFormat); - break; - } - default: break; - } - } - - if ((cbElement)&&(!pEntry->isDeclaration)) - { - // If we have child entries we need to use long XML notation for - // closing the element - "<elementname>blah blah blah</elementname>" - if (nElementI) - { - // "</elementname>\0" - if (lpszMarker) - { - if (nFormat >=0) - { - charmemset(&lpszMarker[nResult], INDENTCHAR,nFormat); - nResult+=nFormat; - } - - lpszMarker[nResult]=_CXML('<'); lpszMarker[nResult+1]=_CXML('/'); - nResult += 2; - xstrcpy(&lpszMarker[nResult], pEntry->lpszName); - nResult += cbElement; - - lpszMarker[nResult]=_CXML('>'); - if (nFormat == -1) nResult++; - else - { - lpszMarker[nResult+1]=_CXML('\n'); - nResult+=2; - } - } else - { - if (nFormat>=0) nResult+=cbElement+4+nFormat; - else if (nFormat==-1) nResult+=cbElement+3; - else nResult+=cbElement+4; - } - } else - { - // If there are no children we can use shorthand XML notation - - // "<elementname/>" - // "/>\0" - if (lpszMarker) - { - lpszMarker[nResult]=_CXML('/'); lpszMarker[nResult+1]=_CXML('>'); - if (nFormat != -1) lpszMarker[nResult+2]=_CXML('\n'); - } - nResult += nFormat == -1 ? 2 : 3; - } - } - - return nResult; -} - -#undef LENSTR - -// Create an XML string -// @param int nFormat - 0 if no formatting is required -// otherwise nonzero for formatted text -// with carriage returns and indentation. -// @param int *pnSize - [out] pointer to the size of the -// returned string not including the -// NULL terminator. -// @return XMLSTR - Allocated XML string, you must free -// this with free(). -XMLSTR XMLNode::createXMLString(int nFormat, int *pnSize) const -{ - if (!d) { if (pnSize) *pnSize=0; return NULL; } - - XMLSTR lpszResult = NULL; - int cbStr; - - // Recursively Calculate the size of the XML string - if (!dropWhiteSpace) nFormat=0; - nFormat = nFormat ? 0 : -1; - cbStr = CreateXMLStringR(d, 0, nFormat); - // Alllocate memory for the XML string + the NULL terminator and - // create the recursively XML string. - lpszResult=(XMLSTR)malloc((cbStr+1)*sizeof(XMLCHAR)); - CreateXMLStringR(d, lpszResult, nFormat); - lpszResult[cbStr]=_CXML('\0'); - if (pnSize) *pnSize = cbStr; - return lpszResult; -} - -int XMLNode::detachFromParent(XMLNodeData *d) -{ - XMLNode *pa=d->pParent->pChild; - int i=0; - while (((void*)(pa[i].d))!=((void*)d)) i++; - d->pParent->nChild--; - if (d->pParent->nChild) memmove(pa+i,pa+i+1,(d->pParent->nChild-i)*sizeof(XMLNode)); - else { free(pa); d->pParent->pChild=NULL; } - return removeOrderElement(d->pParent,eNodeChild,i); -} - -XMLNode::~XMLNode() -{ - if (!d) return; - d->ref_count--; - emptyTheNode(0); -} -void XMLNode::deleteNodeContent() -{ - if (!d) return; - if (d->pParent) { detachFromParent(d); d->pParent=NULL; d->ref_count--; } - emptyTheNode(1); -} -void XMLNode::emptyTheNode(char force) -{ - XMLNodeData *dd=d; // warning: must stay this way! - if ((dd->ref_count==0)||force) - { - if (d->pParent) detachFromParent(d); - int i; - XMLNode *pc; - for(i=0; i<dd->nChild; i++) - { - pc=dd->pChild+i; - pc->d->pParent=NULL; - pc->d->ref_count--; - pc->emptyTheNode(force); - } - myFree(dd->pChild); - for(i=0; i<dd->nText; i++) free((void*)dd->pText[i]); - myFree(dd->pText); - for(i=0; i<dd->nClear; i++) free((void*)dd->pClear[i].lpszValue); - myFree(dd->pClear); - for(i=0; i<dd->nAttribute; i++) - { - free((void*)dd->pAttribute[i].lpszName); - if (dd->pAttribute[i].lpszValue) free((void*)dd->pAttribute[i].lpszValue); - } - myFree(dd->pAttribute); - myFree(dd->pOrder); - myFree((void*)dd->lpszName); - dd->nChild=0; dd->nText=0; dd->nClear=0; dd->nAttribute=0; - dd->pChild=NULL; dd->pText=NULL; dd->pClear=NULL; dd->pAttribute=NULL; - dd->pOrder=NULL; dd->lpszName=NULL; dd->pParent=NULL; - } - if (dd->ref_count==0) - { - free(dd); - d=NULL; - } -} - -XMLNode& XMLNode::operator=( const XMLNode& A ) -{ - // shallow copy - if (this != &A) - { - if (d) { d->ref_count--; emptyTheNode(0); } - d=A.d; - if (d) (d->ref_count) ++ ; - } - return *this; -} - -XMLNode::XMLNode(const XMLNode &A) -{ - // shallow copy - d=A.d; - if (d) (d->ref_count)++ ; -} - -XMLNode XMLNode::deepCopy() const -{ - if (!d) return XMLNode::emptyXMLNode; - XMLNode x(NULL,stringDup(d->lpszName),d->isDeclaration); - XMLNodeData *p=x.d; - int n=d->nAttribute; - if (n) - { - p->nAttribute=n; p->pAttribute=(XMLAttribute*)malloc(n*sizeof(XMLAttribute)); - while (n--) - { - p->pAttribute[n].lpszName=stringDup(d->pAttribute[n].lpszName); - p->pAttribute[n].lpszValue=stringDup(d->pAttribute[n].lpszValue); - } - } - if (d->pOrder) - { - n=(d->nChild+d->nText+d->nClear)*sizeof(int); p->pOrder=(int*)malloc(n); memcpy(p->pOrder,d->pOrder,n); - } - n=d->nText; - if (n) - { - p->nText=n; p->pText=(XMLCSTR*)malloc(n*sizeof(XMLCSTR)); - while(n--) p->pText[n]=stringDup(d->pText[n]); - } - n=d->nClear; - if (n) - { - p->nClear=n; p->pClear=(XMLClear*)malloc(n*sizeof(XMLClear)); - while (n--) - { - p->pClear[n].lpszCloseTag=d->pClear[n].lpszCloseTag; - p->pClear[n].lpszOpenTag=d->pClear[n].lpszOpenTag; - p->pClear[n].lpszValue=stringDup(d->pClear[n].lpszValue); - } - } - n=d->nChild; - if (n) - { - p->nChild=n; p->pChild=(XMLNode*)malloc(n*sizeof(XMLNode)); - while (n--) - { - p->pChild[n].d=NULL; - p->pChild[n]=d->pChild[n].deepCopy(); - p->pChild[n].d->pParent=p; - } - } - return x; -} - -XMLNode XMLNode::addChild(XMLNode childNode, int pos) -{ - XMLNodeData *dc=childNode.d; - if ((!dc)||(!d)) return childNode; - if (!dc->lpszName) - { - // this is a root node: todo: correct fix - int j=pos; - while (dc->nChild) - { - addChild(dc->pChild[0],j); - if (pos>=0) j++; - } - return childNode; - } - if (dc->pParent) { if ((detachFromParent(dc)<=pos)&&(dc->pParent==d)) pos--; } else dc->ref_count++; - dc->pParent=d; -// int nc=d->nChild; -// d->pChild=(XMLNode*)myRealloc(d->pChild,(nc+1),memoryIncrease,sizeof(XMLNode)); - d->pChild=(XMLNode*)addToOrder(0,&pos,d->nChild,d->pChild,sizeof(XMLNode),eNodeChild); - d->pChild[pos].d=dc; - d->nChild++; - return childNode; -} - -void XMLNode::deleteAttribute(int i) -{ - if ((!d)||(i<0)||(i>=d->nAttribute)) return; - d->nAttribute--; - XMLAttribute *p=d->pAttribute+i; - free((void*)p->lpszName); - if (p->lpszValue) free((void*)p->lpszValue); - if (d->nAttribute) memmove(p,p+1,(d->nAttribute-i)*sizeof(XMLAttribute)); else { free(p); d->pAttribute=NULL; } -} - -void XMLNode::deleteAttribute(XMLAttribute *a){ if (a) deleteAttribute(a->lpszName); } -void XMLNode::deleteAttribute(XMLCSTR lpszName) -{ - int j=0; - getAttribute(lpszName,&j); - if (j) deleteAttribute(j-1); -} - -XMLAttribute *XMLNode::updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName,int i) -{ - if (!d) { if (lpszNewValue) free(lpszNewValue); if (lpszNewName) free(lpszNewName); return NULL; } - if (i>=d->nAttribute) - { - if (lpszNewName) return addAttribute_WOSD(lpszNewName,lpszNewValue); - return NULL; - } - XMLAttribute *p=d->pAttribute+i; - if (p->lpszValue&&p->lpszValue!=lpszNewValue) free((void*)p->lpszValue); - p->lpszValue=lpszNewValue; - if (lpszNewName&&p->lpszName!=lpszNewName) { free((void*)p->lpszName); p->lpszName=lpszNewName; }; - return p; -} - -XMLAttribute *XMLNode::updateAttribute_WOSD(XMLAttribute *newAttribute, XMLAttribute *oldAttribute) -{ - if (oldAttribute) return updateAttribute_WOSD((XMLSTR)newAttribute->lpszValue,(XMLSTR)newAttribute->lpszName,oldAttribute->lpszName); - return addAttribute_WOSD((XMLSTR)newAttribute->lpszName,(XMLSTR)newAttribute->lpszValue); -} - -XMLAttribute *XMLNode::updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName,XMLCSTR lpszOldName) -{ - int j=0; - getAttribute(lpszOldName,&j); - if (j) return updateAttribute_WOSD(lpszNewValue,lpszNewName,j-1); - else - { - if (lpszNewName) return addAttribute_WOSD(lpszNewName,lpszNewValue); - else return addAttribute_WOSD(stringDup(lpszOldName),lpszNewValue); - } -} - -int XMLNode::indexText(XMLCSTR lpszValue) const -{ - if (!d) return -1; - int i,l=d->nText; - if (!lpszValue) { if (l) return 0; return -1; } - XMLCSTR *p=d->pText; - for (i=0; i<l; i++) if (lpszValue==p[i]) return i; - return -1; -} - -void XMLNode::deleteText(int i) -{ - if ((!d)||(i<0)||(i>=d->nText)) return; - d->nText--; - XMLCSTR *p=d->pText+i; - free((void*)*p); - if (d->nText) memmove(p,p+1,(d->nText-i)*sizeof(XMLCSTR)); else { free(p); d->pText=NULL; } - removeOrderElement(d,eNodeText,i); -} - -void XMLNode::deleteText(XMLCSTR lpszValue) { deleteText(indexText(lpszValue)); } - -XMLCSTR XMLNode::updateText_WOSD(XMLSTR lpszNewValue, int i) -{ - if (!d) { if (lpszNewValue) free(lpszNewValue); return NULL; } - if (i>=d->nText) return addText_WOSD(lpszNewValue); - XMLCSTR *p=d->pText+i; - if (*p!=lpszNewValue) { free((void*)*p); *p=lpszNewValue; } - return lpszNewValue; -} - -XMLCSTR XMLNode::updateText_WOSD(XMLSTR lpszNewValue, XMLCSTR lpszOldValue) -{ - if (!d) { if (lpszNewValue) free(lpszNewValue); return NULL; } - int i=indexText(lpszOldValue); - if (i>=0) return updateText_WOSD(lpszNewValue,i); - return addText_WOSD(lpszNewValue); -} - -void XMLNode::deleteClear(int i) -{ - if ((!d)||(i<0)||(i>=d->nClear)) return; - d->nClear--; - XMLClear *p=d->pClear+i; - free((void*)p->lpszValue); - if (d->nClear) memmove(p,p+1,(d->nClear-i)*sizeof(XMLClear)); else { free(p); d->pClear=NULL; } - removeOrderElement(d,eNodeClear,i); -} - -int XMLNode::indexClear(XMLCSTR lpszValue) const -{ - if (!d) return -1; - int i,l=d->nClear; - if (!lpszValue) { if (l) return 0; return -1; } - XMLClear *p=d->pClear; - for (i=0; i<l; i++) if (lpszValue==p[i].lpszValue) return i; - return -1; -} - -void XMLNode::deleteClear(XMLCSTR lpszValue) { deleteClear(indexClear(lpszValue)); } -void XMLNode::deleteClear(XMLClear *a) { if (a) deleteClear(a->lpszValue); } - -XMLClear *XMLNode::updateClear_WOSD(XMLSTR lpszNewContent, int i) -{ - if (!d) { if (lpszNewContent) free(lpszNewContent); return NULL; } - if (i>=d->nClear) return addClear_WOSD(lpszNewContent); - XMLClear *p=d->pClear+i; - if (lpszNewContent!=p->lpszValue) { free((void*)p->lpszValue); p->lpszValue=lpszNewContent; } - return p; -} - -XMLClear *XMLNode::updateClear_WOSD(XMLSTR lpszNewContent, XMLCSTR lpszOldValue) -{ - if (!d) { if (lpszNewContent) free(lpszNewContent); return NULL; } - int i=indexClear(lpszOldValue); - if (i>=0) return updateClear_WOSD(lpszNewContent,i); - return addClear_WOSD(lpszNewContent); -} - -XMLClear *XMLNode::updateClear_WOSD(XMLClear *newP,XMLClear *oldP) -{ - if (oldP) return updateClear_WOSD((XMLSTR)newP->lpszValue,(XMLSTR)oldP->lpszValue); - return NULL; -} - -int XMLNode::nChildNode(XMLCSTR name) const -{ - if (!d) return 0; - int i,j=0,n=d->nChild; - XMLNode *pc=d->pChild; - for (i=0; i<n; i++) - { - if (xstricmp(pc->d->lpszName, name)==0) j++; - pc++; - } - return j; -} - -XMLNode XMLNode::getChildNode(XMLCSTR name, int *j) const -{ - if (!d) return emptyXMLNode; - int i=0,n=d->nChild; - if (j) i=*j; - XMLNode *pc=d->pChild+i; - for (; i<n; i++) - { - if (!xstricmp(pc->d->lpszName, name)) - { - if (j) *j=i+1; - return *pc; - } - pc++; - } - return emptyXMLNode; -} - -XMLNode XMLNode::getChildNode(XMLCSTR name, int j) const -{ - if (!d) return emptyXMLNode; - if (j>=0) - { - int i=0; - while (j-->0) getChildNode(name,&i); - return getChildNode(name,&i); - } - int i=d->nChild; - while (i--) if (!xstricmp(name,d->pChild[i].d->lpszName)) break; - if (i<0) return emptyXMLNode; - return getChildNode(i); -} - -XMLNode XMLNode::getChildNodeByPath(XMLCSTR _path, char createMissing, XMLCHAR sep) -{ - XMLSTR path=stringDup(_path); - XMLNode x=getChildNodeByPathNonConst(path,createMissing,sep); - if (path) free(path); - return x; -} - -XMLNode XMLNode::getChildNodeByPathNonConst(XMLSTR path, char createIfMissing, XMLCHAR sep) -{ - if ((!path)||(!(*path))) return *this; - XMLNode xn,xbase=*this; - XMLCHAR *tend1,sepString[2]; sepString[0]=sep; sepString[1]=0; - tend1=xstrstr(path,sepString); - while(tend1) - { - *tend1=0; - xn=xbase.getChildNode(path); - if (xn.isEmpty()) - { - if (createIfMissing) xn=xbase.addChild(path); - else { *tend1=sep; return XMLNode::emptyXMLNode; } - } - *tend1=sep; - xbase=xn; - path=tend1+1; - tend1=xstrstr(path,sepString); - } - xn=xbase.getChildNode(path); - if (xn.isEmpty()&&createIfMissing) xn=xbase.addChild(path); - return xn; -} - -XMLElementPosition XMLNode::positionOfText (int i) const { if (i>=d->nText ) i=d->nText-1; return findPosition(d,i,eNodeText ); } -XMLElementPosition XMLNode::positionOfClear (int i) const { if (i>=d->nClear) i=d->nClear-1; return findPosition(d,i,eNodeClear); } -XMLElementPosition XMLNode::positionOfChildNode(int i) const { if (i>=d->nChild) i=d->nChild-1; return findPosition(d,i,eNodeChild); } -XMLElementPosition XMLNode::positionOfText (XMLCSTR lpszValue) const { return positionOfText (indexText (lpszValue)); } -XMLElementPosition XMLNode::positionOfClear(XMLCSTR lpszValue) const { return positionOfClear(indexClear(lpszValue)); } -XMLElementPosition XMLNode::positionOfClear(XMLClear *a) const { if (a) return positionOfClear(a->lpszValue); return positionOfClear(); } -XMLElementPosition XMLNode::positionOfChildNode(XMLNode x) const -{ - if ((!d)||(!x.d)) return -1; - XMLNodeData *dd=x.d; - XMLNode *pc=d->pChild; - int i=d->nChild; - while (i--) if (pc[i].d==dd) return findPosition(d,i,eNodeChild); - return -1; -} -XMLElementPosition XMLNode::positionOfChildNode(XMLCSTR name, int count) const -{ - if (!name) return positionOfChildNode(count); - int j=0; - do { getChildNode(name,&j); if (j<0) return -1; } while (count--); - return findPosition(d,j-1,eNodeChild); -} - -XMLNode XMLNode::getChildNodeWithAttribute(XMLCSTR name,XMLCSTR attributeName,XMLCSTR attributeValue, int *k) const -{ - int i=0,j; - if (k) i=*k; - XMLNode x; - XMLCSTR t; - do - { - x=getChildNode(name,&i); - if (!x.isEmpty()) - { - if (attributeValue) - { - j=0; - do - { - t=x.getAttribute(attributeName,&j); - if (t&&(xstricmp(attributeValue,t)==0)) { if (k) *k=i; return x; } - } while (t); - } else - { - if (x.isAttributeSet(attributeName)) { if (k) *k=i; return x; } - } - } - } while (!x.isEmpty()); - return emptyXMLNode; -} - -// Find an attribute on an node. -XMLCSTR XMLNode::getAttribute(XMLCSTR lpszAttrib, int *j) const -{ - if (!d) return NULL; - int i=0,n=d->nAttribute; - if (j) i=*j; - XMLAttribute *pAttr=d->pAttribute+i; - for (; i<n; i++) - { - if (xstricmp(pAttr->lpszName, lpszAttrib)==0) - { - if (j) *j=i+1; - return pAttr->lpszValue; - } - pAttr++; - } - return NULL; -} - -char XMLNode::isAttributeSet(XMLCSTR lpszAttrib) const -{ - if (!d) return FALSE; - int i,n=d->nAttribute; - XMLAttribute *pAttr=d->pAttribute; - for (i=0; i<n; i++) - { - if (xstricmp(pAttr->lpszName, lpszAttrib)==0) - { - return TRUE; - } - pAttr++; - } - return FALSE; -} - -XMLCSTR XMLNode::getAttribute(XMLCSTR name, int j) const -{ - if (!d) return NULL; - int i=0; - while (j-->0) getAttribute(name,&i); - return getAttribute(name,&i); -} - -XMLNodeContents XMLNode::enumContents(int i) const -{ - XMLNodeContents c; - if (!d) { c.etype=eNodeNULL; return c; } - if (i<d->nAttribute) - { - c.etype=eNodeAttribute; - c.attrib=d->pAttribute[i]; - return c; - } - i-=d->nAttribute; - c.etype=(XMLElementType)(d->pOrder[i]&3); - i=(d->pOrder[i])>>2; - switch (c.etype) - { - case eNodeChild: c.child = d->pChild[i]; break; - case eNodeText: c.text = d->pText[i]; break; - case eNodeClear: c.clear = d->pClear[i]; break; - default: break; - } - return c; -} - -XMLCSTR XMLNode::getName() const { if (!d) return NULL; return d->lpszName; } -int XMLNode::nText() const { if (!d) return 0; return d->nText; } -int XMLNode::nChildNode() const { if (!d) return 0; return d->nChild; } -int XMLNode::nAttribute() const { if (!d) return 0; return d->nAttribute; } -int XMLNode::nClear() const { if (!d) return 0; return d->nClear; } -int XMLNode::nElement() const { if (!d) return 0; return d->nAttribute+d->nChild+d->nText+d->nClear; } -XMLClear XMLNode::getClear (int i) const { if ((!d)||(i>=d->nClear )) return emptyXMLClear; return d->pClear[i]; } -XMLAttribute XMLNode::getAttribute (int i) const { if ((!d)||(i>=d->nAttribute)) return emptyXMLAttribute; return d->pAttribute[i]; } -XMLCSTR XMLNode::getAttributeName (int i) const { if ((!d)||(i>=d->nAttribute)) return NULL; return d->pAttribute[i].lpszName; } -XMLCSTR XMLNode::getAttributeValue(int i) const { if ((!d)||(i>=d->nAttribute)) return NULL; return d->pAttribute[i].lpszValue; } -XMLCSTR XMLNode::getText (int i) const { if ((!d)||(i>=d->nText )) return NULL; return d->pText[i]; } -XMLNode XMLNode::getChildNode (int i) const { if ((!d)||(i>=d->nChild )) return emptyXMLNode; return d->pChild[i]; } -XMLNode XMLNode::getParentNode ( ) const { if ((!d)||(!d->pParent )) return emptyXMLNode; return XMLNode(d->pParent); } -char XMLNode::isDeclaration ( ) const { if (!d) return 0; return d->isDeclaration; } -char XMLNode::isEmpty ( ) const { return (d==NULL); } -XMLNode XMLNode::emptyNode ( ) { return XMLNode::emptyXMLNode; } - -XMLNode XMLNode::addChild(XMLCSTR lpszName, char isDeclaration, XMLElementPosition pos) - { return addChild_priv(0,stringDup(lpszName),isDeclaration,pos); } -XMLNode XMLNode::addChild_WOSD(XMLSTR lpszName, char isDeclaration, XMLElementPosition pos) - { return addChild_priv(0,lpszName,isDeclaration,pos); } -XMLAttribute *XMLNode::addAttribute(XMLCSTR lpszName, XMLCSTR lpszValue) - { return addAttribute_priv(0,stringDup(lpszName),stringDup(lpszValue)); } -XMLAttribute *XMLNode::addAttribute_WOSD(XMLSTR lpszName, XMLSTR lpszValuev) - { return addAttribute_priv(0,lpszName,lpszValuev); } -XMLCSTR XMLNode::addText(XMLCSTR lpszValue, XMLElementPosition pos) - { return addText_priv(0,stringDup(lpszValue),pos); } -XMLCSTR XMLNode::addText_WOSD(XMLSTR lpszValue, XMLElementPosition pos) - { return addText_priv(0,lpszValue,pos); } -XMLClear *XMLNode::addClear(XMLCSTR lpszValue, XMLCSTR lpszOpen, XMLCSTR lpszClose, XMLElementPosition pos) - { return addClear_priv(0,stringDup(lpszValue),lpszOpen,lpszClose,pos); } -XMLClear *XMLNode::addClear_WOSD(XMLSTR lpszValue, XMLCSTR lpszOpen, XMLCSTR lpszClose, XMLElementPosition pos) - { return addClear_priv(0,lpszValue,lpszOpen,lpszClose,pos); } -XMLCSTR XMLNode::updateName(XMLCSTR lpszName) - { return updateName_WOSD(stringDup(lpszName)); } -XMLAttribute *XMLNode::updateAttribute(XMLAttribute *newAttribute, XMLAttribute *oldAttribute) - { return updateAttribute_WOSD(stringDup(newAttribute->lpszValue),stringDup(newAttribute->lpszName),oldAttribute->lpszName); } -XMLAttribute *XMLNode::updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName,int i) - { return updateAttribute_WOSD(stringDup(lpszNewValue),stringDup(lpszNewName),i); } -XMLAttribute *XMLNode::updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName,XMLCSTR lpszOldName) - { return updateAttribute_WOSD(stringDup(lpszNewValue),stringDup(lpszNewName),lpszOldName); } -XMLCSTR XMLNode::updateText(XMLCSTR lpszNewValue, int i) - { return updateText_WOSD(stringDup(lpszNewValue),i); } -XMLCSTR XMLNode::updateText(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue) - { return updateText_WOSD(stringDup(lpszNewValue),lpszOldValue); } -XMLClear *XMLNode::updateClear(XMLCSTR lpszNewContent, int i) - { return updateClear_WOSD(stringDup(lpszNewContent),i); } -XMLClear *XMLNode::updateClear(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue) - { return updateClear_WOSD(stringDup(lpszNewValue),lpszOldValue); } -XMLClear *XMLNode::updateClear(XMLClear *newP,XMLClear *oldP) - { return updateClear_WOSD(stringDup(newP->lpszValue),oldP->lpszValue); } - -char XMLNode::setGlobalOptions(XMLCharEncoding _characterEncoding, char _guessWideCharChars, - char _dropWhiteSpace, char _removeCommentsInMiddleOfText) -{ - guessWideCharChars=_guessWideCharChars; dropWhiteSpace=_dropWhiteSpace; removeCommentsInMiddleOfText=_removeCommentsInMiddleOfText; -#ifdef _XMLWIDECHAR - if (_characterEncoding) characterEncoding=_characterEncoding; -#else - switch(_characterEncoding) - { - case char_encoding_UTF8: characterEncoding=_characterEncoding; XML_ByteTable=XML_utf8ByteTable; break; - case char_encoding_legacy: characterEncoding=_characterEncoding; XML_ByteTable=XML_legacyByteTable; break; - case char_encoding_ShiftJIS: characterEncoding=_characterEncoding; XML_ByteTable=XML_sjisByteTable; break; - case char_encoding_GB2312: characterEncoding=_characterEncoding; XML_ByteTable=XML_gb2312ByteTable; break; - case char_encoding_Big5: - case char_encoding_GBK: characterEncoding=_characterEncoding; XML_ByteTable=XML_gbk_big5_ByteTable; break; - default: return 1; - } -#endif - return 0; -} - -XMLNode::XMLCharEncoding XMLNode::guessCharEncoding(void *buf,int l, char useXMLEncodingAttribute) -{ -#ifdef _XMLWIDECHAR - return (XMLCharEncoding)0; -#else - if (l<25) return (XMLCharEncoding)0; - if (guessWideCharChars&&(myIsTextWideChar(buf,l))) return (XMLCharEncoding)0; - unsigned char *b=(unsigned char*)buf; - if ((b[0]==0xef)&&(b[1]==0xbb)&&(b[2]==0xbf)) return char_encoding_UTF8; - - // Match utf-8 model ? - XMLCharEncoding bestGuess=char_encoding_UTF8; - int i=0; - while (i<l) - switch (XML_utf8ByteTable[b[i]]) - { - case 4: i++; if ((i<l)&&(b[i]& 0xC0)!=0x80) { bestGuess=char_encoding_legacy; i=l; } // 10bbbbbb ? - case 3: i++; if ((i<l)&&(b[i]& 0xC0)!=0x80) { bestGuess=char_encoding_legacy; i=l; } // 10bbbbbb ? - case 2: i++; if ((i<l)&&(b[i]& 0xC0)!=0x80) { bestGuess=char_encoding_legacy; i=l; } // 10bbbbbb ? - case 1: i++; break; - case 0: i=l; - } - if (!useXMLEncodingAttribute) return bestGuess; - // if encoding is specified and different from utf-8 than it's non-utf8 - // otherwise it's utf-8 - char bb[201]; - l=mmin(l,200); - memcpy(bb,buf,l); // copy buf into bb to be able to do "bb[l]=0" - bb[l]=0; - b=(unsigned char*)strstr(bb,"encoding"); - if (!b) return bestGuess; - b+=8; while XML_isSPACECHAR(*b) b++; if (*b!='=') return bestGuess; - b++; while XML_isSPACECHAR(*b) b++; if ((*b!='\'')&&(*b!='"')) return bestGuess; - b++; while XML_isSPACECHAR(*b) b++; - - if ((xstrnicmp((char*)b,"utf-8",5)==0)|| - (xstrnicmp((char*)b,"utf8",4)==0)) - { - if (bestGuess==char_encoding_legacy) return char_encoding_error; - return char_encoding_UTF8; - } - - if ((xstrnicmp((char*)b,"shiftjis",8)==0)|| - (xstrnicmp((char*)b,"shift-jis",9)==0)|| - (xstrnicmp((char*)b,"sjis",4)==0)) return char_encoding_ShiftJIS; - - if (xstrnicmp((char*)b,"GB2312",6)==0) return char_encoding_GB2312; - if (xstrnicmp((char*)b,"Big5",4)==0) return char_encoding_Big5; - if (xstrnicmp((char*)b,"GBK",3)==0) return char_encoding_GBK; - - return char_encoding_legacy; -#endif -} -#undef XML_isSPACECHAR - -////////////////////////////////////////////////////////// -// Here starts the base64 conversion functions. // -////////////////////////////////////////////////////////// - -static const char base64Fillchar = _CXML('='); // used to mark partial words at the end - -// this lookup table defines the base64 encoding -XMLCSTR base64EncodeTable=_CXML("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); - -// Decode Table gives the index of any valid base64 character in the Base64 table] -// 96: '=' - 97: space char - 98: illegal char - 99: end of string -const unsigned char base64DecodeTable[] = { - 99,98,98,98,98,98,98,98,98,97, 97,98,98,97,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, //00 -29 - 98,98,97,98,98,98,98,98,98,98, 98,98,98,62,98,98,98,63,52,53, 54,55,56,57,58,59,60,61,98,98, //30 -59 - 98,96,98,98,98, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, 15,16,17,18,19,20,21,22,23,24, //60 -89 - 25,98,98,98,98,98,98,26,27,28, 29,30,31,32,33,34,35,36,37,38, 39,40,41,42,43,44,45,46,47,48, //90 -119 - 49,50,51,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, //120 -149 - 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, //150 -179 - 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, //180 -209 - 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, //210 -239 - 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98 //240 -255 -}; - -XMLParserBase64Tool::~XMLParserBase64Tool(){ freeBuffer(); } - -void XMLParserBase64Tool::freeBuffer(){ if (buf) free(buf); buf=NULL; buflen=0; } - -int XMLParserBase64Tool::encodeLength(int inlen, char formatted) -{ - unsigned int i=((inlen-1)/3*4+4+1); - if (formatted) i+=inlen/54; - return i; -} - -XMLSTR XMLParserBase64Tool::encode(unsigned char *inbuf, unsigned int inlen, char formatted) -{ - int i=encodeLength(inlen,formatted),k=17,eLen=inlen/3,j; - alloc(i*sizeof(XMLCHAR)); - XMLSTR curr=(XMLSTR)buf; - for(i=0;i<eLen;i++) - { - // Copy next three bytes into lower 24 bits of int, paying attention to sign. - j=(inbuf[0]<<16)|(inbuf[1]<<8)|inbuf[2]; inbuf+=3; - // Encode the int into four chars - *(curr++)=base64EncodeTable[ j>>18 ]; - *(curr++)=base64EncodeTable[(j>>12)&0x3f]; - *(curr++)=base64EncodeTable[(j>> 6)&0x3f]; - *(curr++)=base64EncodeTable[(j )&0x3f]; - if (formatted) { if (!k) { *(curr++)=_CXML('\n'); k=18; } k--; } - } - eLen=inlen-eLen*3; // 0 - 2. - if (eLen==1) - { - *(curr++)=base64EncodeTable[ inbuf[0]>>2 ]; - *(curr++)=base64EncodeTable[(inbuf[0]<<4)&0x3F]; - *(curr++)=base64Fillchar; - *(curr++)=base64Fillchar; - } else if (eLen==2) - { - j=(inbuf[0]<<8)|inbuf[1]; - *(curr++)=base64EncodeTable[ j>>10 ]; - *(curr++)=base64EncodeTable[(j>> 4)&0x3f]; - *(curr++)=base64EncodeTable[(j<< 2)&0x3f]; - *(curr++)=base64Fillchar; - } - *(curr++)=0; - return (XMLSTR)buf; -} - -unsigned int XMLParserBase64Tool::decodeSize(XMLCSTR data,XMLError *xe) -{ - if (!data) return 0; - if (xe) *xe=eXMLErrorNone; - int size=0; - unsigned char c; - //skip any extra characters (e.g. newlines or spaces) - while (*data) - { -#ifdef _XMLWIDECHAR - if (*data>255) { if (xe) *xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; } -#endif - c=base64DecodeTable[(unsigned char)(*data)]; - if (c<97) size++; - else if (c==98) { if (xe) *xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; } - data++; - } - if (xe&&(size%4!=0)) *xe=eXMLErrorBase64DataSizeIsNotMultipleOf4; - if (size==0) return 0; - do { data--; size--; } while(*data==base64Fillchar); size++; - return (unsigned int)((size*3)/4); -} - -unsigned char XMLParserBase64Tool::decode(XMLCSTR data, unsigned char *buf, int len, XMLError *xe) -{ - if (!data) return 0; - if (xe) *xe=eXMLErrorNone; - int i=0,p=0; - unsigned char d,c; - for(;;) - { - -#ifdef _XMLWIDECHAR -#define BASE64DECODE_READ_NEXT_CHAR(c) \ - do { \ - if (data[i]>255){ c=98; break; } \ - c=base64DecodeTable[(unsigned char)data[i++]]; \ - }while (c==97); \ - if(c==98){ if(xe)*xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; } -#else -#define BASE64DECODE_READ_NEXT_CHAR(c) \ - do { c=base64DecodeTable[(unsigned char)data[i++]]; }while (c==97); \ - if(c==98){ if(xe)*xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; } -#endif - - BASE64DECODE_READ_NEXT_CHAR(c) - if (c==99) { return 2; } - if (c==96) - { - if (p==(int)len) return 2; - if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; - return 1; - } - - BASE64DECODE_READ_NEXT_CHAR(d) - if ((d==99)||(d==96)) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; } - if (p==(int)len) { if (xe) *xe=eXMLErrorBase64DecodeBufferTooSmall; return 0; } - buf[p++]=(unsigned char)((c<<2)|((d>>4)&0x3)); - - BASE64DECODE_READ_NEXT_CHAR(c) - if (c==99) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; } - if (p==(int)len) - { - if (c==96) return 2; - if (xe) *xe=eXMLErrorBase64DecodeBufferTooSmall; - return 0; - } - if (c==96) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; } - buf[p++]=(unsigned char)(((d<<4)&0xf0)|((c>>2)&0xf)); - - BASE64DECODE_READ_NEXT_CHAR(d) - if (d==99 ) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; } - if (p==(int)len) - { - if (d==96) return 2; - if (xe) *xe=eXMLErrorBase64DecodeBufferTooSmall; - return 0; - } - if (d==96) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; } - buf[p++]=(unsigned char)(((c<<6)&0xc0)|d); - } -} -#undef BASE64DECODE_READ_NEXT_CHAR - -void XMLParserBase64Tool::alloc(int newsize) -{ - if ((!buf)&&(newsize)) { buf=malloc(newsize); buflen=newsize; return; } - if (newsize>buflen) { buf=realloc(buf,newsize); buflen=newsize; } -} - -unsigned char *XMLParserBase64Tool::decode(XMLCSTR data, int *outlen, XMLError *xe) -{ - if (xe) *xe=eXMLErrorNone; - if (!data) { *outlen=0; return (unsigned char*)""; } - unsigned int len=decodeSize(data,xe); - if (outlen) *outlen=len; - if (!len) return NULL; - alloc(len+1); - if(!decode(data,(unsigned char*)buf,len,xe)){ return NULL; } - return (unsigned char*)buf; -} - diff --git a/general/xmlParser.h b/general/xmlParser.h deleted file mode 100644 index 80c54756ee7e76c66f5ca6bb79b11245764c0a3b..0000000000000000000000000000000000000000 --- a/general/xmlParser.h +++ /dev/null @@ -1,733 +0,0 @@ -/****************************************************************************/ -/** mainpage mainpage XMLParser library - * \section intro_sec Introduction - * - * This is a basic XML parser written in ANSI C++ for portability. - * It works by using recursion and a node tree for breaking - * down the elements of an XML document. - * - * @version V2.41 - * @author Frank Vanden Berghen - * - * Copyright (c) 2002, Business-Insight - * <a href="http://www.Business-Insight.com">Business-Insight</a> - * All rights reserved. - * See the file <a href="../../AFPL-license.txt">AFPL-license.txt</a> about the licensing terms - * - * \section tutorial First Tutorial - * You can follow a simple <a href="../../xmlParser.html">Tutorial</a> to know the basics... - * - * \section usage General usage: How to include the XMLParser library inside your project. - * - * The library is composed of two files: <a href="../../xmlParser.cpp">xmlParser.cpp</a> and - * <a href="../../xmlParser.h">xmlParser.h</a>. These are the ONLY 2 files that you need when - * using the library inside your own projects. - * - * All the functions of the library are documented inside the comments of the file - * <a href="../../xmlParser.h">xmlParser.h</a>. These comments can be transformed in - * full-fledged HTML documentation using the DOXYGEN software: simply type: "doxygen doxy.cfg" - * - * By default, the XMLParser library uses (char*) for string representation.To use the (wchar_t*) - * version of the library, you need to define the "_UNICODE" preprocessor definition variable - * (this is usually done inside your project definition file) (This is done automatically for you - * when using Visual Studio). - * - * \section example Advanced Tutorial and Many Examples of usage. - * - * Some very small introductory examples are described inside the Tutorial file - * <a href="../../xmlParser.html">xmlParser.html</a> - * - * Some additional small examples are also inside the file <a href="../../xmlTest.cpp">xmlTest.cpp</a> - * (for the "char*" version of the library) and inside the file - * <a href="../../xmlTestUnicode.cpp">xmlTestUnicode.cpp</a> (for the "wchar_t*" - * version of the library). If you have a question, please review these additionnal examples - * before sending an e-mail to the author. - * - * To build the examples: - * - linux/unix: type "make" - * - solaris: type "make -f makefile.solaris" - * - windows: Visual Studio: double-click on xmlParser.dsw - * (under Visual Studio .NET, the .dsp and .dsw files will be automatically converted to .vcproj and .sln files) - * - * In order to build the examples you need some additional files: - * - linux/unix: makefile - * - solaris: makefile.solaris - * - windows: Visual Studio: *.dsp, xmlParser.dsw and also xmlParser.lib and xmlParser.dll - * - * \section debugging Debugging with the XMLParser library - * - * \subsection debugwin Debugging under WINDOWS - * - * Inside Visual C++, the "debug versions" of the memory allocation functions are - * very slow: Do not forget to compile in "release mode" to get maximum speed. - * When I had to debug a software that was using the XMLParser Library, it was usually - * a nightmare because the library was sooOOOoooo slow in debug mode (because of the - * slow memory allocations in Debug mode). To solve this - * problem, during all the debugging session, I am now using a very fast DLL version of the - * XMLParser Library (the DLL is compiled in release mode). Using the DLL version of - * the XMLParser Library allows me to have lightening XML parsing speed even in debug! - * Other than that, the DLL version is useless: In the release version of my tool, - * I always use the normal, ".cpp"-based, XMLParser Library (I simply include the - * <a href="../../xmlParser.cpp">xmlParser.cpp</a> and - * <a href="../../xmlParser.h">xmlParser.h</a> files into the project). - * - * The file <a href="../../XMLNodeAutoexp.txt">XMLNodeAutoexp.txt</a> contains some - * "tweaks" that improve substancially the display of the content of the XMLNode objects - * inside the Visual Studio Debugger. Believe me, once you have seen inside the debugger - * the "smooth" display of the XMLNode objects, you cannot live without it anymore! - * - * \subsection debuglinux Debugging under LINUX/UNIX - * - * The speed of the debug version of the XMLParser library is tolerable so no extra - * work.has been done. - * - ****************************************************************************/ - -#ifndef __INCLUDE_XML_NODE__ -#define __INCLUDE_XML_NODE__ - -#include <stdlib.h> - -#ifdef _UNICODE -// If you comment the next "define" line then the library will never "switch to" _UNICODE (wchar_t*) mode (16/32 bits per characters). -// This is useful when you get error messages like: -// 'XMLNode::openFileHelper' : cannot convert parameter 2 from 'const char [5]' to 'const wchar_t *' -// The _XMLWIDECHAR preprocessor variable force the XMLParser library into either utf16/32-mode (the proprocessor variable -// must be defined) or utf8-mode(the pre-processor variable must be undefined). -#define _XMLWIDECHAR -#endif - -#if defined(WIN32) || defined(UNDER_CE) || defined(_WIN32) || defined(WIN64) || defined(__BORLANDC__) -// comment the next line if you are under windows and the compiler is not Microsoft Visual Studio (6.0 or .NET) or Borland -#define _XMLWINDOWS -#endif - -#ifdef XMLDLLENTRY -#undef XMLDLLENTRY -#endif -#ifdef _USE_XMLPARSER_DLL -#ifdef _DLL_EXPORTS_ -#define XMLDLLENTRY __declspec(dllexport) -#else -#define XMLDLLENTRY __declspec(dllimport) -#endif -#else -#define XMLDLLENTRY -#endif - -// uncomment the next line if you want no support for wchar_t* (no need for the <wchar.h> or <tchar.h> libraries anymore to compile) -//#define XML_NO_WIDE_CHAR - -#ifdef XML_NO_WIDE_CHAR -#undef _XMLWINDOWS -#undef _XMLWIDECHAR -#endif - -#ifdef _XMLWINDOWS -#include <tchar.h> -#else -#define XMLDLLENTRY -#ifndef XML_NO_WIDE_CHAR -#include <wchar.h> // to have 'wcsrtombs' for ANSI version - // to have 'mbsrtowcs' for WIDECHAR version -#endif -#endif - -// Some common types for char set portable code -#ifdef _XMLWIDECHAR - #define _CXML(c) L ## c - #define XMLCSTR const wchar_t * - #define XMLSTR wchar_t * - #define XMLCHAR wchar_t -#else - #define _CXML(c) c - #define XMLCSTR const char * - #define XMLSTR char * - #define XMLCHAR char -#endif -#ifndef FALSE - #define FALSE 0 -#endif /* FALSE */ -#ifndef TRUE - #define TRUE 1 -#endif /* TRUE */ - - -/// Enumeration for XML parse errors. -typedef enum XMLError -{ - eXMLErrorNone = 0, - eXMLErrorMissingEndTag, - eXMLErrorNoXMLTagFound, - eXMLErrorEmpty, - eXMLErrorMissingTagName, - eXMLErrorMissingEndTagName, - eXMLErrorUnmatchedEndTag, - eXMLErrorUnmatchedEndClearTag, - eXMLErrorUnexpectedToken, - eXMLErrorNoElements, - eXMLErrorFileNotFound, - eXMLErrorFirstTagNotFound, - eXMLErrorUnknownCharacterEntity, - eXMLErrorCharacterCodeAbove255, - eXMLErrorCharConversionError, - eXMLErrorCannotOpenWriteFile, - eXMLErrorCannotWriteFile, - - eXMLErrorBase64DataSizeIsNotMultipleOf4, - eXMLErrorBase64DecodeIllegalCharacter, - eXMLErrorBase64DecodeTruncatedData, - eXMLErrorBase64DecodeBufferTooSmall -} XMLError; - - -/// Enumeration used to manage type of data. Use in conjunction with structure XMLNodeContents -typedef enum XMLElementType -{ - eNodeChild=0, - eNodeAttribute=1, - eNodeText=2, - eNodeClear=3, - eNodeNULL=4 -} XMLElementType; - -/// Structure used to obtain error details if the parse fails. -typedef struct XMLResults -{ - enum XMLError error; - int nLine,nColumn; -} XMLResults; - -/// Structure for XML clear (unformatted) node (usually comments) -typedef struct XMLClear { - XMLCSTR lpszValue; XMLCSTR lpszOpenTag; XMLCSTR lpszCloseTag; -} XMLClear; - -/// Structure for XML attribute. -typedef struct XMLAttribute { - XMLCSTR lpszName; XMLCSTR lpszValue; -} XMLAttribute; - -/// XMLElementPosition are not interchangeable with simple indexes -typedef int XMLElementPosition; - -struct XMLNodeContents; - -/** @defgroup XMLParserGeneral The XML parser */ - -/// Main Class representing a XML node -/** - * All operations are performed using this class. - * \note The constructors of the XMLNode class are protected, so use instead one of these four methods to get your first instance of XMLNode: - * <ul> - * <li> XMLNode::parseString </li> - * <li> XMLNode::parseFile </li> - * <li> XMLNode::openFileHelper </li> - * <li> XMLNode::createXMLTopNode (or XMLNode::createXMLTopNode_WOSD)</li> - * </ul> */ -typedef struct XMLDLLENTRY XMLNode -{ - private: - - struct XMLNodeDataTag; - - /// Constructors are protected, so use instead one of: XMLNode::parseString, XMLNode::parseFile, XMLNode::openFileHelper, XMLNode::createXMLTopNode - XMLNode(struct XMLNodeDataTag *pParent, XMLSTR lpszName, char isDeclaration); - /// Constructors are protected, so use instead one of: XMLNode::parseString, XMLNode::parseFile, XMLNode::openFileHelper, XMLNode::createXMLTopNode - XMLNode(struct XMLNodeDataTag *p); - - public: - static XMLCSTR getVersion();///< Return the XMLParser library version number - - /** @defgroup conversions Parsing XML files/strings to an XMLNode structure and Rendering XMLNode's to files/string. - * @ingroup XMLParserGeneral - * @{ */ - - /// Parse an XML string and return the root of a XMLNode tree representing the string. - static XMLNode parseString (XMLCSTR lpXMLString, XMLCSTR tag=NULL, XMLResults *pResults=NULL); - /**< The "parseString" function parse an XML string and return the root of a XMLNode tree. The "opposite" of this function is - * the function "createXMLString" that re-creates an XML string from an XMLNode tree. If the XML document is corrupted, the - * "parseString" method will initialize the "pResults" variable with some information that can be used to trace the error. - * If you still want to parse the file, you can use the APPROXIMATE_PARSING option as explained inside the note at the - * beginning of the "xmlParser.cpp" file. - * - * @param lpXMLString the XML string to parse - * @param tag the name of the first tag inside the XML file. If the tag parameter is omitted, this function returns a node that represents the head of the xml document including the declaration term (<? ... ?>). - * @param pResults a pointer to a XMLResults variable that will contain some information that can be used to trace the XML parsing error. You can have a user-friendly explanation of the parsing error with the "getError" function. - */ - - /// Parse an XML file and return the root of a XMLNode tree representing the file. - static XMLNode parseFile (XMLCSTR filename, XMLCSTR tag=NULL, XMLResults *pResults=NULL); - /**< The "parseFile" function parse an XML file and return the root of a XMLNode tree. The "opposite" of this function is - * the function "writeToFile" that re-creates an XML file from an XMLNode tree. If the XML document is corrupted, the - * "parseFile" method will initialize the "pResults" variable with some information that can be used to trace the error. - * If you still want to parse the file, you can use the APPROXIMATE_PARSING option as explained inside the note at the - * beginning of the "xmlParser.cpp" file. - * - * @param filename the path to the XML file to parse - * @param tag the name of the first tag inside the XML file. If the tag parameter is omitted, this function returns a node that represents the head of the xml document including the declaration term (<? ... ?>). - * @param pResults a pointer to a XMLResults variable that will contain some information that can be used to trace the XML parsing error. You can have a user-friendly explanation of the parsing error with the "getError" function. - */ - - /// Parse an XML file and return the root of a XMLNode tree representing the file. A very crude error checking is made. An attempt to guess the Char Encoding used in the file is made. - static XMLNode openFileHelper(XMLCSTR filename, XMLCSTR tag=NULL); - /**< The "openFileHelper" function reports to the screen all the warnings and errors that occurred during parsing of the XML file. - * This function also tries to guess char Encoding (UTF-8, ASCII or SHIT-JIS) based on the first 200 bytes of the file. Since each - * application has its own way to report and deal with errors, you should rather use the "parseFile" function to parse XML files - * and program yourself thereafter an "error reporting" tailored for your needs (instead of using the very crude "error reporting" - * mechanism included inside the "openFileHelper" function). - * - * If the XML document is corrupted, the "openFileHelper" method will: - * - display an error message on the console (or inside a messageBox for windows). - * - stop execution (exit). - * - * I strongly suggest that you write your own "openFileHelper" method tailored to your needs. If you still want to parse - * the file, you can use the APPROXIMATE_PARSING option as explained inside the note at the beginning of the "xmlParser.cpp" file. - * - * @param filename the path of the XML file to parse. - * @param tag the name of the first tag inside the XML file. If the tag parameter is omitted, this function returns a node that represents the head of the xml document including the declaration term (<? ... ?>). - */ - - static XMLCSTR getError(XMLError error); ///< this gives you a user-friendly explanation of the parsing error - - /// Create an XML string starting from the current XMLNode. - XMLSTR createXMLString(int nFormat=1, int *pnSize=NULL) const; - /**< The returned string should be free'd using the "freeXMLString" function. - * - * If nFormat==0, no formatting is required otherwise this returns an user friendly XML string from a given element - * with appropriate white spaces and carriage returns. if pnSize is given it returns the size in character of the string. */ - - /// Save the content of an xmlNode inside a file - XMLError writeToFile(XMLCSTR filename, - const char *encoding=NULL, - char nFormat=1) const; - /**< If nFormat==0, no formatting is required otherwise this returns an user friendly XML string from a given element with appropriate white spaces and carriage returns. - * If the global parameter "characterEncoding==encoding_UTF8", then the "encoding" parameter is ignored and always set to "utf-8". - * If the global parameter "characterEncoding==encoding_ShiftJIS", then the "encoding" parameter is ignored and always set to "SHIFT-JIS". - * If "_XMLWIDECHAR=1", then the "encoding" parameter is ignored and always set to "utf-16". - * If no "encoding" parameter is given the "ISO-8859-1" encoding is used. */ - /** @} */ - - /** @defgroup navigate Navigate the XMLNode structure - * @ingroup XMLParserGeneral - * @{ */ - XMLCSTR getName() const; ///< name of the node - XMLCSTR getText(int i=0) const; ///< return ith text field - int nText() const; ///< nbr of text field - XMLNode getParentNode() const; ///< return the parent node - XMLNode getChildNode(int i=0) const; ///< return ith child node - XMLNode getChildNode(XMLCSTR name, int i) const; ///< return ith child node with specific name (return an empty node if failing). If i==-1, this returns the last XMLNode with the given name. - XMLNode getChildNode(XMLCSTR name, int *i=NULL) const; ///< return next child node with specific name (return an empty node if failing) - XMLNode getChildNodeWithAttribute(XMLCSTR tagName, - XMLCSTR attributeName, - XMLCSTR attributeValue=NULL, - int *i=NULL) const; ///< return child node with specific name/attribute (return an empty node if failing) - XMLNode getChildNodeByPath(XMLCSTR path, char createNodeIfMissing=0, XMLCHAR sep='/'); - ///< return the first child node with specific path - XMLNode getChildNodeByPathNonConst(XMLSTR path, char createNodeIfMissing=0, XMLCHAR sep='/'); - ///< return the first child node with specific path. - - int nChildNode(XMLCSTR name) const; ///< return the number of child node with specific name - int nChildNode() const; ///< nbr of child node - XMLAttribute getAttribute(int i=0) const; ///< return ith attribute - XMLCSTR getAttributeName(int i=0) const; ///< return ith attribute name - XMLCSTR getAttributeValue(int i=0) const; ///< return ith attribute value - char isAttributeSet(XMLCSTR name) const; ///< test if an attribute with a specific name is given - XMLCSTR getAttribute(XMLCSTR name, int i) const; ///< return ith attribute content with specific name (return a NULL if failing) - XMLCSTR getAttribute(XMLCSTR name, int *i=NULL) const; ///< return next attribute content with specific name (return a NULL if failing) - int nAttribute() const; ///< nbr of attribute - XMLClear getClear(int i=0) const; ///< return ith clear field (comments) - int nClear() const; ///< nbr of clear field - XMLNodeContents enumContents(XMLElementPosition i) const; ///< enumerate all the different contents (attribute,child,text, clear) of the current XMLNode. The order is reflecting the order of the original file/string. NOTE: 0 <= i < nElement(); - int nElement() const; ///< nbr of different contents for current node - char isEmpty() const; ///< is this node Empty? - char isDeclaration() const; ///< is this node a declaration <? .... ?> - XMLNode deepCopy() const; ///< deep copy (duplicate/clone) a XMLNode - static XMLNode emptyNode(); ///< return XMLNode::emptyXMLNode; - /** @} */ - - ~XMLNode(); - XMLNode(const XMLNode &A); ///< to allow shallow/fast copy: - XMLNode& operator=( const XMLNode& A ); ///< to allow shallow/fast copy: - - XMLNode(): d(NULL){}; - static XMLNode emptyXMLNode; - static XMLClear emptyXMLClear; - static XMLAttribute emptyXMLAttribute; - - /** @defgroup xmlModify Create or Update the XMLNode structure - * @ingroup XMLParserGeneral - * The functions in this group allows you to create from scratch (or update) a XMLNode structure. Start by creating your top - * node with the "createXMLTopNode" function and then add new nodes with the "addChild" function. The parameter 'pos' gives - * the position where the childNode, the text or the XMLClearTag will be inserted. The default value (pos=-1) inserts at the - * end. The value (pos=0) insert at the beginning (Insertion at the beginning is slower than at the end). <br> - * - * REMARK: 0 <= pos < nChild()+nText()+nClear() <br> - */ - - /** @defgroup creation Creating from scratch a XMLNode structure - * @ingroup xmlModify - * @{ */ - static XMLNode createXMLTopNode(XMLCSTR lpszName, char isDeclaration=FALSE); ///< Create the top node of an XMLNode structure - XMLNode addChild(XMLCSTR lpszName, char isDeclaration=FALSE, XMLElementPosition pos=-1); ///< Add a new child node - XMLNode addChild(XMLNode nodeToAdd, XMLElementPosition pos=-1); ///< If the "nodeToAdd" has some parents, it will be detached from it's parents before being attached to the current XMLNode - XMLAttribute *addAttribute(XMLCSTR lpszName, XMLCSTR lpszValuev); ///< Add a new attribute - XMLCSTR addText(XMLCSTR lpszValue, XMLElementPosition pos=-1); ///< Add a new text content - XMLClear *addClear(XMLCSTR lpszValue, XMLCSTR lpszOpen=NULL, XMLCSTR lpszClose=NULL, XMLElementPosition pos=-1); - /**< Add a new clear tag - * @param lpszOpen default value "<![CDATA[" - * @param lpszClose default value "]]>" - */ - /** @} */ - - /** @defgroup xmlUpdate Updating Nodes - * @ingroup xmlModify - * Some update functions: - * @{ - */ - XMLCSTR updateName(XMLCSTR lpszName); ///< change node's name - XMLAttribute *updateAttribute(XMLAttribute *newAttribute, XMLAttribute *oldAttribute); ///< if the attribute to update is missing, a new one will be added - XMLAttribute *updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName=NULL,int i=0); ///< if the attribute to update is missing, a new one will be added - XMLAttribute *updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName,XMLCSTR lpszOldName);///< set lpszNewName=NULL if you don't want to change the name of the attribute if the attribute to update is missing, a new one will be added - XMLCSTR updateText(XMLCSTR lpszNewValue, int i=0); ///< if the text to update is missing, a new one will be added - XMLCSTR updateText(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue); ///< if the text to update is missing, a new one will be added - XMLClear *updateClear(XMLCSTR lpszNewContent, int i=0); ///< if the clearTag to update is missing, a new one will be added - XMLClear *updateClear(XMLClear *newP,XMLClear *oldP); ///< if the clearTag to update is missing, a new one will be added - XMLClear *updateClear(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue); ///< if the clearTag to update is missing, a new one will be added - /** @} */ - - /** @defgroup xmlDelete Deleting Nodes or Attributes - * @ingroup xmlModify - * Some deletion functions: - * @{ - */ - /// The "deleteNodeContent" function forces the deletion of the content of this XMLNode and the subtree. - void deleteNodeContent(); - /**< \note The XMLNode instances that are referring to the part of the subtree that has been deleted CANNOT be used anymore!!. Unexpected results will occur if you continue using them. */ - void deleteAttribute(int i=0); ///< Delete the ith attribute of the current XMLNode - void deleteAttribute(XMLCSTR lpszName); ///< Delete the attribute with the given name (the "strcmp" function is used to find the right attribute) - void deleteAttribute(XMLAttribute *anAttribute); ///< Delete the attribute with the name "anAttribute->lpszName" (the "strcmp" function is used to find the right attribute) - void deleteText(int i=0); ///< Delete the Ith text content of the current XMLNode - void deleteText(XMLCSTR lpszValue); ///< Delete the text content "lpszValue" inside the current XMLNode (direct "pointer-to-pointer" comparison is used to find the right text) - void deleteClear(int i=0); ///< Delete the Ith clear tag inside the current XMLNode - void deleteClear(XMLCSTR lpszValue); ///< Delete the clear tag "lpszValue" inside the current XMLNode (direct "pointer-to-pointer" comparison is used to find the clear tag) - void deleteClear(XMLClear *p); ///< Delete the clear tag "p" inside the current XMLNode (direct "pointer-to-pointer" comparison on the lpszName of the clear tag is used to find the clear tag) - /** @} */ - - /** @defgroup xmlWOSD ???_WOSD functions. - * @ingroup xmlModify - * The strings given as parameters for the "add" and "update" methods that have a name with - * the postfix "_WOSD" (that means "WithOut String Duplication")(for example "addText_WOSD") - * will be free'd by the XMLNode class. For example, it means that this is incorrect: - * \code - * xNode.addText_WOSD("foo"); - * xNode.updateAttribute_WOSD("#newcolor" ,NULL,"color"); - * \endcode - * In opposition, this is correct: - * \code - * xNode.addText("foo"); - * xNode.addText_WOSD(stringDup("foo")); - * xNode.updateAttribute("#newcolor" ,NULL,"color"); - * xNode.updateAttribute_WOSD(stringDup("#newcolor"),NULL,"color"); - * \endcode - * Typically, you will never do: - * \code - * char *b=(char*)malloc(...); - * xNode.addText(b); - * free(b); - * \endcode - * ... but rather: - * \code - * char *b=(char*)malloc(...); - * xNode.addText_WOSD(b); - * \endcode - * ('free(b)' is performed by the XMLNode class) - * @{ */ - static XMLNode createXMLTopNode_WOSD(XMLSTR lpszName, char isDeclaration=FALSE); ///< Create the top node of an XMLNode structure - XMLNode addChild_WOSD(XMLSTR lpszName, char isDeclaration=FALSE, XMLElementPosition pos=-1); ///< Add a new child node - XMLAttribute *addAttribute_WOSD(XMLSTR lpszName, XMLSTR lpszValue); ///< Add a new attribute - XMLCSTR addText_WOSD(XMLSTR lpszValue, XMLElementPosition pos=-1); ///< Add a new text content - XMLClear *addClear_WOSD(XMLSTR lpszValue, XMLCSTR lpszOpen=NULL, XMLCSTR lpszClose=NULL, XMLElementPosition pos=-1); ///< Add a new clear Tag - - XMLCSTR updateName_WOSD(XMLSTR lpszName); ///< change node's name - XMLAttribute *updateAttribute_WOSD(XMLAttribute *newAttribute, XMLAttribute *oldAttribute); ///< if the attribute to update is missing, a new one will be added - XMLAttribute *updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName=NULL,int i=0); ///< if the attribute to update is missing, a new one will be added - XMLAttribute *updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName,XMLCSTR lpszOldName); ///< set lpszNewName=NULL if you don't want to change the name of the attribute if the attribute to update is missing, a new one will be added - XMLCSTR updateText_WOSD(XMLSTR lpszNewValue, int i=0); ///< if the text to update is missing, a new one will be added - XMLCSTR updateText_WOSD(XMLSTR lpszNewValue, XMLCSTR lpszOldValue); ///< if the text to update is missing, a new one will be added - XMLClear *updateClear_WOSD(XMLSTR lpszNewContent, int i=0); ///< if the clearTag to update is missing, a new one will be added - XMLClear *updateClear_WOSD(XMLClear *newP,XMLClear *oldP); ///< if the clearTag to update is missing, a new one will be added - XMLClear *updateClear_WOSD(XMLSTR lpszNewValue, XMLCSTR lpszOldValue); ///< if the clearTag to update is missing, a new one will be added - /** @} */ - - /** @defgroup xmlPosition Position helper functions (use in conjunction with the update&add functions - * @ingroup xmlModify - * These are some useful functions when you want to insert a childNode, a text or a XMLClearTag in the - * middle (at a specified position) of a XMLNode tree already constructed. The value returned by these - * methods is to be used as last parameter (parameter 'pos') of addChild, addText or addClear. - * @{ */ - XMLElementPosition positionOfText(int i=0) const; - XMLElementPosition positionOfText(XMLCSTR lpszValue) const; - XMLElementPosition positionOfClear(int i=0) const; - XMLElementPosition positionOfClear(XMLCSTR lpszValue) const; - XMLElementPosition positionOfClear(XMLClear *a) const; - XMLElementPosition positionOfChildNode(int i=0) const; - XMLElementPosition positionOfChildNode(XMLNode x) const; - XMLElementPosition positionOfChildNode(XMLCSTR name, int i=0) const; ///< return the position of the ith childNode with the specified name if (name==NULL) return the position of the ith childNode - /** @} */ - - /// Enumeration for XML character encoding. - typedef enum XMLCharEncoding - { - char_encoding_error=0, - char_encoding_UTF8=1, - char_encoding_legacy=2, - char_encoding_ShiftJIS=3, - char_encoding_GB2312=4, - char_encoding_Big5=5, - char_encoding_GBK=6 // this is actually the same as Big5 - } XMLCharEncoding; - - /** \addtogroup conversions - * @{ */ - - /// Sets the global options for the conversions - static char setGlobalOptions(XMLCharEncoding characterEncoding=XMLNode::char_encoding_UTF8, char guessWideCharChars=1, - char dropWhiteSpace=1, char removeCommentsInMiddleOfText=1); - /**< The "setGlobalOptions" function allows you to change four global parameters that affect string & file - * parsing. First of all, you most-probably will never have to change these 3 global parameters. - * - * @param guessWideCharChars If "guessWideCharChars"=1 and if this library is compiled in WideChar mode, then the - * XMLNode::parseFile and XMLNode::openFileHelper functions will test if the file contains ASCII - * characters. If this is the case, then the file will be loaded and converted in memory to - * WideChar before being parsed. If 0, no conversion will be performed. - * - * @param guessWideCharChars If "guessWideCharChars"=1 and if this library is compiled in ASCII/UTF8/char* mode, then the - * XMLNode::parseFile and XMLNode::openFileHelper functions will test if the file contains WideChar - * characters. If this is the case, then the file will be loaded and converted in memory to - * ASCII/UTF8/char* before being parsed. If 0, no conversion will be performed. - * - * @param characterEncoding This parameter is only meaningful when compiling in char* mode (multibyte character mode). - * In wchar_t* (wide char mode), this parameter is ignored. This parameter should be one of the - * three currently recognized encodings: XMLNode::encoding_UTF8, XMLNode::encoding_ascii, - * XMLNode::encoding_ShiftJIS. - * - * @param dropWhiteSpace In most situations, text fields containing only white spaces (and carriage returns) - * are useless. Even more, these "empty" text fields are annoying because they increase the - * complexity of the user's code for parsing. So, 99% of the time, it's better to drop - * the "empty" text fields. However The XML specification indicates that no white spaces - * should be lost when parsing the file. So to be perfectly XML-compliant, you should set - * dropWhiteSpace=0. A note of caution: if you set "dropWhiteSpace=0", the parser will be - * slower and your code will be more complex. - * - * @param removeCommentsInMiddleOfText To explain this parameter, let's consider this code: - * \code - * XMLNode x=XMLNode::parseString("<a>foo<!-- hello -->bar<!DOCTYPE world >chu</a>","a"); - * \endcode - * If removeCommentsInMiddleOfText=0, then we will have: - * \code - * x.getText(0) -> "foo" - * x.getText(1) -> "bar" - * x.getText(2) -> "chu" - * x.getClear(0) --> "<!-- hello -->" - * x.getClear(1) --> "<!DOCTYPE world >" - * \endcode - * If removeCommentsInMiddleOfText=1, then we will have: - * \code - * x.getText(0) -> "foobar" - * x.getText(1) -> "chu" - * x.getClear(0) --> "<!DOCTYPE world >" - * \endcode - * - * \return "0" when there are no errors. If you try to set an unrecognized encoding then the return value will be "1" to signal an error. - * - * \note Sometime, it's useful to set "guessWideCharChars=0" to disable any conversion - * because the test to detect the file-type (ASCII/UTF8/char* or WideChar) may fail (rarely). */ - - /// Guess the character encoding of the string (ascii, utf8 or shift-JIS) - static XMLCharEncoding guessCharEncoding(void *buffer, int bufLen, char useXMLEncodingAttribute=1); - /**< The "guessCharEncoding" function try to guess the character encoding. You most-probably will never - * have to use this function. It then returns the appropriate value of the global parameter - * "characterEncoding" described in the XMLNode::setGlobalOptions. The guess is based on the content of a buffer of length - * "bufLen" bytes that contains the first bytes (minimum 25 bytes; 200 bytes is a good value) of the - * file to be parsed. The XMLNode::openFileHelper function is using this function to automatically compute - * the value of the "characterEncoding" global parameter. There are several heuristics used to do the - * guess. One of the heuristic is based on the "encoding" attribute. The original XML specifications - * forbids to use this attribute to do the guess but you can still use it if you set - * "useXMLEncodingAttribute" to 1 (this is the default behavior and the behavior of most parsers). - * If an inconsistency in the encoding is detected, then the return value is "0". */ - /** @} */ - - private: - // these are functions and structures used internally by the XMLNode class (don't bother about them): - - typedef struct XMLNodeDataTag // to allow shallow copy and "intelligent/smart" pointers (automatic delete): - { - XMLCSTR lpszName; // Element name (=NULL if root) - int nChild, // Number of child nodes - nText, // Number of text fields - nClear, // Number of Clear fields (comments) - nAttribute; // Number of attributes - char isDeclaration; // Whether node is an XML declaration - '<?xml ?>' - struct XMLNodeDataTag *pParent; // Pointer to parent element (=NULL if root) - XMLNode *pChild; // Array of child nodes - XMLCSTR *pText; // Array of text fields - XMLClear *pClear; // Array of clear fields - XMLAttribute *pAttribute; // Array of attributes - int *pOrder; // order of the child_nodes,text_fields,clear_fields - int ref_count; // for garbage collection (smart pointers) - } XMLNodeData; - XMLNodeData *d; - - char parseClearTag(void *px, void *pa); - char maybeAddTxT(void *pa, XMLCSTR tokenPStr); - int ParseXMLElement(void *pXML); - void *addToOrder(int memInc, int *_pos, int nc, void *p, int size, XMLElementType xtype); - int indexText(XMLCSTR lpszValue) const; - int indexClear(XMLCSTR lpszValue) const; - XMLNode addChild_priv(int,XMLSTR,char,int); - XMLAttribute *addAttribute_priv(int,XMLSTR,XMLSTR); - XMLCSTR addText_priv(int,XMLSTR,int); - XMLClear *addClear_priv(int,XMLSTR,XMLCSTR,XMLCSTR,int); - void emptyTheNode(char force); - static inline XMLElementPosition findPosition(XMLNodeData *d, int index, XMLElementType xtype); - static int CreateXMLStringR(XMLNodeData *pEntry, XMLSTR lpszMarker, int nFormat); - static int removeOrderElement(XMLNodeData *d, XMLElementType t, int index); - static void exactMemory(XMLNodeData *d); - static int detachFromParent(XMLNodeData *d); -} XMLNode; - -/// This structure is given by the function XMLNode::enumContents. -typedef struct XMLNodeContents -{ - /// This dictates what's the content of the XMLNodeContent - enum XMLElementType etype; - /**< should be an union to access the appropriate data. Compiler does not allow union of object with constructor... too bad. */ - XMLNode child; - XMLAttribute attrib; - XMLCSTR text; - XMLClear clear; - -} XMLNodeContents; - -/** @defgroup StringAlloc String Allocation/Free functions - * @ingroup xmlModify - * @{ */ -/// Duplicate (copy in a new allocated buffer) the source string. -XMLDLLENTRY XMLSTR stringDup(XMLCSTR source, int cbData=-1); -/**< This is - * a very handy function when used with all the "XMLNode::*_WOSD" functions (\link xmlWOSD \endlink). - * @param cbData If !=0 then cbData is the number of chars to duplicate. New strings allocated with - * this function should be free'd using the "freeXMLString" function. */ - -/// to free the string allocated inside the "stringDup" function or the "createXMLString" function. -XMLDLLENTRY void freeXMLString(XMLSTR t); // {free(t);} -/** @} */ - -/** @defgroup atoX ato? like functions - * @ingroup XMLParserGeneral - * The "xmlto?" functions are equivalents to the atoi, atol, atof functions. - * The only difference is: If the variable "xmlString" is NULL, than the return value - * is "defautValue". These 6 functions are only here as "convenience" functions for the - * user (they are not used inside the XMLparser). If you don't need them, you can - * delete them without any trouble. - * - * @{ */ -XMLDLLENTRY char xmltob(XMLCSTR xmlString,char defautValue=0); -XMLDLLENTRY int xmltoi(XMLCSTR xmlString,int defautValue=0); -XMLDLLENTRY long xmltol(XMLCSTR xmlString,long defautValue=0); -XMLDLLENTRY double xmltof(XMLCSTR xmlString,double defautValue=.0); -XMLDLLENTRY XMLCSTR xmltoa(XMLCSTR xmlString,XMLCSTR defautValue=_CXML("")); -XMLDLLENTRY XMLCHAR xmltoc(XMLCSTR xmlString,const XMLCHAR defautValue=_CXML('\0')); -/** @} */ - -/** @defgroup ToXMLStringTool Helper class to create XML files using "printf", "fprintf", "cout",... functions. - * @ingroup XMLParserGeneral - * @{ */ -/// Helper class to create XML files using "printf", "fprintf", "cout",... functions. -/** The ToXMLStringTool class helps you creating XML files using "printf", "fprintf", "cout",... functions. - * The "ToXMLStringTool" class is processing strings so that all the characters - * &,",',<,> are replaced by their XML equivalent: - * \verbatim &, ", ', <, > \endverbatim - * Using the "ToXMLStringTool class" and the "fprintf function" is THE most efficient - * way to produce VERY large XML documents VERY fast. - * \note If you are creating from scratch an XML file using the provided XMLNode class - * you must not use the "ToXMLStringTool" class (because the "XMLNode" class does the - * processing job for you during rendering).*/ -typedef struct XMLDLLENTRY ToXMLStringTool -{ -public: - ToXMLStringTool(): buf(NULL),buflen(0){} - ~ToXMLStringTool(); - void freeBuffer();///<call this function when you have finished using this object to release memory used by the internal buffer. - - XMLSTR toXML(XMLCSTR source);///< returns a pointer to an internal buffer that contains a XML-encoded string based on the "source" parameter. - - /** The "toXMLUnSafe" function is deprecated because there is a possibility of - * "destination-buffer-overflow". It converts the string - * "source" to the string "dest". */ - static XMLSTR toXMLUnSafe(XMLSTR dest,XMLCSTR source); ///< deprecated: use "toXML" instead - static int lengthXMLString(XMLCSTR source); ///< deprecated: use "toXML" instead - -private: - XMLSTR buf; - int buflen; -} ToXMLStringTool; -/** @} */ - -/** @defgroup XMLParserBase64Tool Helper class to include binary data inside XML strings using "Base64 encoding". - * @ingroup XMLParserGeneral - * @{ */ -/// Helper class to include binary data inside XML strings using "Base64 encoding". -/** The "XMLParserBase64Tool" class allows you to include any binary data (images, sounds,...) - * into an XML document using "Base64 encoding". This class is completely - * separated from the rest of the xmlParser library and can be removed without any problem. - * To include some binary data into an XML file, you must convert the binary data into - * standard text (using "encode"). To retrieve the original binary data from the - * b64-encoded text included inside the XML file, use "decode". Alternatively, these - * functions can also be used to "encrypt/decrypt" some critical data contained inside - * the XML (it's not a strong encryption at all, but sometimes it can be useful). */ -typedef struct XMLDLLENTRY XMLParserBase64Tool -{ -public: - XMLParserBase64Tool(): buf(NULL),buflen(0){} - ~XMLParserBase64Tool(); - void freeBuffer();///< Call this function when you have finished using this object to release memory used by the internal buffer. - - /** - * @param formatted If "formatted"=true, some space will be reserved for a carriage-return every 72 chars. */ - static int encodeLength(int inBufLen, char formatted=0); ///< return the length of the base64 string that encodes a data buffer of size inBufLen bytes. - - /** - * The "base64Encode" function returns a string containing the base64 encoding of "inByteLen" bytes - * from "inByteBuf". If "formatted" parameter is true, then there will be a carriage-return every 72 chars. - * The string will be free'd when the XMLParserBase64Tool object is deleted. - * All returned strings are sharing the same memory space. */ - XMLSTR encode(unsigned char *inByteBuf, unsigned int inByteLen, char formatted=0); ///< returns a pointer to an internal buffer containing the base64 string containing the binary data encoded from "inByteBuf" - - /// returns the number of bytes which will be decoded from "inString". - static unsigned int decodeSize(XMLCSTR inString, XMLError *xe=NULL); - - /** - * The "decode" function returns a pointer to a buffer containing the binary data decoded from "inString" - * The output buffer will be free'd when the XMLParserBase64Tool object is deleted. - * All output buffer are sharing the same memory space. - * @param inString If "instring" is malformed, NULL will be returned */ - unsigned char* decode(XMLCSTR inString, int *outByteLen=NULL, XMLError *xe=NULL); ///< returns a pointer to an internal buffer containing the binary data decoded from "inString" - - /** - * decodes data from "inString" to "outByteBuf". You need to provide the size (in byte) of "outByteBuf" - * in "inMaxByteOutBuflen". If "outByteBuf" is not large enough or if data is malformed, then "FALSE" - * will be returned; otherwise "TRUE". */ - static unsigned char decode(XMLCSTR inString, unsigned char *outByteBuf, int inMaxByteOutBuflen, XMLError *xe=NULL); ///< deprecated. - -private: - void *buf; - int buflen; - void alloc(int newsize); -}XMLParserBase64Tool; -/** @} */ - -#undef XMLDLLENTRY - -#endif diff --git a/geometry/Building.cpp b/geometry/Building.cpp index 7adcccd123aeb80e434853dfe1c2a81babfc3c1b..a9390569d4530b0187858e853c67fc0dd3f77adf 100644 --- a/geometry/Building.cpp +++ b/geometry/Building.cpp @@ -26,16 +26,21 @@ */ #include "Building.h" -#include "../general/xmlParser.h" +#include "../tinyxml/tinyxml.h" #ifdef _SIMULATOR - #include "../pedestrian/Pedestrian.h" - #include "../mpi/LCGrid.h" - #include "../routing/RoutingEngine.h" +#include "../pedestrian/Pedestrian.h" +#include "../mpi/LCGrid.h" +#include "../routing/RoutingEngine.h" #endif +//#undef _OPENMP + #ifdef _OPENMP #include <omp.h> +#else +#define omp_get_thread_num() 0 +#define omp_get_max_threads() 1 #endif @@ -58,25 +63,27 @@ Building::~Building() { for (int i = 0; i < GetNumberOfRooms(); i++) delete _rooms[i]; +#ifdef _SIMULATOR delete _routingEngine; delete _linkedCellGrid; +#endif if (_pathWayStream.is_open()) _pathWayStream.close(); -// FIXME: -// for (map<int, Crossing*>::const_iterator iter = pCrossings.begin(); -// iter != pCrossings.end(); ++iter) { -// delete iter->second; -// } -// for (map<int, Transition*>::const_iterator iter = pTransitions.begin(); -// iter != pTransitions.end(); ++iter) { -// delete iter->second; -// } -// for (map<int, Hline*>::const_iterator iter = pHlines.begin(); -// iter != pHlines.end(); ++iter) { -// delete iter->second; -// } + + for (map<int, Crossing*>::const_iterator iter = _crossings.begin(); + iter != _crossings.end(); ++iter) { + delete iter->second; + } + for (map<int, Transition*>::const_iterator iter = _transitions.begin(); + iter != _transitions.end(); ++iter) { + delete iter->second; + } + for (map<int, Hline*>::const_iterator iter = _hLines.begin(); + iter != _hLines.end(); ++iter) { + delete iter->second; + } } /************************************************************ @@ -138,6 +145,13 @@ Room* Building::GetRoom(int index) const { +LCGrid* Building::GetGrid() const { + return _linkedCellGrid; +} +/************************************************************* + Sonstiges + ************************************************************/ + void Building::AddRoom(Room* room) { _rooms.push_back(room); } @@ -237,65 +251,92 @@ void Building::InitGeometry() { Log->Write("INFO: \tInit Geometry successful!!!\n"); } +/************************************************************* + Ein-Ausgabe + ************************************************************/ +void Building::LoadBuilding(string filename) { + Log->Write("INFO: \tParsing the geometry file"); + TiXmlDocument docGeo(filename); + if (!docGeo.LoadFile()){ + Log->Write("ERROR: \t%s", docGeo.ErrorDesc()); + Log->Write("ERROR: \t could not parse the geometry file"); + exit(EXIT_FAILURE); + } -void Building::LoadBuilding(string filename) { + TiXmlElement* xRootNode = docGeo.RootElement(); + if( ! xRootNode ) { + Log->Write("ERROR:\tRoot element does not exist"); + exit(EXIT_FAILURE); + } - Log->Write("INFO: \tParsing the geometry file"); + if( xRootNode->ValueStr () != "geometry" ) { + Log->Write("ERROR:\tRoot element value is not 'geometry'."); + exit(EXIT_FAILURE); + } - XMLNode xMainNode = XMLNode::openFileHelper(filename.c_str(), "geometry"); - double version = xmltof(xMainNode.getAttribute("version"), -1); + double version = xmltof(xRootNode->Attribute("version"), -1); if (version < 0.4) { Log->Write("ERROR: \tOnly version > 0.4 supported"); Log->Write("ERROR: \tparsing geometry file failed!"); exit(EXIT_FAILURE); } - _caption = xmltoa(xMainNode.getAttribute("caption"), "virtual building"); + _caption = xmltoa(xRootNode->Attribute("caption"), "virtual building"); + //The file has two main nodes //<rooms> and <transitions> - XMLNode xRoomsNode = xMainNode.getChildNode("rooms"); - int nRooms = xRoomsNode.nChildNode("room"); //processing the rooms node - for (int i = 0; i < nRooms; i++) { - XMLNode xRoom = xRoomsNode.getChildNode("room", i); + TiXmlNode* xRoomsNode = xRootNode->FirstChild("rooms"); + if (!xRoomsNode){ + Log->Write("ERROR: \tThe geometry should have at least one room and one subroom"); + exit(EXIT_FAILURE); + } + + for(TiXmlElement* xRoom = xRoomsNode->FirstChildElement("room"); xRoom; + xRoom = xRoom->NextSiblingElement("room")) { + Room* room = new Room(); - string room_id = xmltoa(xRoom.getAttribute("id"), "-1"); + string room_id = xmltoa(xRoom->Attribute("id"), "-1"); room->SetID(xmltoi(room_id.c_str(), -1)); string caption = "room " + room_id; room->SetCaption( - xmltoa(xRoom.getAttribute("caption"), caption.c_str())); + xmltoa(xRoom->Attribute("caption"), caption.c_str())); - double position = xmltof(xRoom.getAttribute("zpos"), 0.0); + double position = xmltof(xRoom->Attribute("zpos"), 0.0); + + //TODO?? what the hell is that for ? if(position>6.0) position+=50; room->SetZPos(position); //parsing the subrooms - int nSubRooms = xRoom.nChildNode("subroom"); + //processing the rooms node + //TiXmlNode* xSubroomsNode = xRoom->FirstChild("subroom"); + + for(TiXmlElement* xSubRoom = xRoom->FirstChildElement("subroom"); xSubRoom; + xSubRoom = xSubRoom->NextSiblingElement("subroom")) { - for (int s = 0; s < nSubRooms; s++) { - XMLNode xSubroomsNode = xRoom.getChildNode("subroom", s); - string subroom_id = xmltoa(xSubroomsNode.getAttribute("id"), "-1"); - string closed = xmltoa(xSubroomsNode.getAttribute("closed"), "0"); - string type = xmltoa(xSubroomsNode.getAttribute("class"), + string subroom_id = xmltoa(xSubRoom->Attribute("id"), "-1"); + string closed = xmltoa(xSubRoom->Attribute("closed"), "0"); + string type = xmltoa(xSubRoom->Attribute("class"), "subroom"); SubRoom* subroom = NULL; if (type == "stair") { - double up_x = xmltof( xSubroomsNode.getChildNode("up").getAttribute("px"), 0.0); - double up_y = xmltof( xSubroomsNode.getChildNode("up").getAttribute("py"), 0.0); - double down_x = xmltof( xSubroomsNode.getChildNode("down").getAttribute("py"), 0.0); - double down_y = xmltof( xSubroomsNode.getChildNode("down").getAttribute("py"), 0.0); + double up_x = xmltof( xSubRoom->FirstChildElement("up")->Attribute("px"), 0.0); + double up_y = xmltof( xSubRoom->FirstChildElement("up")->Attribute("py"), 0.0); + double down_x = xmltof( xSubRoom->FirstChildElement("down")->Attribute("py"), 0.0); + double down_y = xmltof( xSubRoom->FirstChildElement("down")->Attribute("py"), 0.0); subroom = new Stair(); ((Stair*)subroom)->SetUp(Point(up_x,up_y)); ((Stair*)subroom)->SetDown(Point(down_x,down_y)); @@ -308,49 +349,32 @@ void Building::LoadBuilding(string filename) { subroom->SetSubRoomID(xmltoi(subroom_id.c_str(), -1)); //looking for polygons (walls) - int nPoly = xSubroomsNode.nChildNode("polygon"); - for (int p = 0; p < nPoly; p++) { - XMLNode xPolyVertices = xSubroomsNode.getChildNode("polygon", - p); - int nVertices = - xSubroomsNode.getChildNode("polygon", p).nChildNode( - "vertex"); - - for (int v = 0; v < nVertices - 1; v++) { - double x1 = - xmltof( - xPolyVertices.getChildNode("vertex", v).getAttribute( - "px")); - double y1 = - xmltof( - xPolyVertices.getChildNode("vertex", v).getAttribute( - "py")); - - double x2 = - xmltof( - xPolyVertices.getChildNode("vertex", v + 1).getAttribute( - "px")); - double y2 = - xmltof( - xPolyVertices.getChildNode("vertex", v + 1).getAttribute( - "py")); + for(TiXmlElement* xPolyVertices = xSubRoom->FirstChildElement("polygon"); xPolyVertices; + xPolyVertices = xPolyVertices->NextSiblingElement("polygon")) { + + for (TiXmlElement* xVertex = xPolyVertices->FirstChildElement( + "vertex"); + xVertex && xVertex != xPolyVertices->LastChild("vertex"); + xVertex = xVertex->NextSiblingElement("vertex")) { + + double x1 = xmltof(xVertex->Attribute("px")); + double y1 = xmltof(xVertex->Attribute("py")); + double x2 = xmltof(xVertex->NextSiblingElement("vertex")->Attribute("px")); + double y2 = xmltof(xVertex->NextSiblingElement("vertex")->Attribute("py")); + subroom->AddWall(Wall(Point(x1, y1), Point(x2, y2))); } } //looking for obstacles - int nObst = xSubroomsNode.nChildNode("obstacle"); - - for (int obst = 0; obst < nObst; obst++) { - XMLNode xObstacle = xSubroomsNode.getChildNode("obstacle", - obst); - int nPoly = xObstacle.nChildNode("polygon"); - int id = xmltof(xObstacle.getAttribute("id"), -1); - int height = xmltof(xObstacle.getAttribute("height"), 0); - double closed = xmltof(xObstacle.getAttribute("closed"), 0); - string caption = xmltoa(xObstacle.getAttribute("caption"), - "-1"); + for(TiXmlElement* xObstacle = xSubRoom->FirstChildElement("obstacle"); xObstacle; + xObstacle = xObstacle->NextSiblingElement("obstacle")) { + + int id = xmltof(xObstacle->Attribute("id"), -1); + int height = xmltof(xObstacle->Attribute("height"), 0); + double closed = xmltof(xObstacle->Attribute("closed"), 0); + string caption = xmltoa(xObstacle->Attribute("caption"),"-1"); Obstacle* obstacle = new Obstacle(); obstacle->SetId(id); @@ -358,30 +382,19 @@ void Building::LoadBuilding(string filename) { obstacle->SetClosed(closed); obstacle->SetHeight(height); - for (int p = 0; p < nPoly; p++) { - XMLNode xPolyVertices = xObstacle.getChildNode("polygon", - p); - int nVertices = - xObstacle.getChildNode("polygon", p).nChildNode( - "vertex"); - for (int v = 0; v < nVertices - 1; v++) { - double x1 = - xmltof( - xPolyVertices.getChildNode("vertex", v).getAttribute( - "px")); - double y1 = - xmltof( - xPolyVertices.getChildNode("vertex", v).getAttribute( - "py")); - - double x2 = - xmltof( - xPolyVertices.getChildNode("vertex", - v + 1).getAttribute("px")); - double y2 = - xmltof( - xPolyVertices.getChildNode("vertex", - v + 1).getAttribute("py")); + //looking for polygons (walls) + for(TiXmlElement* xPolyVertices = xObstacle->FirstChildElement("polygon"); xPolyVertices; + xPolyVertices = xPolyVertices->NextSiblingElement("polygon")) { + + for (TiXmlElement* xVertex = xPolyVertices->FirstChildElement( + "vertex"); + xVertex && xVertex != xPolyVertices->LastChild("vertex"); + xVertex = xVertex->NextSiblingElement("vertex")) { + + double x1 = xmltof(xVertex->Attribute("px")); + double y1 = xmltof(xVertex->Attribute("py")); + double x2 = xmltof(xVertex->NextSiblingElement("vertex")->Attribute("px")); + double y2 = xmltof(xVertex->NextSiblingElement("vertex")->Attribute("py")); obstacle->AddWall(Wall(Point(x1, y1), Point(x2, y2))); } } @@ -389,25 +402,21 @@ void Building::LoadBuilding(string filename) { } room->AddSubRoom(subroom); } + //parsing the crossings - XMLNode xCrossingsNode = xRoom.getChildNode("crossings"); - int nCrossing = xCrossingsNode.nChildNode("crossing"); + TiXmlNode* xCrossingsNode = xRoom->FirstChild("crossings"); + if(xCrossingsNode) + for(TiXmlElement* xCrossing = xCrossingsNode->FirstChildElement("crossing"); xCrossing; + xCrossing = xCrossing->NextSiblingElement("crossing")) { - //processing the rooms node - for (int i = 0; i < nCrossing; i++) { - XMLNode xCrossing = xCrossingsNode.getChildNode("crossing", i); - - int id = xmltoi(xCrossing.getAttribute("id"), -1); - int sub1_id = xmltoi(xCrossing.getAttribute("subroom1_id"), -1); - int sub2_id = xmltoi(xCrossing.getAttribute("subroom2_id"), -1); - double x1 = xmltof( - xCrossing.getChildNode("vertex", 0).getAttribute("px")); - double y1 = xmltof( - xCrossing.getChildNode("vertex", 0).getAttribute("py")); - double x2 = xmltof( - xCrossing.getChildNode("vertex", 1).getAttribute("px")); - double y2 = xmltof( - xCrossing.getChildNode("vertex", 1).getAttribute("py")); + int id = xmltoi(xCrossing->Attribute("id"), -1); + int sub1_id = xmltoi(xCrossing->Attribute("subroom1_id"), -1); + int sub2_id = xmltoi(xCrossing->Attribute("subroom2_id"), -1); + + double x1 = xmltof( xCrossing->FirstChildElement("vertex")->Attribute("px")); + double y1 = xmltof( xCrossing->FirstChildElement("vertex")->Attribute("py")); + double x2 = xmltof( xCrossing->LastChild("vertex")->ToElement()->Attribute("px")); + double y2 = xmltof( xCrossing->LastChild("vertex")->ToElement()->Attribute("py")); Crossing* c = new Crossing(); c->SetID(id); @@ -426,25 +435,28 @@ void Building::LoadBuilding(string filename) { AddRoom(room); } - // all rooms are read, now proceed with transitions - XMLNode xTransNode = xMainNode.getChildNode("transitions"); - int nTrans = xTransNode.nChildNode("transition"); - for (int i = 0; i < nTrans; i++) { - XMLNode xTrans = xTransNode.getChildNode("transition", i); + // all rooms are read, now proceed with transitions + TiXmlNode* xTransNode = xRootNode->FirstChild("transitions"); + if(xTransNode) + for(TiXmlElement* xTrans = xTransNode->FirstChildElement("transition"); xTrans; + xTrans = xTrans->NextSiblingElement("transition")) { - int id = xmltoi(xTrans.getAttribute("id"), -1); + int id = xmltoi(xTrans->Attribute("id"), -1); string caption = "door " + id; - caption = xmltoa(xTrans.getAttribute("caption"), caption.c_str()); - int room1_id = xmltoi(xTrans.getAttribute("room1_id"), -1); - int room2_id = xmltoi(xTrans.getAttribute("room2_id"), -1); - int subroom1_id = xmltoi(xTrans.getAttribute("subroom1_id"), -1); - int subroom2_id = xmltoi(xTrans.getAttribute("subroom2_id"), -1); - double x1 = xmltof(xTrans.getChildNode("vertex", 0).getAttribute("px")); - double y1 = xmltof(xTrans.getChildNode("vertex", 0).getAttribute("py")); - double x2 = xmltof(xTrans.getChildNode("vertex", 1).getAttribute("px")); - double y2 = xmltof(xTrans.getChildNode("vertex", 1).getAttribute("py")); - string type = xmltoa(xTrans.getAttribute("type"), "normal"); + caption = xmltoa(xTrans->Attribute("caption"), caption.c_str()); + int room1_id = xmltoi(xTrans->Attribute("room1_id"), -1); + int room2_id = xmltoi(xTrans->Attribute("room2_id"), -1); + int subroom1_id = xmltoi(xTrans->Attribute("subroom1_id"), -1); + int subroom2_id = xmltoi(xTrans->Attribute("subroom2_id"), -1); + string type = xmltoa(xTrans->Attribute("type"), "normal"); + + double x1 = xmltof( xTrans->FirstChildElement("vertex")->Attribute("px")); + double y1 = xmltof( xTrans->FirstChildElement("vertex")->Attribute("py")); + + double x2 = xmltof( xTrans->LastChild("vertex")->ToElement()->Attribute("px")); + double y2 = xmltof( xTrans->LastChild("vertex")->ToElement()->Attribute("py")); + Transition* t = new Transition(); t->SetID(id); @@ -481,6 +493,8 @@ void Building::LoadBuilding(string filename) { AddTransition(t); } + + Log->Write("INFO: \tLoading building file successful!!!\n"); } @@ -620,131 +634,13 @@ Crossing* Building::GetGoal(string caption) const { exit(EXIT_FAILURE); } - - -void Building::LoadTrafficInfo(string filename) { - - Log->Write("INFO:\tLoading the traffic info file"); - - if (filename == "") { - Log->Write("INFO:\t No file supplied !"); - Log->Write("INFO:\t done with loading traffic info file"); - return; - } - - XMLNode xMainNode = XMLNode::openFileHelper(filename.c_str(), "traffic"); - - double version = xmltof(xMainNode.getAttribute("version"), -1); - if (version < 0.4) { - Log->Write("ERROR: \tOnly version > 0.4 supported"); - Log->Write("ERROR: \tparsing traffic file failed!"); - exit(EXIT_FAILURE); - } - - //The file has two main nodes - //<rooms> and <transitions> - - XMLNode xRoomsNode = xMainNode.getChildNode("rooms"); - int nRooms = xRoomsNode.nChildNode("room"); - - //processing the rooms node - for (int i = 0; i < nRooms; i++) { - XMLNode xRoom = xRoomsNode.getChildNode("room", i); - double id = xmltof(xRoom.getAttribute("room_id"), -1); - string state = xmltoa(xRoom.getAttribute("state"), "good"); - RoomState status = (state == "good") ? ROOM_CLEAN : ROOM_SMOKED; - _rooms[id]->SetState(status); - } - - //processing the doors node - XMLNode xDoorsNode = xMainNode.getChildNode("doors"); - int nDoors = xDoorsNode.nChildNode("door"); - - for (int i = 0; i < nDoors; i++) { - XMLNode xDoor = xDoorsNode.getChildNode("door", i); - int id = xmltoi(xDoor.getAttribute("trans_id"), -1); - string state = xmltoa(xDoor.getAttribute("state"), "open"); - - //maybe the door caption is specified ? - if(id==-1){ - string caption=xmltoa(xDoor.getAttribute("caption"), "-1"); - if( (caption!="-1") && (state =="close") ){ - GetTransition(caption)->Close(); - } - } - else { - //store transition in a map and call getTransition/getCrossin - if (state == "open") { - GetTransition(id)->Open(); - } else if (state == "close") { - GetTransition(id)->Close(); - } else { - char tmp[CLENGTH]; - sprintf(tmp, "WARNING:\t Unknown door state: %s", state.c_str()); - Log->Write(tmp); - } - } - } - Log->Write("INFO:\t done with loading traffic info file"); -} - -void Building::StringExplode(string str, string separator, - vector<string>* results) { - size_t found; - found = str.find_first_of(separator); - while (found != string::npos) { - if (found > 0) { - results->push_back(str.substr(0, found)); - } - str = str.substr(found + 1); - found = str.find_first_of(separator); - } - if (str.length() > 0) { - results->push_back(str); - } -} - - -//void Building::InitRoomsAndSubroomsMap(){ -// Log->write("INFO: \tcreating the rooms maps!!!\n"); -// -// for (int i=0;i<16;i++) -// for (int j=0;j<130;j++) -// for (int k=0;k<16;k++) -// for (int l=0;l<130;l++) -// pSubroomConnectionMap[i][j][k][l]=0; -// -// //create the subroom connections map -// for (unsigned int r1=0;r1< pRooms.size();r1++){ -// Room* room1= GetRoom(r1); -// const vector <SubRoom*> sb1s=room1->GetAllSubRooms(); -// -// for (unsigned int r2=0;r2<pRooms.size();r2++){ -// Room* room2= GetRoom(r2); -// const vector <SubRoom*> sb2s=room2->GetAllSubRooms(); -// -// // now looping over all subrooms -// for(unsigned int s1=0;s1<sb1s.size();s1++){ -// for(unsigned int s2=0;s2<sb2s.size();s2++){ -// if(sb1s[s1]->IsDirectlyConnectedWith(sb2s[s2])){ -// pSubroomConnectionMap[r1][s1][r2][s2]=1; -// //cout<<"connected"<<endl; -// }else{ -// pSubroomConnectionMap[r1][s1][r2][s2]=0; -// } -// } -// } -// } -// } -//} - #ifdef _SIMULATOR void Building::Update() { // some peds may change the room via another crossing than the primary intended one // in that case they are set in the wrong room. vector<Pedestrian*> nonConformPeds; - + printf("enter Building::update\n"); for (int i = 0; i < GetNumberOfRooms(); i++) { Room* room = GetRoom(i); @@ -852,20 +748,17 @@ void Building::Update() { // find the new goals, the parallel way unsigned int nSize = _allPedestians.size(); - int nThreads = 1; - -#ifdef _OPENMP - nThreads = omp_get_max_threads(); -#endif + int nThreads = omp_get_max_threads(); // check if worth sharing the work - if (nSize < 12) - nThreads = 1; + //if (nSize < 12) + // nThreads = 1; + int partSize = nSize / nThreads; #pragma omp parallel default(shared) num_threads(nThreads) { - const int threadID = omp_get_thread_num(); + const int threadID = omp_get_thread_num(); int start = threadID * partSize; int end = (threadID + 1) * partSize - 1; if ((threadID == nThreads - 1)) @@ -885,6 +778,7 @@ void Building::Update() { //CleanUpTheScene(); } + void Building::InitPhiAllPeds(double pDt) { for (int i = 0; i < GetNumberOfRooms(); i++) { Room* room = GetRoom(i); @@ -894,9 +788,12 @@ void Building::InitPhiAllPeds(double pDt) { double cosPhi, sinPhi; Pedestrian* ped = sub->GetPedestrian(k); ped->Setdt(pDt); //set the simulation step + ped->SetRoomID(room->GetID(), room->GetCaption()); //a destination could not be found for that pedestrian if (ped->FindRoute() == -1) { - DeletePedFromSim(ped); + // DeletePedFromSim(ped); + sub->DeletePedestrian(k--); + continue; } Line* e = ped->GetExitLine(); const Point& e1 = e->GetPoint1(); @@ -918,7 +815,6 @@ void Building::InitPhiAllPeds(double pDt) { E.SetCosPhi(cosPhi); E.SetSinPhi(sinPhi); ped->SetEllipse(E); - ped->SetRoomID(room->GetID(), room->GetCaption()); } } } @@ -1005,88 +901,6 @@ void Building::InitGrid(double cellSize) { -void Building::InitSavePedPathway(string filename) { - _pathWayStream.open(filename.c_str()); - _savePathway = true; - - if (_pathWayStream.is_open()) { - Log->Write("#INFO:\tsaving pedestrian paths to [ " + filename + " ]"); - _pathWayStream << "##pedestrian ways" << endl; - _pathWayStream << "#nomenclature roomid caption" << endl; - // for (unsigned int r=0;r< pRooms.size();r++){ - // Room* room= GetRoom(r); - // const vector<int>& goals=room->GetAllTransitionsIDs(); - // - // for(unsigned int g=0;g<goals.size();g++){ - // int exitid=goals[g]; - // string exit_caption=pRouting->GetGoal(exitid)->GetCaption(); - // PpathWayStream<<exitid<<" "<<exit_caption<<endl; - // } - // } - // - _pathWayStream << "#data room exit_id" << endl; - } else { - Log->Write("#INFO:\t Unable to open [ " + filename + " ]"); - Log->Write("#INFO:\t saving to stdout"); - - } -} - -void Building::CleanUpTheScene() { - //return; - static int counter = 0; - counter++; - static int totalSliced = 0; - - int updateRate = 80.0 / 0.01; // 20 seconds/pDt - - if (counter % updateRate == 0) { - for (unsigned int i = 0; i < _allPedestians.size(); i++) { - Pedestrian* ped = _allPedestians[i]; - - if (ped->GetDistanceSinceLastRecord() < 0.1) { - //delete from the simulation - DeletePedFromSim(ped); - - totalSliced++; - char msg[CLENGTH]; - sprintf(msg, "INFO:\t slicing Ped %d from room %s, total [%d]", - ped->GetID(), - _rooms[ped->GetRoomID()]->GetCaption().c_str(), - totalSliced); - Log->Write(msg); - } else { - ped->RecordActualPosition(); - } - - } - } - -} - -//bool Building::IsDirectlyConnected(int room1, int subroom1, int room2, -// int subroom2) { -// return pSubroomConnectionMap[room1][subroom1][room2][subroom2]; -//} - - - -Pedestrian* Building::GetPedestrian(int pedID) const { - for (unsigned int i = 0; i < _rooms.size(); i++) { - Room* room = _rooms[i]; - for (int j = 0; j < room->GetNumberOfSubRooms(); j++) { - SubRoom* sub = room->GetSubRoom(j); - for (int k = 0; k < sub->GetNumberOfPedestrians(); k++) { - Pedestrian* p = sub->GetPedestrian(k); - if (p->GetID() == pedID) { - return p; - } - } - } - } - return NULL; -} - void Building::DumpSubRoomInRoom(int roomID, int subID) { SubRoom* sub = GetRoom(roomID)->GetSubRoom(subID); if (sub->GetNumberOfPedestrians() == 0) @@ -1097,8 +911,10 @@ void Building::DumpSubRoomInRoom(int roomID, int subID) { cout << " ID: " << ped->GetID(); cout << " Index: " << p << endl; } + } + void Building::LoadRoutingInfo(string filename) { Log->Write("INFO:\tLoading extra routing information"); if (filename == "") { @@ -1107,36 +923,50 @@ void Building::LoadRoutingInfo(string filename) { return; } - XMLNode xMainNode = XMLNode::openFileHelper(filename.c_str(), "routing"); + TiXmlDocument docRouting(filename); + if (!docRouting.LoadFile()){ + Log->Write("ERROR: \t%s", docRouting.ErrorDesc()); + Log->Write("ERROR: \t could not parse the routing file"); + exit(EXIT_FAILURE); + } + + TiXmlElement* xRootNode = docRouting.RootElement(); + if( ! xRootNode ) { + Log->Write("ERROR:\tRoot element does not exist"); + exit(EXIT_FAILURE); + } + + if( xRootNode->ValueStr () != "routing" ) { + Log->Write("ERROR:\tRoot element value is not 'routing'."); + exit(EXIT_FAILURE); + } - double version = xmltof(xMainNode.getAttribute("version"), -1); + double version = xmltof(xRootNode->Attribute("version"), -1); if (version < 0.4) { Log->Write("ERROR: \tOnly version > 0.4 supported"); Log->Write("ERROR: \tparsing routing file failed!"); exit(EXIT_FAILURE); } - //actually only contains one Hlines node - XMLNode xHlinesNode = xMainNode.getChildNode("Hlines"); - int nHlines = xHlinesNode.nChildNode("Hline"); + //parsing the crossings + TiXmlNode* xHlinesNode = xRootNode->FirstChild("Hlines"); - //processing the rooms node - for (int i = 0; i < nHlines; i++) { - XMLNode hline = xHlinesNode.getChildNode("hline", i); - double id = xmltof(hline.getAttribute("id"), -1); - int room_id = xmltoi(hline.getAttribute("room_id"), -1); - int subroom_id = xmltoi(hline.getAttribute("subroom_id"), -1); + if(xHlinesNode) + for(TiXmlElement* hline = xHlinesNode->FirstChildElement("Hline"); hline; + hline = hline->NextSiblingElement("Hline")) { - double x1 = xmltof(hline.getChildNode("vertex", 0).getAttribute("px")); - double y1 = xmltof(hline.getChildNode("vertex", 0).getAttribute("py")); - double x2 = xmltof(hline.getChildNode("vertex", 1).getAttribute("px")); - double y2 = xmltof(hline.getChildNode("vertex", 1).getAttribute("py")); + double id = xmltof(hline->Attribute("id"), -1); + int room_id = xmltoi(hline->Attribute("room_id"), -1); + int subroom_id = xmltoi(hline->Attribute("subroom_id"), -1); + double x1 = xmltof( hline->FirstChildElement("vertex")->Attribute("px")); + double y1 = xmltof( hline->FirstChildElement("vertex")->Attribute("py")); + double x2 = xmltof( hline->LastChild("vertex")->ToElement()->Attribute("px")); + double y2 = xmltof( hline->LastChild("vertex")->ToElement()->Attribute("py")); Room* room = _rooms[room_id]; SubRoom* subroom = room->GetSubRoom(subroom_id); - //new implementation Hline* h = new Hline(); h->SetID(id); @@ -1147,22 +977,21 @@ void Building::LoadRoutingInfo(string filename) { AddHline(h); subroom->AddHline(h); - } //load the pre-defined trips - XMLNode xTripsNode = xMainNode.getChildNode("trips"); - int nTrips = xTripsNode.nChildNode("trip"); + TiXmlNode* xTripsNode = xRootNode->FirstChild("trips"); - //processing the rooms node - for (int i = 0; i < nTrips; i++) { - XMLNode trip = xTripsNode.getChildNode("trip", i); - double id = xmltof(trip.getAttribute("id"), -1); + if(xTripsNode) + for(TiXmlElement* trip = xTripsNode->FirstChildElement("trip"); trip; + trip = trip->NextSiblingElement("trip")) { + + double id = xmltof(trip->Attribute("id"), -1); if (id == -1) { Log->Write("ERROR:\t id missing for trip"); exit(EXIT_FAILURE); } - string sTrip = trip.getText(); + string sTrip = trip->FirstChild()->ValueStr(); vector<string> vTrip; vTrip.clear(); @@ -1174,7 +1003,84 @@ void Building::LoadRoutingInfo(string filename) { } _routingEngine->AddTrip(vTrip); } - Log->Write("INFO:\t done with loading extra routing information"); + Log->Write("INFO:\tdone with loading extra routing information"); +} + +void Building::LoadTrafficInfo(string filename) { + + Log->Write("INFO:\tLoading the traffic info file"); + + if (filename == "") { + Log->Write("INFO:\t No file supplied !"); + Log->Write("INFO:\tdone with loading traffic info file"); + return; + } + + TiXmlDocument docTraffic(filename); + if (!docTraffic.LoadFile()){ + Log->Write("ERROR: \t%s", docTraffic.ErrorDesc()); + Log->Write("ERROR: \t could not parse the traffic file"); + exit(EXIT_FAILURE); + } + + TiXmlElement* xRootNode = docTraffic.RootElement(); + if( ! xRootNode ) { + Log->Write("ERROR:\tRoot element does not exist"); + exit(EXIT_FAILURE); + } + + if( xRootNode->ValueStr () != "traffic" ) { + Log->Write("ERROR:\tRoot element value is not 'traffic'."); + exit(EXIT_FAILURE); + } + + double version = xmltof(xRootNode->Attribute("version"), -1); + if (version < 0.4) { + Log->Write("ERROR: \tOnly version > 0.4 supported"); + Log->Write("ERROR: \tparsing traffic file failed!"); + exit(EXIT_FAILURE); + } + + //processing the rooms node + TiXmlNode* xRoomsNode = xRootNode->FirstChild("rooms"); + for(TiXmlElement* xRoom = xRoomsNode->FirstChildElement("room"); xRoom; + xRoom = xRoom->NextSiblingElement("room")) { + + double id = xmltof(xRoom->Attribute("room_id"), -1); + string state = xmltoa(xRoom->Attribute("state"), "good"); + RoomState status = (state == "good") ? ROOM_CLEAN : ROOM_SMOKED; + _rooms[id]->SetState(status); + } + + //processing the doors node + TiXmlNode* xDoorsNode = xRootNode->FirstChild("doors"); + for(TiXmlElement* xDoor = xDoorsNode->FirstChildElement("door"); xDoor; + xDoor = xDoor->NextSiblingElement("door")) { + + int id = xmltoi(xDoor->Attribute("trans_id"), -1); + string state = xmltoa(xDoor->Attribute("state"), "open"); + + //maybe the door caption is specified ? + if(id==-1){ + string caption=xmltoa(xDoor->Attribute("caption"), "-1"); + if( (caption!="-1") && (state =="close") ){ + GetTransition(caption)->Close(); + } + } + else { + //store transition in a map and call getTransition/getCrossin + if (state == "open") { + GetTransition(id)->Open(); + } else if (state == "close") { + GetTransition(id)->Close(); + } else { + char tmp[CLENGTH]; + sprintf(tmp, "WARNING:\t Unknown door state: %s", state.c_str()); + Log->Write(tmp); + } + } + } + Log->Write("INFO:\t done with loading traffic info file"); } void Building::DeletePedestrian(Pedestrian* ped) { @@ -1234,6 +1140,99 @@ void Building::AddPedestrian(Pedestrian* ped) { } + +void Building::InitSavePedPathway(string filename) { + _pathWayStream.open(filename.c_str()); + _savePathway = true; + + if (_pathWayStream.is_open()) { + Log->Write("#INFO:\tsaving pedestrian paths to [ " + filename + " ]"); + _pathWayStream << "##pedestrian ways" << endl; + _pathWayStream << "#nomenclature roomid caption" << endl; + // for (unsigned int r=0;r< pRooms.size();r++){ + // Room* room= GetRoom(r); + // const vector<int>& goals=room->GetAllTransitionsIDs(); + // + // for(unsigned int g=0;g<goals.size();g++){ + // int exitid=goals[g]; + // string exit_caption=pRouting->GetGoal(exitid)->GetCaption(); + // PpathWayStream<<exitid<<" "<<exit_caption<<endl; + // } + // } + // + _pathWayStream << "#data room exit_id" << endl; + } else { + Log->Write("#INFO:\t Unable to open [ " + filename + " ]"); + Log->Write("#INFO:\t saving to stdout"); + + } +} + +void Building::CleanUpTheScene() { + //return; + static int counter = 0; + counter++; + static int totalSliced = 0; + + int updateRate = 80.0 / 0.01; // 20 seconds/pDt + + if (counter % updateRate == 0) { + for (unsigned int i = 0; i < _allPedestians.size(); i++) { + Pedestrian* ped = _allPedestians[i]; + + if (ped->GetDistanceSinceLastRecord() < 0.1) { + //delete from the simulation + DeletePedFromSim(ped); + + totalSliced++; + char msg[CLENGTH]; + sprintf(msg, "INFO:\t slicing Ped %d from room %s, total [%d]", + ped->GetID(), + _rooms[ped->GetRoomID()]->GetCaption().c_str(), + totalSliced); + Log->Write(msg); + } else { + ped->RecordActualPosition(); + } + + } + } + +} + + +void Building::StringExplode(string str, string separator, + vector<string>* results) { + size_t found; + found = str.find_first_of(separator); + while (found != string::npos) { + if (found > 0) { + results->push_back(str.substr(0, found)); + } + str = str.substr(found + 1); + found = str.find_first_of(separator); + } + if (str.length() > 0) { + results->push_back(str); + } +} + +Pedestrian* Building::GetPedestrian(int pedID) const { + for (unsigned int i = 0; i < _rooms.size(); i++) { + Room* room = _rooms[i]; + for (int j = 0; j < room->GetNumberOfSubRooms(); j++) { + SubRoom* sub = room->GetSubRoom(j); + for (int k = 0; k < sub->GetNumberOfPedestrians(); k++) { + Pedestrian* p = sub->GetPedestrian(k); + if (p->GetID() == pedID) { + return p; + } + } + } + } + return NULL; +} + int Building::GetNumberOfPedestrians() const { int sum = 0; for (unsigned int wa = 0; wa < _rooms.size(); wa++) { @@ -1242,12 +1241,7 @@ int Building::GetNumberOfPedestrians() const { return sum; } -LCGrid* Building::GetGrid() const { - return _linkedCellGrid; -} - // FIXME: you should get rid of this method - Crossing* Building::GetGoal(int index) { if (_transitions.count(index) == 1) { return _transitions[index]; @@ -1270,4 +1264,5 @@ Crossing* Building::GetGoal(int index) { } } -#endif //_SIMULATOR + +#endif // _SIMULATOR diff --git a/geometry/Building.h b/geometry/Building.h index abfb45e290dff59f0454d7b1ab1c844630fec935..5fe4869b16cacc8a6847a0169acdaa0da666b667 100644 --- a/geometry/Building.h +++ b/geometry/Building.h @@ -121,7 +121,6 @@ public: void AddHline(Hline* line); - // Ein-Ausgabe void LoadBuilding(std::string filename); // Laedt Geometrie-Datei void LoadTrafficInfo(std::string filename); @@ -135,7 +134,6 @@ public: //bool IsDirectlyConnected(int room1, int subroom1,int room2, int subroom2); private: - // wird nur innerhalb von Building benötigt void StringExplode(std::string str, std::string separator, std::vector<std::string>* results); }; diff --git a/geometry/Line.cpp b/geometry/Line.cpp index d3581af5d8a1d38dbc6b0e2e531745d07653ddc8..b3bd07ed4d98300b4abddf3c826f6b23d42e5266 100644 --- a/geometry/Line.cpp +++ b/geometry/Line.cpp @@ -31,7 +31,6 @@ #include <cmath> #include <sstream> -#include <cstdlib> int Line::_static_UID=0; @@ -132,7 +131,7 @@ Point Line::NormalVec() const { norm = sqrt(nx * nx + ny * ny); if (fabs(norm) < J_EPS) { Log->Write("ERROR: \tLine::NormalVec() norm==0\n"); - exit(EXIT_FAILURE); + exit(0); } nx /= norm; ny /= norm; @@ -222,7 +221,7 @@ bool Line::IsInLine(const Point& p) const { lambda = (py - ay) / (by - ay); } else { Log->Write("ERROR: \tIsInLine: Endpunkt = Startpunkt!!!"); - exit(EXIT_FAILURE); + exit(0); } return (0 <= lambda) && (lambda <= 1); } @@ -293,12 +292,12 @@ double Line::LengthSquare() const { bool Line::IntersectionWith(const Line& l) const { - double deltaACy = this->_point1.GetY() - l.GetPoint1().GetY(); + double deltaACy = _point1.GetY() - l.GetPoint1().GetY(); double deltaDCx = l.GetPoint2().GetX() - l.GetPoint1().GetX(); - double deltaACx = this->_point1.GetX() - l.GetPoint1().GetX(); + double deltaACx = _point1.GetX() - l.GetPoint1().GetX(); double deltaDCy = l.GetPoint2().GetY() - l.GetPoint1().GetY(); - double deltaBAx = this->_point2.GetX() - this->_point1.GetX(); - double deltaBAy = this->_point2.GetY() - this->_point1.GetY(); + double deltaBAx = _point2.GetX() - _point1.GetX(); + double deltaBAy = _point2.GetY() - _point1.GetY(); double denominator = deltaBAx * deltaDCy - deltaBAy * deltaDCx; double numerator = deltaACy * deltaDCx - deltaACx * deltaDCy; @@ -335,6 +334,29 @@ bool Line::IntersectionWith(const Line& l) const { return true; } +bool Line::IsHorizontal(){ + return fabs (_point1._y-_point2._y ) <= J_EPS; +} + +bool Line::IsVertical(){ + return fabs (_point1._x-_point2._x ) <= J_EPS; +} + +int Line::WichSide(const Point& pt) { + //special case for horizontal lines + if (IsVertical()) { + //left + if (pt._x < _point1._x) + return 0; + //right or colinear + if (pt._x >= _point1._x) + return 1; + } + + return ((_point2._x - _point1._x) * (pt._y - _point1._y) + - (_point2._y - _point1._y) * (pt._x - _point1._x)) > 0; +} + bool Line::IntersectionWithCircle(const Point& centre, double radius /*cm for pedestrians*/){ double r=radius; diff --git a/geometry/Line.h b/geometry/Line.h index 834003de320b77eaa8f33b9a38e8a7ef789f7cc9..eb707b911f303a5c0f3ef56e6dcb4835fb76f595 100644 --- a/geometry/Line.h +++ b/geometry/Line.h @@ -71,7 +71,6 @@ public: */ void SetPoint2(const Point& p); - /** * Set/Get the first end point of the line */ @@ -87,7 +86,6 @@ public: */ const Point& GetCentre(void) const; - /** * @return a normal vector to this line */ @@ -130,7 +128,6 @@ public: */ double DistToSquare(const Point& p) const; - /** * @return the length (Norm) of the line */ @@ -165,6 +162,27 @@ public: */ bool IntersectionWithCircle(const Point& centre, double radius=0.30 /*m for pedestrians*/); + /** + * return the same value if the checked points are all situated on the same side. + * @return 0 or 1 depending on which side of the line the point is located. + */ + int WichSide (const Point& pt); + + /** + * @return true if the point is located in the left hand side of the line. + * For horizontal lines return true if the point is above the line. + */ + bool IsLeft (const Point& pt); + + /** + * @return true for horizontal lines + */ + bool IsHorizontal(); + + /** + * @return true for vertical lines + */ + bool IsVertical(); /** * @return a nice formated string describing the line diff --git a/geometry/Obstacle.cpp b/geometry/Obstacle.cpp index d0ba34d676dd9b36da49b3da27c3d075d7f22dc9..254d4996d2bd62764b702b4f8fd0f077c871339b 100644 --- a/geometry/Obstacle.cpp +++ b/geometry/Obstacle.cpp @@ -30,7 +30,6 @@ #include "Wall.h" #include "Point.h" -#include <cstdlib> using namespace std; diff --git a/geometry/Room.cpp b/geometry/Room.cpp index cefd707c6830cd0eaf2d513e4d9d6b30fed939e1..40dab33a4ddb479b89f766dd32495e7b22d100e6 100644 --- a/geometry/Room.cpp +++ b/geometry/Room.cpp @@ -122,9 +122,24 @@ SubRoom* Room::GetSubRoom(int index) const { } +#ifdef _SIMULATOR + +int Room::GetNumberOfPedestrians() const { + int sum = 0; + for (int i = 0; i < GetNumberOfSubRooms(); i++) { + sum += GetSubRoom(i)->GetNumberOfPedestrians(); + } + return sum; +} + +#endif // _SIMULATOR + RoomState Room::GetState() const { return _state; } + + + /************************************************************* Sonstige Funktionen ************************************************************/ @@ -141,6 +156,10 @@ void Room::DeleteSubRoom(int index) { } } +/************************************************************* + Ein-Ausgabe + ************************************************************/ + void Room::WriteToErrorLog() const { char tmp[CLENGTH]; @@ -171,15 +190,3 @@ void Room::SetOutputHandler(OutputHandler* oh){ OutputHandler* Room::GetOutputHandler() const { return _outputFile; } - -#ifdef _SIMULATOR - -int Room::GetNumberOfPedestrians() const { - int sum = 0; - for (int i = 0; i < GetNumberOfSubRooms(); i++) { - sum += GetSubRoom(i)->GetNumberOfPedestrians(); - } - return sum; -} - -#endif diff --git a/geometry/Room.h b/geometry/Room.h index 357c5f451f507d571e1e475668e2488c0e6f4637..7917089bc9899b8a92036f9c140313c95fd31be5 100644 --- a/geometry/Room.h +++ b/geometry/Room.h @@ -116,13 +116,11 @@ public: */ SubRoom* GetSubRoom(int index) const; -#ifdef _SIMULATOR /** * @return the number of pedestrians in the rooms (all subrooms) */ int GetNumberOfPedestrians() const; -#endif /** * @return the state for this room */ diff --git a/geometry/SubRoom.cpp b/geometry/SubRoom.cpp index e16f4eaf3899db9db83c368ec165441e6c621789..0d60b8808f5296e9385fdb6aa98ca3b4541467f3 100644 --- a/geometry/SubRoom.cpp +++ b/geometry/SubRoom.cpp @@ -33,14 +33,11 @@ #include "Wall.h" #ifdef _SIMULATOR - #include "../pedestrian/Pedestrian.h" - -#endif +#endif //_SIMULATOR #include <cmath> - using namespace std; /************************************************************ @@ -71,7 +68,7 @@ SubRoom::SubRoom() { #ifdef _SIMULATOR _peds = vector<Pedestrian* > (); -#endif +#endif //_SIMULATOR } @@ -87,24 +84,23 @@ SubRoom::SubRoom(const SubRoom& orig) { #ifdef _SIMULATOR _peds = orig.GetAllPedestrians(); -#endif - +#endif //_SIMULATOR } SubRoom::~SubRoom() { if (_walls.size() > 0) _walls.clear(); if (_poly.size() > 0) _poly.clear(); + for (unsigned int i = 0; i < _obstacles.size(); i++) { + delete _obstacles[i]; + } + _obstacles.clear(); #ifdef _SIMULATOR for (unsigned int i = 0; i < _peds.size(); i++) { delete _peds[i]; } -#endif +#endif //_SIMULATOR - for (unsigned int i = 0; i < _obstacles.size(); i++) { - delete _obstacles[i]; - } - _obstacles.clear(); } // Setter -Funktionen @@ -120,29 +116,8 @@ void SubRoom::SetRoomID(int ID) { _roomID = ID; } -//void SubRoom::SetAllWalls(const vector<Wall>& walls) { -// _walls = walls; -//} -// -//void SubRoom::SetWall(const Wall& wall, int index) { -// if ((index >= 0) && (index < GetAnzWalls())) { -// _walls[index] = wall; -// } else { -// Log->Write("ERROR: Wrong Index in SubRoom::SetWall()"); -// exit(0); -// } -//} - -//void SubRoom::SetPolygon(const vector<Point>& poly) { -// _poly = poly; -//} - -//void SubRoom::SetArea(double a) { -// _area = a; -//} -// Getter - Funktionen int SubRoom::GetSubRoomID() const { return _id; @@ -188,79 +163,22 @@ const vector<Point>& SubRoom::GetPolygon() const { return _poly; } + const vector<Obstacle*>& SubRoom::GetAllObstacles() const { return _obstacles; } -void SubRoom::AddObstacle(Obstacle* obs){ - _obstacles.push_back(obs); -} -#ifdef _SIMULATOR -void SubRoom::SetAllPedestrians(const vector<Pedestrian*>& peds) { - _peds = peds; -} - -void SubRoom::SetPedestrian(Pedestrian* ped, int index) { - if ((index >= 0) && (index < GetNumberOfPedestrians())) { - _peds[index] = ped; - } else { - Log->Write("ERROR: Wrong Index in SubRoom::SetPedestrian()"); - exit(0); - } -} -int SubRoom::GetNumberOfPedestrians() const { - return _peds.size(); -} - -const vector<Pedestrian*>& SubRoom::GetAllPedestrians() const { - return _peds; -} - -Pedestrian* SubRoom::GetPedestrian(int index) const { - if ((index >= 0) && (index < (int) GetNumberOfPedestrians())) - return _peds[index]; - else { - Log->Write("ERROR: Wrong 'index' in SubRoom::GetPedestrian()"); - exit(0); - } -} - - -void SubRoom::AddPedestrian(Pedestrian* ped) { - _peds.push_back(ped); -} - - -void SubRoom::DeletePedestrian(int index) { - if ((index >= 0) && (index < (int) GetNumberOfPedestrians())) { - _peds.erase(_peds.begin() + index); - - } else { - Log->Write("ERROR: Wrong Index in SubRoom::DeletePedestrian()"); - exit(0); - } -} - -bool SubRoom::IsInSubRoom(Pedestrian* ped) const { - Point pos = ped->GetPos(); - if (ped->GetExitLine()->DistTo(pos) <= J_EPS_GOAL) - return true; - else - return IsInSubRoom(pos); +int SubRoom::GetNumberOfGoalIDs() const { + return _goalIDs.size(); } -void SubRoom::ClearAllPedestrians(){ - for(unsigned int p=0;p<_peds.size();p++){ - delete _peds[p]; - } - _peds.clear(); +const vector<int>& SubRoom::GetAllGoalIDs() const { + return _goalIDs; } -#endif - // Sonstiges void SubRoom::AddWall(const Wall& w) { @@ -268,16 +186,11 @@ void SubRoom::AddWall(const Wall& w) { } -int SubRoom::GetNumberOfGoalIDs() const { - return _goalIDs.size(); -} - -const vector<int>& SubRoom::GetAllGoalIDs() const { - return _goalIDs; +void SubRoom::AddObstacle(Obstacle* obs){ + _obstacles.push_back(obs); } - void SubRoom::AddGoalID(int ID) { _goalIDs.push_back(ID); } @@ -506,12 +419,12 @@ const double* SubRoom::GetPlanEquation() const { return _planeEquation; } - - - double SubRoom::GetElevation(const Point& p) { return _planeEquation[0] * p._x + _planeEquation[1] * p._y + _planeEquation[2]; } + + + /************************************************************ NormalSubRoom ************************************************************/ @@ -902,3 +815,70 @@ bool Stair::IsInSubRoom(const Point& ped) const { return rueck; } + +#ifdef _SIMULATOR + +void SubRoom::SetAllPedestrians(const vector<Pedestrian*>& peds) { + _peds = peds; +} + +void SubRoom::SetPedestrian(Pedestrian* ped, int index) { + if ((index >= 0) && (index < GetNumberOfPedestrians())) { + _peds[index] = ped; + } else { + Log->Write("ERROR: Wrong Index in SubRoom::SetPedestrian()"); + exit(0); + } +} + +bool SubRoom::IsInSubRoom(Pedestrian* ped) const { + Point pos = ped->GetPos(); + if (ped->GetExitLine()->DistTo(pos) <= J_EPS_GOAL) + return true; + else + return IsInSubRoom(pos); +} + + +int SubRoom::GetNumberOfPedestrians() const { + return _peds.size(); +} + +const vector<Pedestrian*>& SubRoom::GetAllPedestrians() const { + return _peds; +} + + +Pedestrian* SubRoom::GetPedestrian(int index) const { + if ((index >= 0) && (index < (int) GetNumberOfPedestrians())) + return _peds[index]; + else { + Log->Write("ERROR: Wrong 'index' in SubRoom::GetPedestrian()"); + exit(0); + } +} + + +void SubRoom::AddPedestrian(Pedestrian* ped) { + _peds.push_back(ped); +} + + +void SubRoom::DeletePedestrian(int index) { + if ((index >= 0) && (index < (int) GetNumberOfPedestrians())) { + _peds.erase(_peds.begin() + index); + + } else { + Log->Write("ERROR: Wrong Index in SubRoom::DeletePedestrian()"); + exit(0); + } +} + +void SubRoom::ClearAllPedestrians(){ + for(unsigned int p=0;p<_peds.size();p++){ + delete _peds[p]; + } + _peds.clear(); +} + +#endif // _SIMULATOR diff --git a/tinyxml/tinystr.cpp b/tinyxml/tinystr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..066576820516de3bae97b08f9258468f9cc136f4 --- /dev/null +++ b/tinyxml/tinystr.cpp @@ -0,0 +1,111 @@ +/* +www.sourceforge.net/projects/tinyxml + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TIXML_USE_STL + +#include "tinystr.h" + +// Error value for find primitive +const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1); + + +// Null rep. +TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } }; + + +void TiXmlString::reserve (size_type cap) +{ + if (cap > capacity()) + { + TiXmlString tmp; + tmp.init(length(), cap); + memcpy(tmp.start(), data(), length()); + swap(tmp); + } +} + + +TiXmlString& TiXmlString::assign(const char* str, size_type len) +{ + size_type cap = capacity(); + if (len > cap || cap > 3*(len + 8)) + { + TiXmlString tmp; + tmp.init(len); + memcpy(tmp.start(), str, len); + swap(tmp); + } + else + { + memmove(start(), str, len); + set_size(len); + } + return *this; +} + + +TiXmlString& TiXmlString::append(const char* str, size_type len) +{ + size_type newsize = length() + len; + if (newsize > capacity()) + { + reserve (newsize + capacity()); + } + memmove(finish(), str, len); + set_size(newsize); + return *this; +} + + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) +{ + TiXmlString tmp; + tmp.reserve(a.length() + b.length()); + tmp += a; + tmp += b; + return tmp; +} + +TiXmlString operator + (const TiXmlString & a, const char* b) +{ + TiXmlString tmp; + TiXmlString::size_type b_len = static_cast<TiXmlString::size_type>( strlen(b) ); + tmp.reserve(a.length() + b_len); + tmp += a; + tmp.append(b, b_len); + return tmp; +} + +TiXmlString operator + (const char* a, const TiXmlString & b) +{ + TiXmlString tmp; + TiXmlString::size_type a_len = static_cast<TiXmlString::size_type>( strlen(a) ); + tmp.reserve(a_len + b.length()); + tmp.append(a, a_len); + tmp += b; + return tmp; +} + + +#endif // TIXML_USE_STL diff --git a/tinyxml/tinystr.h b/tinyxml/tinystr.h new file mode 100644 index 0000000000000000000000000000000000000000..89cca3341564c0337e3e940bfa4ae053e84e656c --- /dev/null +++ b/tinyxml/tinystr.h @@ -0,0 +1,305 @@ +/* +www.sourceforge.net/projects/tinyxml + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TIXML_USE_STL + +#ifndef TIXML_STRING_INCLUDED +#define TIXML_STRING_INCLUDED + +#include <assert.h> +#include <string.h> + +/* The support for explicit isn't that universal, and it isn't really + required - it is used to check that the TiXmlString class isn't incorrectly + used. Be nice to old compilers and macro it here: +*/ +#if defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + #define TIXML_EXPLICIT explicit +#elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + #define TIXML_EXPLICIT explicit +#else + #define TIXML_EXPLICIT +#endif + + +/* + TiXmlString is an emulation of a subset of the std::string template. + Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. + Only the member functions relevant to the TinyXML project have been implemented. + The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase + a string and there's no more room, we allocate a buffer twice as big as we need. +*/ +class TiXmlString +{ + public : + // The size type used + typedef size_t size_type; + + // Error value for find primitive + static const size_type npos; // = -1; + + + // TiXmlString empty constructor + TiXmlString () : rep_(&nullrep_) + { + } + + // TiXmlString copy constructor + TiXmlString ( const TiXmlString & copy) : rep_(0) + { + init(copy.length()); + memcpy(start(), copy.data(), length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0) + { + init( static_cast<size_type>( strlen(copy) )); + memcpy(start(), copy, length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0) + { + init(len); + memcpy(start(), str, len); + } + + // TiXmlString destructor + ~TiXmlString () + { + quit(); + } + + TiXmlString& operator = (const char * copy) + { + return assign( copy, (size_type)strlen(copy)); + } + + TiXmlString& operator = (const TiXmlString & copy) + { + return assign(copy.start(), copy.length()); + } + + + // += operator. Maps to append + TiXmlString& operator += (const char * suffix) + { + return append(suffix, static_cast<size_type>( strlen(suffix) )); + } + + // += operator. Maps to append + TiXmlString& operator += (char single) + { + return append(&single, 1); + } + + // += operator. Maps to append + TiXmlString& operator += (const TiXmlString & suffix) + { + return append(suffix.data(), suffix.length()); + } + + + // Convert a TiXmlString into a null-terminated char * + const char * c_str () const { return rep_->str; } + + // Convert a TiXmlString into a char * (need not be null terminated). + const char * data () const { return rep_->str; } + + // Return the length of a TiXmlString + size_type length () const { return rep_->size; } + + // Alias for length() + size_type size () const { return rep_->size; } + + // Checks if a TiXmlString is empty + bool empty () const { return rep_->size == 0; } + + // Return capacity of string + size_type capacity () const { return rep_->capacity; } + + + // single char extraction + const char& at (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // [] operator + char& operator [] (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // find a char in a string. Return TiXmlString::npos if not found + size_type find (char lookup) const + { + return find(lookup, 0); + } + + // find a char in a string from an offset. Return TiXmlString::npos if not found + size_type find (char tofind, size_type offset) const + { + if (offset >= length()) return npos; + + for (const char* p = c_str() + offset; *p != '\0'; ++p) + { + if (*p == tofind) return static_cast< size_type >( p - c_str() ); + } + return npos; + } + + void clear () + { + //Lee: + //The original was just too strange, though correct: + // TiXmlString().swap(*this); + //Instead use the quit & re-init: + quit(); + init(0,0); + } + + /* Function to reserve a big amount of data when we know we'll need it. Be aware that this + function DOES NOT clear the content of the TiXmlString if any exists. + */ + void reserve (size_type cap); + + TiXmlString& assign (const char* str, size_type len); + + TiXmlString& append (const char* str, size_type len); + + void swap (TiXmlString& other) + { + Rep* r = rep_; + rep_ = other.rep_; + other.rep_ = r; + } + + private: + + void init(size_type sz) { init(sz, sz); } + void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } + char* start() const { return rep_->str; } + char* finish() const { return rep_->str + rep_->size; } + + struct Rep + { + size_type size, capacity; + char str[1]; + }; + + void init(size_type sz, size_type cap) + { + if (cap) + { + // Lee: the original form: + // rep_ = static_cast<Rep*>(operator new(sizeof(Rep) + cap)); + // doesn't work in some cases of new being overloaded. Switching + // to the normal allocation, although use an 'int' for systems + // that are overly picky about structure alignment. + const size_type bytesNeeded = sizeof(Rep) + cap; + const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); + rep_ = reinterpret_cast<Rep*>( new int[ intsNeeded ] ); + + rep_->str[ rep_->size = sz ] = '\0'; + rep_->capacity = cap; + } + else + { + rep_ = &nullrep_; + } + } + + void quit() + { + if (rep_ != &nullrep_) + { + // The rep_ is really an array of ints. (see the allocator, above). + // Cast it back before delete, so the compiler won't incorrectly call destructors. + delete [] ( reinterpret_cast<int*>( rep_ ) ); + } + } + + Rep * rep_; + static Rep nullrep_; + +} ; + + +inline bool operator == (const TiXmlString & a, const TiXmlString & b) +{ + return ( a.length() == b.length() ) // optimization on some platforms + && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare +} +inline bool operator < (const TiXmlString & a, const TiXmlString & b) +{ + return strcmp(a.c_str(), b.c_str()) < 0; +} + +inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } +inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } +inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } +inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } + +inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } +inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } +inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } +inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); +TiXmlString operator + (const TiXmlString & a, const char* b); +TiXmlString operator + (const char* a, const TiXmlString & b); + + +/* + TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. + Only the operators that we need for TinyXML have been developped. +*/ +class TiXmlOutStream : public TiXmlString +{ +public : + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const TiXmlString & in) + { + *this += in; + return *this; + } + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const char * in) + { + *this += in; + return *this; + } + +} ; + +#endif // TIXML_STRING_INCLUDED +#endif // TIXML_USE_STL diff --git a/tinyxml/tinyxml.cpp b/tinyxml/tinyxml.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c161dfcb934e855ff679998c34669bfdd66aa21 --- /dev/null +++ b/tinyxml/tinyxml.cpp @@ -0,0 +1,1886 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include <ctype.h> + +#ifdef TIXML_USE_STL +#include <sstream> +#include <iostream> +#endif + +#include "tinyxml.h" + +FILE* TiXmlFOpen( const char* filename, const char* mode ); + +bool TiXmlBase::condenseWhiteSpace = true; + +// Microsoft compiler security +FILE* TiXmlFOpen( const char* filename, const char* mode ) +{ + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + FILE* fp = 0; + errno_t err = fopen_s( &fp, filename, mode ); + if ( !err && fp ) + return fp; + return 0; + #else + return fopen( filename, mode ); + #endif +} + +void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); + #else + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); + #endif + + //*ME: warning C4267: convert 'size_t' to 'int' + //*ME: Int-Cast to make compiler happy ... + outString->append( buf, (int)strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; + target->location = location; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + assert( node->parent == 0 || node->parent == this ); + assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); + + if ( node->Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + delete node; + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + node->parent = this; + + node->prev = lastChild; + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( !replaceThis ) + return 0; + + if ( replaceThis->parent != this ) + return 0; + + if ( withThis.ToDocument() ) { + // A document can never be a child. Thanks to Noam. + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( !removeThis ) { + return false; + } + + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + + +const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +void TiXmlElement::RemoveAttribute( const char * name ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING str( name ); + TiXmlAttribute* node = attributeSet.Find( str ); + #else + TiXmlAttribute* node = attributeSet.Find( name ); + #endif + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +const TiXmlElement* TiXmlNode::FirstChildElement() const +{ + const TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + const TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + + +TiXmlElement::TiXmlElement (const char * _value) + : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) + : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) + : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +TiXmlElement& TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); + return *this; +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char* TiXmlElement::Attribute( const char* name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return node->Value(); + return 0; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( attrib ) + return &attrib->ValueStr(); + return 0; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, int* i ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const char* result = 0; + + if ( attrib ) { + result = attrib->Value(); + if ( i ) { + attrib->QueryIntValue( i ); + } + } + return result; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const std::string* result = 0; + + if ( attrib ) { + result = &attrib->ValueStr(); + if ( i ) { + attrib->QueryIntValue( i ); + } + } + return result; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, double* d ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const char* result = 0; + + if ( attrib ) { + result = attrib->Value(); + if ( d ) { + attrib->QueryDoubleValue( d ); + } + } + return result; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const std::string* result = 0; + + if ( attrib ) { + result = &attrib->ValueStr(); + if ( d ) { + attrib->QueryDoubleValue( d ); + } + } + return result; +} +#endif + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryIntValue( ival ); +} + + +int TiXmlElement::QueryUnsignedAttribute( const char* name, unsigned* value ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + int ival = 0; + int result = node->QueryIntValue( &ival ); + *value = (unsigned)ival; + return result; +} + + +int TiXmlElement::QueryBoolAttribute( const char* name, bool* bval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + int result = TIXML_WRONG_TYPE; + if ( StringEqual( node->Value(), "true", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "yes", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "1", true, TIXML_ENCODING_UNKNOWN ) ) + { + *bval = true; + result = TIXML_SUCCESS; + } + else if ( StringEqual( node->Value(), "false", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "no", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "0", true, TIXML_ENCODING_UNKNOWN ) ) + { + *bval = false; + result = TIXML_SUCCESS; + } + return result; +} + + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryIntValue( ival ); +} +#endif + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryDoubleValue( dval ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryDoubleValue( dval ); +} +#endif + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetIntValue( val ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, int val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetIntValue( val ); + } +} +#endif + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetDoubleValue( val ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetDoubleAttribute( const std::string& name, double val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetDoubleValue( val ); + } +} +#endif + + +void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( cname ); + if ( attrib ) { + attrib->SetValue( cvalue ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& _name, const std::string& _value ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( _name ); + if ( attrib ) { + attrib->SetValue( _value ); + } +} +#endif + + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + assert( cfile ); + for ( i=0; i<depth; i++ ) { + fprintf( cfile, " " ); + } + + fprintf( cfile, "<%s", value.c_str() ); + + const TiXmlAttribute* attrib; + for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a <foo /> node + // 2) An element with only a text child is printed as <foo> text </foo> + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "</%s>", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + if ( !node->ToText() ) + { + fprintf( cfile, "\n" ); + } + node->Print( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i<depth; ++i ) { + fprintf( cfile, " " ); + } + fprintf( cfile, "</%s>", value.c_str() ); + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + const TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + +bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this, attributeSet.First() ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +const char* TiXmlElement::GetText() const +{ + const TiXmlNode* child = this->FirstChild(); + if ( child ) { + const TiXmlText* childText = child->ToText(); + if ( childText ) { + return childText->Value(); + } + } + return 0; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + copy.CopyTo( this ); +} + + +TiXmlDocument& TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); + return *this; +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + return LoadFile( Value(), encoding ); +} + + +bool TiXmlDocument::SaveFile() const +{ + return SaveFile( Value() ); +} + +bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) +{ + TIXML_STRING filename( _filename ); + value = filename; + + // reading in binary mode so that tinyxml can normalize the EOL + FILE* file = TiXmlFOpen( value.c_str (), "rb" ); + + if ( file ) + { + bool result = LoadFile( file, encoding ); + fclose( file ); + return result; + } + else + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } +} + +bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) +{ + if ( !file ) + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Delete the existing data: + Clear(); + location.Clear(); + + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = 0; + fseek( file, 0, SEEK_END ); + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + + // Strange case, but good to handle up front. + if ( length <= 0 ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Subtle bug here. TinyXml did use fgets. But from the XML spec: + // 2.11 End-of-Line Handling + // <snip> + // <quote> + // ...the XML processor MUST behave as if it normalized all line breaks in external + // parsed entities (including the document entity) on input, before parsing, by translating + // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to + // a single #xA character. + // </quote> + // + // It is not clear fgets does that, and certainly isn't clear it works cross platform. + // Generally, you expect fgets to translate from the convention of the OS to the c/unix + // convention, and not work generally. + + /* + while( fgets( buf, sizeof(buf), file ) ) + { + data += buf; + } + */ + + char* buf = new char[ length+1 ]; + buf[0] = 0; + + if ( fread( buf, length, 1, file ) != 1 ) { + delete [] buf; + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Process the buffer in place to normalize new lines. (See comment above.) + // Copies from the 'p' to 'q' pointer, where p can advance faster if + // a newline-carriage return is hit. + // + // Wikipedia: + // Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or + // CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)... + // * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others + // * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS + // * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9 + + const char* p = buf; // the read head + char* q = buf; // the write head + const char CR = 0x0d; + const char LF = 0x0a; + + buf[length] = 0; + while( *p ) { + assert( p < (buf+length) ); + assert( q <= (buf+length) ); + assert( q <= p ); + + if ( *p == CR ) { + *q++ = LF; + p++; + if ( *p == LF ) { // check for CR+LF (and skip LF) + p++; + } + } + else { + *q++ = *p++; + } + } + assert( q <= (buf+length) ); + *q = 0; + + Parse( buf, 0, encoding ); + + delete [] buf; + return !Error(); +} + + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = TiXmlFOpen( filename, "w" ); + if ( fp ) + { + bool result = SaveFile( fp ); + fclose( fp ); + return result; + } + return false; +} + + +bool TiXmlDocument::SaveFile( FILE* fp ) const +{ + if ( useMicrosoftBOM ) + { + const unsigned char TIXML_UTF_LEAD_0 = 0xefU; + const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; + const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + + fputc( TIXML_UTF_LEAD_0, fp ); + fputc( TIXML_UTF_LEAD_1, fp ); + fputc( TIXML_UTF_LEAD_2, fp ); + } + Print( fp, 0 ); + return (ferror(fp) == 0); +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorId = errorId; + target->errorDesc = errorDesc; + target->tabsize = tabsize; + target->errorLocation = errorLocation; + target->useMicrosoftBOM = useMicrosoftBOM; + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + + +bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +const TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +/* +TiXmlAttribute* TiXmlAttribute::Next() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} +*/ + +const TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +/* +TiXmlAttribute* TiXmlAttribute::Previous() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} +*/ + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + TIXML_STRING n, v; + + EncodeString( name, &n ); + EncodeString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) { + if ( cfile ) { + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; + } + } + else { + if ( cfile ) { + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; + } + } +} + + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); + #else + sprintf (buf, "%d", _value); + #endif + SetValue (buf); +} + +void TiXmlAttribute::SetDoubleValue( double _value ) +{ + char buf [256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%g", _value); + #else + sprintf (buf, "%g", _value); + #endif + SetValue (buf); +} + +int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +double TiXmlAttribute::DoubleValue() const +{ + return atof (value.c_str ()); +} + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) +{ + copy.CopyTo( this ); +} + + +TiXmlComment& TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); + return *this; +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( int i=0; i<depth; i++ ) + { + fprintf( cfile, " " ); + } + fprintf( cfile, "<!--%s-->", value.c_str() ); +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + if ( cdata ) + { + int i; + fprintf( cfile, "\n" ); + for ( i=0; i<depth; i++ ) { + fprintf( cfile, " " ); + } + fprintf( cfile, "<![CDATA[%s]]>\n", value.c_str() ); // unformatted output + } + else + { + TIXML_STRING buffer; + EncodeString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); + } +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); + target->cdata = cdata; +} + + +bool TiXmlText::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) + : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) + : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) + : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + copy.CopyTo( this ); +} + + +TiXmlDeclaration& TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); + return *this; +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + if ( cfile ) fprintf( cfile, "<?xml " ); + if ( str ) (*str) += "<?xml "; + + if ( !version.empty() ) { + if ( cfile ) fprintf (cfile, "version=\"%s\" ", version.c_str ()); + if ( str ) { (*str) += "version=\""; (*str) += version; (*str) += "\" "; } + } + if ( !encoding.empty() ) { + if ( cfile ) fprintf (cfile, "encoding=\"%s\" ", encoding.c_str ()); + if ( str ) { (*str) += "encoding=\""; (*str) += encoding; (*str) += "\" "; } + } + if ( !standalone.empty() ) { + if ( cfile ) fprintf (cfile, "standalone=\"%s\" ", standalone.c_str ()); + if ( str ) { (*str) += "standalone=\""; (*str) += standalone; (*str) += "\" "; } + } + if ( cfile ) fprintf( cfile, "?>" ); + if ( str ) (*str) += "?>"; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i<depth; i++ ) + fprintf( cfile, " " ); + fprintf( cfile, "<%s>", value.c_str() ); +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ + #ifdef TIXML_USE_STL + assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. + #else + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. + #endif + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + + +#ifdef TIXML_USE_STL +TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const std::string& _name ) +{ + TiXmlAttribute* attrib = Find( _name ); + if ( !attrib ) { + attrib = new TiXmlAttribute(); + Add( attrib ); + attrib->SetName( _name ); + } + return attrib; +} +#endif + + +TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} + + +TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const char* _name ) +{ + TiXmlAttribute* attrib = Find( _name ); + if ( !attrib ) { + attrib = new TiXmlAttribute(); + Add( attrib ); + attrib->SetName( _name ); + } + return attrib; +} + + +#ifdef TIXML_USE_STL +std::istream& operator>> (std::istream & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +#ifdef TIXML_USE_STL +std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out << printer.Str(); + + return out; +} + + +std::string& operator<< (std::string& out, const TiXmlNode& base ) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out.append( printer.Str() ); + + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && i<count; + child = child->NextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && i<count; + child = child->NextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && i<count; + child = child->NextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && i<count; + child = child->NextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + DoIndent(); + buffer += "<"; + buffer += element.Value(); + + for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) + { + buffer += " "; + attrib->Print( 0, 0, &buffer ); + } + + if ( !element.FirstChild() ) + { + buffer += " />"; + DoLineBreak(); + } + else + { + buffer += ">"; + if ( element.FirstChild()->ToText() + && element.LastChild() == element.FirstChild() + && element.FirstChild()->ToText()->CDATA() == false ) + { + simpleTextPrint = true; + // no DoLineBreak()! + } + else + { + DoLineBreak(); + } + } + ++depth; + return true; +} + + +bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) +{ + --depth; + if ( !element.FirstChild() ) + { + // nothing. + } + else + { + if ( simpleTextPrint ) + { + simpleTextPrint = false; + } + else + { + DoIndent(); + } + buffer += "</"; + buffer += element.Value(); + buffer += ">"; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlText& text ) +{ + if ( text.CDATA() ) + { + DoIndent(); + buffer += "<![CDATA["; + buffer += text.Value(); + buffer += "]]>"; + DoLineBreak(); + } + else if ( simpleTextPrint ) + { + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + } + else + { + DoIndent(); + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) +{ + DoIndent(); + declaration.Print( 0, 0, &buffer ); + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlComment& comment ) +{ + DoIndent(); + buffer += "<!--"; + buffer += comment.Value(); + buffer += "-->"; + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) +{ + DoIndent(); + buffer += "<"; + buffer += unknown.Value(); + buffer += ">"; + DoLineBreak(); + return true; +} + diff --git a/tinyxml/tinyxml.h b/tinyxml/tinyxml.h new file mode 100644 index 0000000000000000000000000000000000000000..b3f08d658abb934b968d4ff57c97b7686b3ef207 --- /dev/null +++ b/tinyxml/tinyxml.h @@ -0,0 +1,1807 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#define TIXML_USE_STL + +#ifndef TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +// Help out windows: +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif + +#ifdef TIXML_USE_STL + #include <string> + #include <iostream> + #include <sstream> + #define TIXML_STRING std::string +#else + #include "tinystr.h" + #define TIXML_STRING TiXmlString +#endif + +// Deprecated library function hell. Compilers want to use the +// new safe versions. This probably doesn't fully address the problem, +// but it gets closer. There are too many compilers for me to fully +// test. If you get compilation troubles, undefine TIXML_SAFE +#define TIXML_SAFE + +#ifdef TIXML_SAFE + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + // Microsoft visual studio, version 2005 and higher. + #define TIXML_SNPRINTF _snprintf_s + #define TIXML_SSCANF sscanf_s + #elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + //#pragma message( "Using _sn* functions." ) + #define TIXML_SNPRINTF _snprintf + #define TIXML_SSCANF sscanf + #elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_SSCANF sscanf + #else + #define TIXML_SNPRINTF snprintf + #define TIXML_SSCANF sscanf + #endif +#endif + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 6; +const int TIXML_PATCH_VERSION = 2; + +/* Internal structure for tracking location of items + in the XML file. +*/ +struct TiXmlCursor +{ + TiXmlCursor() { Clear(); } + void Clear() { row = col = -1; } + + int row; // 0 based. + int col; // 0 based. +}; + + +/** + Implements the interface to the "Visitor pattern" (see the Accept() method.) + If you call the Accept() method, it requires being passed a TiXmlVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves + are simply called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, <b>no children of this node or its sibilings</b> will be Visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting. + + You should never change the document from a callback. + + @sa TiXmlNode::Accept() +*/ +class TiXmlVisitor +{ +public: + virtual ~TiXmlVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; } + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; } + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; } + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; } + + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; } + /// Visit a text node + virtual bool Visit( const TiXmlText& /*text*/ ) { return true; } + /// Visit a comment node + virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; } + /// Visit an unknown node + virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; } +}; + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim +*/ +class TiXmlBase +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) {} + virtual ~TiXmlBase() {} + + /** All TinyXml classes can print themselves to a filestream + or the string class (TiXmlString in non-STL mode, std::string + in STL mode.) Either or both cfile and str can be null. + + This is a formatted print, and will insert + tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + value is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const { return location.row + 1; } + int Column() const { return location.col + 1; } ///< See Row() + + void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data. + void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. + const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + + /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, + or they will be transformed into entities! + */ + static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out ); + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + TIXML_ERROR_PARSING_CDATA, + TIXML_ERROR_DOCUMENT_TOP_ONLY, + + TIXML_ERROR_STRING_COUNT + }; + +protected: + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + inline static bool IsWhiteSpace( int c ) + { + if ( c < 256 ) + return IsWhiteSpace( (char) c ); + return false; // Again, only truly correct for English/Latin...but usually works. + } + + #ifdef TIXML_USE_STL + static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ); + static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag ); + #endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((const unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), + // and the null terminator isn't needed + for( int i=0; p[i] && i<*length; ++i ) { + _value[i] = p[i]; + } + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to English words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. +*/ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: + #ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + + #endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + TINYXML_DOCUMENT, + TINYXML_ELEMENT, + TINYXML_COMMENT, + TINYXML_UNKNOWN, + TINYXML_TEXT, + TINYXML_DECLARATION, + TINYXML_TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char *Value() const { return value.c_str (); } + + #ifdef TIXML_USE_STL + /** Return Value() as a std::string. If you only use STL, + this is more efficient than calling Value(). + Only available in STL mode. + */ + const std::string& ValueStr() const { return value; } + #endif + + const TIXML_STRING& ValueTStr() const { return value; } + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) { value = _value;} + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() { return parent; } + const TiXmlNode* Parent() const { return parent; } + + const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild() { return firstChild; } + const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + /// The first child of this node with the matching 'value'. Will be null if none found. + TiXmlNode* FirstChild( const char * _value ) { + // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe) + // call the method, cast the return back to non-const. + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value )); + } + const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild() { return lastChild; } + + const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + TiXmlNode* LastChild( const char * _value ) { + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value )); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. + #endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) ); + } + + /// This flavor of IterateChildren searches for children with a particular 'value' + const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + #endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling() const { return prev; } + TiXmlNode* PreviousSibling() { return prev; } + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling( const char * ) const; + TiXmlNode* PreviousSibling( const char *_prev ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Navigate to a sibling node. + const TiXmlNode* NextSibling() const { return next; } + TiXmlNode* NextSibling() { return next; } + + /// Navigate to a sibling node with the given 'value'. + const TiXmlNode* NextSibling( const char * ) const; + TiXmlNode* NextSibling( const char* _next ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement() const; + TiXmlElement* NextSiblingElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement( const char * ) const; + TiXmlElement* NextSiblingElement( const char *_next ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement() const; + TiXmlElement* FirstChildElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() ); + } + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement( const char * _value ) const; + TiXmlElement* FirstChildElement( const char * _value ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: TINYXML_DOCUMENT, TINYXML_ELEMENT, TINYXML_COMMENT, + TINYXML_UNKNOWN, TINYXML_TEXT, and TINYXML_DECLARATION. + */ + int Type() const { return type; } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + const TiXmlDocument* GetDocument() const; + TiXmlDocument* GetDocument() { + return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() ); + } + + /// Returns true if this node has no children. + bool NoChildren() const { return !firstChild; } + + virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + + /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the TiXmlVisitor interface. + + This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + TiXmlPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( TiXmlVisitor* visitor ) const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + + #ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0; + #endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. +*/ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() + { + document = 0; + prev = next = 0; + } + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + #endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const { return name.c_str(); } ///< Return the name of this attribute. + const char* Value() const { return value.c_str(); } ///< Return the value of this attribute. + #ifdef TIXML_USE_STL + const std::string& ValueStr() const { return value; } ///< Return the value of this attribute. + #endif + int IntValue() const; ///< Return the value of this attribute, converted to an integer. + double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + // Get the tinyxml string representation + const TIXML_STRING& NameTStr() const { return name; } + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* _value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* _value ) const; + + void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. + void SetValue( const char* _value ) { value = _value; } ///< Set the value. + + void SetIntValue( int _value ); ///< Set the value from an integer. + void SetDoubleValue( double _value ); ///< Set the value from a double. + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) { name = _name; } + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + const TiXmlAttribute* Next() const; + TiXmlAttribute* Next() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); + } + + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + const TiXmlAttribute* Previous() const; + TiXmlAttribute* Previous() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); + } + + bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } + bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } + bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) { document = doc; } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. +*/ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + + TiXmlAttribute* Find( const char* _name ) const; + TiXmlAttribute* FindOrCreate( const char* _name ); + +# ifdef TIXML_USE_STL + TiXmlAttribute* Find( const std::string& _name ) const; + TiXmlAttribute* FindOrCreate( const std::string& _name ); +# endif + + +private: + //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), + //*ME: this class must be also use a hidden/disabled copy-constructor !!! + TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed + void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) + + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); + #endif + + TiXmlElement( const TiXmlElement& ); + + TiXmlElement& operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* _value ) const; + /// QueryUnsignedAttribute examines the attribute - see QueryIntAttribute(). + int QueryUnsignedAttribute( const char* name, unsigned* _value ) const; + /** QueryBoolAttribute examines the attribute - see QueryIntAttribute(). + Note that '1', 'true', or 'yes' are considered true, while '0', 'false' + and 'no' are considered false. + */ + int QueryBoolAttribute( const char* name, bool* _value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* _value ) const; + /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). + int QueryFloatAttribute( const char* name, float* _value ) const { + double d; + int result = QueryDoubleAttribute( name, &d ); + if ( result == TIXML_SUCCESS ) { + *_value = (float)d; + } + return result; + } + + #ifdef TIXML_USE_STL + /// QueryStringAttribute examines the attribute - see QueryIntAttribute(). + int QueryStringAttribute( const char* name, std::string* _value ) const { + const char* cstr = Attribute( name ); + if ( cstr ) { + *_value = std::string( cstr ); + return TIXML_SUCCESS; + } + return TIXML_NO_ATTRIBUTE; + } + + /** Template form of the attribute query which will try to read the + attribute into the specified type. Very easy, very powerful, but + be careful to make sure to call this with the correct type. + + NOTE: This method doesn't work correctly for 'string' types that contain spaces. + + @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE + */ + template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + std::stringstream sstream( node->ValueStr() ); + sstream >> *outValue; + if ( !sstream.fail() ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; + } + + int QueryValueAttribute( const std::string& name, std::string* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + *outValue = node->ValueStr(); + return TIXML_SUCCESS; + } + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * _value ); + + #ifdef TIXML_USE_STL + const std::string* Attribute( const std::string& name ) const; + const std::string* Attribute( const std::string& name, int* i ) const; + const std::string* Attribute( const std::string& name, double* d ) const; + int QueryIntAttribute( const std::string& name, int* _value ) const; + int QueryDoubleAttribute( const std::string& name, double* _value ) const; + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ); + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ); + ///< STL std::string form. + void SetDoubleAttribute( const std::string& name, double value ); + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); + #ifdef TIXML_USE_STL + void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. + #endif + + const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. + TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } + const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. + TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the TiXmlText child + and accessing it directly. + + If the first child of 'this' is a TiXmlText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + <foo>This is text</foo> + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + <foo><b>This is text</b></foo> + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + <foo>This is <b>text</b></foo> + @endverbatim + GetText() will return "This is ". + + WARNING: GetText() accesses a child node - don't become confused with the + similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are + safe type casts on the referenced node. + */ + const char* GetText() const; + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. +*/ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {} + /// Construct a comment from text. + TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) { + SetValue( _value ); + } + TiXmlComment( const TiXmlComment& ); + TiXmlComment& operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + // Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif +// virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCDATA() and query it with CDATA(). +*/ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /** Constructor for text element. By default, it is treated as + normal, encoded text. If you want it be output as a CDATA text + element, set the parameter _cdata to 'true' + */ + TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT) + { + SetValue( initValue ); + cdata = false; + } + virtual ~TiXmlText() {} + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT) + { + SetValue( initValue ); + cdata = false; + } + #endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TINYXML_TEXT ) { copy.CopyTo( this ); } + TiXmlText& operator=( const TiXmlText& base ) { base.CopyTo( this ); return *this; } + + // Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /// Queries whether this represents text using a CDATA section. + bool CDATA() const { return cdata; } + /// Turns on or off a CDATA representation of text. + void SetCDATA( bool _cdata ) { cdata = _cdata; } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + bool Blank() const; // returns true if all white space and new lines + // [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + bool cdata; // true if this should be input and output as a CDATA style text element +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + <?xml version="1.0" standalone="yes"?> + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. +*/ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + TiXmlDeclaration& operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() {} + + /// Version. Will return an empty string if none was found. + const char *Version() const { return version.c_str (); } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const { return encoding.c_str (); } + /// Is this a standalone document? + const char *Standalone() const { return standalone.c_str (); } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + // Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) {} + virtual ~TiXmlUnknown() {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) { copy.CopyTo( this ); } + TiXmlUnknown& operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); return *this; } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + // Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected: + void CopyTo( TiXmlUnknown* target ) const; + + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. +*/ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); + #endif + + TiXmlDocument( const TiXmlDocument& copy ); + TiXmlDocument& operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument() {} + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + /** Load a file using the given FILE*. Returns true if successful. Note that this method + doesn't stream - the entire object pointed at by the FILE* + will be interpreted as an XML file. TinyXML doesn't stream in XML from the current + file location. Streaming may be added in the future. + */ + bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given FILE*. Returns true if successful. + bool SaveFile( FILE* ) const; + + #ifdef TIXML_USE_STL + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { + return LoadFile( filename.c_str(), encoding ); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { + return SaveFile( filename.c_str() ); + } + #endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + const TiXmlElement* RootElement() const { return FirstChildElement(); } + TiXmlElement* RootElement() { return FirstChildElement(); } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const { return error; } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const { return errorDesc.c_str (); } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + int ErrorId() const { return errorId; } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() const { return errorLocation.row+1; } + int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() + + /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) + to report the correct values for row and column. It does not change the output + or input in any way. + + By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) { tabsize = _tabsize; } + + int TabSize() const { return tabsize; } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() { error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Write the document to standard out using formatted printing ("pretty print"). */ + void Print() const { Print( stdout, 0 ); } + + /* Write the document to a string using formatted printing ("pretty print"). This + will allocate a character array (new char[]) and return it as a pointer. The + calling code pust call delete[] on the return char* to avoid a memory leak. + */ + //char* PrintToMemory() const; + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + + virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + // [internal use] + virtual TiXmlNode* Clone() const; + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; + bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + <Document> + <Element attributeA = "valueA"> + <Child attributeB = "value1" /> + <Child attributeB = "value2" /> + </Element> + <Document> + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim +*/ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } + TiXmlHandle operator=( const TiXmlHandle& ref ) { if ( &ref != this ) this->node = ref.node; return *this; } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + + #ifdef TIXML_USE_STL + TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } + TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } + + TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } + TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } + #endif + + /** Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* ToNode() const { return node; } + /** Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } + /** Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } + /** Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } + + /** @deprecated use ToNode. + Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* Node() const { return ToNode(); } + /** @deprecated use ToElement. + Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* Element() const { return ToElement(); } + /** @deprecated use ToText() + Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* Text() const { return ToText(); } + /** @deprecated use ToUnknown() + Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* Unknown() const { return ToUnknown(); } + +private: + TiXmlNode* node; +}; + + +/** Print to memory functionality. The TiXmlPrinter is useful when you need to: + + -# Print to memory (especially in non-STL mode) + -# Control formatting (line endings, etc.) + + When constructed, the TiXmlPrinter is in its default "pretty printing" mode. + Before calling Accept() you can call methods to control the printing + of the XML document. After TiXmlNode::Accept() is called, the printed document can + be accessed via the CStr(), Str(), and Size() methods. + + TiXmlPrinter uses the Visitor API. + @verbatim + TiXmlPrinter printer; + printer.SetIndent( "\t" ); + + doc.Accept( &printer ); + fprintf( stdout, "%s", printer.CStr() ); + @endverbatim +*/ +class TiXmlPrinter : public TiXmlVisitor +{ +public: + TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), + buffer(), indent( " " ), lineBreak( "\n" ) {} + + virtual bool VisitEnter( const TiXmlDocument& doc ); + virtual bool VisitExit( const TiXmlDocument& doc ); + + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + virtual bool VisitExit( const TiXmlElement& element ); + + virtual bool Visit( const TiXmlDeclaration& declaration ); + virtual bool Visit( const TiXmlText& text ); + virtual bool Visit( const TiXmlComment& comment ); + virtual bool Visit( const TiXmlUnknown& unknown ); + + /** Set the indent characters for printing. By default 4 spaces + but tab (\t) is also useful, or null/empty string for no indentation. + */ + void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } + /// Query the indention string. + const char* Indent() { return indent.c_str(); } + /** Set the line breaking string. By default set to newline (\n). + Some operating systems prefer other characters, or can be + set to the null/empty string for no indenation. + */ + void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } + /// Query the current line breaking string. + const char* LineBreak() { return lineBreak.c_str(); } + + /** Switch over to "stream printing" which is the most dense formatting without + linebreaks. Common when the XML is needed for network transmission. + */ + void SetStreamPrinting() { indent = ""; + lineBreak = ""; + } + /// Return the result. + const char* CStr() { return buffer.c_str(); } + /// Return the length of the result string. + size_t Size() { return buffer.size(); } + + #ifdef TIXML_USE_STL + /// Return the result. + const std::string& Str() { return buffer; } + #endif + +private: + void DoIndent() { + for( int i=0; i<depth; ++i ) + buffer += indent; + } + void DoLineBreak() { + buffer += lineBreak; + } + + int depth; + bool simpleTextPrint; + TIXML_STRING buffer; + TIXML_STRING indent; + TIXML_STRING lineBreak; +}; + + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif diff --git a/tinyxml/tinyxmlerror.cpp b/tinyxml/tinyxmlerror.cpp new file mode 100644 index 0000000000000000000000000000000000000000..538c21d0bd95fb114e70636bc1a776937975a912 --- /dev/null +++ b/tinyxml/tinyxmlerror.cpp @@ -0,0 +1,52 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "tinyxml.h" + +// The goal of the seperate error file is to make the first +// step towards localization. tinyxml (currently) only supports +// english error messages, but the could now be translated. +// +// It also cleans up the code a bit. +// + +const char* TiXmlBase::errorString[ TiXmlBase::TIXML_ERROR_STRING_COUNT ] = +{ + "No error", + "Error", + "Failed to open file", + "Error parsing Element.", + "Failed to read Element name", + "Error reading Element value.", + "Error reading Attributes.", + "Error: empty tag.", + "Error reading end tag.", + "Error parsing Unknown.", + "Error parsing Comment.", + "Error parsing Declaration.", + "Error document empty.", + "Error null (0) or unexpected EOF found in input stream.", + "Error parsing CDATA.", + "Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.", +}; diff --git a/tinyxml/tinyxmlparser.cpp b/tinyxml/tinyxmlparser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a7e0137264ed4da6c19a6e75f34477c9761e8f3d --- /dev/null +++ b/tinyxml/tinyxmlparser.cpp @@ -0,0 +1,1639 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include <ctype.h> +#include <stddef.h> +#include <assert.h> + +#include "tinyxml.h" + +//#define DEBUG_PARSER +#if defined( DEBUG_PARSER ) +# if defined( DEBUG ) && defined( _MSC_VER ) +# include <windows.h> +# define TIXML_LOG OutputDebugString +# else +# define TIXML_LOG printf +# endif +#endif + +// Note tha "PutString" hardcodes the same list. This +// is less flexible than it appears. Changing the entries +// or order will break putstring. +TiXmlBase::Entity TiXmlBase::entity[ TiXmlBase::NUM_ENTITY ] = +{ + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 6, '\'' } +}; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// Including the basic of this table, which determines the #bytes in the +// sequence from the lead byte. 1 placed for invalid sequences -- +// although the result will be junk, pass it through as much as possible. +// Beware of the non-characters in UTF-8: +// ef bb bf (Microsoft "lead bytes") +// ef bf be +// ef bf bf + +const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +const int TiXmlBase::utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; + + +void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) + *length = 1; + else if ( input < 0x800 ) + *length = 2; + else if ( input < 0x10000 ) + *length = 3; + else if ( input < 0x200000 ) + *length = 4; + else + { *length = 0; return; } // This code won't covert this correctly anyway. + + output += *length; + + // Scary scary fall throughs. + switch (*length) + { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + } +} + + +/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalpha( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalpha( anyByte ); +// } +} + + +/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalnum( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalnum( anyByte ); +// } +} + + +class TiXmlParsingData +{ + friend class TiXmlDocument; + public: + void Stamp( const char* now, TiXmlEncoding encoding ); + + const TiXmlCursor& Cursor() const { return cursor; } + + private: + // Only used by the document! + TiXmlParsingData( const char* start, int _tabsize, int row, int col ) + { + assert( start ); + stamp = start; + tabsize = _tabsize; + cursor.row = row; + cursor.col = col; + } + + TiXmlCursor cursor; + const char* stamp; + int tabsize; +}; + + +void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) +{ + assert( now ); + + // Do nothing if the tabsize is 0. + if ( tabsize < 1 ) + { + return; + } + + // Get the current row, column. + int row = cursor.row; + int col = cursor.col; + const char* p = stamp; + assert( p ); + + while ( p < now ) + { + // Treat p as unsigned, so we have a happy compiler. + const unsigned char* pU = (const unsigned char*)p; + + // Code contributed by Fletcher Dunn: (modified by lee) + switch (*pU) { + case 0: + // We *should* never get here, but in case we do, don't + // advance past the terminating null character, ever + return; + + case '\r': + // bump down to the next line + ++row; + col = 0; + // Eat the character + ++p; + + // Check for \r\n sequence, and treat this as a single character + if (*p == '\n') { + ++p; + } + break; + + case '\n': + // bump down to the next line + ++row; + col = 0; + + // Eat the character + ++p; + + // Check for \n\r sequence, and treat this as a single + // character. (Yes, this bizarre thing does occur still + // on some arcane platforms...) + if (*p == '\r') { + ++p; + } + break; + + case '\t': + // Eat the character + ++p; + + // Skip to next tab stop + col = (col / tabsize + 1) * tabsize; + break; + + case TIXML_UTF_LEAD_0: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( *(p+1) && *(p+2) ) + { + // In these cases, don't advance the column. These are + // 0-width spaces. + if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) + p += 3; + else + { p +=3; ++col; } // A normal character. + } + } + else + { + ++p; + ++col; + } + break; + + default: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // Eat the 1 to 4 byte utf8 character. + int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; + if ( step == 0 ) + step = 1; // Error case from bad encoding, but handle gracefully. + p += step; + + // Just advance one column, of course. + ++col; + } + else + { + ++p; + ++col; + } + break; + } + } + cursor.row = row; + cursor.col = col; + assert( cursor.row >= -1 ); + assert( cursor.col >= -1 ); + stamp = p; + assert( stamp ); +} + + +const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) +{ + if ( !p || !*p ) + { + return 0; + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + while ( *p ) + { + const unsigned char* pU = (const unsigned char*)p; + + // Skip the stupid Microsoft UTF-8 Byte order marks + if ( *(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==TIXML_UTF_LEAD_1 + && *(pU+2)==TIXML_UTF_LEAD_2 ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbeU ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbfU ) + { + p += 3; + continue; + } + + if ( IsWhiteSpace( *p ) ) // Still using old rules for white space. + ++p; + else + break; + } + } + else + { + while ( *p && IsWhiteSpace( *p ) ) + ++p; + } + + return p; +} + +#ifdef TIXML_USE_STL +/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) +{ + for( ;; ) + { + if ( !in->good() ) return false; + + int c = in->peek(); + // At this scope, we can't get to a document. So fail silently. + if ( !IsWhiteSpace( c ) || c <= 0 ) + return true; + + *tag += (char) in->get(); + } +} + +/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) +{ + //assert( character > 0 && character < 128 ); // else it won't work in utf-8 + while ( in->good() ) + { + int c = in->peek(); + if ( c == character ) + return true; + if ( c <= 0 ) // Silent failure: can't get document at this scope + return false; + + in->get(); + *tag += (char) c; + } + return false; +} +#endif + +// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The +// "assign" optimization removes over 10% of the execution time. +// +const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) +{ + // Oddly, not supported on some comilers, + //name->clear(); + // So use this: + *name = ""; + assert( p ); + + // Names start with letters or underscores. + // Of course, in unicode, tinyxml has no idea what a letter *is*. The + // algorithm is generous. + // + // After that, they can be letters, underscores, numbers, + // hyphens, or colons. (Colons are valid ony for namespaces, + // but tinyxml can't tell namespaces from names.) + if ( p && *p + && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) + { + const char* start = p; + while( p && *p + && ( IsAlphaNum( (unsigned char ) *p, encoding ) + || *p == '_' + || *p == '-' + || *p == '.' + || *p == ':' ) ) + { + //(*name) += *p; // expensive + ++p; + } + if ( p-start > 0 ) { + name->assign( start, p-start ); + } + return p; + } + return 0; +} + +const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) +{ + // Presume an entity, and pull it out. + TIXML_STRING ent; + int i; + *length = 0; + + if ( *(p+1) && *(p+1) == '#' && *(p+2) ) + { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) + { + // Hexadecimal. + if ( !*(p+3) ) return 0; + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != 'x' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else if ( *q >= 'a' && *q <= 'f' ) + ucs += mult * (*q - 'a' + 10); + else if ( *q >= 'A' && *q <= 'F' ) + ucs += mult * (*q - 'A' + 10 ); + else + return 0; + mult *= 16; + --q; + } + } + else + { + // Decimal. + if ( !*(p+2) ) return 0; + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != '#' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else + return 0; + mult *= 10; + --q; + } + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + } + else + { + *value = (char)ucs; + *length = 1; + } + return p + delta + 1; + } + + // Now try to match it. + for( i=0; i<NUM_ENTITY; ++i ) + { + if ( strncmp( entity[i].str, p, entity[i].strLength ) == 0 ) + { + assert( strlen( entity[i].str ) == entity[i].strLength ); + *value = entity[i].chr; + *length = 1; + return ( p + entity[i].strLength ); + } + } + + // So it wasn't an entity, its unrecognized, or something like that. + *value = *p; // Don't put back the last one, since we return it! + //*length = 1; // Leave unrecognized entities - this doesn't really work. + // Just writes strange XML. + return p+1; +} + + +bool TiXmlBase::StringEqual( const char* p, + const char* tag, + bool ignoreCase, + TiXmlEncoding encoding ) +{ + assert( p ); + assert( tag ); + if ( !p || !*p ) + { + assert( 0 ); + return false; + } + + const char* q = p; + + if ( ignoreCase ) + { + while ( *q && *tag && ToLower( *q, encoding ) == ToLower( *tag, encoding ) ) + { + ++q; + ++tag; + } + + if ( *tag == 0 ) + return true; + } + else + { + while ( *q && *tag && *q == *tag ) + { + ++q; + ++tag; + } + + if ( *tag == 0 ) // Have we found the end of the tag, and everything equal? + return true; + } + return false; +} + +const char* TiXmlBase::ReadText( const char* p, + TIXML_STRING * text, + bool trimWhiteSpace, + const char* endTag, + bool caseInsensitive, + TiXmlEncoding encoding ) +{ + *text = ""; + if ( !trimWhiteSpace // certain tags always keep whitespace + || !condenseWhiteSpace ) // if true, whitespace is always kept + { + // Keep all the white space. + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) + ) + { + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + text->append( cArr, len ); + } + } + else + { + bool whitespace = false; + + // Remove leading white space: + p = SkipWhiteSpace( p, encoding ); + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) ) + { + if ( *p == '\r' || *p == '\n' ) + { + whitespace = true; + ++p; + } + else if ( IsWhiteSpace( *p ) ) + { + whitespace = true; + ++p; + } + else + { + // If we've found whitespace, add it before the + // new character. Any whitespace just becomes a space. + if ( whitespace ) + { + (*text) += ' '; + whitespace = false; + } + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + if ( len == 1 ) + (*text) += cArr[0]; // more efficient + else + text->append( cArr, len ); + } + } + } + if ( p && *p ) + p += strlen( endTag ); + return ( p && *p ) ? p : 0; +} + +#ifdef TIXML_USE_STL + +void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + // The basic issue with a document is that we don't know what we're + // streaming. Read something presumed to be a tag (and hope), then + // identify it, and call the appropriate stream method on the tag. + // + // This "pre-streaming" will never read the closing ">" so the + // sub-tag can orient itself. + + if ( !StreamTo( in, '<', tag ) ) + { + SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + while ( in->good() ) + { + int tagIndex = (int) tag->length(); + while ( in->good() && in->peek() != '>' ) + { + int c = in->get(); + if ( c <= 0 ) + { + SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + break; + } + (*tag) += (char) c; + } + + if ( in->good() ) + { + // We now have something we presume to be a node of + // some sort. Identify it, and call the node to + // continue streaming. + TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); + + if ( node ) + { + node->StreamIn( in, tag ); + bool isElement = node->ToElement() != 0; + delete node; + node = 0; + + // If this is the root element, we're done. Parsing will be + // done by the >> operator. + if ( isElement ) + { + return; + } + } + else + { + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + } + } + // We should have returned sooner. + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); +} + +#endif + +const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) +{ + ClearError(); + + // Parse away, at the document level. Since a document + // contains nothing but other tags, most of what happens + // here is skipping white space. + if ( !p || !*p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + // Note that, for a document, this needs to come + // before the while space skip, so that parsing + // starts from the pointer we are given. + location.Clear(); + if ( prevData ) + { + location.row = prevData->cursor.row; + location.col = prevData->cursor.col; + } + else + { + location.row = 0; + location.col = 0; + } + TiXmlParsingData data( p, TabSize(), location.row, location.col ); + location = data.Cursor(); + + if ( encoding == TIXML_ENCODING_UNKNOWN ) + { + // Check for the Microsoft UTF-8 lead bytes. + const unsigned char* pU = (const unsigned char*)p; + if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 + && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 + && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) + { + encoding = TIXML_ENCODING_UTF8; + useMicrosoftBOM = true; + } + } + + p = SkipWhiteSpace( p, encoding ); + if ( !p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + while ( p && *p ) + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, &data, encoding ); + LinkEndChild( node ); + } + else + { + break; + } + + // Did we get encoding info? + if ( encoding == TIXML_ENCODING_UNKNOWN + && node->ToDeclaration() ) + { + TiXmlDeclaration* dec = node->ToDeclaration(); + const char* enc = dec->Encoding(); + assert( enc ); + + if ( *enc == 0 ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice + else + encoding = TIXML_ENCODING_LEGACY; + } + + p = SkipWhiteSpace( p, encoding ); + } + + // Was this empty? + if ( !firstChild ) { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); + return 0; + } + + // All is well. + return p; +} + +void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + // The first error in a chain is more accurate - don't set again! + if ( error ) + return; + + assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); + error = true; + errorId = err; + errorDesc = errorString[ errorId ]; + + errorLocation.Clear(); + if ( pError && data ) + { + data->Stamp( pError, encoding ); + errorLocation = data->Cursor(); + } +} + + +TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) +{ + TiXmlNode* returnNode = 0; + + p = SkipWhiteSpace( p, encoding ); + if( !p || !*p || *p != '<' ) + { + return 0; + } + + p = SkipWhiteSpace( p, encoding ); + + if ( !p || !*p ) + { + return 0; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: <!-- + // - Decleration: <?xml + // - Everthing else is unknown to tinyxml. + // + + const char* xmlHeader = { "<?xml" }; + const char* commentHeader = { "<!--" }; + const char* dtdHeader = { "<!" }; + const char* cdataHeader = { "<![CDATA[" }; + + if ( StringEqual( p, xmlHeader, true, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Declaration\n" ); + #endif + returnNode = new TiXmlDeclaration(); + } + else if ( StringEqual( p, commentHeader, false, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Comment\n" ); + #endif + returnNode = new TiXmlComment(); + } + else if ( StringEqual( p, cdataHeader, false, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing CDATA\n" ); + #endif + TiXmlText* text = new TiXmlText( "" ); + text->SetCDATA( true ); + returnNode = text; + } + else if ( StringEqual( p, dtdHeader, false, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Unknown(1)\n" ); + #endif + returnNode = new TiXmlUnknown(); + } + else if ( IsAlpha( *(p+1), encoding ) + || *(p+1) == '_' ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Element\n" ); + #endif + returnNode = new TiXmlElement( "" ); + } + else + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Unknown(2)\n" ); + #endif + returnNode = new TiXmlUnknown(); + } + + if ( returnNode ) + { + // Set the parent, so it can report errors + returnNode->parent = this; + } + return returnNode; +} + +#ifdef TIXML_USE_STL + +void TiXmlElement::StreamIn (std::istream * in, TIXML_STRING * tag) +{ + // We're called with some amount of pre-parsing. That is, some of "this" + // element is in "tag". Go ahead and stream to the closing ">" + while( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c ; + + if ( c == '>' ) + break; + } + + if ( tag->length() < 3 ) return; + + // Okay...if we are a "/>" tag, then we're done. We've read a complete tag. + // If not, identify and stream. + + if ( tag->at( tag->length() - 1 ) == '>' + && tag->at( tag->length() - 2 ) == '/' ) + { + // All good! + return; + } + else if ( tag->at( tag->length() - 1 ) == '>' ) + { + // There is more. Could be: + // text + // cdata text (which looks like another node) + // closing tag + // another node. + for ( ;; ) + { + StreamWhiteSpace( in, tag ); + + // Do we have text? + if ( in->good() && in->peek() != '<' ) + { + // Yep, text. + TiXmlText text( "" ); + text.StreamIn( in, tag ); + + // What follows text is a closing tag or another node. + // Go around again and figure it out. + continue; + } + + // We now have either a closing tag...or another node. + // We should be at a "<", regardless. + if ( !in->good() ) return; + assert( in->peek() == '<' ); + int tagIndex = (int) tag->length(); + + bool closingTag = false; + bool firstCharFound = false; + + for( ;; ) + { + if ( !in->good() ) + return; + + int c = in->peek(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + if ( c == '>' ) + break; + + *tag += (char) c; + in->get(); + + // Early out if we find the CDATA id. + if ( c == '[' && tag->size() >= 9 ) + { + size_t len = tag->size(); + const char* start = tag->c_str() + len - 9; + if ( strcmp( start, "<![CDATA[" ) == 0 ) { + assert( !closingTag ); + break; + } + } + + if ( !firstCharFound && c != '<' && !IsWhiteSpace( c ) ) + { + firstCharFound = true; + if ( c == '/' ) + closingTag = true; + } + } + // If it was a closing tag, then read in the closing '>' to clean up the input stream. + // If it was not, the streaming will be done by the tag. + if ( closingTag ) + { + if ( !in->good() ) + return; + + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + assert( c == '>' ); + *tag += (char) c; + + // We are done, once we've found our closing tag. + return; + } + else + { + // If not a closing tag, id it, and stream. + const char* tagloc = tag->c_str() + tagIndex; + TiXmlNode* node = Identify( tagloc, TIXML_DEFAULT_ENCODING ); + if ( !node ) + return; + node->StreamIn( in, tag ); + delete node; + node = 0; + + // No return: go around from the beginning: text, closing tag, or node. + } + } + } +} +#endif + +const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + TiXmlDocument* document = GetDocument(); + + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding ); + return 0; + } + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + if ( *p != '<' ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding ); + return 0; + } + + p = SkipWhiteSpace( p+1, encoding ); + + // Read the name. + const char* pErr = p; + + p = ReadName( p, &value, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding ); + return 0; + } + + TIXML_STRING endTag ("</"); + endTag += value; + + // Check for and read attributes. Also look for an empty + // tag or an end tag. + while ( p && *p ) + { + pErr = p; + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + if ( *p == '/' ) + { + ++p; + // Empty tag. + if ( *p != '>' ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding ); + return 0; + } + return (p+1); + } + else if ( *p == '>' ) + { + // Done with attributes (if there were any.) + // Read the value -- which can include other + // elements -- read the end tag, and return. + ++p; + p = ReadValue( p, data, encoding ); // Note this is an Element method, and will set the error if one happens. + if ( !p || !*p ) { + // We were looking for the end tag, but found nothing. + // Fix for [ 1663758 ] Failure to report error on bad XML + if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); + return 0; + } + + // We should find the end tag now + // note that: + // </foo > and + // </foo> + // are both valid end tags. + if ( StringEqual( p, endTag.c_str(), false, encoding ) ) + { + p += endTag.length(); + p = SkipWhiteSpace( p, encoding ); + if ( p && *p && *p == '>' ) { + ++p; + return p; + } + if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); + return 0; + } + else + { + if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); + return 0; + } + } + else + { + // Try to read an attribute: + TiXmlAttribute* attrib = new TiXmlAttribute(); + if ( !attrib ) + { + return 0; + } + + attrib->SetDocument( document ); + pErr = p; + p = attrib->Parse( p, data, encoding ); + + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding ); + delete attrib; + return 0; + } + + // Handle the strange case of double attributes: + #ifdef TIXML_USE_STL + TiXmlAttribute* node = attributeSet.Find( attrib->NameTStr() ); + #else + TiXmlAttribute* node = attributeSet.Find( attrib->Name() ); + #endif + if ( node ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding ); + delete attrib; + return 0; + } + + attributeSet.Add( attrib ); + } + } + return p; +} + + +const char* TiXmlElement::ReadValue( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + TiXmlDocument* document = GetDocument(); + + // Read in text and elements in any order. + const char* pWithWhiteSpace = p; + p = SkipWhiteSpace( p, encoding ); + + while ( p && *p ) + { + if ( *p != '<' ) + { + // Take what we have, make a text element. + TiXmlText* textNode = new TiXmlText( "" ); + + if ( !textNode ) + { + return 0; + } + + if ( TiXmlBase::IsWhiteSpaceCondensed() ) + { + p = textNode->Parse( p, data, encoding ); + } + else + { + // Special case: we want to keep the white space + // so that leading spaces aren't removed. + p = textNode->Parse( pWithWhiteSpace, data, encoding ); + } + + if ( !textNode->Blank() ) + LinkEndChild( textNode ); + else + delete textNode; + } + else + { + // We hit a '<' + // Have we hit a new element or an end tag? This could also be + // a TiXmlText in the "CDATA" style. + if ( StringEqual( p, "</", false, encoding ) ) + { + return p; + } + else + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, data, encoding ); + LinkEndChild( node ); + } + else + { + return 0; + } + } + } + pWithWhiteSpace = p; + p = SkipWhiteSpace( p, encoding ); + } + + if ( !p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ELEMENT_VALUE, 0, 0, encoding ); + } + return p; +} + + +#ifdef TIXML_USE_STL +void TiXmlUnknown::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + + +const char* TiXmlUnknown::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + TiXmlDocument* document = GetDocument(); + p = SkipWhiteSpace( p, encoding ); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + if ( !p || !*p || *p != '<' ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, p, data, encoding ); + return 0; + } + ++p; + value = ""; + + while ( p && *p && *p != '>' ) + { + value += *p; + ++p; + } + + if ( !p ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding ); + } + if ( p && *p == '>' ) + return p+1; + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlComment::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + + if ( c == '>' + && tag->at( tag->length() - 2 ) == '-' + && tag->at( tag->length() - 3 ) == '-' ) + { + // All is well. + return; + } + } +} +#endif + + +const char* TiXmlComment::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + TiXmlDocument* document = GetDocument(); + value = ""; + + p = SkipWhiteSpace( p, encoding ); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + const char* startTag = "<!--"; + const char* endTag = "-->"; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // [ 1475201 ] TinyXML parses entities in comments + // Oops - ReadText doesn't work, because we don't want to parse the entities. + // p = ReadText( p, &value, false, endTag, false, encoding ); + // + // from the XML spec: + /* + [Definition: Comments may appear anywhere in a document outside other markup; in addition, + they may appear within the document type declaration at places allowed by the grammar. + They are not part of the document's character data; an XML processor MAY, but need not, + make it possible for an application to retrieve the text of comments. For compatibility, + the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity + references MUST NOT be recognized within comments. + + An example of a comment: + + <!-- declarations for <head> & <body> --> + */ + + value = ""; + // Keep all the white space. + while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) + { + value.append( p, 1 ); + ++p; + } + if ( p && *p ) + p += strlen( endTag ); + + return p; +} + + +const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) return 0; + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + // Read the name, the '=' and the value. + const char* pErr = p; + p = ReadName( p, &name, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p || *p != '=' ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + ++p; // skip '=' + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + const char* end; + const char SINGLE_QUOTE = '\''; + const char DOUBLE_QUOTE = '\"'; + + if ( *p == SINGLE_QUOTE ) + { + ++p; + end = "\'"; // single quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else if ( *p == DOUBLE_QUOTE ) + { + ++p; + end = "\""; // double quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else + { + // All attribute values should be in single or double quotes. + // But this is such a common error that the parser will try + // its best, even without them. + value = ""; + while ( p && *p // existence + && !IsWhiteSpace( *p ) // whitespace + && *p != '/' && *p != '>' ) // tag end + { + if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { + // [ 1451649 ] Attribute values with trailing quotes not handled correctly + // We did not have an opening quote but seem to have a + // closing one. Give up and throw an error. + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + value += *p; + ++p; + } + } + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->peek(); + if ( !cdata && (c == '<' ) ) + { + return; + } + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + in->get(); // "commits" the peek made above + + if ( cdata && c == '>' && tag->size() >= 3 ) { + size_t len = tag->size(); + if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { + // terminator of cdata. + return; + } + } + } +} +#endif + +const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + value = ""; + TiXmlDocument* document = GetDocument(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + const char* const startTag = "<![CDATA["; + const char* const endTag = "]]>"; + + if ( cdata || StringEqual( p, startTag, false, encoding ) ) + { + cdata = true; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // Keep all the white space, ignore the encoding, etc. + while ( p && *p + && !StringEqual( p, endTag, false, encoding ) + ) + { + value += *p; + ++p; + } + + TIXML_STRING dummy; + p = ReadText( p, &dummy, false, endTag, false, encoding ); + return p; + } + else + { + bool ignoreWhite = true; + + const char* end = "<"; + p = ReadText( p, &value, ignoreWhite, end, false, encoding ); + if ( p && *p ) + return p-1; // don't truncate the '<' + return 0; + } +} + +#ifdef TIXML_USE_STL +void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "<?xml", true, _encoding ) ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + version = ""; + encoding = ""; + standalone = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "version", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + version = attrib.Value(); + } + else if ( StringEqual( p, "encoding", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + encoding = attrib.Value(); + } + else if ( StringEqual( p, "standalone", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + standalone = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} + +bool TiXmlText::Blank() const +{ + for ( unsigned i=0; i<value.length(); i++ ) + if ( !IsWhiteSpace( value[i] ) ) + return false; + return true; +} +