mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-24 10:52:52 +00:00
330 lines
8.9 KiB
C++
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;
|
|
}
|