Rev 1221 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
671 | werner | 1 | /******************************************************************************************** |
2 | ** iLand - an individual based forest landscape and disturbance model |
||
3 | ** http://iland.boku.ac.at |
||
4 | ** Copyright (C) 2009- Werner Rammer, Rupert Seidl |
||
5 | ** |
||
6 | ** This program is free software: you can redistribute it and/or modify |
||
7 | ** it under the terms of the GNU General Public License as published by |
||
8 | ** the Free Software Foundation, either version 3 of the License, or |
||
9 | ** (at your option) any later version. |
||
10 | ** |
||
11 | ** This program is distributed in the hope that it will be useful, |
||
12 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
14 | ** GNU General Public License for more details. |
||
15 | ** |
||
16 | ** You should have received a copy of the GNU General Public License |
||
17 | ** along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
18 | ********************************************************************************************/ |
||
19 | |||
530 | werner | 20 | #include "xmlhelper.h" |
21 | #include "helper.h" |
||
22 | #include "exception.h" |
||
23 | |||
24 | |||
93 | Werner | 25 | /** @class XmlHelper |
26 | XmlHelper wraps a XML file and provides some convenient functions to |
||
27 | retrieve values. Internally XmlHelper uses a QDomDocument (the full structure is |
||
28 | kept in memory so the size is restricted). |
||
29 | Use node() to get a QDomElement or use value() to directly retrieve the node value. |
||
30 | Nodes could be addressed relative to a node defined by setCurrentNode() using a ".". |
||
31 | The notation is as follows: |
||
186 | werner | 32 | - a '.' character defines a hierarchy |
33 | - [] the Nth element of the same hierarchical layer can be retrieved by [n-1] |
||
34 | Use also the convenience functions valueBool() and valueDouble(). |
||
35 | While all the value/node etc. functions parse the DOM tree at every call, the data accessed by paramValue() - type |
||
36 | functions is parsed only once during startup and stored in a QVariant array. Accessible are all nodes that are children of the |
||
37 | "<parameter>"-node. |
||
38 | |||
93 | Werner | 39 | @code |
40 | QDomElement e,f |
||
41 | e = xml.node("first.second.third"); // e points to "third" |
||
42 | xml.setCurrentNode("first"); |
||
43 | f = xml.node(".second.third"); // e and f are equal |
||
44 | e = xml.node("level1[2].level2[3].level3"); // 3rd element of level 1, ... |
||
45 | int x = xml.value(".second", "0").toInt(); // node value of "second" or "0" if not found. |
||
186 | werner | 46 | if (xml.valueBool("enabled")) // use of valueBool with default value (false) |
47 | ... |
||
192 | werner | 48 | XmlHelper xml_sec(xml.node("first.second")); // create a xml-helper with top node=first.second |
49 | xml_sec.valueDouble("third"); // use this |
||
93 | Werner | 50 | @endcode |
51 | |||
52 | */ |
||
53 | |||
54 | XmlHelper::XmlHelper() |
||
55 | { |
||
56 | } |
||
194 | werner | 57 | XmlHelper::~XmlHelper() |
58 | { |
||
59 | //qDebug() << "xml helper destroyed"; |
||
60 | } |
||
192 | werner | 61 | /** Create a XmlHelper instance with @p topNode as top node. |
62 | The xml tree is not copied. |
||
63 | */ |
||
93 | Werner | 64 | XmlHelper::XmlHelper(QDomElement topNode) |
65 | { |
||
66 | mTopNode = topNode; |
||
67 | mCurrentTop = topNode; |
||
68 | } |
||
69 | |||
70 | void XmlHelper::loadFromFile(const QString &fileName) |
||
71 | { |
||
72 | mDoc.clear(); |
||
73 | QString xmlFile = Helper::loadTextFile(fileName); |
||
74 | |||
75 | if (!xmlFile.isEmpty()) { |
||
76 | |||
77 | QString errMsg; |
||
78 | int errLine, errCol; |
||
79 | if (!mDoc.setContent(xmlFile, &errMsg, &errLine, &errCol)) { |
||
102 | Werner | 80 | throw IException(QString("Error in xml-file!\nError applying xml line %1, col %2.\nMessage: %3").arg(errLine).arg(errCol).arg(errMsg)); |
93 | Werner | 81 | } |
82 | } else { |
||
102 | Werner | 83 | throw IException("xmlfile does not exist or is empty!"); |
93 | Werner | 84 | } |
85 | mCurrentTop = mDoc.documentElement(); // top element |
||
86 | mTopNode = mCurrentTop; |
||
137 | Werner | 87 | |
88 | // fill parameter cache |
||
191 | werner | 89 | QDomElement e = node("model.parameter"); |
137 | Werner | 90 | e = e.firstChildElement(); |
91 | mParamCache.clear(); |
||
92 | while (!e.isNull()) { |
||
93 | mParamCache[e.nodeName()] = e.text(); |
||
94 | e = e.nextSiblingElement(); |
||
95 | } |
||
93 | Werner | 96 | } |
97 | |||
137 | Werner | 98 | /** numeric values of elements in the section <parameter> are stored in a QHash structure for faster access. |
99 | with paramValue() these data can be accessed. |
||
100 | */ |
||
145 | Werner | 101 | double XmlHelper::paramValue(const QString ¶mName, const double defaultValue) const |
137 | Werner | 102 | { |
103 | if (mParamCache.contains(paramName)) |
||
104 | return mParamCache.value(paramName).toDouble(); |
||
105 | return defaultValue; |
||
106 | } |
||
145 | Werner | 107 | QString XmlHelper::paramValueString(const QString ¶mName, const QString &defaultValue) const |
137 | Werner | 108 | { |
109 | if (mParamCache.contains(paramName)) |
||
110 | return mParamCache.value(paramName); |
||
111 | return defaultValue; |
||
112 | } |
||
113 | |||
171 | werner | 114 | bool XmlHelper::paramValueBool(const QString ¶mName, const bool &defaultValue) const |
115 | { |
||
116 | if (mParamCache.contains(paramName)) { |
||
359 | werner | 117 | QString v = mParamCache.value(paramName).trimmed(); |
118 | bool ret = (v=="1" || v=="true"); |
||
119 | return ret; |
||
171 | werner | 120 | } |
121 | return defaultValue; |
||
122 | } |
||
123 | |||
104 | Werner | 124 | bool XmlHelper::hasNode(const QString &path) const |
125 | { |
||
126 | return !node(path).isNull(); |
||
127 | } |
||
93 | Werner | 128 | |
102 | Werner | 129 | QString XmlHelper::value(const QString &path, const QString &defaultValue) const |
93 | Werner | 130 | { |
131 | QDomElement e = node(path); |
||
211 | werner | 132 | if (e.isNull()) { |
133 | qDebug() << "Warning: xml: node" << path << "is not present."; |
||
93 | Werner | 134 | return defaultValue; |
211 | werner | 135 | } else { |
189 | iland | 136 | if (e.text().isEmpty()) |
137 | return defaultValue; |
||
138 | else |
||
139 | return e.text(); |
||
140 | } |
||
93 | Werner | 141 | } |
181 | werner | 142 | bool XmlHelper::valueBool(const QString &path, const bool defaultValue) const |
143 | { |
||
144 | QDomElement e = node(path); |
||
211 | werner | 145 | if (e.isNull()) { |
146 | qDebug() << "Warning: xml: node" << path << "is not present."; |
||
181 | werner | 147 | return defaultValue; |
280 | werner | 148 | } |
149 | QString v = e.text(); |
||
181 | werner | 150 | if (v=="true" || v=="True" || v=="1") |
151 | return true; |
||
152 | else |
||
153 | return false; |
||
154 | } |
||
155 | double XmlHelper::valueDouble(const QString &path, const double defaultValue) const |
||
156 | { |
||
157 | QDomElement e = node(path); |
||
211 | werner | 158 | if (e.isNull()) { |
159 | qDebug() << "Warning: xml: node" << path << "is not present."; |
||
181 | werner | 160 | return defaultValue; |
211 | werner | 161 | } else { |
189 | iland | 162 | if (e.text().isEmpty()) |
163 | return defaultValue; |
||
164 | else |
||
165 | return e.text().toDouble(); |
||
166 | } |
||
181 | werner | 167 | } |
93 | Werner | 168 | |
1102 | werner | 169 | int XmlHelper::valueInt(const QString &path, const int defaultValue) const |
170 | { |
||
171 | double dbl_val = valueDouble(path, defaultValue); |
||
172 | return static_cast<int>(dbl_val); |
||
173 | } |
||
174 | |||
93 | Werner | 175 | /// retreives node with given @p path and a element where isNull() is true if nothing is found. |
102 | Werner | 176 | QDomElement XmlHelper::node(const QString &path) const |
93 | Werner | 177 | { |
178 | QStringList elem = path.split('.', QString::SkipEmptyParts); |
||
179 | QDomElement c; |
||
180 | if (path.count()>0 && path.at(0) == '.') |
||
181 | c = mCurrentTop; |
||
182 | else |
||
183 | c = mTopNode; |
||
184 | foreach (QString level, elem) { |
||
185 | if (level.indexOf('[')<0) { |
||
186 | c = c.firstChildElement(level); |
||
187 | if (c.isNull()) |
||
188 | break; |
||
189 | } else { |
||
190 | int pos = level.indexOf('['); |
||
191 | level.chop(1); // drop closing bracket |
||
192 | int ind = level.right( level.length() - pos -1).toInt(); |
||
193 | QString name = level.left(pos); |
||
194 | c = c.firstChildElement(name); |
||
195 | while (ind>0 && !c.isNull()) { |
||
196 | c = c.nextSiblingElement(); |
||
197 | ind--; |
||
198 | } |
||
199 | if (c.isNull()) |
||
200 | break; |
||
201 | } |
||
202 | } |
||
192 | werner | 203 | //qDebug() << "node-request:" << path; |
93 | Werner | 204 | return c; |
205 | } |
||
206 | |||
280 | werner | 207 | // writers |
208 | bool XmlHelper::setNodeValue(QDomElement &node, const QString &value) |
||
209 | { |
||
210 | if (!node.isNull() && node.hasChildNodes()) { |
||
211 | node.firstChild().toText().setData(value); |
||
212 | return true; |
||
213 | } |
||
214 | return false; |
||
215 | } |
||
216 | bool XmlHelper::setNodeValue(const QString &path, const QString &value) |
||
217 | { |
||
218 | QDomElement e = node(path); |
||
341 | werner | 219 | if (e.isNull()) { |
220 | qDebug() << "XML: attempting to set value of" << path << ": node not present."; |
||
221 | return false; |
||
222 | } |
||
280 | werner | 223 | return setNodeValue(e,value); |
224 | } |
||
225 | |||
93 | Werner | 226 | // private recursive loop |
777 | werner | 227 | void XmlHelper::dump_rec(QDomElement c, QStringList &stack, QStringList &out) |
93 | Werner | 228 | { |
229 | if (c.isNull()) |
||
230 | return; |
||
231 | QDomElement ch = c.firstChildElement(); |
||
232 | bool hasChildren = !ch.isNull(); |
||
233 | bool nChildren = !ch.isNull() && !ch.nextSiblingElement().isNull(); |
||
234 | int child_index=-1; |
||
235 | while (!ch.isNull()) { |
||
236 | if (nChildren) { |
||
237 | child_index++; |
||
238 | stack.push_back(QString("%1[%2]").arg(ch.nodeName()).arg(child_index)); |
||
239 | } else |
||
240 | stack.push_back(ch.nodeName()); |
||
777 | werner | 241 | dump_rec(ch, stack, out); |
93 | Werner | 242 | stack.pop_back(); |
243 | ch = ch.nextSiblingElement(); |
||
244 | } |
||
245 | QString self; |
||
246 | if (!hasChildren) |
||
247 | self = c.text(); |
||
248 | self = QString("%1: %3").arg(stack.join("."), self); |
||
249 | out.push_back(self); |
||
250 | } |
||
251 | |||
252 | QString XmlHelper::dump(const QString &path, int levels) |
||
253 | { |
||
777 | werner | 254 | Q_UNUSED(levels); |
93 | Werner | 255 | QDomElement c = node(path); |
256 | |||
257 | QStringList stack; |
||
258 | stack.push_back(c.nodeName()); |
||
259 | QStringList result; |
||
260 | dump_rec(c, stack, result); |
||
261 | return result.join("\n"); |
||
262 | } |