57 #define strcasecmp _stricmp
79 T
string_to(
const std::string& s, std::function<std::string()> where){
81 const char* last=s.data()+s.size();
82 auto [p,e]=std::from_chars(s.data(),last,val);
83 if(p!=last)
OOFEM_ERROR(
"%s: error parsing '%s' as typeid '%s' (leftover chars)",where().c_str(),s.c_str(),
typeid(T).name());
84 if(e==std::errc())
return val;
85 OOFEM_ERROR(
"%s: error parsing '%s' as typeid '%s' (std::from_chars error).",where().c_str(),s.c_str(),
typeid(T).name());
90 if(std::regex_match(s,std::regex(
"[0-9]+"))){
91 return Range(std::atoi(s.c_str()));
94 if(std::regex_match(s,match,std::regex(
"\\s*([0-9]+)\\s*(-|[.]{2,3})\\s*([0-9]+)"))){
95 assert(match.size()==4);
96 return Range(std::atoi(match[1].str().c_str()),std::atoi(match[3].str().c_str()));
98 OOFEM_ERROR(
"%s: error parsing '%s' as range (single integer or range between two integers separated with -, .., ...).",where().c_str(),s.c_str());
101 bool string_to(
const std::string& s, std::function<std::string()> where){
102 if(s==
"0" || s==
"n" || s==
"N" || s==
"no" || s==
"No" || s==
"NO" ){
return false; }
103 if(s==
"1" || s==
"y" || s==
"Y" || s==
"yes" || s==
"Yes" || s==
"YES"){
return true; }
104 OOFEM_ERROR(
"%s: error parsing '%s' as bool (alllowed values: 0, n, N, no, No, NO; 1, y, Y, yes, Yes, YES).",where().c_str(),s.c_str())
109 std::string
_lrtrim(std::string &s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](
int c) {return !std::isspace(c);}));
return s; }
116 std::function<std::string()>
loc;
120 loc=[rec,
this](){
return rec->
loc(this->node); };
123 Tokens(
const std::string& attr_,
const std::string& str_, std::function<std::string()> loc_,
const char* sep_regex=
"\\s+"):
attr(attr_),
str(str_),
loc(loc_){
128 if(
str.empty())
return;
129 const std::regex sep_re(sep_regex);
130 std::copy(std::sregex_token_iterator(
str.begin(),
str.end(), sep_re, -1),
131 std::sregex_token_iterator(),
132 std::back_inserter(
toks)
137 template<
typename T> T
as(
size_t ix){
138 if(ix>
size())
OOFEM_ERROR(
"%s: attribute %s: invalid index %d in sequence of length %d: %s",
loc().c_str(),
attr.c_str(),(
int)ix,(
int)
size(),
str.c_str());
139 const std::string& s(
toks[ix]);
140 return string_to<T>(s,[
this,ix](){
return loc()+
": attribute "+
attr+
" ["+std::to_string(ix)+
"]"; });
150 this->
recId=lastRecId+1;
152 if(lastRecId>0 && this->
recId<=lastRecId)
OOFEM_WARNING(
"%s: descencing ids (previous %d, now %d)",
loc().c_str(),lastRecId,
recId);
157 _XML_DEBUG(
"id="<<
id<<
", name="<<name<<
", optional="<<optional);
161 if(optional)
return 0;
166 for([[maybe_unused]] pugi::xml_node n: ch.children()) ret++;
172 if(!has && !optional)
OOFEM_ERROR(
"%s: %s has no no child node '%s' (required).",
loc().c_str(),
node.name(),name.c_str());
177 XMLInputRecord :: giveRecordKeywordField(std :: string &answer){
180 else answer=
node.name();
190 pugi::xml_attribute att=
node.attribute(
id.c_str());
192 for(att=
node.first_attribute(); att; att=att.next_attribute()){
193 if(strcasecmp(att.name(),
id.c_str())==0){
194 std::cerr<<
"XML: case-insensitive hasField ('"<<att.name()<<
"', requested '"<<
id<<
"')"<<std::endl;
204 for(
size_t i=0; i<n2.size(); i++)
if(n2[i]==
'(' || n2[i]==
')' || n2[i]==
'/') n2[i]=
'_';
210 pugi::xml_attribute att=
node.attribute(n2.c_str());
213 for (att=
node.first_attribute(); att; att=att.next_attribute()){
215 #define strcasecmp _stricmp
217 if(strcasecmp(att.name(),name)==0){
218 std::cerr<<
"XML: case-insensitive match ('"<<att.name()<<
"', requested '"<<name<<
"')"<<std::endl;
223 if(!att)
OOFEM_ERROR(
"%s: no such attribute: %s",
loc().c_str(),n2.c_str());
224 std::string ret=att.as_string();
226 return std::make_tuple(ret,
node);
231 pugi::xml_document tmp;
232 pugi::xml_node xNotseen=tmp.append_child(
node.name());
233 pugi::xml_node xNotempty=tmp.append_child(
node.name());
234 int nNotseen=0, nNotempty=0;
235 for([[maybe_unused]]
const pugi::xml_attribute& a:
node.attributes()) {
236 if(std::string(a.name())==
SeenMark)
continue;
237 bool queried(
attrQueried.count(a.name())), read(
attrRead.count(a.name())), hasValue=(a.value()[0]!=0);
239 if(!queried){ xNotseen.append_attribute(a.name())=a.value(); nNotseen++;
continue; }
240 if( hasValue){ xNotempty.append_attribute(a.name())=a.value(); nNotempty++; }
242 if(nNotseen==0 && nNotempty==0)
return;
244 std::ostringstream oss; oss<<
loc();
245 if(nNotseen){ oss<<
"\n attribute"<<(nNotseen>1?
"s":
"")<<
" ignored:\n "; xNotseen.print(oss); }
246 if(nNotempty){ oss<<
"\n attribute"<<(nNotempty>1?
"s":
"")<<
" with ignored non-empty value:\n "; xNotempty.print(oss); }
250 pugi::xml_document tmp;
251 pugi::xml_node n=tmp.append_child(
node.name());
252 for([[maybe_unused]]
const pugi::xml_attribute& a:
node.attributes()) {
254 n.append_attribute(a.name())=a.value();
256 std::ostringstream oss; n.print(oss,
"",pugi::format_raw);
260 OOFEM_ERROR(
"%s: not (yet?) implemented.",__PRETTY_FUNCTION__);
272 for(
size_t i=0; i<tt.
size(); i++) answer[i]=tt.
as<
double>(i);
273 _XML_DEBUG(tt.
loc()<<
": parsed attribute "<<
id<<
" as "<<answer);
276 Tokens trows(
id,
this,
"\\s*;\\s*");
277 int rows=trows.
size();
278 for(
int row=0; row<rows; row++){
279 Tokens tcols(
id,trows.
toks[row],[
this,trows](){ return this->loc(trows.node); },
"\\s+");
280 if(row==0){ answer.
resize(rows,tcols.
size()); }
281 else if((
int)answer.
cols()!=(
int)tcols.
size())
OOFEM_ERROR(
"%s: row %d has inconsistent number of columns (%d != %d)",
loc().c_str(),rows,(
int)answer.
cols(),(
int)tcols.
size());
282 for(
int col=0; col<answer.
cols(); col++){ answer(row,col)=tcols.
as<
double>(col); }
284 _XML_DEBUG(tt.loc()<<
": parsed attribute "<<
id<<
" as "<<answer);
290 for(
size_t i=0; i<tt.
size(); i++) answer[i]=tt.
as<
int>(i);
291 _XML_DEBUG(tt.
loc()<<
": parsed attribute "<<
id<<
" as "<<answer);
294 std::string s; pugi::xml_node n;
299 std::string s; pugi::xml_node n;
304 if(!
hasField(
id)){ answer=
false;
return; }
305 std::string s; pugi::xml_node n;
310 Tokens tt(
id,
this,
"\\s*,\\s*");
311 for(
size_t i=0; i<tt.size(); i++) answer.push_back(tt.as<
Range>(i));
314 std::string s; pugi::xml_node n;
316 auto where=[
this,n,id](){
return loc(n)+
": attribute '"+
id+
"'"; };
318 else if(s[0]==
'$'){ std::string s2=s.substr(1,s.size()-2); answer.
setSimpleExpression(s2); }
322 std::string s; pugi::xml_node n;
324 Tokens items(
id,
this,
"\\s*;\\s*");
325 for(
const std::string& tok: items.
toks){
326 Tokens kv(
id,tok,[
this,items](){
return this->
loc(items.
node); },
"\\s+");
327 if(kv.size()!=2)
OOFEM_ERROR(
"%s: dictionary items must have 2 whitespace-separated fields (%d tokens found)",
loc(n).c_str(),(
int)kv.size());
329 if(std::regex_match(kv.toks[0],std::regex(
"[0-9]+"))){ key=std::atoi(kv.toks[0].c_str()); }
330 else if(kv.toks[0].size()==1){ key=(char)kv.toks[0][0]; }
331 else OOFEM_ERROR(
"%s: dictionary key must be integer or single letter (not '%s')",
loc().c_str(),kv.toks[0].c_str());
332 answer.
add(key,kv.as<
double>(1));
339 XMLInputRecord :: giveField(std :: vector< std :: string > &answer,
InputFieldType id)
341 int indx = this->giveKeywordIndx(
id);
345 auto ptr = scanInteger(tokenizer.giveToken(++indx), size);
346 if ( ptr ==
nullptr || *ptr != 0 ) {
349 answer.reserve(size);
351 for (
int i = 1; i <= size; i++ ) {
352 answer.push_back( tokenizer.giveToken(indx + i) );
353 setReadFlag(indx + i);
Pair * add(int aKey, double value)
void resize(Index rows, Index cols)
void setSimpleExpression(std ::string &val)
void setReference(int val)
void setValue(double val)
pugi::xml_node giveNamedChild(const pugi::xml_node &parent, const std::string &name)
#define OOFEM_WARNING(...)
std::string _lrtrim(std::string &s)
T string_to(const std::string &s, std::function< std::string()> where)
const char * InputFieldType
Identifier of fields in input records.
void assertSize(size_t req)
std::function< std::string()> loc
Tokens(const std::string &attr_, XMLInputRecord *rec, const char *sep_regex="\\s+")
void _makeToks(const char *sep_regex)
Tokens(const std::string &attr_, const std::string &str_, std::function< std::string()> loc_, const char *sep_regex="\\s+")
std::vector< std::string > toks