OOFEM 3.0
Loading...
Searching...
No Matches
xmlinputrecord.C
Go to the documentation of this file.
1/*
2 *
3 * ##### ##### ###### ###### ### ###
4 * ## ## ## ## ## ## ## ### ##
5 * ## ## ## ## #### #### ## # ##
6 * ## ## ## ## ## ## ## ##
7 * ## ## ## ## ## ## ## ##
8 * ##### ##### ## ###### ## ##
9 *
10 *
11 * OOFEM : Object Oriented Finite Element Code
12 *
13 * Copyright (C) 1993 - 2025 Borek Patzak
14 *
15 *
16 *
17 * Czech Technical University, Faculty of Civil Engineering,
18 * Department of Structural Mechanics, 166 29 Prague, Czech Republic
19 *
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Lesser General Public
22 * License as published by the Free Software Foundation; either
23 * version 2.1 of the License, or (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Lesser General Public License for more details.
29 *
30 * You should have received a copy of the GNU Lesser General Public
31 * License along with this library; if not, write to the Free Software
32 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
33 */
34
35#include "xmlinputrecord.h"
36#include "xmldatareader.h"
37#include "intarray.h"
38#include "floatarray.h"
39#include "floatmatrix.h"
40#include "dictionary.h"
41#include "range.h"
42#include "scalarfunction.h"
43
44#include <cstdlib>
45#include <cstdio>
46#include <cstring>
47#include <cctype>
48#include <ostream>
49#include <sstream>
50#include <iostream>
51#include <regex>
52#include <charconv>
53#include <cassert>
54#include <string.h>
55
56#ifdef _MSC_VER
57 #define strcasecmp _stricmp
58#endif
59
60
61// #define _XML_DEBUG(m) std::cerr<<__FUNCTION__<<": "<<m<<std::endl;
62#define _XML_DEBUG(m)
63
64
65namespace oofem {
66
67 bool XMLInputRecord::node_seen_get(const pugi::xml_node& n){ return !!n.attribute(SeenMark); }
68
69 void XMLInputRecord::node_seen_set(pugi::xml_node& n, bool seen){
70 if(seen && !n.attribute(SeenMark)) n.append_attribute(SeenMark);
71 else if(!seen && n.attribute(SeenMark)) n.remove_attribute(SeenMark);
72 }
73
74 std::string XMLInputRecord::loc(const pugi::xml_node& node) const {
75 return _reader()->loc(node);
76 }
77
78 template<typename T>
79 T string_to(const std::string& s, std::function<std::string()> where){
80 T val;
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());
86 }
87
88 template<>
89 Range string_to(const std::string& s, std::function<std::string()> where){
90 if(std::regex_match(s,std::regex("[0-9]+"))){
91 return Range(std::atoi(s.c_str()));
92 }
93 std::smatch match;
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()));
97 }
98 OOFEM_ERROR("%s: error parsing '%s' as range (single integer or range between two integers separated with -, .., ...).",where().c_str(),s.c_str());
99 };
100 template<>
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())
105 }
106
107
108 // trim string from both sides
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; }
110
111 struct Tokens{
112 std::string attr;
113 std::string str;
114 pugi::xml_node node;
115 std::vector<std::string> toks;
116 std::function<std::string()> loc;
117 Tokens(const std::string& attr_, XMLInputRecord* rec, const char* sep_regex="\\s+"): attr(attr_) {
118 std::tie(str,node)=rec->_attr_traced_read_with_node(attr.c_str());
119 //std::cerr<<"attr_="<<attr_<<", attr="<<attr<<", offset="<<node.offset_debug()<<std::endl;
120 loc=[rec,this](){ return rec->loc(this->node); };
121 _makeToks(sep_regex);
122 };
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_){
124 str=_lrtrim(str);
125 _makeToks(sep_regex);
126 }
127 void _makeToks(const char* sep_regex){
128 if(str.empty()) return; // this would create spurious empty token, stay on zero size instead
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)
133 );
134 };
135 size_t size() { return toks.size(); }
136 void assertSize(size_t req){ if(size()!=req) OOFEM_ERROR("%s: attribute %s: length mismatch (%d items, %d required)",loc().c_str(),attr.c_str(),(int)size(),(int)req); }
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)+"]"; });
141 }
142 };
143
144 XMLInputRecord :: XMLInputRecord(XMLDataReader* reader_, const pugi::xml_node& node_): InputRecord((DataReader*)reader_), node(node_) {
145 node_seen_set(node,true);
146 _XML_DEBUG(loc()<<": node.name()="<<node.name());
147 }
148
149 int XMLInputRecord::setRecId(int lastRecId){
150 this->recId=lastRecId+1;
151 giveOptionalField(this->recId,"id");
152 if(lastRecId>0 && this->recId<=lastRecId) OOFEM_WARNING("%s: descencing ids (previous %d, now %d)",loc().c_str(),lastRecId,recId);
153 return this->recId;
154 };
155
156 int XMLInputRecord::giveGroupCount(InputFieldType id, const std::string& name, bool optional){
157 _XML_DEBUG("id="<<id<<", name="<<name<<", optional="<<optional);
158 _XML_DEBUG(loc(node)<<", node.name()="<<node.name());
159 pugi::xml_node ch=_reader()->giveNamedChild(node,name);
160 if(!ch){
161 if(optional) return 0; // return DataReader::NoSuchGroup;
162 OOFEM_ERROR("%s: %s has no child node %s.",loc().c_str(),node.name(),name.c_str());
163 }
164 int ret=0;
165 node_seen_set(ch,true);
166 for([[maybe_unused]] pugi::xml_node n: ch.children()) ret++;
167 _XML_DEBUG(node.name()<<" has "<<ret<<" children.");
168 return ret;
169 }
170 bool XMLInputRecord::hasChild(InputFieldType id, const std::string& name, bool optional){
171 bool has=!!_reader()->giveNamedChild(node,name);
172 if(!has && !optional) OOFEM_ERROR("%s: %s has no no child node '%s' (required).",loc().c_str(),node.name(),name.c_str());
173 return has;
174 }
175
176 void
177 XMLInputRecord :: giveRecordKeywordField(std :: string &answer){
178 _XML_DEBUG("node.name()="<<node.name());
179 if(node.attribute("type")) answer=_attr_traced_read("type");
180 else answer=node.name();
181 }
182 void XMLInputRecord::giveRecordKeywordField(std::string& answer, int& value){
183 _XML_DEBUG("node.name()="<<node.name());
184 answer=node.name();
185 value=recId;
186 }
187
189 std::string id=xmlizeAttrName(id0);
190 pugi::xml_attribute att=node.attribute(id.c_str());
191 if(!att){ // retry case-insensitive
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;
195 break;
196 }
197 }
198 }
199 if(att){ attrQueried.insert(att.name()); }
200 return !!att;
201 }
202 std::string XMLInputRecord::xmlizeAttrName(const std::string& s){
203 std::string n2(s);
204 for(size_t i=0; i<n2.size(); i++) if(n2[i]=='(' || n2[i]==')' || n2[i]=='/') n2[i]='_';
205 return n2;
206 }
207
208 std::tuple<std::string,pugi::xml_node> XMLInputRecord::_attr_traced_read_with_node(const char* name){
209 std::string n2=xmlizeAttrName(std::string(name));
210 pugi::xml_attribute att=node.attribute(n2.c_str());
211 if(!att){
212 // retry case-insensitive
213 for (att=node.first_attribute(); att; att=att.next_attribute()){
214 #ifdef _MSC_VER
215 #define strcasecmp _stricmp
216 #endif
217 if(strcasecmp(att.name(),name)==0){
218 std::cerr<<"XML: case-insensitive match ('"<<att.name()<<"', requested '"<<name<<"')"<<std::endl;
219 break;
220 }
221 }
222 }
223 if(!att) OOFEM_ERROR("%s: no such attribute: %s",loc().c_str(),n2.c_str());
224 std::string ret=att.as_string();
225 attrRead.insert(att.name());
226 return std::make_tuple(ret,node);
227 }
228
229 void XMLInputRecord::finish(bool wrn) {
230 _XML_DEBUG(loc());
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);
238 if(read) continue;
239 if(!queried){ xNotseen.append_attribute(a.name())=a.value(); nNotseen++; continue; }
240 if(/* !read && queried && */ hasValue){ xNotempty.append_attribute(a.name())=a.value(); nNotempty++; }
241 }
242 if(nNotseen==0 && nNotempty==0) return;
243 if(!wrn) 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); }
247 OOFEM_WARNING("%s",oss.str().c_str());
248 }
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()) {
253 if(std::string(a.name())==XMLInputRecord::SeenMark) continue;
254 n.append_attribute(a.name())=a.value();
255 }
256 std::ostringstream oss; n.print(oss,"",pugi::format_raw);
257 return oss.str();
258 }
260 OOFEM_ERROR("%s: not (yet?) implemented.",__PRETTY_FUNCTION__);
261 }
262
263 void XMLInputRecord::giveField(std::string& answer, InputFieldType id){
264 pugi::xml_node node;
265 std::tie(answer,node)=_attr_traced_read_with_node(id);
266 _XML_DEBUG(loc(node)<<": "<<node.name()<<"::"<<id<<"="<<answer);
267 }
269 Tokens tt(id,this);
270 // std::cerr<<id<<": FloatArray from '"<<tt.str<<"' ("<<tt.size()<<" items)"<<std::endl;
271 answer.resize(tt.size());
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);
274 }
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); }
283 }
284 _XML_DEBUG(tt.loc()<<": parsed attribute "<<id<<" as "<<answer);
285 }
287 Tokens tt(id,this);
288 //std::cerr<<id<<": IntArray from '"<<tt.str<<" ("<<tt.size()<<" items"<<std::endl;
289 answer.resize(tt.size());
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);
292 }
294 std::string s; pugi::xml_node n;
295 std::tie(s,n)=_attr_traced_read_with_node(id);
296 answer=string_to<double>(s,[this,n,id](){ return loc(n)+": attribute '"+id+"'"; });
297 }
299 std::string s; pugi::xml_node n;
300 std::tie(s,n)=_attr_traced_read_with_node(id);
301 answer=string_to<int>(s,[this,n,id](){ return loc(n)+": attribute '"+id+"'"; });
302 }
304 if(!hasField(id)){ answer=false; return; }
305 std::string s; pugi::xml_node n;
306 std::tie(s,n)=_attr_traced_read_with_node(id);
307 answer=string_to<bool>(s,[this,n,id](){ return loc(n)+": attribute '"+id+"'"; });
308 }
309 void XMLInputRecord::giveField(std::list<Range>& answer, InputFieldType id){
310 Tokens tt(id,this,"\\s*,\\s*");
311 for(size_t i=0; i<tt.size(); i++) answer.push_back(tt.as<Range>(i));
312 }
314 std::string s; pugi::xml_node n;
315 std::tie(s,n)=_attr_traced_read_with_node(id);
316 auto where=[this,n,id](){ return loc(n)+": attribute '"+id+"'"; };
317 if(s[0]=='@') answer.setReference(string_to<int>(s.substr(1,s.size()-1),where));
318 else if(s[0]=='$'){ std::string s2=s.substr(1,s.size()-2); answer.setSimpleExpression(s2); }
319 else answer.setValue(string_to<double>(s,where));
320 }
322 std::string s; pugi::xml_node n;
323 std::tie(s,n)=_attr_traced_read_with_node(id);
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); },/*sep_regex*/"\\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());
328 int key;
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));
333 }
334
335 }
336
337#if 0
338 void
339 XMLInputRecord :: giveField(std :: vector< std :: string > &answer, InputFieldType id)
340 {
341 int indx = this->giveKeywordIndx(id);
342 if ( indx ) {
343 int size;
344 setReadFlag(indx);
345 auto ptr = scanInteger(tokenizer.giveToken(++indx), size);
346 if ( ptr == nullptr || *ptr != 0 ) {
347 throw BadFormatInputException(*this, id, lineNumber);
348 }
349 answer.reserve(size);
350 setReadFlag(indx);
351 for ( int i = 1; i <= size; i++ ) {
352 answer.push_back( tokenizer.giveToken(indx + i) );
353 setReadFlag(indx + i);
354 }
355
356 } else {
357 throw MissingKeywordInputException(*this, id, lineNumber);
358 }
359 }
360
361#endif
362} // end namespace oofem
Pair * add(int aKey, double value)
Definition dictionary.C:79
void resize(Index s)
Definition floatarray.C:94
void resize(Index rows, Index cols)
Definition floatmatrix.C:79
void giveOptionalField(T &answer, InputFieldType id)
void resize(int n)
Definition intarray.C:73
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)
std::string loc() const
std::string giveRecordAsString() const override
Returns string representation of record in OOFEMs text format.
std::set< std::string > attrRead
void giveField(int &answer, InputFieldType id) override
Reads the integer field value.
std::string loc() const
static constexpr char SeenMark[]
bool hasField(InputFieldType id) override
Returns true if record contains field identified by idString keyword.
void giveRecordKeywordField(std ::string &answer, int &value) override
Reads the record id field (type of record) and its corresponding number.
int giveGroupCount(InputFieldType id, const std::string &name, bool optional) override
std::tuple< std::string, pugi::xml_node > _attr_traced_read_with_node(const char *name)
bool hasChild(InputFieldType id, const std::string &name, bool optional) override
std::string _attr_traced_read(const char *name)
std::string giveRecordInTXTFormat() const override
static void node_seen_set(pugi::xml_node &n, bool seen)
std::set< std::string > attrQueried
XMLDataReader * _reader() const
void finish(bool wrn=true) override
Terminates the current record session and if the flag is true, warning is printed for unscanned token...
static bool node_seen_get(const pugi::xml_node &n)
static std::string xmlizeAttrName(const std::string &s)
int setRecId(int lastRecId)
#define OOFEM_WARNING(...)
Definition error.h:80
#define OOFEM_ERROR(...)
Definition error.h:79
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.
Definition inputrecord.h:59
std::string attr
void assertSize(size_t req)
std::function< std::string()> loc
std::string str
Tokens(const std::string &attr_, XMLInputRecord *rec, const char *sep_regex="\\s+")
void _makeToks(const char *sep_regex)
pugi::xml_node node
Tokens(const std::string &attr_, const std::string &str_, std::function< std::string()> loc_, const char *sep_regex="\\s+")
T as(size_t ix)
std::vector< std::string > toks
#define _XML_DEBUG(m)

This page is part of the OOFEM-3.0 documentation. Copyright Copyright (C) 1994-2025 Borek Patzak Bořek Patzák
Project e-mail: oofem@fsv.cvut.cz
Generated at for OOFEM by doxygen 1.15.0 written by Dimitri van Heesch, © 1997-2011