46#include <bits/stdc++.h>
50#define strcasecmp _stricmp
60 std::filesystem::path xml=(xml_);
61 if(xml.is_relative() && parent){
62 std::filesystem::path parentXml(
xmlFiles[parent.root()]);
63 xml=parentXml.parent_path()/xml;
64 _XML_DEBUG(
"Relative path "<<xml_<<
" resolved to "<<xml<<
" (parent is "<<parentXml<<
")");
67 std::ifstream i(xml,std::ifstream::in|std::ios::binary);
68 if(!i.is_open())
OOFEM_ERROR(
"Error opening %s: %s",xml.c_str(),std::strerror(errno));
71 while(i.good()){
char c=i.get(); pos++;
if(c==
'\n') br.push_back(pos); }
72 std::vector<size_t> nl;
73 nl.reserve(br.size());
74 #pragma GCC diagnostic push
75 #pragma GCC diagnostic ignored "-Waggressive-loop-optimizations"
76 nl.assign(br.begin(),br.end());
77 #pragma GCC diagnostic pop
78 _XML_DEBUG(xml<<
": "<< nl.size()<<
" newlines found, about to parse the XML...");
79 docs[parent]=pugi::xml_document();
81 auto& doc=
docs[parent];
82 pugi::xml_parse_result result=doc.load_file(xml.c_str());
84 auto [line,col]=
offset2lc(nl,result.offset);
85 OOFEM_ERROR(
"Error parsing %s:%ld:%ld: %s",xml.c_str(),line,col,result.description());
89 size_t nChildren=std::distance(doc.begin(),doc.end());
90 if(nChildren!=1)
OOFEM_ERROR(
"Error reading %s: must have exactly 1 top-level tag (not %ld)",xml.c_str(),nChildren);
91 _XML_DEBUG(xml<<
": "<<
" parsing done, root is '"<<doc.first_child().name()<<
"'");
97 if(
auto xi=
docs.find(n); xi!=
docs.end())
return xi->second.first_child();
98 pugi::xml_attribute href=n.attribute(
"href");
99 if(!href)
OOFEM_ERROR(
"%s: xi:include must have 'href' attribute.",
loc().c_str());
101 return loadXml(n,href.value()).first_child();
105 _XML_DEBUG(
"finding child named '"<<name<<
"'");
106 for(pugi::xml_node c=parent.first_child(); c; c=c.next_sibling()){
111 if(strcasecmp(c.name(),name.c_str())==0){
112 std::cerr<<
loc(parent)<<
": case-insensitive XML tag match ('"<<c.name()<<
"', requested '"<<name<<
"')"<<std::endl;
123 return pugi::xml_node();
130 pugi::xml_node root=
stack.back().parent;
131 pugi::xml_node output_node=root.child(
"Output"), description_node=root.child(
"Description");
132 if(!output_node)
OOFEM_ERROR(
"Error reading %s: <Output> node missing in %s.",
topXmlFile.c_str(),root.name());
133 if(!description_node)
OOFEM_ERROR(
"Error reading %s: <Description> node missing.",
topXmlFile.c_str());
141 bool XMLDataReader :: canRead(
const std::string& xml){
142 return std::filesystem::path(xml).extension() ==
".xml";
146 if(nl.empty())
return std::make_tuple(0,0);
147 size_t ix=std::distance(nl.begin(),std::lower_bound(nl.begin(),nl.end(),offset));
149 return std::make_tuple(ix+1,offset-(ix==0?0:nl[ix-1]));
153 std::ostringstream oss;
155 oss<<
xmlFiles.at(n.root())<<
":"<<line<<
":"<<col;
170 for([[maybe_unused]] pugi::xml_node n:
stack.back().parent.children()) ret++;
176 std::ostringstream oss;
186 pugi::xml_node ch1=grp.first_child();
193 if(!std::uncaught_exceptions()){
194 if(
stack.back().parent.name()!=name)
OOFEM_ERROR(
"Error reading %s: %s: bottom-most group is %s, request to leave %s.",
loc().c_str(),
giveStackPath().c_str(),
stack.back().parent.name(),name.c_str());
201 if(!r)
OOFEM_ERROR(
"Error reading %s: input record is not a XMLInputRecord?",
loc().c_str());
208 if(!r)
OOFEM_ERROR(
"Error reading %s: input record is not a XMLInputRecord?",
loc().c_str());
212 if(!std::uncaught_exceptions()){
223 for(
const auto& [xml,doc]:
docs){
224 while((n=doc.find_node([](
const pugi::xml_node& n)->bool{ return !XMLInputRecord::node_seen_get(n); }))){
227 std::ostringstream oss;
228 oss<<
"Unprocessed XML fragment "<<
loc(n)<<
"\n";
231 n.parent().remove_child(n);
255 else if(tip.
curr.name()==tag)
break;
256 else if(strcasecmp(tip.
curr.name(),tag.c_str())==0){
257 std::cerr<<
loc(tip.
curr)<<
": case-insensitive XML tag match ('"<<tip.
curr.name()<<
"', requested '"<<tag<<
"')"<<std::endl;
270 tip.
lastRecord=std::make_shared<XMLInputRecord>(
this,n);
std::string outputFileName
Output file name (first line in OOFEM input files).
InputRecordType
Determines the type of input record.
std::string description
Description line (second line in OOFEM input files).
static constexpr const char * InputRecordTags[]
std::vector< StackItem > stack
void enterGroup(const std::string &name) override
pugi::xml_node giveNamedChild(const pugi::xml_node &parent, const std::string &name)
pugi::xml_document & loadXml(pugi::xml_node parent, const std::string &xml)
std::map< pugi::xml_node, std::string > xmlFiles
int giveCurrentGroupCount()
int giveGroupCount(const std::string &name) override
void leaveRecord(InputRecord *) override
std::map< pugi::xml_node, pugi::xml_document > docs
std::tuple< size_t, size_t > offset2lc(const std::vector< size_t > &nl, size_t offset) const
const std::string XiIncludeTag
std::string giveStackPath()
std::shared_ptr< InputRecord > topRecord
std::map< pugi::xml_node, std::vector< size_t > > newlines
void enterRecord(InputRecord *) override
void leaveGroup(const std::string &name) override
pugi::xml_node resolveXiInclude(const pugi::xml_node &n)
XMLDataReader(const std::string &xmlFile)
#define OOFEM_WARNING(...)
std::shared_ptr< XMLInputRecord > lastRecord
std::set< pugi::xml_node > seen