Commit 2817fbcc authored by Kirill Terekhov's avatar Kirill Terekhov

VTU format reader

Quick and dirty reader for XML VTK files of unstructured grids.
parent 259c9384
......@@ -3045,6 +3045,7 @@ namespace INMOST
void LoadXML(std::string File);
void LoadPMF(std::string File);
void LoadVTK(std::string File);
void LoadVTU(std::string File);
void LoadPVTK(std::string File);
void LoadMKF(std::string File);
/// Acceptable file formats for writing
......
......@@ -206,23 +206,18 @@ namespace INMOST
///Retrive a child of current XML tag with number n.
const XMLTree & GetChild(int n) const {return children[n];}
///Retrive a child of current XML tag with name
const XMLTree & GetChild(std::string name) const
{
int n = FindChild(name);
assert(n != NumChildren());
return children[n];
}
///Returns NULL if not found.
const XMLTree * GetChild(std::string name) const;
///Retrive a child of current XML tag with attribute
///Returns NULL if not found.
const XMLTree * GetChildWithAttrib(std::string name, std::string value) const;
///Retrive number of children.
int NumChildren() const {return (int)children.size();}
///Retrive attribute of current XML tag with number n.
const XMLAttrib & GetAttrib(int n) const {return tag.GetAttib(n);}
///Retrive attribute of current XML tag with name.
const std::string & GetAttrib(std::string name) const
{
int n = FindAttrib(name);
assert(n != NumAttrib());
return GetAttrib(n).value;
}
///Returns NULL if not found.
const std::string & GetAttrib(std::string name) const;
///Retrive number of attributes.
int NumAttrib() const {return tag.NumAttrib();}
///Retrive the name of the tag.
......
......@@ -4,6 +4,7 @@ set(SOURCE
${CMAKE_CURRENT_SOURCE_DIR}/mesh_pmf_file.cpp
${CMAKE_CURRENT_SOURCE_DIR}/mesh_xml_file.cpp
${CMAKE_CURRENT_SOURCE_DIR}/mesh_vtk_file.cpp
${CMAKE_CURRENT_SOURCE_DIR}/mesh_vtu_file.cpp
${CMAKE_CURRENT_SOURCE_DIR}/mesh_pvtk_file.cpp
${CMAKE_CURRENT_SOURCE_DIR}/mesh_gmsh_file.cpp
${CMAKE_CURRENT_SOURCE_DIR}/mesh_gmv_file.cpp
......
......@@ -110,8 +110,10 @@ namespace INMOST
LoadMKF(File);
else if(LFile.find(".msh") != std::string::npos ) // GMSH file format
LoadMSH(File);
else if(LFile.find(".xml") != std::string::npos) //new mesh format
LoadXML(File);
else if (LFile.find(".xml") != std::string::npos) //new mesh format
LoadXML(File);
else if (LFile.find(".vtu") != std::string::npos)
LoadVTU(File);
else if(LFile.find(".pmf") != std::string::npos) //this is inner parallel/platform mesh format
LoadPMF(File);
else throw NotImplemented;
......@@ -151,7 +153,8 @@ namespace INMOST
else if(LFile.find(".gmv") != std::string::npos) return false;
else if(LFile.find(".pvtk") != std::string::npos) return true;
else if(LFile.find(".pmf") != std::string::npos) return true;
else if(LFile.find(".xml") != std::string::npos) return true;
else if(LFile.find(".xml") != std::string::npos) return true;
else if (LFile.find(".vtu") != std::string::npos) return false;
throw NotImplemented;
}
}
......
......@@ -1638,6 +1638,7 @@ safe_output:
j = j + 1 + cp[j];
if( c_nodes.size() != 6 ) throw BadFile;
const integer nodesnum[18] = {0,2,5,3,1,4,5,2,0,3,4,1,3,5,4,0,1,2};
//const integer nodesnum[18] = { 0, 3, 5, 2, 0, 1, 4, 3, 1, 4, 5, 2, 3, 4, 5, 0, 2, 1 };
const integer sizes[5] = {4,4,4,3,3};
Cell c = CreateCell(c_nodes,nodesnum,sizes,5).first;
newcells[i] = c->GetHandle();
......@@ -1660,12 +1661,57 @@ safe_output:
}
case 15: //VTK_PENTAGONAL_PRISM
{
printf("no order description for VTK_PENTAGONAL_PRISM\n");
for (int k = j + 1; k < j + 1 + cp[j]; k++)
{
c_nodes.push_back(newnodes[cp[k]]);
RemMarker(newnodes[cp[k]], unused_marker);
}
j = j + 1 + cp[j];
if (c_nodes.size() != 10) throw BadFile;
const integer nodesnum[30] =
{
0, 1, 6, 5,
1, 2, 7, 6,
2, 3, 8, 7,
3, 4, 9, 8,
4, 0, 5, 9,
5, 6, 7, 8, 9,
4, 3, 2, 1, 0
};
//5, 6, 7, 8, 9
//0, 1, 2, 3, 4
const integer sizes[7] = { 4, 4, 4, 4, 4, 5, 5 };
Cell c = CreateCell(c_nodes, nodesnum, sizes, 7).first;
newcells[i] = c->GetHandle();
//printf("no order description for VTK_PENTAGONAL_PRISM\n");
break;
}
case 16: //VTK_HEXAGONAL_PRISM
{
printf("no order description for VTK_HEXAGONAL_PRISM\n");
for (int k = j + 1; k < j + 1 + cp[j]; k++)
{
c_nodes.push_back(newnodes[cp[k]]);
RemMarker(newnodes[cp[k]], unused_marker);
}
j = j + 1 + cp[j];
if (c_nodes.size() != 12) throw BadFile;
const integer nodesnum[36] =
{
0, 1, 7, 6,
1, 2, 8, 7,
2, 3, 9, 8,
3, 4, 10, 9,
4, 5, 11, 10,
5, 0, 6, 11,
6, 7, 8, 9, 10, 11,
5, 4, 3, 2, 1, 0
};
//6, 7, 8, 9, 10, 11
//0, 1, 2, 3, 4, 5
const integer sizes[8] = { 4, 4, 4, 4, 4, 4, 6, 6 };
Cell c = CreateCell(c_nodes, nodesnum, sizes, 8).first;
newcells[i] = c->GetHandle();
//printf("no order description for VTK_HEXAGONAL_PRISM\n");
break;
}
case 21: //VTK_QUADRATIC_EDGE
......
#ifdef _MSC_VER //kill some warnings
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "inmost.h"
#include <cfloat>
#if defined(USE_MESH)
static int __isnan__(double x) { return x != x; }
//static int isinf(double x) { return !isnan(x) && isnan(x - x); }
static int __isinf__(double x) { return fabs(x) > DBL_MAX; }
static int __isbad(double x) { return __isnan__(x) || __isinf__(x); }
namespace INMOST
{
void Mesh::LoadVTU(std::string File)
{
int verbosity = 0;
for (INMOST_DATA_ENUM_TYPE k = 0; k < file_options.size(); ++k)
{
if (file_options[k].first == "VERBOSITY")
{
verbosity = atoi(file_options[k].second.c_str());
if (verbosity < 0 || verbosity > 2)
{
printf("%s:%d Unknown verbosity option: %s\n", __FILE__, __LINE__, file_options[k].second.c_str());
verbosity = 1;
}
}
}
MarkerType unused_marker = CreateMarker();
int grid_is_2d = 2;
for (INMOST_DATA_ENUM_TYPE k = 0; k < file_options.size(); ++k)
{
if (file_options[k].first == "VTK_GRID_DIMS")
{
if (file_options[k].second == "AUTO")
grid_is_2d = 2;
if (atoi(file_options[k].second.c_str()) == 2)
grid_is_2d = 1;
else if (atoi(file_options[k].second.c_str()) == 3)
grid_is_2d = 0;
}
}
//Determine whether there are already 3d elements so that the grid is 3d
if (grid_is_2d == 2 && NumberOfCells())
{
for (Mesh::iteratorCell it = BeginCell(); it != EndCell() && grid_is_2d == 2; ++it)
if (it->GetElementDimension() == 3)
grid_is_2d = 0;
}
std::vector<HandleType> old_nodes(NumberOfNodes());
{
unsigned qq = 0;
for (Mesh::iteratorNode it = BeginNode(); it != EndNode(); ++it)
old_nodes[qq++] = *it;
}
if (!old_nodes.empty())
{
std::sort(old_nodes.begin(), old_nodes.end(), CentroidComparator(this));
//for(std::vector<HandleType>::iterator it = old_nodes.begin(); it != old_nodes.end(); ++it)
//{
// Storage::real_array c = RealArrayDF(*it,CoordsTag());
// REPORT_VAL("coord: ",c[0] << " " << c[1] << " " << c[2]);
//}
}
std::vector<Tag> datatags;
std::vector<HandleType> newnodes;
std::vector<HandleType> newpolyh;
std::vector<HandleType> newcells;
std::fstream f(File.c_str(), std::ios::in);
XMLReader r(File, f);
XMLReader::XMLTree t = r.ReadXML();
if (t.GetName() == "VTKFile")
{
assert(t.GetAttrib("type") == "UnstructuredGrid");
if (t.FindAttrib("compression") != t.NumAttrib())
{
std::cout << "Compression is specified in " << File << " but not supported" << std::endl;
throw BadFile;
}
const XMLReader::XMLTree * da, * pd;
const XMLReader::XMLTree * v = t.GetChild("UnstructuredGrid")->GetChild("Piece");
int nnodes, ncells, ncoords;
nnodes = atoi(v->GetAttrib("NumberOfPoints").c_str());
ncells = atoi(v->GetAttrib("NumberOfCells").c_str());
//first read in all the nodes
{
da = v->GetChild("Points")->GetChild("DataArray");
ncoords = atoi(da->GetAttrib("NumberOfComponents").c_str());
std::stringstream readcoords(da->GetContents());
Storage::real xyz[3] = { 0.0, 0.0, 0.0 };
newnodes.reserve(nnodes);
for (int q = 0; q < nnodes; ++q)
{
for (int l = 0; l < ncoords; ++l)
readcoords >> xyz[l];
int find = -1;
if (!old_nodes.empty())
{
std::vector<HandleType>::iterator it = std::lower_bound(old_nodes.begin(), old_nodes.end(), xyz, CentroidComparator(this));
if (it != old_nodes.end())
{
Storage::real_array c = RealArrayDF(*it, CoordsTag());
if (CentroidComparator(this).Compare(c.data(), xyz) == 0)
find = static_cast<int>(it - old_nodes.begin());
}
}
if (find == -1)
{
newnodes.push_back(CreateNode(xyz)->GetHandle());
SetMarker(newnodes.back(), unused_marker);
}
else
newnodes.push_back(old_nodes[find]);
}
}
//read all the faces
da = v->GetChild("Cells")->GetChildWithAttrib("Name", "faces");
if( da )
{
std::stringstream faces(v->GetChild("Cells")->GetChildWithAttrib("Name", "faces")->GetContents());
std::stringstream faceoffsets(v->GetChild("Cells")->GetChildWithAttrib("Name", "faceoffsets")->GetContents());
int cconn, coffset = 0, totread = 0, nread, nfaces, nfacenodes;
ElementArray<Node> hnodes(this);
ElementArray<Face> hfaces(this);
while (!faceoffsets.eof())
{
faceoffsets >> coffset;
nread = coffset - totread;
faces >> nfaces;
hfaces.resize(nfaces);
totread++;
for (int q = 0; q < nfaces; ++q)
{
faces >> nfacenodes;
hnodes.resize(nfacenodes);
totread++;
for (int l = 0; l < nfacenodes; ++l)
{
faces >> cconn;
hnodes.at(l) = newnodes[cconn];
RemMarker(newnodes[cconn], unused_marker);
totread++;
}
hfaces[q] = CreateFace(hnodes).first;
}
newpolyh.push_back(CreateCell(hfaces).first.GetHandle());
}
}
//read all the cells
{
std::stringstream conn(v->GetChild("Cells")->GetChildWithAttrib("Name", "connectivity")->GetContents());
std::stringstream type(v->GetChild("Cells")->GetChildWithAttrib("Name", "types")->GetContents());
std::stringstream offset(v->GetChild("Cells")->GetChildWithAttrib("Name", "offsets")->GetContents());
int ctype, coffset = 0, totread = 0, nread, cconn, npolyh = 0;
ElementArray<Face> hfaces(this);
ElementArray<Node> hnodes(this);
newcells.resize(ncells);
for (int q = 0; q < ncells; ++q)
{
type >> ctype;
offset >> coffset;
nread = coffset - totread;
hnodes.resize(nread);
for (int l = 0; l < nread; ++l)
{
conn >> cconn;
hnodes.at(l) = newnodes[cconn];
RemMarker(newnodes[cconn], unused_marker);
totread++;
}
if (ctype == 10)
{
assert(nread == 4);
const integer nodesnum[12] = { 0, 2, 1, 0, 1, 3, 1, 2, 3, 0, 3, 2 };
const integer sizes[4] = { 3, 3, 3, 3 };
newcells[q] = CreateCell(hnodes, nodesnum, sizes, 4).first.GetHandle();
}
else if (ctype == 12 || ctype == 11) // VTK_HEXAHEDRON or VTK_VOXEL
{
assert(nread == 8);
if( ctype == 11 )
{
HandleType temp;
temp = hnodes.at(2);
hnodes.at(2) = hnodes.at(3);
hnodes.at(3) = temp;
temp = hnodes.at(6);
hnodes.at(6) = hnodes.at(7);
hnodes.at(7) = temp;
}
const integer nodesnum[24] = { 0, 4, 7, 3, 1, 2, 6, 5, 0, 1, 5, 4, 3, 7, 6, 2, 0, 3, 2, 1, 4, 5, 6, 7 };
const integer sizes[6] = { 4, 4, 4, 4, 4, 4 };
newcells[q] = CreateCell(hnodes, nodesnum, sizes, 6).first.GetHandle();
}
else if (ctype == 13) // VTK_WEDGE
{
assert(nread == 6);
const integer nodesnum[18] = { 0, 2, 5, 3, 1, 4, 5, 2, 0, 3, 4, 1, 3, 5, 4, 0, 1, 2 };
//const integer nodesnum[18] = { 0, 3, 5, 2, 0, 1, 4, 3, 1, 4, 5, 2, 3, 4, 5, 0, 2, 1 };
const integer sizes[5] = { 4, 4, 4, 3, 3 };
newcells[q] = CreateCell(hnodes, nodesnum, sizes, 5).first.GetHandle();
}
else if (ctype == 14) //VTK_PYRAMID
{
assert(nread == 5);
const integer nodesnum[16] = { 0, 4, 3, 0, 1, 4, 1, 2, 4, 3, 4, 2, 0, 3, 2, 1 };
const integer sizes[5] = { 3, 3, 3, 3, 4 };
newcells[q] = CreateCell(hnodes, nodesnum, sizes, 5).first.GetHandle();
}
else if (ctype == 15) //VTK_PENTAGONAL_PRISM
{
assert(nread == 10);
const integer nodesnum[30] =
{
0, 1, 6, 5,
1, 2, 7, 6,
2, 3, 8, 7,
3, 4, 9, 8,
4, 0, 5, 9,
5, 6, 7, 8, 9,
4, 3, 2, 1, 0
};
//5, 6, 7, 8, 9
//0, 1, 2, 3, 4
const integer sizes[7] = { 4, 4, 4, 4, 4, 5, 5 };
newcells[q] = CreateCell(hnodes, nodesnum, sizes, 7).first.GetHandle();
}
else if (ctype == 16) //VTK_HEXAGONAL_PRISM
{
assert(nread == 12);
const integer nodesnum[36] =
{
0, 1, 7, 6,
1, 2, 8, 7,
2, 3, 9, 8,
3, 4, 10, 9,
4, 5, 11, 10,
5, 0, 6, 11,
6, 7, 8, 9, 10, 11,
5, 4, 3, 2, 1, 0
};
//6, 7, 8, 9, 10, 11
//0, 1, 2, 3, 4, 5
const integer sizes[8] = { 4, 4, 4, 4, 4, 4, 6, 6 };
newcells[q] = CreateCell(hnodes, nodesnum, sizes, 8).first.GetHandle();
}
else if (ctype == 42) //VTK_POLYHEDRON
newcells[q] = newpolyh[npolyh++];
else std::cout << __FILE__ << ":" << __LINE__ << " Implement VTK format " << ctype << std::endl;
}
}
//read data
{
std::string dname[2] = { "PointData", "CellData" };
ElementType dtype[2] = { NODE, CELL };
HandleType * darray[2] = { &newnodes[0], &newcells[0] };
int dsize[2] = { nnodes, ncells };
for (int j = 0; j < 2; ++j)
{
da = v->GetChild(dname[j]);
if (da)
{
for (int k = 0; k < da->NumChildren(); ++k)
{
pd = &da->GetChild(k);
if (pd->GetName() == "DataArray")
{
int ncomps = 1;
int nca = pd->FindAttrib("NumberOfComponents");
if (nca != pd->NumAttrib()) ncomps = atoi(pd->GetAttrib(nca).value.c_str());
TagRealArray t = CreateTag(pd->GetAttrib("Name"), DATA_REAL, dtype[j], NONE, ncomps);
std::stringstream inp(pd->GetContents());
for (int l = 0; l < dsize[j]; ++l)
{
for (INMOST_DATA_ENUM_TYPE q = 0; q < t.GetSize(); ++q)
inp >> t[darray[j][l]][q];
}
}
else std::cout << __FILE__ << ":" << __LINE__ << "I don't know yet what is " << pd->GetName() << " in point data" << std::endl;
}
}
}
}
}
else
{
std::cout << "Root tag is not VTKFile, " << File << " is not a valid .vtu file" << std::endl;
throw BadFile;
}
ReleaseMarker(unused_marker,FACE|NODE);
f.close();
}
}
#endif
......@@ -34,7 +34,7 @@ namespace INMOST
for(int q = 0; q < TagParallelMesh.NumAttrib(); ++q)
{
XMLReader::XMLAttrib & attr = TagParallelMesh.GetAttib(q);
const XMLReader::XMLAttrib & attr = TagParallelMesh.GetAttib(q);
if( ToLower(attr.name) == "number" ) nmeshes = atoi(attr.value.c_str());
else reader.Report("Unused attribute for ParallelMesh %s='%s'",attr.name.c_str(),attr.value.c_str());
}
......@@ -44,7 +44,7 @@ namespace INMOST
bool repair_orientation = false;
for(int q = 0; q < TagMesh.NumAttrib(); ++q)
{
XMLReader::XMLAttrib & attr = TagMesh.GetAttib(q);
const XMLReader::XMLAttrib & attr = TagMesh.GetAttib(q);
if( ToLower(attr.name) == "repairorientation" ) repair_orientation = reader.ParseBool(attr.value);
else reader.Report("Unused attribute for Tags %s='%s'",attr.name.c_str(),attr.value.c_str());
}
......@@ -69,7 +69,7 @@ namespace INMOST
for(int q = 0; q < TagNodes.NumAttrib(); ++q)
{
XMLReader::XMLAttrib & attr = TagNodes.GetAttib(q);
const XMLReader::XMLAttrib & attr = TagNodes.GetAttib(q);
if( ToLower(attr.name) == "number" )
{
nnodes = atoi(attr.value.c_str());
......@@ -170,7 +170,7 @@ namespace INMOST
for(int q = 0; q < TagElems.NumAttrib(); ++q)
{
XMLReader::XMLAttrib & attr = TagElems.GetAttib(q);
const XMLReader::XMLAttrib & attr = TagElems.GetAttib(q);
if( ToLower(attr.name) == "number" )
{
nelems = atoi(attr.value.c_str());
......@@ -202,7 +202,7 @@ namespace INMOST
ElementType subtype = NODE;
for(int q = 0; q < TagConns.NumAttrib(); ++q)
{
XMLReader::XMLAttrib & attr = TagConns.GetAttib(q);
const XMLReader::XMLAttrib & attr = TagConns.GetAttib(q);
if( ToLower(attr.name) == "number" )
{
nexpectconns = atoi(attr.value.c_str());
......@@ -276,7 +276,8 @@ namespace INMOST
break;
case 6: //Wedge or Prism
{
const integer nodesnum[18] = {0,2,5,3,1,4,5,2,0,3,4,1,3,5,4,0,1,2};
//const integer nodesnum[18] = {0,2,5,3,1,4,5,2,0,3,4,1,3,5,4,0,1,2};
const integer nodesnum[18] = { 0, 3, 5, 2, 0, 1, 4, 3, 1, 4, 5, 2, 3, 4, 5, 0, 2, 1 };
const integer sizes[5] = {4,4,4,3,3};
elems->push_back(CreateCell(subarr.Convert<Node>(),nodesnum,sizes,5).first.GetHandle());
}
......@@ -367,7 +368,7 @@ namespace INMOST
bool matchntags = false;
for(int q = 0; q < TagSetsData.NumAttrib(); ++q)
{
XMLReader::XMLAttrib & attr = TagSetsData.GetAttib(q);
const XMLReader::XMLAttrib & attr = TagSetsData.GetAttib(q);
if( ToLower(attr.name) == "number" )
{
ntags = atoi(attr.value.c_str());
......@@ -390,7 +391,7 @@ namespace INMOST
ElementType defined = NONE;
for(int q = 0; q < TagTag.NumAttrib(); ++q)
{
XMLReader::XMLAttrib & attr = TagTag.GetAttib(q);
const XMLReader::XMLAttrib & attr = TagTag.GetAttib(q);
if( ToLower(attr.name) == "name" ) tagname = attr.value;
else if( ToLower(attr.name) == "size" )
{
......@@ -452,7 +453,7 @@ namespace INMOST
bool matchsets = false;
for(int q = 0; q < TagSetsData.NumAttrib(); ++q)
{
XMLReader::XMLAttrib & attr = TagSetsData.GetAttib(q);
const XMLReader::XMLAttrib & attr = TagSetsData.GetAttib(q);
if( ToLower(attr.name) == "number" )
{
nsets = atoi(attr.value.c_str());
......@@ -475,7 +476,7 @@ namespace INMOST
nsets_read++;
for(int q = 0; q < Set.NumAttrib(); ++q)
{
XMLReader::XMLAttrib & attr = Set.GetAttib(q);
const XMLReader::XMLAttrib & attr = Set.GetAttib(q);
if( ToLower(attr.name) == "size" )
{
expectsize = atoi(attr.value.c_str());
......@@ -588,7 +589,7 @@ namespace INMOST
for(int q = 0; q < TagSetsData.NumAttrib(); ++q)
{
XMLReader::XMLAttrib & attr = TagSetsData.GetAttib(q);
const XMLReader::XMLAttrib & attr = TagSetsData.GetAttib(q);
if( ToLower(attr.name) == "number" )
{
ndata = atoi(attr.value.c_str());
......@@ -607,7 +608,7 @@ namespace INMOST
ElementType etype = NONE;
for(int q = 0; q < TagDataSet.NumAttrib(); ++q)
{
XMLReader::XMLAttrib & attr = TagDataSet.GetAttib(q);
const XMLReader::XMLAttrib & attr = TagDataSet.GetAttib(q);
if( ToLower(attr.name) == "settype" )
{
if( ToLower(attr.value) == "cells" ) etype = CELL;
......
......@@ -587,6 +587,7 @@ namespace INMOST
if( verbose > 1 ) Report("info: skipping comments");
_state = ReadCommentQuestion;
SkipComments(WaitTag);
_state = Intro; //wait for '<' again
}
else if( c == '!' ) //can be ![CDATA[
{
......@@ -599,6 +600,7 @@ namespace INMOST
if( verbose > 1 ) Report("info: skipping comments");
_state = ReadCommentExclamation;
SkipComments(WaitTag);
_state = Intro; //wait for '<' again
}
else Report("unexpected character %c while reading comment",c);
}
......@@ -618,7 +620,7 @@ namespace INMOST
_state = ReadCloseTagSlash;
done = true;
}
else if( isalpha(c) )
else if( isalpha(c) || c == '_' )
{
if( verbose > 1 ) Report("info: reading tag name");
ret.push_back(c);
......@@ -793,7 +795,7 @@ namespace INMOST
done = true;
}
}
else if( isalpha(c) )
else if( isalpha(c) || c == '_' )
{
if( verbose > 1 ) Report("info: reading attribute name");
ret.push_back(c);
......@@ -814,7 +816,7 @@ namespace INMOST
_state = WaitAttributeValue;
done = true;
}
else if( isalpha(c) ) ret.push_back(c);
else if (isalpha(c) || isdigit(c) || c == '_' || c == '-' || c == '.' || c == ':') ret.push_back(c);
else
{
Report("Unexpected symbol %c while reading attribute name",c);
......@@ -1695,7 +1697,34 @@ namespace INMOST
return k;
return NumAttrib();
}
const XMLReader::XMLTree * XMLReader::XMLTree::GetChild(std::string name) const
{
int n = FindChild(name);
if (n != NumChildren())
return &children[n];
else
return NULL;
}
const XMLReader::XMLTree * XMLReader::XMLTree::GetChildWithAttrib(std::string name, std::string value) const
{
for (int k = 0; k < NumChildren(); ++k)
{
const XMLTree & t = GetChild(k);
int q = t.FindAttrib(name);
if (q != t.NumAttrib() && t.GetAttrib(q).value == value)
return &t;
}
return NULL;
}
const std::string & XMLReader::XMLTree::GetAttrib(std::string name) const
{
int n = FindAttrib(name);
assert(n != NumAttrib());
return GetAttrib(n).value;
}
void WriteXML(const XMLReader::XMLTree & t, std::ostream & output, int offset)
{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment