OSDN Git Service

ed9d0d3a012584aed26099b04bf3dbeae9ee0e0a
[qt-creator-jp/qt-creator-jp.git] / src / plugins / qmldesigner / designercore / filemanager / changepropertyvisitor.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "changepropertyvisitor.h"
35
36 #include <qmljs/parser/qmljsast_p.h>
37 #include <qmljs/parser/qmljsengine_p.h>
38
39 using namespace QmlJS;
40 using namespace QmlJS::AST;
41 using namespace QmlDesigner;
42 using namespace QmlDesigner::Internal;
43
44 ChangePropertyVisitor::ChangePropertyVisitor(QmlDesigner::TextModifier &modifier,
45                                              quint32 parentLocation,
46                                              const QString &name,
47                                              const QString &value,
48                                              QmlRefactoring::PropertyType propertyType):
49         QMLRewriter(modifier),
50         m_parentLocation(parentLocation),
51         m_name(name),
52         m_value(value),
53         m_propertyType(propertyType)
54 {
55 }
56
57 bool ChangePropertyVisitor::visit(QmlJS::AST::UiObjectDefinition *ast)
58 {
59     if (didRewriting())
60         return false;
61
62     const quint32 objectStart = ast->firstSourceLocation().offset;
63
64     if (objectStart == m_parentLocation) {
65         // FIXME: change this to use the QmlJS::Rewriter class
66         replaceInMembers(ast->initializer, m_name);
67         return false;
68     }
69
70     return !didRewriting();
71 }
72
73 bool ChangePropertyVisitor::visit(QmlJS::AST::UiObjectBinding *ast)
74 {
75     if (didRewriting())
76         return false;
77
78     const quint32 objectStart = ast->qualifiedTypeNameId->identifierToken.offset;
79
80     if (objectStart == m_parentLocation) {
81         // FIXME: change this to use the QmlJS::Rewriter class
82         replaceInMembers(ast->initializer, m_name);
83         return false;
84     }
85
86     return !didRewriting();
87 }
88
89 // FIXME: duplicate code in the QmlJS::Rewriter class, remove this
90 void ChangePropertyVisitor::replaceInMembers(UiObjectInitializer *initializer,
91                                              const QString &propertyName)
92 {
93     QString prefix, suffix;
94     int dotIdx = propertyName.indexOf(QLatin1Char('.'));
95     if (dotIdx != -1) {
96         prefix = propertyName.left(dotIdx);
97         suffix = propertyName.mid(dotIdx + 1);
98     }
99
100     for (UiObjectMemberList *members = initializer->members; members; members = members->next) {
101         UiObjectMember *member = members->member;
102
103         // for non-grouped properties:
104         if (isMatchingPropertyMember(propertyName, member)) {
105             switch (m_propertyType) {
106             case QmlRefactoring::ArrayBinding:
107                 insertIntoArray(cast<UiArrayBinding*>(member));
108                 break;
109
110             case QmlRefactoring::ObjectBinding:
111                 replaceMemberValue(member, false);
112                 break;
113
114             case QmlRefactoring::ScriptBinding:
115                 replaceMemberValue(member, nextMemberOnSameLine(members));
116                 break;
117
118             default:
119                 Q_ASSERT(!"Unhandled QmlRefactoring::PropertyType");
120             }
121
122             break;
123         }
124         // for grouped properties:
125         else if (!prefix.isEmpty()) {
126             if (UiObjectDefinition *def = cast<UiObjectDefinition *>(member)) {
127                 if (flatten(def->qualifiedTypeNameId) == prefix) {
128                     replaceInMembers(def->initializer, suffix);
129                 }
130             }
131         }
132     }
133 }
134
135 // FIXME: duplicate code in the QmlJS::Rewriter class, remove this
136 void ChangePropertyVisitor::replaceMemberValue(UiObjectMember *propertyMember, bool needsSemicolon)
137 {
138     QString replacement = m_value;
139     int startOffset = -1;
140     int endOffset = -1;
141     if (UiObjectBinding *objectBinding = AST::cast<UiObjectBinding *>(propertyMember)) {
142         startOffset = objectBinding->qualifiedTypeNameId->identifierToken.offset;
143         endOffset = objectBinding->initializer->rbraceToken.end();
144     } else if (UiScriptBinding *scriptBinding = AST::cast<UiScriptBinding *>(propertyMember)) {
145         startOffset = scriptBinding->statement->firstSourceLocation().offset;
146         endOffset = scriptBinding->statement->lastSourceLocation().end();
147     } else if (UiArrayBinding *arrayBinding = AST::cast<UiArrayBinding *>(propertyMember)) {
148         startOffset = arrayBinding->lbracketToken.offset;
149         endOffset = arrayBinding->rbracketToken.end();
150     } else if (UiPublicMember *publicMember = AST::cast<UiPublicMember*>(propertyMember)) {
151         if (publicMember->expression) {
152             startOffset = publicMember->expression->firstSourceLocation().offset;
153             if (publicMember->semicolonToken.isValid())
154                 endOffset = publicMember->semicolonToken.end();
155             else
156                 endOffset = publicMember->expression->lastSourceLocation().offset;
157         } else {
158             startOffset = publicMember->lastSourceLocation().end();
159             endOffset = startOffset;
160             if (publicMember->semicolonToken.isValid())
161                 startOffset = publicMember->semicolonToken.offset;
162             replacement.prepend(QLatin1String(": "));
163         }
164     } else {
165         return;
166     }
167
168     if (needsSemicolon)
169         replacement += ';';
170
171     replace(startOffset, endOffset - startOffset, replacement);
172     setDidRewriting(true);
173 }
174
175 // FIXME: duplicate code in the QmlJS::Rewriter class, remove this
176 bool ChangePropertyVisitor::isMatchingPropertyMember(const QString &propName,
177                                                      UiObjectMember *member)
178 {
179     if (UiObjectBinding *objectBinding = AST::cast<UiObjectBinding *>(member)) {
180         return propName == flatten(objectBinding->qualifiedId);
181     } else if (UiScriptBinding *scriptBinding = AST::cast<UiScriptBinding *>(member)) {
182         return propName == flatten(scriptBinding->qualifiedId);
183     } else if (UiArrayBinding *arrayBinding = AST::cast<UiArrayBinding *>(member)) {
184         return propName == flatten(arrayBinding->qualifiedId);
185     } else if (UiPublicMember *publicMember = AST::cast<UiPublicMember *>(member)) {
186         return propName == publicMember->name->asString();
187     } else {
188         return false;
189     }
190 }
191
192 // FIXME: duplicate code in the QmlJS::Rewriter class, remove this
193 bool ChangePropertyVisitor::nextMemberOnSameLine(UiObjectMemberList *members)
194 {
195     if (members && members->next && members->next->member) {
196         return members->next->member->firstSourceLocation().startLine == members->member->lastSourceLocation().startLine;
197     } else {
198         return false;
199     }
200 }
201
202 // FIXME: duplicate code in the QmlJS::Rewriter class, remove this
203 void ChangePropertyVisitor::insertIntoArray(QmlJS::AST::UiArrayBinding *ast)
204 {
205     if (!ast)
206         return;
207
208     UiObjectMember *lastMember = 0;
209     for (UiArrayMemberList *iter = ast->members; iter; iter = iter->next) {
210         lastMember = iter->member;
211     }
212
213     if (!lastMember)
214         return;
215
216     const int insertionPoint = lastMember->lastSourceLocation().end();
217     const int depth = calculateIndentDepth(lastMember->firstSourceLocation());
218     const QString indentedArrayMember = addIndentation(m_value, depth);
219     replace(insertionPoint, 0, QLatin1String(",\n") + indentedArrayMember);
220     setDidRewriting(true);
221 }