1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
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
16 ** GNU Lesser General Public License Usage
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.
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.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "changepropertyvisitor.h"
36 #include <qmljs/parser/qmljsast_p.h>
37 #include <qmljs/parser/qmljsengine_p.h>
39 using namespace QmlJS;
40 using namespace QmlJS::AST;
41 using namespace QmlDesigner;
42 using namespace QmlDesigner::Internal;
44 ChangePropertyVisitor::ChangePropertyVisitor(QmlDesigner::TextModifier &modifier,
45 quint32 parentLocation,
48 QmlRefactoring::PropertyType propertyType):
49 QMLRewriter(modifier),
50 m_parentLocation(parentLocation),
53 m_propertyType(propertyType)
57 bool ChangePropertyVisitor::visit(QmlJS::AST::UiObjectDefinition *ast)
62 const quint32 objectStart = ast->firstSourceLocation().offset;
64 if (objectStart == m_parentLocation) {
65 // FIXME: change this to use the QmlJS::Rewriter class
66 replaceInMembers(ast->initializer, m_name);
70 return !didRewriting();
73 bool ChangePropertyVisitor::visit(QmlJS::AST::UiObjectBinding *ast)
78 const quint32 objectStart = ast->qualifiedTypeNameId->identifierToken.offset;
80 if (objectStart == m_parentLocation) {
81 // FIXME: change this to use the QmlJS::Rewriter class
82 replaceInMembers(ast->initializer, m_name);
86 return !didRewriting();
89 // FIXME: duplicate code in the QmlJS::Rewriter class, remove this
90 void ChangePropertyVisitor::replaceInMembers(UiObjectInitializer *initializer,
91 const QString &propertyName)
93 QString prefix, suffix;
94 int dotIdx = propertyName.indexOf(QLatin1Char('.'));
96 prefix = propertyName.left(dotIdx);
97 suffix = propertyName.mid(dotIdx + 1);
100 for (UiObjectMemberList *members = initializer->members; members; members = members->next) {
101 UiObjectMember *member = members->member;
103 // for non-grouped properties:
104 if (isMatchingPropertyMember(propertyName, member)) {
105 switch (m_propertyType) {
106 case QmlRefactoring::ArrayBinding:
107 insertIntoArray(cast<UiArrayBinding*>(member));
110 case QmlRefactoring::ObjectBinding:
111 replaceMemberValue(member, false);
114 case QmlRefactoring::ScriptBinding:
115 replaceMemberValue(member, nextMemberOnSameLine(members));
119 Q_ASSERT(!"Unhandled QmlRefactoring::PropertyType");
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);
135 // FIXME: duplicate code in the QmlJS::Rewriter class, remove this
136 void ChangePropertyVisitor::replaceMemberValue(UiObjectMember *propertyMember, bool needsSemicolon)
138 QString replacement = m_value;
139 int startOffset = -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();
156 endOffset = publicMember->expression->lastSourceLocation().offset;
158 startOffset = publicMember->lastSourceLocation().end();
159 endOffset = startOffset;
160 if (publicMember->semicolonToken.isValid())
161 startOffset = publicMember->semicolonToken.offset;
162 replacement.prepend(QLatin1String(": "));
171 replace(startOffset, endOffset - startOffset, replacement);
172 setDidRewriting(true);
175 // FIXME: duplicate code in the QmlJS::Rewriter class, remove this
176 bool ChangePropertyVisitor::isMatchingPropertyMember(const QString &propName,
177 UiObjectMember *member)
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();
192 // FIXME: duplicate code in the QmlJS::Rewriter class, remove this
193 bool ChangePropertyVisitor::nextMemberOnSameLine(UiObjectMemberList *members)
195 if (members && members->next && members->next->member) {
196 return members->next->member->firstSourceLocation().startLine == members->member->lastSourceLocation().startLine;
202 // FIXME: duplicate code in the QmlJS::Rewriter class, remove this
203 void ChangePropertyVisitor::insertIntoArray(QmlJS::AST::UiArrayBinding *ast)
208 UiObjectMember *lastMember = 0;
209 for (UiArrayMemberList *iter = ast->members; iter; iter = iter->next) {
210 lastMember = iter->member;
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);