Akonadi

xmldocument.cpp
1 /*
2  Copyright (c) 2009 Volker Krause <[email protected]>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 
20 #include "xmldocument.h"
21 #include "format_p.h"
22 #include "xmlreader.h"
23 
24 #include <KLocalizedString>
25 
26 #include <qdom.h>
27 #include <QFile>
28 
29 #ifdef HAVE_LIBXML2
30 #include <libxml/parser.h>
31 #include <libxml/xmlIO.h>
32 #include <libxml/xmlschemas.h>
33 #include <QStandardPaths>
34 #endif
35 
36 using namespace Akonadi;
37 
38 // helper class for dealing with libxml resource management
39 template <typename T, void FreeFunc(T)> class XmlPtr
40 {
41 public:
42  XmlPtr(const T &t) : p(t) {}
43 
44  ~XmlPtr()
45  {
46  FreeFunc(p);
47  }
48 
49  operator T() const
50  {
51  return p;
52  }
53 
54  operator bool() const
55  {
56  return p != nullptr;
57  }
58 
59 private:
60  Q_DISABLE_COPY(XmlPtr)
61  T p;
62 };
63 
64 static QDomElement findElementByRidHelper(const QDomElement &elem, const QString &rid, const QString &elemName)
65 {
66  if (elem.isNull()) {
67  return QDomElement();
68  }
69  if (elem.tagName() == elemName && elem.attribute(Format::Attr::remoteId()) == rid) {
70  return elem;
71  }
72  const QDomNodeList children = elem.childNodes();
73  for (int i = 0; i < children.count(); ++i) {
74  const QDomElement child = children.at(i).toElement();
75  if (child.isNull()) {
76  continue;
77  }
78  const QDomElement rv = findElementByRidHelper(child, rid, elemName);
79  if (!rv.isNull()) {
80  return rv;
81  }
82  }
83  return QDomElement();
84 }
85 
86 namespace Akonadi
87 {
88 
89 class XmlDocumentPrivate
90 {
91 public:
92  XmlDocumentPrivate() :
93  valid(false)
94  {
95  lastError = i18n("No data loaded.");
96  }
97 
98  QDomElement findElementByRid(const QString &rid, const QString &elemName) const
99  {
100  return findElementByRidHelper(document.documentElement(), rid, elemName);
101  }
102 
103  QDomDocument document;
104  QString lastError;
105  bool valid;
106 };
107 
108 }
109 
111  d(new XmlDocumentPrivate)
112 {
113  const QDomElement rootElem = d->document.createElement(Format::Tag::root());
114  d->document.appendChild(rootElem);
115 }
116 
118  d(new XmlDocumentPrivate)
119 {
120  loadFile(fileName);
121 }
122 
123 XmlDocument::~XmlDocument()
124 {
125  delete d;
126 }
127 
129 {
130  d->valid = false;
131  d->document = QDomDocument();
132 
133  if (fileName.isEmpty()) {
134  d->lastError = i18n("No filename specified");
135  return false;
136  }
137 
138  QFile file(fileName);
139  QByteArray data;
140  if (file.exists()) {
141  if (!file.open(QIODevice::ReadOnly)) {
142  d->lastError = i18n("Unable to open data file '%1'.", fileName);
143  return false;
144  }
145  data = file.readAll();
146  } else {
147  d->lastError = i18n("File %1 does not exist.", fileName);
148  return false;
149  }
150 
151 #ifdef HAVE_LIBXML2
152  // schema validation
153  XmlPtr<xmlDocPtr, xmlFreeDoc> sourceDoc(xmlParseMemory(data.constData(), data.length()));
154  if (!sourceDoc) {
155  d->lastError = i18n("Unable to parse data file '%1'.", fileName);
156  return false;
157  }
158 
159  const QString &schemaFileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf5/akonadi/akonadi-xml.xsd"));
160  XmlPtr<xmlDocPtr, xmlFreeDoc> schemaDoc(xmlReadFile(schemaFileName.toLocal8Bit().constData(), nullptr, XML_PARSE_NONET));
161  if (!schemaDoc) {
162  d->lastError = i18n("Schema definition could not be loaded and parsed.");
163  return false;
164  }
165  XmlPtr<xmlSchemaParserCtxtPtr, xmlSchemaFreeParserCtxt> parserContext(xmlSchemaNewDocParserCtxt(schemaDoc));
166  if (!parserContext) {
167  d->lastError = i18n("Unable to create schema parser context.");
168  return false;
169  }
170  XmlPtr<xmlSchemaPtr, xmlSchemaFree> schema(xmlSchemaParse(parserContext));
171  if (!schema) {
172  d->lastError = i18n("Unable to create schema.");
173  return false;
174  }
175  XmlPtr<xmlSchemaValidCtxtPtr, xmlSchemaFreeValidCtxt> validationContext(xmlSchemaNewValidCtxt(schema));
176  if (!validationContext) {
177  d->lastError = i18n("Unable to create schema validation context.");
178  return false;
179  }
180 
181  if (xmlSchemaValidateDoc(validationContext, sourceDoc) != 0) {
182  d->lastError = i18n("Invalid file format.");
183  return false;
184  }
185 #endif
186 
187  // DOM loading
188  QString errMsg;
189  if (!d->document.setContent(data, true, &errMsg)) {
190  d->lastError = i18n("Unable to parse data file: %1", errMsg);
191  return false;
192  }
193 
194  d->valid = true;
195  d->lastError.clear();
196  return true;
197 }
198 
199 bool XmlDocument::writeToFile(const QString &fileName) const
200 {
201  QFile f(fileName);
202  if (!f.open(QFile::WriteOnly)) {
203  d->lastError = f.errorString();
204  return false;
205  }
206 
207  f.write(d->document.toByteArray(2));
208 
209  d->lastError.clear();
210  return true;
211 }
212 
214 {
215  return d->valid;
216 }
217 
219 {
220  return d->lastError;
221 }
222 
224 {
225  return d->document;
226 }
227 
229 {
230  if (collection == Collection::root()) {
231  return d->document.documentElement();
232  }
233  if (collection.remoteId().isEmpty()) {
234  return QDomElement();
235  }
236  if (collection.parentCollection().remoteId().isEmpty() && collection.parentCollection() != Collection::root()) {
237  return d->findElementByRid(collection.remoteId(), Format::Tag::collection());
238  }
239  QDomElement parent = collectionElement(collection.parentCollection());
240  if (parent.isNull()) {
241  return QDomElement();
242  }
243  const QDomNodeList children = parent.childNodes();
244  for (int i = 0; i < children.count(); ++i) {
245  const QDomElement child = children.at(i).toElement();
246  if (child.isNull()) {
247  continue;
248  }
249  if (child.tagName() == Format::Tag::collection() && child.attribute(Format::Attr::remoteId()) == collection.remoteId()) {
250  return child;
251  }
252  }
253  return QDomElement();
254 }
255 
257 {
258  return d->findElementByRid(rid, Format::Tag::item());
259 }
260 
262 {
263  return d->findElementByRid(rid, Format::Tag::collection());
264 }
265 
267 {
268  const QDomElement elem = d->findElementByRid(rid, Format::Tag::collection());
269  return XmlReader::elementToCollection(elem);
270 }
271 
272 Item XmlDocument::itemByRemoteId(const QString &rid, bool includePayload) const
273 {
274  return XmlReader::elementToItem(itemElementByRemoteId(rid), includePayload);
275 }
276 
278 {
279  return XmlReader::readCollections(d->document.documentElement());
280 }
281 
283 {
284  return XmlReader::readTags(d->document.documentElement());
285 }
286 
288 {
289  QDomElement parentElem = collectionElement(parentCollection);
290 
291  if (parentElem.isNull()) {
292  d->lastError = QStringLiteral("Parent node not found.");
293  return Collection::List();
294  }
295 
296  Collection::List rv;
297  const QDomNodeList children = parentElem.childNodes();
298  for (int i = 0; i < children.count(); ++i) {
299  const QDomElement childElem = children.at(i).toElement();
300  if (childElem.isNull() || childElem.tagName() != Format::Tag::collection()) {
301  continue;
302  }
304  c.setParentCollection(parentCollection);
305  rv.append(c);
306  }
307 
308  return rv;
309 }
310 
311 Item::List XmlDocument::items(const Akonadi::Collection &collection, bool includePayload) const
312 {
313  const QDomElement colElem = collectionElement(collection);
314  if (colElem.isNull()) {
315  d->lastError = i18n("Unable to find collection %1", collection.name());
316  return Item::List();
317  } else {
318  d->lastError.clear();
319  }
320 
321  Item::List items;
322  const QDomNodeList children = colElem.childNodes();
323  for (int i = 0; i < children.count(); ++i) {
324  const QDomElement itemElem = children.at(i).toElement();
325  if (itemElem.isNull() || itemElem.tagName() != Format::Tag::item()) {
326  continue;
327  }
328  items += XmlReader::elementToItem(itemElem, includePayload);
329  }
330 
331  return items;
332 }
AKONADI_XML_EXPORT Item elementToItem(const QDomElement &elem, bool includePayload=true)
Converts an item element.
Definition: xmlreader.cpp:149
bool isValid() const
Returns true if the document could be parsed successfully.
QString name() const
Returns the i18n&#39;ed name of the collection.
Definition: collection.cpp:225
void append(const T &value)
QDomElement collectionElement(const Collection &collection) const
Returns the DOM element representing collection.
QString attribute(const QString &name, const QString &defValue) const const
AKONADI_XML_EXPORT Collection::List readCollections(const QDomElement &elem)
Reads recursively all collections starting from the given DOM element.
Definition: xmlreader.cpp:86
QString errorString() const const
QDomDocument & document() const
Returns the DOM document for this XML document.
bool writeToFile(const QString &fileName) const
Writes the current document into the given file.
bool loadFile(const QString &fileName)
Parses the given XML file and validates it.
Represents a collection of PIM items.
Definition: collection.h:76
AKONADI_XML_EXPORT Collection elementToCollection(const QDomElement &elem)
Converts a collection element.
Definition: xmlreader.cpp:66
XmlDocument()
Creates an empty document.
int length() const const
bool exists() const const
QDomNodeList childNodes() const const
QVector< Collection > List
Describes a list of collections.
Definition: collection.h:87
void setParentCollection(const Collection &parent)
Set the parent collection of this object.
Definition: collection.cpp:220
QDomElement toElement() const const
int count() const const
QDomElement itemElementByRemoteId(const QString &rid) const
Returns the DOM element representing the item with the given remote id.
bool isEmpty() const const
const char * constData() const const
QString lastError() const
Returns the last error occurred during file loading/parsing.
QByteArray readAll()
Tag::List tags() const
Returns the tags defined in this document.
QDomElement collectionElementByRemoteId(const QString &rid) const
Returns the DOM element representing the collection with the given remote id.
static Collection root()
Returns the root collection.
Definition: collection.cpp:303
Collection parentCollection() const
Returns the parent collection of this object.
Definition: collection.cpp:211
virtual bool open(QIODevice::OpenMode mode) override
Collection collectionByRemoteId(const QString &rid) const
Returns the collection with the given remote id.
QByteArray toLocal8Bit() const const
Collection::List childCollections(const Collection &parentCollection) const
Returns the immediate child collections of parentCollection.
bool isNull() const const
QString i18n(const char *text, const TYPE &arg...)
Collection::List collections() const
Returns the collections defined in this document.
Helper integration between Akonadi and Qt.
QString remoteId() const
Returns the remote id of the collection.
Definition: collection.cpp:122
qint64 write(const char *data, qint64 maxSize)
QString tagName() const const
Item::List items(const Collection &collection, bool includePayload=true) const
Returns the items in the given collection.
AKONADI_XML_EXPORT Tag::List readTags(const QDomElement &elem)
Reads recursively all tags starting from the given DOM element.
Definition: xmlreader.cpp:129
Item itemByRemoteId(const QString &rid, bool includePayload=true) const
Returns the item with the given remote id.
QString locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
QDomNode at(int index) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Fri Jun 5 2020 23:08:56 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.