2
Copyright (c) 2009 Volker Krause <vkrause@kde.org>
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.
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.
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
20
#include "xmldocument.h"
22
#include "xmlreader.h"
27
#include <KStandardDirs>
33
#include <libxml/parser.h>
34
#include <libxml/xmlIO.h>
35
#include <libxml/xmlschemas.h>
38
static const KCatalogLoader loader( QLatin1String( "libakonadi-xml") );
40
using namespace Akonadi;
42
// helper class for dealing with libxml resource management
43
template <typename T, void FreeFunc(T)> class XmlPtr
46
XmlPtr( const T &t ) : p( t ) {}
67
static QDomElement findElementByRidHelper( const QDomElement &elem, const QString &rid, const QString &elemName )
71
if ( elem.tagName() == elemName && elem.attribute( Format::Attr::remoteId() ) == rid )
73
const QDomNodeList children = elem.childNodes();
74
for ( int i = 0; i < children.count(); ++i ) {
75
const QDomElement child = children.at( i ).toElement();
78
const QDomElement rv = findElementByRidHelper( child, rid, elemName );
87
class XmlDocumentPrivate
90
XmlDocumentPrivate() :
93
lastError = i18n( "No data loaded." );
96
QDomElement findElementByRid( const QString &rid, const QString &elemName ) const
98
return findElementByRidHelper( document.documentElement(), rid, elemName );
101
QDomDocument document;
108
XmlDocument::XmlDocument() :
109
d( new XmlDocumentPrivate )
111
const QDomElement rootElem = d->document.createElement( Format::Tag::root() );
112
d->document.appendChild( rootElem );
115
XmlDocument::XmlDocument(const QString& fileName) :
116
d( new XmlDocumentPrivate )
118
loadFile( fileName );
121
XmlDocument::~XmlDocument()
126
bool Akonadi::XmlDocument::loadFile(const QString& fileName)
129
d->document = QDomDocument();
131
if ( fileName.isEmpty() ) {
132
d->lastError = i18n( "No filename specified" );
136
QFile file( fileName );
138
if ( file.exists() ) {
139
if ( !file.open( QIODevice::ReadOnly ) ) {
140
d->lastError = i18n( "Unable to open data file '%1'.", fileName );
143
data = file.readAll();
145
d->lastError = i18n( "File %1 does not exist.", fileName );
151
XmlPtr<xmlDocPtr, xmlFreeDoc> sourceDoc( xmlParseMemory( data.constData(), data.length() ) );
153
d->lastError = i18n( "Unable to parse data file '%1'.", fileName );
157
const QString &schemaFileName = KGlobal::dirs()->findResource( "data", QLatin1String("akonadi/akonadi-xml.xsd") );
158
XmlPtr<xmlDocPtr, xmlFreeDoc> schemaDoc( xmlReadFile( schemaFileName.toLocal8Bit(), 0, XML_PARSE_NONET ) );
160
d->lastError = i18n( "Schema definition could not be loaded and parsed." );
163
XmlPtr<xmlSchemaParserCtxtPtr, xmlSchemaFreeParserCtxt> parserContext( xmlSchemaNewDocParserCtxt( schemaDoc ) );
164
if ( !parserContext ) {
165
d->lastError = i18n( "Unable to create schema parser context." );
168
XmlPtr<xmlSchemaPtr, xmlSchemaFree> schema( xmlSchemaParse( parserContext ) );
170
d->lastError = i18n( "Unable to create schema." );
173
XmlPtr<xmlSchemaValidCtxtPtr, xmlSchemaFreeValidCtxt> validationContext( xmlSchemaNewValidCtxt( schema ) );
174
if ( !validationContext ) {
175
d->lastError = i18n( "Unable to create schema validation context." );
179
if ( xmlSchemaValidateDoc( validationContext, sourceDoc ) != 0 ) {
180
d->lastError = i18n( "Invalid file format." );
187
if ( !d->document.setContent( data, true, &errMsg ) ) {
188
d->lastError = i18n( "Unable to parse data file: %1", errMsg );
193
d->lastError.clear();
197
bool XmlDocument::writeToFile(const QString& fileName) const
200
if ( !f.open( QFile::WriteOnly ) ) {
201
d->lastError = f.errorString();
205
f.write( d->document.toByteArray( 2 ) );
207
d->lastError.clear();
211
bool XmlDocument::isValid() const
216
QString XmlDocument::lastError() const
221
QDomDocument& XmlDocument::document() const
226
QDomElement XmlDocument::collectionElementByRemoteId(const QString& rid) const
228
return d->findElementByRid( rid, Format::Tag::collection() );
231
QDomElement XmlDocument::collectionElement( const Collection &collection ) const
233
if ( collection == Collection::root() )
234
return d->document.documentElement();
235
if ( collection.remoteId().isEmpty() )
236
return QDomElement();
237
if ( collection.parentCollection().remoteId().isEmpty() && collection.parentCollection() != Collection::root() )
238
return d->findElementByRid( collection.remoteId(), Format::Tag::collection() );
239
QDomElement parent = collectionElement( collection.parentCollection() );
240
if ( parent.isNull() )
241
return QDomElement();
242
const QDomNodeList children = parent.childNodes();
243
for ( int i = 0; i < children.count(); ++i ) {
244
const QDomElement child = children.at( i ).toElement();
245
if ( child.isNull() )
247
if ( child.tagName() == Format::Tag::collection() && child.attribute( Format::Attr::remoteId() ) == collection.remoteId() )
250
return QDomElement();
253
QDomElement XmlDocument::itemElementByRemoteId(const QString& rid) const
255
return d->findElementByRid( rid, Format::Tag::item() );
258
Collection XmlDocument::collectionByRemoteId(const QString& rid) const
260
const QDomElement elem = collectionElementByRemoteId( rid );
261
return XmlReader::elementToCollection( elem );
264
Item XmlDocument::itemByRemoteId(const QString& rid, bool includePayload) const
266
return XmlReader::elementToItem( itemElementByRemoteId( rid ), includePayload );
269
Collection::List XmlDocument::collections() const
271
return XmlReader::readCollections( d->document.documentElement() );
274
Collection::List XmlDocument::childCollections(const QString& parentCollectionRid) const
277
c.setRemoteId( parentCollectionRid );
278
return childCollections( c );
281
Collection::List XmlDocument::childCollections( const Collection &parentCollection ) const
283
QDomElement parentElem = collectionElement( parentCollection );
285
if ( parentElem.isNull() ) {
286
d->lastError = QLatin1String( "Parent node not found." );
287
return Collection::List();
291
const QDomNodeList children = parentElem.childNodes();
292
for ( int i = 0; i < children.count(); ++i ) {
293
const QDomElement childElem = children.at( i ).toElement();
294
if ( childElem.isNull() || childElem.tagName() != Format::Tag::collection() )
296
Collection c = XmlReader::elementToCollection( childElem );
297
c.setParentCollection( parentCollection );
305
Item::List XmlDocument::items(const Akonadi::Collection& collection, bool includePayload) const
307
const QDomElement colElem = collectionElement( collection );
308
if ( colElem.isNull() ) {
309
d->lastError = i18n( "Unable to find collection %1", collection.name() );
312
d->lastError.clear();
316
const QDomNodeList children = colElem.childNodes();
317
for ( int i = 0; i < children.count(); ++i ) {
318
const QDomElement itemElem = children.at( i ).toElement();
319
if ( itemElem.isNull() || itemElem.tagName() != Format::Tag::item() )
321
items += XmlReader::elementToItem( itemElem, includePayload );