1 /*
2 * %kadu copyright begin%
3 * Copyright 2014 Rafał Przemysław Malinowski (rafal.przemyslaw.malinowski@gmail.com)
4 * %kadu copyright end%
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (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
20 #include <QtCore/QDateTime>
21 #include <QtCore/QVector>
22 #include <QtXml/QDomElement>
23 #include <QtXml/QDomNodeList>
24
25 #include <errno.h>
26
27 #include "configuration/configuration-read-error-exception.h"
28 #include "debug.h"
29
30 #include "configuration-api.h"
31
ConfigurationApi()32 ConfigurationApi::ConfigurationApi()
33 {
34 auto root = DomDocument.createElement("Kadu");
35 DomDocument.appendChild(root);
36 }
37
ConfigurationApi(const QString & content)38 ConfigurationApi::ConfigurationApi(const QString &content)
39 {
40 if (!DomDocument.setContent(content))
41 {
42 throw ConfigurationReadErrorException();
43 }
44
45 if (DomDocument.documentElement().tagName() != "Kadu")
46 {
47 throw ConfigurationReadErrorException();
48 }
49 }
50
touch(const QString & version)51 void ConfigurationApi::touch(const QString &version)
52 {
53 rootElement().setAttribute("last_save_time", QDateTime::currentDateTime().toString());
54 rootElement().setAttribute("last_save_version", version);
55 }
56
configuration() const57 QString ConfigurationApi::configuration() const
58 {
59 return DomDocument.toString();
60 }
61
rootElement()62 QDomElement ConfigurationApi::rootElement()
63 {
64 return DomDocument.documentElement();
65 }
66
createElement(QDomElement parent,const QString & tag_name)67 QDomElement ConfigurationApi::createElement(QDomElement parent, const QString& tag_name)
68 {
69 const QDomElement &elem = DomDocument.createElement(tag_name);
70 parent.appendChild(elem);
71 return elem;
72 }
73
findElement(const QDomElement & parent,const QString & tag_name) const74 QDomElement ConfigurationApi::findElement(const QDomElement &parent, const QString& tag_name) const
75 {
76 return parent.firstChildElement(tag_name);
77 }
78
findElementByProperty(const QDomElement & parent,const QString & tag_name,const QString & property_name,const QString & property_value) const79 QDomElement ConfigurationApi::findElementByProperty(const QDomElement &parent, const QString &tag_name,
80 const QString &property_name, const QString &property_value) const
81 {
82 for (QDomElement elem = parent.firstChildElement(tag_name); !elem.isNull(); elem = elem.nextSiblingElement(tag_name))
83 {
84 const QString &val = elem.attribute(property_name);
85 if (val == property_value)
86 return elem;
87 }
88 return QDomNode().toElement();
89 }
90
findElementByFileNameProperty(const QDomElement & parent,const QString & tag_name,const QString & property_name,const QString & property_value) const91 QDomElement ConfigurationApi::findElementByFileNameProperty(const QDomElement &parent, const QString &tag_name,
92 const QString &property_name, const QString &property_value) const
93 {
94 for (QDomElement elem = parent.firstChildElement(tag_name); !elem.isNull(); elem = elem.nextSiblingElement(tag_name))
95 {
96 QString val = elem.attribute(property_name);
97 if (val.section('/', -1).section('\\', -1) == property_value)
98 return elem;
99 }
100 return QDomNode().toElement();
101 }
102
accessElement(const QDomElement & parent,const QString & tag_name)103 QDomElement ConfigurationApi::accessElement(const QDomElement &parent, const QString& tag_name)
104 {
105 const QDomElement &elem = findElement(parent, tag_name);
106 if (elem.isNull())
107 return createElement(parent, tag_name);
108 else
109 return elem;
110 }
111
accessElementByProperty(const QDomElement & parent,const QString & tag_name,const QString & property_name,const QString & property_value)112 QDomElement ConfigurationApi::accessElementByProperty(const QDomElement &parent, const QString& tag_name,
113 const QString& property_name, const QString& property_value)
114 {
115 QDomElement elem = findElementByProperty(parent, tag_name,
116 property_name, property_value);
117 if (elem.isNull())
118 {
119 elem = createElement(parent, tag_name);
120 elem.setAttribute(property_name, property_value);
121 }
122 return elem;
123 }
124
accessElementByFileNameProperty(const QDomElement & parent,const QString & tag_name,const QString & property_name,const QString & property_value)125 QDomElement ConfigurationApi::accessElementByFileNameProperty(const QDomElement &parent, const QString& tag_name,
126 const QString& property_name, const QString& property_value)
127 {
128 QDomElement elem = findElementByFileNameProperty(parent, tag_name,
129 property_name, property_value);
130 if (elem.isNull())
131 {
132 elem = createElement(parent, tag_name);
133 elem.setAttribute(property_name, property_value);
134 }
135 return elem;
136 }
137
removeChildren(QDomElement parent)138 void ConfigurationApi::removeChildren(QDomElement parent)
139 {
140 while (parent.hasChildNodes())
141 {
142 parent.firstChild().clear();
143 parent.removeChild(parent.firstChild());
144 }
145 }
146
removeNodes(QDomElement parentNode,const QVector<QDomElement> & elements)147 void ConfigurationApi::removeNodes(QDomElement parentNode, const QVector<QDomElement> &elements)
148 {
149 foreach (const QDomElement &element, elements)
150 parentNode.removeChild(element);
151 }
152
removeNamedNodes(QDomElement parentNode,const QVector<QDomElement> & elements,const QString & name)153 void ConfigurationApi::removeNamedNodes(QDomElement parentNode, const QVector<QDomElement> &elements, const QString &name)
154 {
155 foreach (const QDomElement &element, elements)
156 if (isElementNamed(element, name))
157 parentNode.removeChild(element);
158 }
159
removeUuidNodes(QDomElement parentNode,const QVector<QDomElement> & elements,const QString & uuid)160 void ConfigurationApi::removeUuidNodes(QDomElement parentNode, const QVector<QDomElement> &elements, const QString &uuid)
161 {
162 foreach (const QDomElement &element, elements)
163 if (isElementUuid(element, uuid))
164 parentNode.removeChild(element);
165 }
166
isElementNamed(const QDomElement & element,const QString & name)167 bool ConfigurationApi::isElementNamed(const QDomElement &element, const QString &name)
168 {
169 return element.hasAttribute("name") && name == element.attribute("name");
170 }
171
isElementUuid(const QDomElement & element,const QString & uuid)172 bool ConfigurationApi::isElementUuid(const QDomElement &element, const QString &uuid)
173 {
174 return element.hasAttribute("uuid") && uuid == element.attribute("uuid");
175 }
176
hasNode(const QString & nodeTagName)177 bool ConfigurationApi::hasNode(const QString &nodeTagName)
178 {
179 return !getNode(nodeTagName, ModeFind).isNull();
180 }
181
hasNode(const QDomElement & parentNode,const QString & nodeTagName)182 bool ConfigurationApi::hasNode(const QDomElement &parentNode, const QString &nodeTagName)
183 {
184 return !getNode(parentNode, nodeTagName, ModeFind).isNull();
185 }
186
getNode(const QString & nodeTagName,GetNodeMode getMode)187 QDomElement ConfigurationApi::getNode(const QString &nodeTagName, GetNodeMode getMode)
188 {
189 return getNode(DomDocument.documentElement(), nodeTagName, getMode);
190 }
191
getNamedNode(const QString & nodeTagName,const QString & nodeName,GetNodeMode getMode)192 QDomElement ConfigurationApi::getNamedNode(const QString &nodeTagName, const QString &nodeName, GetNodeMode getMode)
193 {
194 return getNamedNode(DomDocument.documentElement(), nodeTagName, nodeName, getMode);
195 }
196
getUuidNode(const QString & nodeTagName,const QString & nodeUuid,GetNodeMode getMode)197 QDomElement ConfigurationApi::getUuidNode(const QString &nodeTagName, const QString &nodeUuid, GetNodeMode getMode)
198 {
199 return getUuidNode(DomDocument.documentElement(), nodeTagName, nodeUuid, getMode);
200 }
201
getNode(QDomElement parentNode,const QString & nodeTagName,GetNodeMode getMode)202 QDomElement ConfigurationApi::getNode(QDomElement parentNode, const QString &nodeTagName, GetNodeMode getMode)
203 {
204 if (ModeCreate == getMode)
205 {
206 QVector<QDomElement> nodes = getNodes(parentNode, nodeTagName);
207 removeNodes(parentNode, nodes);
208 }
209 else if (ModeAppend != getMode)
210 {
211 QDomElement elem = parentNode.firstChildElement(nodeTagName);
212 if (!elem.isNull())
213 return elem;
214 }
215
216 QDomElement result;
217 if (ModeFind != getMode)
218 {
219 result = DomDocument.createElement(nodeTagName);
220 parentNode.appendChild(result);
221 }
222
223 return result;
224 }
225
getNamedNode(QDomElement parentNode,const QString & nodeTagName,const QString & nodeName,GetNodeMode getMode)226 QDomElement ConfigurationApi::getNamedNode(QDomElement parentNode, const QString &nodeTagName, const QString &nodeName, GetNodeMode getMode)
227 {
228 QVector<QDomElement> nodes = getNodes(parentNode, nodeTagName);
229
230 if (ModeCreate == getMode)
231 removeNamedNodes(parentNode, nodes, nodeName);
232
233 if (ModeGet == getMode || ModeFind == getMode)
234 foreach (const QDomElement &element, nodes)
235 if (isElementNamed(element, nodeName))
236 return element;
237
238 QDomElement result;
239 if (ModeFind != getMode)
240 {
241 result = DomDocument.createElement(nodeTagName);
242 result.setAttribute("name", nodeName);
243 parentNode.appendChild(result);
244 }
245 return result;
246 }
247
getUuidNode(QDomElement parentNode,const QString & nodeTagName,const QString & nodeUuid,GetNodeMode getMode)248 QDomElement ConfigurationApi::getUuidNode(QDomElement parentNode, const QString &nodeTagName, const QString &nodeUuid, GetNodeMode getMode)
249 {
250 QVector<QDomElement> nodes = getNodes(parentNode, nodeTagName);
251
252 if (ModeCreate == getMode)
253 removeUuidNodes(parentNode, nodes, nodeUuid);
254
255 if (ModeGet == getMode || ModeFind == getMode)
256 foreach (const QDomElement &element, nodes)
257 if (isElementUuid(element, nodeUuid))
258 return element;
259
260 QDomElement result;
261 if (ModeFind != getMode)
262 {
263 result = DomDocument.createElement(nodeTagName);
264 result.setAttribute("uuid", nodeUuid);
265 parentNode.appendChild(result);
266 }
267 return result;
268 }
269
getNodes(const QDomElement & parent,const QString & nodeTagName)270 QVector<QDomElement> ConfigurationApi::getNodes(const QDomElement &parent, const QString &nodeTagName)
271 {
272 QVector<QDomElement> result;
273
274 for (QDomElement elem = parent.firstChildElement(nodeTagName); !elem.isNull(); elem = elem.nextSiblingElement(nodeTagName))
275 result.append(elem);
276
277 return result;
278 }
279
cdataOrText(const QString & text)280 QDomNode ConfigurationApi::cdataOrText(const QString &text)
281 {
282 if (text.trimmed() != text)
283 return DomDocument.createCDATASection(text);
284 else
285 return DomDocument.createTextNode(text);
286 }
287
appendTextNode(const QDomElement & parentNode,const QString & nodeTagName,const QString & nodeContent)288 void ConfigurationApi::appendTextNode(const QDomElement &parentNode, const QString &nodeTagName, const QString &nodeContent)
289 {
290 QDomElement element = getNode(parentNode, nodeTagName, ModeAppend);
291 element.appendChild(cdataOrText(nodeContent));
292 }
293
createTextNode(const QDomElement & parentNode,const QString & nodeTagName,const QString & nodeContent)294 void ConfigurationApi::createTextNode(const QDomElement &parentNode, const QString &nodeTagName, const QString &nodeContent)
295 {
296 QDomElement element = getNode(parentNode, nodeTagName, ModeCreate);
297 element.appendChild(cdataOrText(nodeContent));
298 }
299
createNamedTextNode(const QDomElement & parentNode,const QString & nodeTagName,const QString & nodeName,const QString & nodeContent)300 void ConfigurationApi::createNamedTextNode(const QDomElement &parentNode, const QString &nodeTagName,
301 const QString &nodeName, const QString &nodeContent)
302 {
303 QDomElement element = getNamedNode(parentNode, nodeTagName, nodeName, ModeCreate);
304 element.appendChild(cdataOrText(nodeContent));
305 }
306
getTextNode(const QDomElement & parentNode,const QString & nodeTagName,const QString & defaultValue)307 QString ConfigurationApi::getTextNode(const QDomElement &parentNode, const QString &nodeTagName, const QString &defaultValue)
308 {
309 QDomElement element = getNode(parentNode, nodeTagName, ModeFind);
310 if (element.isNull())
311 return defaultValue;
312
313 return element.text();
314 }
315
removeNode(QDomElement parentNode,const QString & nodeTagName)316 void ConfigurationApi::removeNode(QDomElement parentNode, const QString& nodeTagName)
317 {
318 QDomElement elementToRemove = getNode(parentNode, nodeTagName, ModeFind);
319 while (!elementToRemove.isNull())
320 {
321 parentNode.removeChild(elementToRemove);
322 elementToRemove = getNode(parentNode, nodeTagName, ModeFind);
323 }
324 }
325