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

List:       kde-commits
Subject:    KDE/kdelibs/kdecore
From:       David Faure <faure () kde ! org>
Date:       2010-12-09 16:39:47
Message-ID: 20101209163947.979A0AC8A5 () svn ! kde ! org
[Download RAW message or body]

SVN commit 1204914 by dfaure:

Fix deletion of groups so that a group doesn't exist() after being deleted :-)
A group now exists if it has at least one non-deleted entry (including in subgroups).

+ added unittest for deleting a nested group, and check what gets written to disk.
BUG: 192266
FIXED-IN: 4.6


 M  +54 -32    config/kconfig.cpp  
 M  +3 -0      config/kconfig_p.h  
 M  +10 -6     config/kconfigdata.h  
 M  +8 -3      config/kconfigini.cpp  
 M  +32 -2     tests/kconfigtest.cpp  


--- trunk/KDE/kdelibs/kdecore/config/kconfig.cpp #1204913:1204914
@@ -207,9 +207,10 @@
     QSet<QString> groups;
 
     for (KEntryMap::ConstIterator entryMapIt( d->entryMap.constBegin() ); entryMapIt \
                != d->entryMap.constEnd(); ++entryMapIt) {
-        const QByteArray group = entryMapIt.key().mGroup;
-        if (entryMapIt.key().mKey.isNull() && !group.isEmpty() && group != \
                "<default>" && group != "$Version") {
-            QString groupname = QString::fromUtf8(group);
+        const KEntryKey& key = entryMapIt.key();	
+        const QByteArray group = key.mGroup;
+        if (key.mKey.isNull() && !group.isEmpty() && group != "<default>" && group \
!= "$Version") { +            const QString groupname = QString::fromUtf8(group);
             groups << groupname.left(groupname.indexOf(QLatin1Char('\x1d')));
         }
     }
@@ -222,23 +223,53 @@
     QByteArray theGroup = group + '\x1d';
     QSet<QString> groups;
 
-    for (KEntryMap::ConstIterator entryMapIt( entryMap.constBegin() ); entryMapIt != \
                entryMap.constEnd(); ++entryMapIt)
-        if (entryMapIt.key().mKey.isNull() && \
                entryMapIt.key().mGroup.startsWith(theGroup)) {
-            const QString groupname = \
QString::fromUtf8(entryMapIt.key().mGroup.mid(theGroup.length())); +    for \
(KEntryMap::ConstIterator entryMapIt( entryMap.constBegin() ); entryMapIt != \
entryMap.constEnd(); ++entryMapIt) { +        const KEntryKey& key = \
entryMapIt.key();	 +        if (key.mKey.isNull() && key.mGroup.startsWith(theGroup)) \
{ +            const QString groupname = \
                QString::fromUtf8(key.mGroup.mid(theGroup.length()));
             groups << groupname.left(groupname.indexOf(QLatin1Char('\x1d')));
         }
+    }
 
     return groups.toList();
 }
 
-QStringList KConfig::keyList(const QString& aGroup) const
+// List all sub groups, including subsubgroups
+QSet<QByteArray> KConfigPrivate::allSubGroups(const QByteArray& parentGroup) const
 {
-    Q_D(const KConfig);
+    QSet<QByteArray> groups;
+    QByteArray theGroup = parentGroup + '\x1d';
+    groups << parentGroup;
+
+    for (KEntryMap::const_iterator entryMapIt = entryMap.begin(); entryMapIt != \
entryMap.end(); ++entryMapIt) { +        const KEntryKey& key = entryMapIt.key();
+        if (key.mKey.isNull() && key.mGroup.startsWith(theGroup)) {
+            groups << key.mGroup;
+        }
+    }
+    return groups;
+}
+
+bool KConfigPrivate::hasNonDeletedEntries(const QByteArray& group) const
+{
+    const QSet<QByteArray> allGroups = allSubGroups(group);
+
+    Q_FOREACH(const QByteArray& aGroup, allGroups) {
+        // Could be optimized, let's use the slow way for now
+        // Check for any non-deleted entry
+        if (!keyListImpl(aGroup).isEmpty())
+            return true;
+    }
+    return false;
+}
+
+
+QStringList KConfigPrivate::keyListImpl(const QByteArray& theGroup) const
+{
     QStringList keys;
-    const QByteArray theGroup(aGroup.isEmpty() ? "<default>" : aGroup.toUtf8());
 
-    const KEntryMapConstIterator theEnd = d->entryMap.constEnd();
-    KEntryMapConstIterator it = d->entryMap.findEntry(theGroup);
+    const KEntryMapConstIterator theEnd = entryMap.constEnd();
+    KEntryMapConstIterator it = entryMap.findEntry(theGroup);
     if (it != theEnd) {
         ++it; // advance past the special group entry marker
 
@@ -254,6 +285,13 @@
     return keys;
 }
 
+QStringList KConfig::keyList(const QString& aGroup) const
+{
+    Q_D(const KConfig);
+    const QByteArray theGroup(aGroup.isEmpty() ? "<default>" : aGroup.toUtf8());
+    return d->keyListImpl(theGroup);
+}
+
 QMap<QString,QString> KConfig::entryMap(const QString& aGroup) const
 {
     Q_D(const KConfig);
@@ -657,18 +695,9 @@
     Q_D(KConfig);
     KEntryMap::EntryOptions options = \
convertToOptions(flags)|KEntryMap::EntryDeleted;  
-    QByteArray theGroup = aGroup + '\x1d';
-    QSet<QByteArray> groups;
-    groups << aGroup;
-
-    for (KEntryMap::ConstIterator entryMapIt( d->entryMap.constBegin() ); entryMapIt \
                != d->entryMap.constEnd(); ++entryMapIt) {
-        if (entryMapIt.key().mKey.isNull() && \
                entryMapIt.key().mGroup.startsWith(theGroup)) {
-            groups << entryMapIt.key().mGroup;
-        }
-    }
-
+    const QSet<QByteArray> groups = d->allSubGroups(aGroup);
     foreach (const QByteArray& group, groups) {
-        const QStringList keys = keyList(QString::fromUtf8(group));
+        const QStringList keys = d->keyListImpl(group);
         foreach (const QString& _key, keys) {
             const QByteArray &key = _key.toUtf8();
             if (d->canWriteEntry(group, key.constData())) {
@@ -709,18 +738,11 @@
 {
     Q_D(const KConfig);
 
-    if (d->entryMap.hasEntry(aGroup)) return true;
+    // No need to look for the actual group entry anymore, or for subgroups:
+    // a group exists if it contains any non-deleted entry.
 
-    QByteArray subGroupMarker = aGroup + '\x1d';
-    // Because this group could have only subgroups but no entries we have to
-    // search for group markers
-    for (KEntryMap::ConstIterator entryMapIt( d->entryMap.constBegin() ); entryMapIt \
                != d->entryMap.constEnd(); ++entryMapIt) {
-        if (entryMapIt.key().mGroup.startsWith(subGroupMarker)) {
-            return true;
+    return d->hasNonDeletedEntries(aGroup);
         }
-    }
-    return false;
-}
 
 bool KConfigPrivate::canWriteEntry(const QByteArray& group, const char* key, bool \
isDefault) const  {
--- trunk/KDE/kdelibs/kdecore/config/kconfig_p.h #1204913:1204914
@@ -61,6 +61,9 @@
     // of @p source with @p destination
     void copyGroup(const QByteArray& source, const QByteArray& destination,
                    KConfigGroup *otherGroup, KConfigBase::WriteConfigFlags flags) \
const; +    QStringList keyListImpl(const QByteArray& theGroup) const;
+    QSet<QByteArray> allSubGroups(const QByteArray& parentGroup) const;
+    bool hasNonDeletedEntries(const QByteArray& group) const;
 
 protected:
     KSharedPtr<KConfigBackend> mBackend;
--- trunk/KDE/kdelibs/kdecore/config/kconfigdata.h #1204913:1204914
@@ -226,12 +226,13 @@
             if (key.isEmpty()) { // inserting a group marker
                 k.mGroup = group;
                 e.bImmutable = (options&EntryImmutable);
-                if(it == end())
-                {
+                if (options&EntryDeleted) { qWarning("Internal KConfig error: cannot \
mark groups as deleted"); } +                if(it == end()) {
                     insert(k, e);
                     return true;
-                } else if(it.value() == e)
+                } else if(it.value() == e) {
                     return false;
+                }
                 
                 it.value() = e;
                 return true;
@@ -274,7 +275,7 @@
                 e.bDeleted = false; // setting a value to a previously deleted entry
             e.bExpand = (options&EntryExpansion);
 
-             //qDebug() << "to [" << group << "," << key << "] =" << e.mValue;
+            //qDebug() << "to [" << group << "," << key << "] =" << e.mValue << \
"newKey=" << newKey;  if(newKey)
             {
                 insert(k, e);
@@ -365,10 +366,13 @@
             const ConstIterator it = findEntry(group, key, flags);
             if (it == constEnd())
                 return false;
-            if (key.isNull()) // looking for group marker
+            if (it->bDeleted)
+                return false;
+            if (key.isNull()) { // looking for group marker
                 return it->mValue.isNull();
-            return !it->bDeleted;
         }
+            return true;
+        }
 
         bool getEntryOption(const ConstIterator& it, EntryOption option) const
         {
--- trunk/KDE/kdelibs/kdecore/config/kconfigini.cpp #1204913:1204914
@@ -368,8 +368,9 @@
     Q_ASSERT(!filePath().isEmpty());
 
     KEntryMap writeMap;
-    bool bGlobal = options & WriteGlobal;
+    const bool bGlobal = options & WriteGlobal;
 
+    // First, reparse the file on disk, to merge our changes with the ones done by \
other apps  {
         ParseOptions opts = ParseExpansions;
         if (bGlobal)
@@ -378,6 +379,7 @@
         if (info != ParseOk) // either there was an error or the file became \
immutable  return false;
     }
+
     const KEntryMapIterator end = entryMap.end();
     for (KEntryMapIterator it=entryMap.begin(); it != end; ++it) {
         if (!it.key().mKey.isEmpty() && !it->bDirty) // not dirty, doesn't overwrite \
entry in writeMap. skips default entries, too. @@ -392,11 +394,14 @@
             } else {
                 KEntryKey defaultKey = key;
                 defaultKey.bDefault = true;
-                if (!entryMap.contains(defaultKey))
+                if (!entryMap.contains(defaultKey)) {
                     writeMap.remove(key); // remove the deleted entry if there is no \
                default
-                else
+                    //qDebug() << "Detected as deleted=>removed:" << key.mGroup << \
key.mKey << "global=" << bGlobal; +                } else {
                     writeMap[key] = *it; // otherwise write an explicitly deleted \
entry +                    //qDebug() << "Detected as deleted=>[$d]:" << key.mGroup \
<< key.mKey << "global=" << bGlobal;  }
+            }
             it->bDirty = false;
         }
     }
--- trunk/KDE/kdelibs/kdecore/tests/kconfigtest.cpp #1204913:1204914
@@ -133,6 +133,9 @@
   cg = KConfigGroup(&cg, "Nested Group 2.1");
   cg.writeEntry( "stringEntry3", STRINGENTRY3 );
 
+  cg = KConfigGroup(&ct, "Nested Group 3");
+  cg.writeEntry( "stringEntry3", STRINGENTRY3 );
+
   cg = KConfigGroup(&sc, "List Types" );
   cg.writeEntry( "listOfIntsEntry1", INTLISTENTRY1 );
   cg.writeEntry( "listOfByteArraysEntry1", BYTEARRAYLISTENTRY1 );
@@ -667,10 +670,25 @@
   KConfig sc( "kconfigtest" );
 
   KConfigGroup ct(&sc, "Complex Types");
+
+  // First delete a nested group
+  KConfigGroup delgr(&ct, "Nested Group 3");
+  QVERIFY(delgr.exists());
+  QVERIFY(ct.hasGroup("Nested Group 3"));
+  delgr.deleteGroup();
+  QVERIFY(!delgr.exists());
+  QVERIFY(!ct.hasGroup("Nested Group 3"));
+  QVERIFY(ct.groupList().contains("Nested Group 3"));
+
   KConfigGroup ng(&ct, "Nested Group 2");
+  QVERIFY(sc.hasGroup("Complex Types"));
+  QVERIFY(!sc.hasGroup("Does not exist"));
   sc.deleteGroup("Complex Types");
   QCOMPARE(sc.group("Complex Types").keyList().count(), 0);
-  QVERIFY(sc.group("Complex Types").exists()); // yep, we deleted it, but it still \
"exists"... +  QVERIFY(!sc.hasGroup("Complex Types")); // #192266
+  QVERIFY(!sc.group("Complex Types").exists());
+  QVERIFY(!ct.hasGroup("Nested Group 1"));
+
   QCOMPARE(ct.group("Nested Group 1").keyList().count(), 0);
   QCOMPARE(ct.group("Nested Group 2").keyList().count(), 0);
   QCOMPARE(ng.group("Nested Group 2.1").keyList().count(), 0);
@@ -682,6 +700,17 @@
   QVERIFY( !sc.entryMap("Hello").isEmpty() ); //not deleted group
   QVERIFY( sc.entryMap("FooBar").isEmpty() ); //inexistant group
 
+  cg.sync();
+  // Check what happens on disk
+  const QList<QByteArray> lines = readLines();
+  //qDebug() << lines;
+  QVERIFY(!lines.contains("[Complex Types]\n"));
+  QVERIFY(!lines.contains("[Complex Types][Nested Group 1]\n"));
+  QVERIFY(!lines.contains("[Complex Types][Nested Group 2]\n"));
+  QVERIFY(!lines.contains("[Complex Types][Nested Group 2.1]\n"));
+  QVERIFY(!lines.contains("[AAA]\n"));
+  QVERIFY(lines.contains("[Hello]\n")); // a group that was not deleted
+
   // test for entries that are marked as deleted when there is no default
   KConfig cf("kconfigtest", KConfig::SimpleConfig); // make sure there are no \
defaults  cg = cf.group("Portable Devices");
@@ -1394,10 +1423,11 @@
     // Current state: [ca] and [de] entries left... oops.
     //qDebug() << readLinesFrom(file);
 
-    // The group still exists but the values are all gone.
+    // Bug: The group still exists [because of the localized entries]...
     QVERIFY(cg.exists());
     QVERIFY(!cg.hasKey("foo"));
     QVERIFY(!cg.hasKey("foostring"));
+    QEXPECT_FAIL("", "Currently localized values are not deleted correctly", \
Continue);  QVERIFY(!cg.hasKey("foobool"));
 
     // Now switch the locale to "de" and repeat the checks. All values


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

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