kde-playground/kdepimlibs/akonadi/xml/xmldocument.cpp
2015-04-14 21:49:29 +00:00

330 lines
8.9 KiB
C++

/*
Copyright (c) 2009 Volker Krause <vkrause@kde.org>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#include "xmldocument.h"
#include "format_p.h"
#include "xmlreader.h"
#include <KDebug>
#include <KGlobal>
#include <KLocalizedString>
#include <KStandardDirs>
#include <qdom.h>
#include <qfile.h>
#ifdef HAVE_LIBXML2
#include <libxml/parser.h>
#include <libxml/xmlIO.h>
#include <libxml/xmlschemas.h>
#endif
static const KCatalogLoader loader( QLatin1String( "libakonadi-xml") );
using namespace Akonadi;
// helper class for dealing with libxml resource management
template <typename T, void FreeFunc(T)> class XmlPtr
{
public:
XmlPtr( const T &t ) : p( t ) {}
~XmlPtr()
{
FreeFunc( p );
}
operator T() const
{
return p;
}
operator bool() const
{
return p != 0;
}
private:
T p;
};
static QDomElement findElementByRidHelper( const QDomElement &elem, const QString &rid, const QString &elemName )
{
if ( elem.isNull() )
return QDomElement();
if ( elem.tagName() == elemName && elem.attribute( Format::Attr::remoteId() ) == rid )
return elem;
const QDomNodeList children = elem.childNodes();
for ( int i = 0; i < children.count(); ++i ) {
const QDomElement child = children.at( i ).toElement();
if ( child.isNull() )
continue;
const QDomElement rv = findElementByRidHelper( child, rid, elemName );
if ( !rv.isNull() )
return rv;
}
return QDomElement();
}
namespace Akonadi {
class XmlDocumentPrivate
{
public:
XmlDocumentPrivate() :
valid( false )
{
lastError = i18n( "No data loaded." );
}
QDomElement findElementByRid( const QString &rid, const QString &elemName ) const
{
return findElementByRidHelper( document.documentElement(), rid, elemName );
}
QDomDocument document;
QString lastError;
bool valid;
};
}
XmlDocument::XmlDocument() :
d( new XmlDocumentPrivate )
{
const QDomElement rootElem = d->document.createElement( Format::Tag::root() );
d->document.appendChild( rootElem );
}
XmlDocument::XmlDocument(const QString& fileName) :
d( new XmlDocumentPrivate )
{
loadFile( fileName );
}
XmlDocument::~XmlDocument()
{
delete d;
}
bool Akonadi::XmlDocument::loadFile(const QString& fileName)
{
d->valid = false;
d->document = QDomDocument();
if ( fileName.isEmpty() ) {
d->lastError = i18n( "No filename specified" );
return false;
}
QFile file( fileName );
QByteArray data;
if ( file.exists() ) {
if ( !file.open( QIODevice::ReadOnly ) ) {
d->lastError = i18n( "Unable to open data file '%1'.", fileName );
return false;
}
data = file.readAll();
} else {
d->lastError = i18n( "File %1 does not exist.", fileName );
return false;
}
#ifdef HAVE_LIBXML2
// schema validation
XmlPtr<xmlDocPtr, xmlFreeDoc> sourceDoc( xmlParseMemory( data.constData(), data.length() ) );
if ( !sourceDoc ) {
d->lastError = i18n( "Unable to parse data file '%1'.", fileName );
return false;
}
const QString &schemaFileName = KGlobal::dirs()->findResource( "data", QLatin1String("akonadi/akonadi-xml.xsd") );
XmlPtr<xmlDocPtr, xmlFreeDoc> schemaDoc( xmlReadFile( schemaFileName.toLocal8Bit(), 0, XML_PARSE_NONET ) );
if ( !schemaDoc ) {
d->lastError = i18n( "Schema definition could not be loaded and parsed." );
return false;
}
XmlPtr<xmlSchemaParserCtxtPtr, xmlSchemaFreeParserCtxt> parserContext( xmlSchemaNewDocParserCtxt( schemaDoc ) );
if ( !parserContext ) {
d->lastError = i18n( "Unable to create schema parser context." );
return false;
}
XmlPtr<xmlSchemaPtr, xmlSchemaFree> schema( xmlSchemaParse( parserContext ) );
if ( !schema ) {
d->lastError = i18n( "Unable to create schema." );
return false;
}
XmlPtr<xmlSchemaValidCtxtPtr, xmlSchemaFreeValidCtxt> validationContext( xmlSchemaNewValidCtxt( schema ) );
if ( !validationContext ) {
d->lastError = i18n( "Unable to create schema validation context." );
return false;
}
if ( xmlSchemaValidateDoc( validationContext, sourceDoc ) != 0 ) {
d->lastError = i18n( "Invalid file format." );
return false;
}
#endif
// DOM loading
QString errMsg;
if ( !d->document.setContent( data, true, &errMsg ) ) {
d->lastError = i18n( "Unable to parse data file: %1", errMsg );
return false;
}
d->valid = true;
d->lastError.clear();
return true;
}
bool XmlDocument::writeToFile(const QString& fileName) const
{
QFile f( fileName );
if ( !f.open( QFile::WriteOnly ) ) {
d->lastError = f.errorString();
return false;
}
f.write( d->document.toByteArray( 2 ) );
d->lastError.clear();
return true;
}
bool XmlDocument::isValid() const
{
return d->valid;
}
QString XmlDocument::lastError() const
{
return d->lastError;
}
QDomDocument& XmlDocument::document() const
{
return d->document;
}
QDomElement XmlDocument::collectionElementByRemoteId(const QString& rid) const
{
return d->findElementByRid( rid, Format::Tag::collection() );
}
QDomElement XmlDocument::collectionElement( const Collection &collection ) const
{
if ( collection == Collection::root() )
return d->document.documentElement();
if ( collection.remoteId().isEmpty() )
return QDomElement();
if ( collection.parentCollection().remoteId().isEmpty() && collection.parentCollection() != Collection::root() )
return d->findElementByRid( collection.remoteId(), Format::Tag::collection() );
QDomElement parent = collectionElement( collection.parentCollection() );
if ( parent.isNull() )
return QDomElement();
const QDomNodeList children = parent.childNodes();
for ( int i = 0; i < children.count(); ++i ) {
const QDomElement child = children.at( i ).toElement();
if ( child.isNull() )
continue;
if ( child.tagName() == Format::Tag::collection() && child.attribute( Format::Attr::remoteId() ) == collection.remoteId() )
return child;
}
return QDomElement();
}
QDomElement XmlDocument::itemElementByRemoteId(const QString& rid) const
{
return d->findElementByRid( rid, Format::Tag::item() );
}
Collection XmlDocument::collectionByRemoteId(const QString& rid) const
{
const QDomElement elem = collectionElementByRemoteId( rid );
return XmlReader::elementToCollection( elem );
}
Item XmlDocument::itemByRemoteId(const QString& rid, bool includePayload) const
{
return XmlReader::elementToItem( itemElementByRemoteId( rid ), includePayload );
}
Collection::List XmlDocument::collections() const
{
return XmlReader::readCollections( d->document.documentElement() );
}
Tag::List XmlDocument::tags() const
{
return XmlReader::readTags( d->document.documentElement() );
}
Collection::List XmlDocument::childCollections(const QString& parentCollectionRid) const
{
Collection c;
c.setRemoteId( parentCollectionRid );
return childCollections( c );
}
Collection::List XmlDocument::childCollections( const Collection &parentCollection ) const
{
QDomElement parentElem = collectionElement( parentCollection );
if ( parentElem.isNull() ) {
d->lastError = QLatin1String( "Parent node not found." );
return Collection::List();
}
Collection::List rv;
const QDomNodeList children = parentElem.childNodes();
for ( int i = 0; i < children.count(); ++i ) {
const QDomElement childElem = children.at( i ).toElement();
if ( childElem.isNull() || childElem.tagName() != Format::Tag::collection() )
continue;
Collection c = XmlReader::elementToCollection( childElem );
c.setParentCollection( parentCollection );
rv.append( c );
}
return rv;
}
Item::List XmlDocument::items(const Akonadi::Collection& collection, bool includePayload) const
{
const QDomElement colElem = collectionElement( collection );
if ( colElem.isNull() ) {
d->lastError = i18n( "Unable to find collection %1", collection.name() );
return Item::List();
} else {
d->lastError.clear();
}
Item::List items;
const QDomNodeList children = colElem.childNodes();
for ( int i = 0; i < children.count(); ++i ) {
const QDomElement itemElem = children.at( i ).toElement();
if ( itemElem.isNull() || itemElem.tagName() != Format::Tag::item() )
continue;
items += XmlReader::elementToItem( itemElem, includePayload );
}
return items;
}