[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [kdepim/akregator_port] /: support parsing of the outline hierarchy
From:       Frank Osterfeld <frank.osterfeld () kdemail ! net>
Date:       2011-07-27 12:12:12
Message-ID: 20110727121212.E68C9A60D1 () git ! kde ! org
[Download RAW message or body]

Git commit 04b548d7763190b28052841ea9f71e0e3c9ed74e by Frank Osterfeld.
Committed on 08/11/2009 at 12:13.
Pushed by cgiboudeaux into branch 'akregator_port'.

support parsing of the outline hierarchy from OPML, to not lose the folder structure

svn path=/branches/work/akonadi-ports/kdepim/; revision=1046299

M  +69   -19   krssresource/opmlparser.h
M  +185  -137  krssresource/opmlparser.cpp
M  +8    -5    resources/opmlresource/opmljobs.cpp
M  +16   -10   krssresource/importopmljob.cpp

http://commits.kde.org/kdepim/04b548d7763190b28052841ea9f71e0e3c9ed74e

diff --git a/krssresource/importopmljob.cpp b/krssresource/importopmljob.cpp
index 74c8f79..80ada00 100644
--- a/krssresource/importopmljob.cpp
+++ b/krssresource/importopmljob.cpp
@@ -38,7 +38,7 @@
 #include <cassert>
 
 using namespace KRssResource;
-using boost::shared_ptr;
+using namespace boost;
 
 ImportOpmlJob::ImportOpmlJob( const KUrl& path, QObject *parent )
     : KJob( parent ), m_backendJob( 0 ), m_path( path ), m_pendingJobs( 0 )
@@ -208,22 +208,28 @@ void ImportOpmlJob::slotTagsCreated( KJob *job )
     const TagsCreateJob * const tjob = qobject_cast<const TagsCreateJob*>( job );
     assert( job );
     const QList<KRss::Tag> tags = tjob->tags();
+    QHash<QString,KRss::Tag> tagByLabel;
 
-    int currentFeed = 0;
-    const QList<ParsedFeed> parsedFeeds = m_opmlReader.feeds();
+    Q_FOREACH ( const KRss::Tag& i, tags )
+            tagByLabel.insert( i.label(), i );
+
+    const QList<shared_ptr<const ParsedFeed> > parsedFeeds = m_opmlReader.feeds();
     QList<Akonadi::Collection> feedsToImport;
-    Q_FOREACH( const ParsedFeed& parsedFeed, parsedFeeds ) {
-        KRss::FeedCollection feed = parsedFeed.toAkonadiCollection();
+    Q_FOREACH( const shared_ptr<const ParsedFeed>& parsedFeed, parsedFeeds ) {
+        KRss::FeedCollection feed = parsedFeed->toAkonadiCollection();
         feed.setParent( m_rootCollection );
-        const QList<int> tagIndexes = m_opmlReader.tagsForFeeds().value( currentFeed \
);  kDebug() << "Current feed:" << feed.title();
-        Q_FOREACH( int tagIndex, tagIndexes ) {
-            kDebug() << "Adding tag:" << tags.at( tagIndex ).label() << ", id:" << \
                tags.at( tagIndex ).id();
-            feed.addTag( tags.at( tagIndex ).id() );
+        QStringList folderTitles = parsedFeed->parentFolderTitles();
+        folderTitles.erase( std::unique( folderTitles.begin(), folderTitles.end() ), \
folderTitles.end() ); +        Q_FOREACH( const QString& i, folderTitles ) {
+            const KRss::Tag tag = tagByLabel.value( i );
+            if ( tag.isNull() )
+                continue;
+            kDebug() << "Adding tag:" << tag.label() << ", id:" << tag.id();
+            feed.addTag( tag.id() );
         }
         if ( !m_defaultTag.isEmpty() )
             feed.addTag( tags.last().id() ); // the last one is the default tag
-        ++currentFeed;
         feedsToImport.append( feed );
     }
 
diff --git a/krssresource/opmlparser.cpp b/krssresource/opmlparser.cpp
index be67290..df1c897 100644
--- a/krssresource/opmlparser.cpp
+++ b/krssresource/opmlparser.cpp
@@ -26,93 +26,104 @@
 #include <QtXml/QXmlAttributes>
 #include <memory>
 
+using namespace boost;
 using namespace KRssResource;
 
-class ParsedFeed::Private : public QSharedData
-{
+class ParsedNode::Private {
 public:
-    Private() {}
+    QString title;
+    QHash<QString, QString> attributes;
+    weak_ptr<ParsedFolder> parent;
+};
 
-    Private( const Private& other );
+ParsedNode::ParsedNode() : d( new Private ) {
+}
 
-    bool operator!=( const Private& other ) const
-    {
-        return !( *this == other );
-    }
+weak_ptr<ParsedFolder> ParsedNode::parent() const {
+    return d->parent;
+}
 
-    bool operator==( const Private& other ) const
-    {
-        return title == other.title
-            && xmlUrl == other.xmlUrl
-            && htmlUrl == other.htmlUrl
-            && description == other.description
-            && type == other.type
-            && attributes == other.attributes;
-    }
+void ParsedNode::setParent( const weak_ptr<ParsedFolder>& parent ) {
+    d->parent = parent;
+}
 
-    QString title;
-    QString xmlUrl;
-    QString htmlUrl;
-    QString description;
-    QString type;
-    QHash<QString, QString> attributes;
-};
+ParsedNode::~ParsedNode() {
+    delete d;
+}
 
-ParsedFeed::Private::Private( const Private& other )
-    : QSharedData( other ),
-    title( other.title ),
-    xmlUrl( other.xmlUrl ),
-    htmlUrl( other.htmlUrl ),
-    description( other.description ),
-    type( other.type ),
-    attributes( other.attributes )
-{
+QString ParsedNode::title() const {
+    return d->title;
 }
 
-ParsedFeed::ParsedFeed() : d( new Private )
-{
+void ParsedNode::setTitle( const QString& title ) {
+    d->title = title;
 }
 
-ParsedFeed::~ParsedFeed()
+QString ParsedNode::attribute( const QString& key ) const {
+    return d->attributes.value( key );
+}
+
+QHash<QString, QString> ParsedNode::attributes() const
 {
+    return d->attributes;
 }
 
-void ParsedFeed::swap( ParsedFeed& other )
+void ParsedNode::setAttribute( const QString& key, const QString& value )
 {
-    std::swap( d, other.d );
+    d->attributes.insert( key, value );
 }
 
-ParsedFeed& ParsedFeed::operator=( const ParsedFeed& other )
+void ParsedNode::setAttributes( const QHash<QString, QString>& attributes )
 {
-    ParsedFeed copy( other );
-    swap( copy );
-    return *this;
+    d->attributes = attributes;
 }
 
-bool ParsedFeed::operator==( const ParsedFeed& other ) const
+QStringList ParsedNode::parentFolderTitles() const {
+    QStringList labels;
+    shared_ptr<ParsedNode> it = parent().lock();
+    while ( it ) {
+        labels.push_back( it->title() );
+        it = it->parent().lock();
+    }
+    return labels;
+}
+
+class ParsedFeed::Private
+{
+public:
+    Private() {}
+
+    QString xmlUrl;
+    QString htmlUrl;
+    QString description;
+    QString type;
+};
+
+ParsedFeed::ParsedFeed() : d( new Private )
 {
-    return *d == *(other.d);
 }
 
-bool ParsedFeed::operator!=( const ParsedFeed& other ) const
+ParsedFeed::~ParsedFeed()
 {
-    return *d != *(other.d);
+    delete d;
 }
 
-ParsedFeed::ParsedFeed( const ParsedFeed& other ) : d( other.d )
+bool ParsedFeed::isFolder() const
 {
+    return false;
 }
 
-QString ParsedFeed::title() const
+QList<shared_ptr<const ParsedFeed> > ParsedFeed::feeds() const
 {
-    return d->title;
+  return QList<shared_ptr<const ParsedFeed> >() << static_pointer_cast<const \
ParsedFeed>( shared_from_this() );  }
 
-void ParsedFeed::setTitle( const QString& title )
+QList<shared_ptr<const ParsedFolder> > ParsedFeed::folders() const
 {
-    d->title = title;
+  return QList<shared_ptr<const ParsedFolder> >();
 }
 
+
 QString ParsedFeed::xmlUrl() const
 {
     return d->xmlUrl;
@@ -153,48 +164,78 @@ void ParsedFeed::setType( const QString& type )
     d->type = type;
 }
 
-QHash<QString, QString> ParsedFeed::attributes() const
-{
-    return d->attributes;
-}
-
-void ParsedFeed::setAttribute( const QString& key, const QString& value )
-{
-    d->attributes.insert( key, value );
-}
-
-void ParsedFeed::setAttributes( const QHash<QString, QString>& attributes )
-{
-    d->attributes = attributes;
-}
 
 Akonadi::Collection ParsedFeed::toAkonadiCollection() const
 {
     KRss::FeedCollection feed;
-    feed.setRemoteId( d->attributes.value( QLatin1String("remoteid") ) );
-    feed.setTitle( d->title );
+    feed.setRemoteId( attribute( QLatin1String("remoteid") ) );
+    feed.setTitle( title() );
     feed.setXmlUrl( d->xmlUrl );
     feed.setHtmlUrl( d->htmlUrl );
     feed.setDescription( d->description );
     feed.setFeedType( d->type );
-    feed.setName( d->title );
+    feed.setName( title() );
     feed.setContentMimeTypes( QStringList( QLatin1String("application/rss+xml") ) );
     return feed;
 }
 
-ParsedFeed ParsedFeed::fromAkonadiCollection( const Akonadi::Collection& collection \
) +shared_ptr<ParsedFeed> ParsedFeed::fromAkonadiCollection( const \
Akonadi::Collection& collection )  {
     const KRss::FeedCollection feedCollection = collection;
-    ParsedFeed parsedFeed;
-    parsedFeed.d->title = feedCollection.title();
-    parsedFeed.d->xmlUrl = feedCollection.xmlUrl();
-    parsedFeed.d->htmlUrl = feedCollection.htmlUrl();
-    parsedFeed.d->description = feedCollection.description();
-    parsedFeed.d->type = feedCollection.feedType();
-    parsedFeed.d->attributes.insert( QLatin1String("remoteid"), \
feedCollection.remoteId() ); +    shared_ptr<ParsedFeed> parsedFeed( new ParsedFeed \
); +    parsedFeed->setTitle( feedCollection.title() );
+    parsedFeed->d->xmlUrl = feedCollection.xmlUrl();
+    parsedFeed->d->htmlUrl = feedCollection.htmlUrl();
+    parsedFeed->d->description = feedCollection.description();
+    parsedFeed->d->type = feedCollection.feedType();
+    parsedFeed->setAttribute( QLatin1String("remoteid"), feedCollection.remoteId() \
);  return parsedFeed;
 }
 
+class ParsedFolder::Private {
+public:
+    QList<shared_ptr<ParsedNode> > children;
+};
+
+ParsedFolder::ParsedFolder() : d( new Private ) {
+
+}
+
+ParsedFolder::~ParsedFolder() {
+    delete d;
+}
+
+bool ParsedFolder::isFolder() const {
+    return true;
+}
+
+QList<shared_ptr<const ParsedFeed> > ParsedFolder::feeds() const {
+  QList<shared_ptr<const ParsedFeed> > f;
+  Q_FOREACH( const shared_ptr<const ParsedNode>& i, d->children )
+      f << i->feeds();
+  return f;
+}
+
+QList<shared_ptr<const ParsedFolder> > ParsedFolder::folders() const {
+  QList<shared_ptr<const ParsedFolder> > f;
+  f << static_pointer_cast<const ParsedFolder>( shared_from_this() );
+  Q_FOREACH( const shared_ptr<const ParsedNode>& i, d->children )
+      f << i->folders();
+  return f;
+}
+
+QList<shared_ptr<ParsedNode> > ParsedFolder::children() const {
+    return d->children;
+}
+
+void ParsedFolder::setChildren( const QList<shared_ptr<ParsedNode> >& children ) {
+    d->children = children;
+}
+
+void ParsedFolder::addChild( const shared_ptr<ParsedNode>& child ) {
+    d->children.push_back( child );
+}
+
 class OpmlReader::Private
 {
 public:
@@ -202,12 +243,10 @@ public:
 
     QStringRef attributeValue( const QXmlStreamAttributes& attributes, const \
QString& name );  void readBody( QXmlStreamReader& reader );
-    void readOutline( QXmlStreamReader& reader, QStringList& currentTags );
+    void readOutline( QXmlStreamReader& reader, const shared_ptr<ParsedFolder> \
&parent );  void readUnknownElement( QXmlStreamReader& reader );
 
-    QList<ParsedFeed> m_feeds;
-    QList<QString > m_tags;
-    QHash<int, QList<int> > m_tagsForFeeds;
+    QList<shared_ptr<ParsedNode> > m_topNodes;
 };
 
 QStringRef OpmlReader::Private::attributeValue( const QXmlStreamAttributes& \
attributes, const QString& name ) @@ -233,8 +272,7 @@ void \
OpmlReader::Private::readBody( QXmlStreamReader& reader )  
         if ( reader.isStartElement() ) {
             if ( reader.name().toString().toLower() == QLatin1String("outline") ) {
-                QStringList currentTags;
-                readOutline( reader, currentTags );
+                readOutline( reader, shared_ptr<ParsedFolder>() );
             }
             else {
                 readUnknownElement( reader );
@@ -243,22 +281,30 @@ void OpmlReader::Private::readBody( QXmlStreamReader& reader )
     }
 }
 
-void OpmlReader::Private::readOutline( QXmlStreamReader& reader, QStringList& \
currentTags ) +void OpmlReader::Private::readOutline( QXmlStreamReader& reader, const \
shared_ptr<ParsedFolder>& parent )  {
     Q_ASSERT( reader.isStartElement() && reader.name().toString().toLower() == \
QLatin1String("outline") );  
-    bool isFolder = false;
+    shared_ptr<ParsedNode> newNode;
     const QString xmlUrl = attributeValue( reader.attributes(), \
QLatin1String("xmlurl") ).toString();  
     if ( xmlUrl.isEmpty() ) {
         const QStringRef textAttribute = attributeValue( reader.attributes(), \
QLatin1String("text") );  if ( !textAttribute.isEmpty() ) {
             // this attribute seem to represent a folder
-            isFolder = true;
-            if ( !currentTags.contains( textAttribute.toString() ) )
-                currentTags.append( textAttribute.toString() );
+            shared_ptr<ParsedFolder> newFolder( new ParsedFolder );
+            newNode = newFolder;
+            const QXmlStreamAttributes attrs = reader.attributes();
 
-            kDebug() << "Current tags:" << currentTags;
+            Q_FOREACH( const QXmlStreamAttribute& attr, attrs ) {
+                if ( attr.name().toString().toLower() == QLatin1String("title") )
+                    newFolder->setTitle( attr.value().toString() );
+                else if ( newFolder->title().isEmpty() && \
attr.name().toString().toLower() == QLatin1String("text")  ) +                    \
newFolder->setTitle( attr.value().toString() ); +                else {
+                    newFolder->setAttribute( attr.name().toString(), \
attr.value().toString() ); +                }
+            }
         }
         else {
             kDebug() << "Encountered an empty outline";
@@ -271,54 +317,49 @@ void OpmlReader::Private::readOutline( QXmlStreamReader& \
reader, QStringList& cu  else {
         // this is a feed
         kDebug() << "Feed:" << xmlUrl;
-        isFolder = false;
-        ParsedFeed feed;
+        shared_ptr<ParsedFeed> newFeed( new ParsedFeed );
+        newNode = newFeed;
         const QXmlStreamAttributes attrs = reader.attributes();
         Q_FOREACH( const QXmlStreamAttribute& attr, attrs ) {
             if ( attr.name().toString().toLower() == QLatin1String("title") )
-                feed.setTitle( attr.value().toString() );
-            else if ( attr.name().toString().toLower() == QLatin1String("text") && \
                feed.title().isEmpty() )
-                feed.setTitle( attr.value().toString() );
+                newFeed->setTitle( attr.value().toString() );
+            else if ( newFeed->title().isEmpty() && attr.name().toString().toLower() \
== QLatin1String("text")  ) +                newFeed->setTitle( \
                attr.value().toString() );
             else if ( attr.name().toString().toLower() == QLatin1String("htmlurl") )
-                feed.setHtmlUrl( attr.value().toString() );
+                newFeed->setHtmlUrl( attr.value().toString() );
             else if ( attr.name().toString().toLower() == QLatin1String("xmlurl") )
-                feed.setXmlUrl( attr.value().toString() );
+                newFeed->setXmlUrl( attr.value().toString() );
             else if ( attr.name().toString().toLower() == \
                QLatin1String("description") )
-                feed.setDescription( attr.value().toString() );
+                newFeed->setDescription( attr.value().toString() );
             else if ( attr.name().toString().toLower() == QLatin1String("type") )
-                feed.setType( attr.value().toString() );
+                newFeed->setType( attr.value().toString() );
             else if ( attr.name().toString().toLower() == QLatin1String("category") \
                ) {
                 const QStringList categories = attr.value().toString().split( \
QRegExp( QLatin1String("[,/]") ), QString::SkipEmptyParts ); +#if 0
                 Q_FOREACH( const QString& category, categories ) {
                     if ( !currentTags.contains( category ) )
                         currentTags.append( category );
                 }
+#endif
             }
             else {
-                feed.setAttribute( attr.name().toString(), attr.value().toString() \
); +                newFeed->setAttribute( attr.name().toString(), \
attr.value().toString() );  }
         }
 
-        if ( feed.title().isEmpty() )
-            feed.setTitle( xmlUrl );
+        if ( newFeed->title().isEmpty() )
+            newFeed->setTitle( xmlUrl );
 
-        if ( feed.type().isEmpty() )
-            feed.setType( QLatin1String("rss") );
-
-        // everything is parsed
-        QList<int> currentTagIds;
-        Q_FOREACH( const QString& tag, currentTags ) {
-            int index = m_tags.indexOf( tag );
-            if ( index == -1 ) {
-                m_tags.append( tag );
-                index = m_tags.count() - 1;
-            }
-            currentTagIds.append( index );
-        }
+        if ( newFeed->type().isEmpty() )
+            newFeed->setType( QLatin1String("rss") );
+    }
 
-        m_feeds.append( feed );
-        m_tagsForFeeds.insert( m_feeds.count() - 1, currentTagIds );
+    if ( parent ) {
+        parent->addChild( newNode );
+        newNode->setParent( parent );
     }
+    else
+        m_topNodes.append( newNode );
 
     while ( !reader.atEnd() ) {
         reader.readNext();
@@ -327,17 +368,12 @@ void OpmlReader::Private::readOutline( QXmlStreamReader& \
reader, QStringList& cu  break;
 
         if ( reader.isStartElement() ) {
-            if ( reader.name().toString().toLower() == QLatin1String("outline") && \
                isFolder )
-                readOutline( reader, currentTags );
+            if ( reader.name().toString().toLower() == QLatin1String("outline") && \
newNode->isFolder() ) +                readOutline( reader, \
static_pointer_cast<ParsedFolder>( newNode ) );  else
                 readUnknownElement( reader );
         }
     }
-
-    // once we are back from recursion remove the added tag
-    // from the top of the list
-    if ( isFolder )
-        currentTags.removeLast();
 }
 
 void OpmlReader::Private::readUnknownElement( QXmlStreamReader& reader )
@@ -365,19 +401,31 @@ OpmlReader::~OpmlReader()
     delete d;
 }
 
-QList<ParsedFeed> OpmlReader::feeds() const
+QList<shared_ptr<const ParsedFeed> > OpmlReader::feeds() const
 {
-    return d->m_feeds;
+    QList<shared_ptr<const ParsedFeed> > f;
+    Q_FOREACH ( const shared_ptr<const ParsedNode>& i, d->m_topNodes )
+        f << i->feeds();
+    return f;
 }
 
-QList<QString> OpmlReader::tags() const
+QList<shared_ptr<const ParsedNode> > OpmlReader::topLevelNodes() const
 {
-    return d->m_tags;
+    QList<shared_ptr<const ParsedNode> > l;
+    Q_FOREACH( const shared_ptr<ParsedNode>& i, d->m_topNodes )
+        l << i;
+    return l;
 }
 
-QHash<int, QList<int> > OpmlReader::tagsForFeeds() const
+QStringList OpmlReader::tags() const
 {
-    return d->m_tagsForFeeds;
+    QStringList titles;
+    QList<shared_ptr<const ParsedFolder> > f;
+    Q_FOREACH ( const shared_ptr<const ParsedNode>& i, d->m_topNodes )
+        f << i->folders();
+    Q_FOREACH ( const shared_ptr<const ParsedFolder>& i, f )
+            titles << i->title();
+    return titles;
 }
 
 void OpmlReader::readOpml( QXmlStreamReader& reader )
@@ -404,28 +452,28 @@ void OpmlReader::readOpml( QXmlStreamReader& reader )
     }
 }
 
-void OpmlWriter::writeOpml( QXmlStreamWriter& writer, const QList<ParsedFeed>& feeds \
) +void OpmlWriter::writeOpml( QXmlStreamWriter& writer, const \
QList<shared_ptr<ParsedFeed> >& feeds )  {
     writer.writeStartElement( QLatin1String("opml") );
     writer.writeAttribute( QLatin1String("version"), QLatin1String("2.0") );
     writer.writeEmptyElement( QLatin1String("head") );
     writer.writeStartElement( QLatin1String("body") );
-    Q_FOREACH( const ParsedFeed& feed, feeds ) {
+    Q_FOREACH( const shared_ptr<ParsedFeed>& feed, feeds ) {
         writeOutline( writer, feed );
     }
     writer.writeEndElement(); // body
     writer.writeEndElement(); // opml
 }
 
-void OpmlWriter::writeOutline( QXmlStreamWriter& writer, const ParsedFeed& feed )
+void OpmlWriter::writeOutline( QXmlStreamWriter& writer, const \
shared_ptr<ParsedFeed>& feed )  {
     writer.writeStartElement( QLatin1String("outline") );
-    writer.writeAttribute( QLatin1String("text"), feed.title() );
-    writer.writeAttribute( QLatin1String("title"), feed.title() );
-    writer.writeAttribute( QLatin1String("description"), feed.description() );
-    writer.writeAttribute( QLatin1String("htmlUrl"), feed.htmlUrl() );
-    writer.writeAttribute( QLatin1String("xmlUrl"), feed.xmlUrl() );
-    QHashIterator<QString, QString> it( feed.attributes() );
+    writer.writeAttribute( QLatin1String("text"), feed->title() );
+    writer.writeAttribute( QLatin1String("title"), feed->title() );
+    writer.writeAttribute( QLatin1String("description"), feed->description() );
+    writer.writeAttribute( QLatin1String("htmlUrl"), feed->htmlUrl() );
+    writer.writeAttribute( QLatin1String("xmlUrl"), feed->xmlUrl() );
+    QHashIterator<QString, QString> it( feed->attributes() );
     while ( it.hasNext() ) {
         it.next();
         writer.writeAttribute( it.key(), it.value() );
diff --git a/krssresource/opmlparser.h b/krssresource/opmlparser.h
index 696eb57..c393fb8 100644
--- a/krssresource/opmlparser.h
+++ b/krssresource/opmlparser.h
@@ -24,28 +24,60 @@
 #include <QtCore/QString>
 #include <QtCore/QList>
 #include <QtCore/QHash>
-#include <QtCore/QSharedDataPointer>
+#include <QtCore/QStringList>
 #include <QtCore/QXmlStreamReader>
 
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include <boost/weak_ptr.hpp>
+
 class QXmlStreamWriter;
 class QXmlStreamAttributes;
 
 namespace KRssResource {
 
-class KRSSRESOURCE_EXPORT ParsedFeed
+class ParsedFeed;
+class ParsedFolder;
+
+class KRSSRESOURCE_EXPORT ParsedNode : public \
boost::enable_shared_from_this<ParsedNode>  {
 public:
-    ParsedFeed();
-    ParsedFeed( const ParsedFeed& other );
-    ~ParsedFeed();
+    ParsedNode();
+    virtual ~ParsedNode();
 
-    void swap( ParsedFeed& other );
-    ParsedFeed& operator=( const ParsedFeed& other );
-    bool operator==( const ParsedFeed& other ) const;
-    bool operator!=( const ParsedFeed& other ) const;
+    boost::weak_ptr<ParsedFolder> parent() const;
+    void setParent( const boost::weak_ptr<ParsedFolder>& parent );
 
+    virtual bool isFolder() const = 0;
+    virtual QList<boost::shared_ptr<const ParsedFeed> > feeds() const = 0;
+    virtual QList<boost::shared_ptr<const ParsedFolder> > folders() const = 0;
     QString title() const;
     void setTitle( const QString& title );
+
+    QString attribute( const QString& key ) const;
+    QHash<QString, QString> attributes() const;
+    void setAttribute( const QString& key, const QString& value );
+    void setAttributes( const QHash<QString, QString>& attributes );
+
+    QStringList parentFolderTitles() const;
+
+private:
+    Q_DISABLE_COPY(ParsedNode);
+    class Private;
+    Private* const d;
+};
+
+class KRSSRESOURCE_EXPORT ParsedFeed : public ParsedNode
+{
+public:
+    ParsedFeed();
+    ~ParsedFeed();
+
+    /* reimp */ bool isFolder() const;
+    /* reimp */ QList<boost::shared_ptr<const ParsedFeed> > feeds() const;
+    /* reimp */ QList<boost::shared_ptr<const ParsedFolder> > folders() const;
+
     QString xmlUrl() const;
     void setXmlUrl( const QString& xmlUrl );
     QString htmlUrl() const;
@@ -54,16 +86,33 @@ public:
     void setDescription( const QString& description );
     QString type() const;
     void setType( const QString& type );
-    QHash<QString, QString> attributes() const;
-    void setAttribute( const QString& key, const QString& value );
-    void setAttributes( const QHash<QString, QString>& attributes );
 
     Akonadi::Collection toAkonadiCollection() const;
-    static ParsedFeed fromAkonadiCollection( const Akonadi::Collection& collection \
); +    static boost::shared_ptr<ParsedFeed> fromAkonadiCollection( const \
Akonadi::Collection& collection );  
 private:
+    Q_DISABLE_COPY(ParsedFeed);
     class Private;
-    QSharedDataPointer<Private> d;
+    Private* const d;
+};
+
+class KRSSRESOURCE_EXPORT ParsedFolder : public ParsedNode
+{
+public:
+    ParsedFolder();
+    ~ParsedFolder();
+
+    /* reimp */ bool isFolder() const;
+    /* reimp */ QList<boost::shared_ptr<const ParsedFeed> > feeds() const;
+    /* reimp */ QList<boost::shared_ptr<const ParsedFolder> > folders() const;
+
+    QList<boost::shared_ptr<ParsedNode> > children() const;
+    void setChildren( const QList<boost::shared_ptr<ParsedNode> >& children );
+    void addChild( const boost::shared_ptr<ParsedNode>& child );
+
+private:
+    class Private;
+    Private* const d;
 };
 
 class KRSSRESOURCE_EXPORT OpmlReader
@@ -74,9 +123,10 @@ public:
 
     void readOpml( QXmlStreamReader& reader );
 
-    QList<ParsedFeed> feeds() const;
-    QList<QString> tags() const;
-    QHash<int, QList<int> > tagsForFeeds() const;
+    QList<boost::shared_ptr<const ParsedFeed> > feeds() const;
+    QList<boost::shared_ptr<const ParsedNode> > topLevelNodes() const;
+
+    QStringList tags() const;
 
 private:
     class Private;
@@ -87,8 +137,8 @@ private:
 class KRSSRESOURCE_EXPORT OpmlWriter
 {
 public:
-    static void writeOpml( QXmlStreamWriter& writer, const QList<ParsedFeed>& feeds \
                );
-    static void writeOutline( QXmlStreamWriter& writer, const ParsedFeed& feed );
+    static void writeOpml( QXmlStreamWriter& writer, const \
QList<boost::shared_ptr<ParsedFeed> >& feeds ); +    static void writeOutline( \
QXmlStreamWriter& writer, const boost::shared_ptr<ParsedFeed>& feed );  };
 
 } //namespace KRssResource
diff --git a/resources/opmlresource/opmljobs.cpp \
b/resources/opmlresource/opmljobs.cpp index 520f18c..c953638 100644
--- a/resources/opmlresource/opmljobs.cpp
+++ b/resources/opmlresource/opmljobs.cpp
@@ -28,14 +28,17 @@
 #include <QtCore/QXmlStreamReader>
 #include <QtCore/QUuid>
 
+#include <boost/shared_ptr.hpp>
+
+using namespace boost;
 using namespace KRss;
 using namespace KRssResource;
 
 namespace {
 
-static QList<ParsedFeed> toParsedFeedList( const QList<Akonadi::Collection>& feeds )
+static QList<shared_ptr<ParsedFeed> > toParsedFeedList( const \
QList<Akonadi::Collection>& feeds )  {
-    QList<ParsedFeed> parsedFeeds;
+    QList<shared_ptr<ParsedFeed> > parsedFeeds;
     Q_FOREACH( const Akonadi::Collection& feed, feeds ) {
         if ( feed.parent() != Akonadi::Collection::root().id() )
             parsedFeeds.append( ParsedFeed::fromAkonadiCollection( feed ) );
@@ -150,9 +153,9 @@ void OpmlFeedsRetrieveJob::slotGetFinished( KJob *job )
     m_feedsCache.insert( top.remoteId(), top );
 
     bool needsPut = false;
-    const QList<ParsedFeed> parsedFeeds = opmlReader.feeds();
-    Q_FOREACH( const ParsedFeed& parsedFeed, parsedFeeds ) {
-        FeedCollection feed = parsedFeed.toAkonadiCollection();
+    const QList<shared_ptr<const ParsedFeed> > parsedFeeds = opmlReader.feeds();
+    Q_FOREACH( const shared_ptr<const ParsedFeed>& parsedFeed, parsedFeeds ) {
+        FeedCollection feed = parsedFeed->toAkonadiCollection();
         if ( feed.remoteId().isEmpty() ) {
             feed.setRemoteId( QUuid::createUuid().toString() );
             needsPut = true;


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic