OSDN Git Service

It's 2011 now.
[qt-creator-jp/qt-creator-jp.git] / src / libs / qmljs / qmljspropertyreader.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 "qmljspropertyreader.h"
35 #include "qmljsdocument.h"
36 #include <qmljs/parser/qmljsast_p.h>
37 #include <qmljs/qmljscheck.h>
38
39 namespace QmlJS {
40
41 using namespace AST;
42
43 namespace {
44
45 static inline QString deEscape(const QString &value)
46 {
47     QString result = value;
48
49     result.replace(QLatin1String("\\\\"), QLatin1String("\\"));
50     result.replace(QLatin1String("\\\""), QLatin1String("\""));
51     result.replace(QLatin1String("\\\t"), QLatin1String("\t"));
52     result.replace(QLatin1String("\\\r"), QLatin1String("\r"));
53     result.replace(QLatin1String("\\\n"), QLatin1String("\n"));
54
55     return result;
56 }
57
58 static inline QString stripQuotes(const QString &str)
59 {
60     if ((str.startsWith(QLatin1Char('"')) && str.endsWith(QLatin1Char('"')))
61             || (str.startsWith(QLatin1Char('\'')) && str.endsWith(QLatin1Char('\''))))
62         return str.mid(1, str.length() - 2);
63
64     return str;
65 }
66
67 static bool isLiteralValue(ExpressionNode *expr)
68 {
69     if (cast<NumericLiteral*>(expr))
70         return true;
71     else if (cast<StringLiteral*>(expr))
72         return true;
73     else if (UnaryPlusExpression *plusExpr = cast<UnaryPlusExpression*>(expr))
74         return isLiteralValue(plusExpr->expression);
75     else if (UnaryMinusExpression *minusExpr = cast<UnaryMinusExpression*>(expr))
76         return isLiteralValue(minusExpr->expression);
77     else if (cast<TrueLiteral*>(expr))
78         return true;
79     else if (cast<FalseLiteral*>(expr))
80         return true;
81     else
82         return false;
83 }
84
85 static inline bool isLiteralValue(UiScriptBinding *script)
86 {
87     if (!script || !script->statement)
88         return false;
89
90     ExpressionStatement *exprStmt = cast<ExpressionStatement *>(script->statement);
91     if (exprStmt)
92         return isLiteralValue(exprStmt->expression);
93     else
94         return false;
95 }
96
97 static inline QString textAt(const Document::Ptr doc,
98                                   const SourceLocation &from,
99                                   const SourceLocation &to)
100 {
101     return doc->source().mid(from.offset, to.end() - from.begin());
102 }
103
104 static inline int propertyType(const QString &typeName)
105 {
106     if (typeName == QLatin1String("bool"))
107         return QMetaType::type("bool");
108     else if (typeName == QLatin1String("color"))
109         return QMetaType::type("QColor");
110     else if (typeName == QLatin1String("date"))
111         return QMetaType::type("QDate");
112     else if (typeName == QLatin1String("int"))
113         return QMetaType::type("int");
114     else if (typeName == QLatin1String("real"))
115         return QMetaType::type("double");
116     else if (typeName == QLatin1String("double"))
117         return QMetaType::type("double");
118     else if (typeName == QLatin1String("string"))
119         return QMetaType::type("QString");
120     else if (typeName == QLatin1String("url"))
121         return QMetaType::type("QUrl");
122     else if (typeName == QLatin1String("variant"))
123         return QMetaType::type("QVariant");
124     else
125         return -1;
126 }
127
128 static inline QString flatten(UiQualifiedId *qualifiedId)
129 {
130     QString result;
131
132     for (UiQualifiedId *iter = qualifiedId; iter; iter = iter->next) {
133         if (!iter->name)
134             continue;
135
136         if (!result.isEmpty())
137             result.append(QLatin1Char('.'));
138
139         result.append(iter->name->asString());
140     }
141     return result;
142 }
143
144 static bool isEnum(AST::Statement *ast);
145
146 bool isEnum(AST::ExpressionNode *ast)
147 {
148     if (!ast)
149         return false;
150
151     if (FieldMemberExpression *memberExpr = cast<AST::FieldMemberExpression*>(ast))
152         return isEnum(memberExpr->base);
153     else if (cast<IdentifierExpression*>(ast))
154         return true;
155     else
156         return false;
157 }
158
159 bool isEnum(AST::ExpressionStatement *ast)
160 {
161     if (!ast)
162         return false;
163
164     if (FieldMemberExpression *memberExpr = cast<AST::FieldMemberExpression*>(ast->expression))
165         return isEnum(memberExpr->base);
166     else if (cast<IdentifierExpression*>(ast->expression))
167         return true;
168     else
169         return false;
170 }
171
172 static bool isEnum(AST::Statement *ast)
173 {
174     if (!ast)
175         return false;
176
177     if (ExpressionStatement *exprStmt = cast<ExpressionStatement*>(ast)) {
178         return isEnum(exprStmt->expression);
179     }
180     return false;
181 }
182
183 static QString cleanupSemicolon(const QString &str)
184 {
185     QString out = str;
186     while (out.endsWith(QLatin1Char(';')))
187         out.chop(1);
188     return out;
189 }
190
191 } // anonymous namespace
192
193 PropertyReader::PropertyReader(Document::Ptr doc, AST::UiObjectInitializer *ast)
194 {
195     m_ast = ast;
196     m_doc = doc;
197
198     if (!m_doc)
199         return;
200
201     for (UiObjectMemberList *members = ast->members; members; members = members->next) {
202         UiObjectMember *member = members->member;
203
204         if (UiScriptBinding *property = AST::cast<UiScriptBinding *>(member)) {
205             if (!property->qualifiedId)
206                 continue; // better safe than sorry.
207             const QString propertyName = flatten(property->qualifiedId);
208             const QString astValue = cleanupSemicolon(textAt(doc,
209                               property->statement->firstSourceLocation(),
210                               property->statement->lastSourceLocation()));
211             if (isLiteralValue(property)) {
212                 m_properties.insert(propertyName, QVariant(deEscape(stripQuotes(astValue))));
213             } else if (isEnum(property->statement)) { //enum
214                  m_properties.insert(propertyName, QVariant(astValue));
215                  m_bindingOrEnum.append(propertyName);
216             }
217         } else if (UiObjectDefinition *objectDefinition = cast<UiObjectDefinition *>(member)) { //font { bold: true }
218             const QString propertyName = objectDefinition->qualifiedTypeNameId->name->asString();
219             if (!propertyName.isEmpty() && !propertyName.at(0).isUpper()) {
220                 for (UiObjectMemberList *iter = objectDefinition->initializer->members; iter; iter = iter->next) {
221                     UiObjectMember *objectMember = iter->member;
222                     if (UiScriptBinding *property = cast<UiScriptBinding *>(objectMember)) {
223                         const QString propertyNamePart2 = flatten(property->qualifiedId);
224                         const QString astValue = cleanupSemicolon(textAt(doc,
225                             property->statement->firstSourceLocation(),
226                             property->statement->lastSourceLocation()));
227                         if (isLiteralValue(property)) {
228                             m_properties.insert(propertyName + '.' + propertyNamePart2, QVariant(deEscape(stripQuotes(astValue))));
229                         } else if (isEnum(property->statement)) { //enum
230                             m_properties.insert(propertyName + '.' + propertyNamePart2, QVariant(astValue));
231                             m_bindingOrEnum.append(propertyName + '.' + propertyNamePart2);
232                         }
233                     }
234                 }
235             }
236         } else if (UiObjectBinding* objectBinding = cast<UiObjectBinding *>(member)) {
237             UiObjectInitializer *initializer = objectBinding->initializer;
238             const QString astValue = cleanupSemicolon(textAt(doc,
239                               initializer->lbraceToken,
240                               initializer->rbraceToken));
241             const QString propertyName = objectBinding->qualifiedId->name->asString();
242             m_properties.insert(propertyName, QVariant(astValue));
243         }
244     }
245 }
246
247 QLinearGradient PropertyReader::parseGradient(const QString &propertyName,  bool *isBound) const
248 {
249     if (!m_doc)
250         return QLinearGradient();
251
252     *isBound = false;
253
254     for (UiObjectMemberList *members = m_ast->members; members; members = members->next) {
255         UiObjectMember *member = members->member;
256
257         if (UiObjectBinding* objectBinding = cast<UiObjectBinding *>(member)) {
258             UiObjectInitializer *initializer = objectBinding->initializer;
259             const QString astValue = cleanupSemicolon(textAt(m_doc,
260                                                              initializer->lbraceToken,
261                                                              initializer->rbraceToken));
262             const QString objectPropertyName = objectBinding->qualifiedId->name->asString();
263             const QString typeName = objectBinding->qualifiedTypeNameId->name->asString();
264             if (objectPropertyName == propertyName && typeName.contains("Gradient")) {
265                 QLinearGradient gradient;
266                 QVector<QGradientStop> stops;
267
268                 for (UiObjectMemberList *members = initializer->members; members; members = members->next) {
269                     UiObjectMember *member = members->member;
270                     if (UiObjectDefinition *objectDefinition = cast<UiObjectDefinition *>(member)) {
271                         PropertyReader localParser(m_doc, objectDefinition->initializer);
272                         if (localParser.hasProperty("color") && localParser.hasProperty("position")) {
273                             QColor color = QmlJS::toQColor(localParser.readProperty("color").toString());
274                             qreal position = localParser.readProperty("position").toReal();
275                             if (localParser.isBindingOrEnum("color") || localParser.isBindingOrEnum("position"))
276                                 *isBound = true;
277                             stops.append( QPair<qreal, QColor>(position, color));
278                         }
279                     }
280                 }
281
282                 gradient.setStops(stops);
283                 return gradient;
284             }
285         }
286     }
287     return QLinearGradient();
288 }
289
290 } //QmlJS