From 6237bbb453eb64456d18c09311c79e48e4600c93 Mon Sep 17 00:00:00 2001
From: Erik Verbruggen <erik.verbruggen@nokia.com>
Date: Thu, 22 Apr 2010 18:50:08 +0200
Subject: [PATCH] Fixed rewriting for grouped properties.

---
 .../filemanager/changepropertyvisitor.cpp     | 44 ++++++++++----
 .../core/filemanager/changepropertyvisitor.h  |  6 +-
 .../filemanager/removepropertyvisitor.cpp     | 59 +++++++++++++++----
 .../core/filemanager/removepropertyvisitor.h  |  5 +-
 .../qml/qmldesigner/coretests/testcore.cpp    | 24 ++++++--
 5 files changed, 105 insertions(+), 33 deletions(-)

diff --git a/src/plugins/qmldesigner/core/filemanager/changepropertyvisitor.cpp b/src/plugins/qmldesigner/core/filemanager/changepropertyvisitor.cpp
index 6d0e4972852..9192e95ed8a 100644
--- a/src/plugins/qmldesigner/core/filemanager/changepropertyvisitor.cpp
+++ b/src/plugins/qmldesigner/core/filemanager/changepropertyvisitor.cpp
@@ -58,7 +58,7 @@ bool ChangePropertyVisitor::visit(QmlJS::AST::UiObjectDefinition *ast)
     const quint32 objectStart = ast->firstSourceLocation().offset;
 
     if (objectStart == m_parentLocation) {
-        replaceInMembers(ast->initializer);
+        replaceInMembers(ast->initializer, m_name);
         return false;
     }
 
@@ -73,30 +73,39 @@ bool ChangePropertyVisitor::visit(QmlJS::AST::UiObjectBinding *ast)
     const quint32 objectStart = ast->qualifiedTypeNameId->identifierToken.offset;
 
     if (objectStart == m_parentLocation) {
-        replaceInMembers(ast->initializer);
+        replaceInMembers(ast->initializer, m_name);
         return false;
     }
 
     return !didRewriting();
 }
 
-void ChangePropertyVisitor::replaceInMembers(UiObjectInitializer *initializer)
+void ChangePropertyVisitor::replaceInMembers(UiObjectInitializer *initializer,
+                                             const QString &propertyName)
 {
+    QString prefix, suffix;
+    int dotIdx = propertyName.indexOf(QLatin1Char('.'));
+    if (dotIdx != -1) {
+        prefix = propertyName.left(dotIdx);
+        suffix = propertyName.mid(dotIdx + 1);
+    }
+
     for (UiObjectMemberList *members = initializer->members; members; members = members->next) {
-        UiObjectMember *propertyMember = members->member;
+        UiObjectMember *member = members->member;
 
-        if (isMatchingPropertyMember(propertyMember)) {
+        // for non-grouped properties:
+        if (isMatchingPropertyMember(propertyName, member)) {
             switch (m_propertyType) {
             case QmlRefactoring::ArrayBinding:
-                insertIntoArray(cast<UiArrayBinding*>(propertyMember));
+                insertIntoArray(cast<UiArrayBinding*>(member));
                 break;
 
             case QmlRefactoring::ObjectBinding:
-                replaceMemberValue(propertyMember, false);
+                replaceMemberValue(member, false);
                 break;
 
             case QmlRefactoring::ScriptBinding:
-                replaceMemberValue(propertyMember, nextMemberOnSameLine(members));
+                replaceMemberValue(member, nextMemberOnSameLine(members));
                 break;
 
             default:
@@ -105,6 +114,14 @@ void ChangePropertyVisitor::replaceInMembers(UiObjectInitializer *initializer)
 
             break;
         }
+        // for grouped properties:
+        else if (!prefix.isEmpty()) {
+            if (UiObjectDefinition *def = cast<UiObjectDefinition *>(member)) {
+                if (flatten(def->qualifiedTypeNameId) == prefix) {
+                    replaceInMembers(def->initializer, suffix);
+                }
+            }
+        }
     }
 }
 
@@ -147,16 +164,17 @@ void ChangePropertyVisitor::replaceMemberValue(UiObjectMember *propertyMember, b
     setDidRewriting(true);
 }
 
-bool ChangePropertyVisitor::isMatchingPropertyMember(QmlJS::AST::UiObjectMember *member) const
+bool ChangePropertyVisitor::isMatchingPropertyMember(const QString &propName,
+                                                     UiObjectMember *member)
 {
     if (UiObjectBinding *objectBinding = AST::cast<UiObjectBinding *>(member)) {
-        return m_name == flatten(objectBinding->qualifiedId);
+        return propName == flatten(objectBinding->qualifiedId);
     } else if (UiScriptBinding *scriptBinding = AST::cast<UiScriptBinding *>(member)) {
-        return m_name == flatten(scriptBinding->qualifiedId);
+        return propName == flatten(scriptBinding->qualifiedId);
     } else if (UiArrayBinding *arrayBinding = AST::cast<UiArrayBinding *>(member)) {
-        return m_name == flatten(arrayBinding->qualifiedId);
+        return propName == flatten(arrayBinding->qualifiedId);
     } else if (UiPublicMember *publicMember = AST::cast<UiPublicMember *>(member)) {
-        return m_name == publicMember->name->asString();
+        return propName == publicMember->name->asString();
     } else {
         return false;
     }
diff --git a/src/plugins/qmldesigner/core/filemanager/changepropertyvisitor.h b/src/plugins/qmldesigner/core/filemanager/changepropertyvisitor.h
index 3728bf3baa0..6dd92c4e687 100644
--- a/src/plugins/qmldesigner/core/filemanager/changepropertyvisitor.h
+++ b/src/plugins/qmldesigner/core/filemanager/changepropertyvisitor.h
@@ -50,9 +50,11 @@ protected:
     virtual bool visit(QmlJS::AST::UiObjectBinding *ast);
 
 private:
-    void replaceInMembers(QmlJS::AST::UiObjectInitializer *initializer);
+    void replaceInMembers(QmlJS::AST::UiObjectInitializer *initializer,
+                          const QString &propertyName);
     void replaceMemberValue(QmlJS::AST::UiObjectMember *propertyMember, bool needsSemicolon);
-    bool isMatchingPropertyMember(QmlJS::AST::UiObjectMember *member) const;
+    static bool isMatchingPropertyMember(const QString &propName,
+                                         QmlJS::AST::UiObjectMember *member);
     static bool nextMemberOnSameLine(QmlJS::AST::UiObjectMemberList *members);
 
     void insertIntoArray(QmlJS::AST::UiArrayBinding* ast);
diff --git a/src/plugins/qmldesigner/core/filemanager/removepropertyvisitor.cpp b/src/plugins/qmldesigner/core/filemanager/removepropertyvisitor.cpp
index 2c6d04fa151..a3dcd72f77f 100644
--- a/src/plugins/qmldesigner/core/filemanager/removepropertyvisitor.cpp
+++ b/src/plugins/qmldesigner/core/filemanager/removepropertyvisitor.cpp
@@ -65,35 +65,68 @@ bool RemovePropertyVisitor::visit(QmlJS::AST::UiObjectDefinition *ast)
 
 void RemovePropertyVisitor::removeFrom(QmlJS::AST::UiObjectInitializer *ast)
 {
-    UiObjectMember *previousMember = 0, *wantedMember = 0, *nextMember = 0;
+    QString prefix;
+    int dotIdx = propertyName.indexOf(QLatin1Char('.'));
+    if (dotIdx != -1)
+        prefix = propertyName.left(dotIdx);
 
     for (UiObjectMemberList *it = ast->members; it; it = it->next) {
-        if (memberNameMatchesPropertyName(it->member)) {
-            wantedMember = it->member;
+        UiObjectMember *member = it->member;
 
-            if (it->next)
-                nextMember = it->next->member;
-
-            break;
+        // run full name match (for ungrouped properties):
+        if (memberNameMatchesPropertyName(propertyName, member)) {
+            removeMember(member);
+        }
+        // check for grouped properties:
+        else if (!prefix.isEmpty()) {
+            if (UiObjectDefinition *def = cast<UiObjectDefinition *>(member)) {
+                if (flatten(def->qualifiedTypeNameId) == prefix) {
+                    removeGroupedProperty(def);
+                }
+            }
         }
+    }
+}
+
+void RemovePropertyVisitor::removeGroupedProperty(UiObjectDefinition *ast)
+{
+    int dotIdx = propertyName.indexOf(QLatin1Char('.'));
+    if (dotIdx == -1)
+        return;
 
-        previousMember = it->member;
+    const QString propName = propertyName.mid(dotIdx + 1);
+
+    UiObjectMember *wanted = 0;
+    unsigned memberCount = 0;
+    for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+        ++memberCount;
+        UiObjectMember *member = it->member;
+
+        if (!wanted && memberNameMatchesPropertyName(propName, member)) {
+            wanted = member;
+        }
     }
 
-    if (!wantedMember)
+    if (!wanted)
         return;
+    if (memberCount == 1)
+        removeMember(ast);
+    else
+        removeMember(wanted);
+}
 
-    int start = wantedMember->firstSourceLocation().offset;
-    int end = wantedMember->lastSourceLocation().end();
+void RemovePropertyVisitor::removeMember(UiObjectMember *member)
+{
+    int start = member->firstSourceLocation().offset;
+    int end = member->lastSourceLocation().end();
 
     includeSurroundingWhitespace(start, end);
 
     replace(start, end - start, QLatin1String(""));
-
     setDidRewriting(true);
 }
 
-bool RemovePropertyVisitor::memberNameMatchesPropertyName(QmlJS::AST::UiObjectMember *ast) const
+bool RemovePropertyVisitor::memberNameMatchesPropertyName(const QString &propertyName, UiObjectMember *ast)
 {
     if (UiPublicMember *publicMember = cast<UiPublicMember*>(ast))
         return publicMember->name->asString() == propertyName;
diff --git a/src/plugins/qmldesigner/core/filemanager/removepropertyvisitor.h b/src/plugins/qmldesigner/core/filemanager/removepropertyvisitor.h
index 74b313d57c4..4c1908855e3 100644
--- a/src/plugins/qmldesigner/core/filemanager/removepropertyvisitor.h
+++ b/src/plugins/qmldesigner/core/filemanager/removepropertyvisitor.h
@@ -50,7 +50,10 @@ protected:
 
 private:
     void removeFrom(QmlJS::AST::UiObjectInitializer *ast);
-    bool memberNameMatchesPropertyName(QmlJS::AST::UiObjectMember *ast) const;
+    static bool memberNameMatchesPropertyName(const QString &propertyName,
+                                              QmlJS::AST::UiObjectMember *ast);
+    void removeGroupedProperty(QmlJS::AST::UiObjectDefinition *ast);
+    void removeMember(QmlJS::AST::UiObjectMember *ast);
 
 private:
     quint32 parentLocation;
diff --git a/tests/auto/qml/qmldesigner/coretests/testcore.cpp b/tests/auto/qml/qmldesigner/coretests/testcore.cpp
index 8f57c67ad8d..854946b7e01 100644
--- a/tests/auto/qml/qmldesigner/coretests/testcore.cpp
+++ b/tests/auto/qml/qmldesigner/coretests/testcore.cpp
@@ -545,11 +545,11 @@ void TestCore::testRewriterGroupedProperties()
                                   "    pointSize: 10\n"
                                   "    underline: true\n"
                                   "  }\n"
-                                  "}");
+                                  "}\n");
 
-    QPlainTextEdit textEdit1;
-    textEdit1.setPlainText(qmlString);
-    NotIndentingTextEditModifier modifier1(&textEdit1);
+    QPlainTextEdit textEdit;
+    textEdit.setPlainText(qmlString);
+    NotIndentingTextEditModifier modifier1(&textEdit);
 
     QScopedPointer<Model> model1(Model::create("Qt/Text"));
 
@@ -565,6 +565,22 @@ void TestCore::testRewriterGroupedProperties()
     ModelNode rootModelNode = testRewriterView1->rootModelNode();
     QCOMPARE(rootModelNode.property(QLatin1String("font.pointSize")).toVariantProperty().value().toDouble(), 10.0);
     QCOMPARE(rootModelNode.property(QLatin1String("font.underline")).toVariantProperty().value().toBool(), true);
+
+    rootModelNode.removeProperty(QLatin1String("font.underline"));
+    QCOMPARE(rootModelNode.property(QLatin1String("font.pointSize")).toVariantProperty().value().toDouble(), 10.0);
+    QVERIFY(!rootModelNode.hasProperty(QLatin1String("font.underline")));
+
+    rootModelNode.variantProperty(QLatin1String("font.pointSize")).setValue(20.0);
+    QCOMPARE(rootModelNode.property(QLatin1String("font.pointSize")).toVariantProperty().value().toDouble(), 20.0);
+
+    rootModelNode.removeProperty(QLatin1String("font.pointSize"));
+    const QLatin1String expected("\n"
+                                 "import Qt 4.6\n"
+                                 "\n"
+                                 "Text {\n"
+                                 "}\n");
+
+    QCOMPARE(textEdit.toPlainText(), expected);
 }
 
 void TestCore::loadSubItems()
-- 
GitLab