Commit b03ef971 authored by Erik Verbruggen's avatar Erik Verbruggen
Browse files

Fixed reading of grouped properties.

Task-number: BAUHAUS-620
parent 071722fb
......@@ -62,7 +62,7 @@ static inline QString stripQuotes(const QString &str)
return str;
}
static inline QString descape(const QString &value)
static inline QString deEscape(const QString &value)
{
QString result = value;
......@@ -157,7 +157,7 @@ static inline int propertyType(const QString &typeName)
static inline QVariant convertDynamicPropertyValueToVariant(const QString &astValue,
const QString &astType)
{
const QString cleanedValue = descape(stripQuotes(astValue.trimmed()));
const QString cleanedValue = deEscape(stripQuotes(astValue.trimmed()));
if (astType.isEmpty())
return QString();
......@@ -228,7 +228,7 @@ public:
/// When something is changed here, also change Check::checkScopeObjectMember in
/// qmljscheck.cpp
/// ### Maybe put this into the context as a helper method.
bool lookupProperty(const UiQualifiedId *id, const Interpreter::Value **property = 0, const Interpreter::ObjectValue **parentObject = 0, QString *name = 0)
bool lookupProperty(const QString &prefix, const UiQualifiedId *id, const Interpreter::Value **property = 0, const Interpreter::ObjectValue **parentObject = 0, QString *name = 0)
{
QList<const Interpreter::ObjectValue *> scopeObjects = m_context->scopeChain().qmlScopeObjects;
if (scopeObjects.isEmpty())
......@@ -240,7 +240,12 @@ public:
if (! id->name) // possible after error recovery
return false;
QString propertyName = id->name->asString();
QString propertyName;
if (prefix.isEmpty())
propertyName = id->name->asString();
else
propertyName = prefix;
if (name)
*name = propertyName;
......@@ -279,7 +284,9 @@ public:
// member lookup
const UiQualifiedId *idPart = id;
while (idPart->next) {
if (prefix.isEmpty())
idPart = idPart->next;
for (; idPart; idPart = idPart->next) {
objectValue = Interpreter::value_cast<const Interpreter::ObjectValue *>(value);
if (! objectValue) {
// if (idPart->name)
......@@ -290,13 +297,12 @@ public:
if (parentObject)
*parentObject = objectValue;
if (! idPart->next->name) {
if (! idPart->name) {
// somebody typed "id." and error recovery still gave us a valid tree,
// so just bail out here.
return false;
}
idPart = idPart->next;
propertyName = idPart->name->asString();
if (name)
*name = propertyName;
......@@ -335,14 +341,14 @@ public:
return false;
}
QVariant convertToVariant(const QString &astValue, UiQualifiedId *propertyId)
QVariant convertToVariant(const QString &astValue, const QString &propertyPrefix, UiQualifiedId *propertyId)
{
const QString cleanedValue = descape(stripQuotes(astValue.trimmed()));
const QString cleanedValue = deEscape(stripQuotes(astValue.trimmed()));
const Interpreter::Value *property = 0;
const Interpreter::ObjectValue *containingObject = 0;
QString name;
if (!lookupProperty(propertyId, &property, &containingObject, &name)) {
qWarning() << "Unknown property" << flatten(propertyId)
if (!lookupProperty(propertyPrefix, propertyId, &property, &containingObject, &name)) {
qWarning() << "Unknown property" << propertyPrefix + QLatin1Char('.') + flatten(propertyId)
<< "on line" << propertyId->identifierToken.startLine
<< "column" << propertyId->identifierToken.startColumn;
return QVariant(cleanedValue);
......@@ -384,7 +390,7 @@ public:
return v;
}
QVariant convertToEnum(Statement *rhs, UiQualifiedId *propertyId)
QVariant convertToEnum(Statement *rhs, const QString &propertyPrefix, UiQualifiedId *propertyId)
{
ExpressionStatement *eStmt = cast<ExpressionStatement *>(rhs);
if (!eStmt || !eStmt->expression)
......@@ -392,7 +398,7 @@ public:
const Interpreter::ObjectValue *containingObject = 0;
QString name;
if (!lookupProperty(propertyId, 0, &containingObject, &name)) {
if (!lookupProperty(propertyPrefix, propertyId, 0, &containingObject, &name)) {
return QVariant();
}
......@@ -640,7 +646,7 @@ void TextToModelMerger::syncNode(ModelNode &modelNode,
if (UiArrayBinding *array = cast<UiArrayBinding *>(member)) {
const QString astPropertyName = flatten(array->qualifiedId);
if (typeName == QLatin1String("Qt/PropertyChanges") || context->lookupProperty(array->qualifiedId)) {
if (typeName == QLatin1String("Qt/PropertyChanges") || context->lookupProperty(QString(), array->qualifiedId)) {
AbstractProperty modelProperty = modelNode.property(astPropertyName);
QList<UiObjectMember *> arrayMembers;
for (UiArrayMemberList *iter = array->members; iter; iter = iter->next)
......@@ -653,8 +659,19 @@ void TextToModelMerger::syncNode(ModelNode &modelNode,
qWarning() << "Skipping invalid array property" << astPropertyName
<< "for node type" << modelNode.type();
}
} else if (cast<UiObjectDefinition *>(member)) {
defaultPropertyItems.append(member);
} else if (UiObjectDefinition *def = cast<UiObjectDefinition *>(member)) {
const QString name = def->qualifiedTypeNameId->name->asString();
if (name.isEmpty() || !name.at(0).isUpper()) {
QStringList props = syncGroupedProperties(modelNode,
name,
def->initializer->members,
context,
differenceHandler);
foreach (const QString &prop, props)
modelPropertyNames.remove(prop);
} else {
defaultPropertyItems.append(member);
}
} else if (UiObjectBinding *binding = cast<UiObjectBinding *>(member)) {
const QString astPropertyName = flatten(binding->qualifiedId);
if (binding->hasOnToken) {
......@@ -663,7 +680,7 @@ void TextToModelMerger::syncNode(ModelNode &modelNode,
const Interpreter::Value *propertyType = 0;
const Interpreter::ObjectValue *containingObject = 0;
QString name;
if (context->lookupProperty(binding->qualifiedId, &propertyType, &containingObject, &name) || typeName == QLatin1String("Qt/PropertyChanges")) {
if (context->lookupProperty(QString(), binding->qualifiedId, &propertyType, &containingObject, &name) || typeName == QLatin1String("Qt/PropertyChanges")) {
AbstractProperty modelProperty = modelNode.property(astPropertyName);
if (context->isArrayProperty(propertyType, containingObject, name)) {
syncArrayProperty(modelProperty, QList<QmlJS::AST::UiObjectMember*>() << member, context, differenceHandler);
......@@ -677,59 +694,7 @@ void TextToModelMerger::syncNode(ModelNode &modelNode,
}
}
} else if (UiScriptBinding *script = cast<UiScriptBinding *>(member)) {
const QString astPropertyName = flatten(script->qualifiedId);
QString astValue;
if (script->statement) {
astValue = textAt(context->doc(),
script->statement->firstSourceLocation(),
script->statement->lastSourceLocation());
astValue = astValue.trimmed();
if (astValue.endsWith(QLatin1Char(';')))
astValue = astValue.left(astValue.length() - 1);
astValue = astValue.trimmed();
}
if (astPropertyName == QLatin1String("id")) {
syncNodeId(modelNode, astValue, differenceHandler);
} else if (isSignalPropertyName(astPropertyName)) {
// skip signals
} else if (isLiteralValue(script)) {
if (typeName == QLatin1String("Qt/PropertyChanges")) {
AbstractProperty modelProperty = modelNode.property(astPropertyName);
const QVariant variantValue(descape(stripQuotes(astValue)));
syncVariantProperty(modelProperty, variantValue, QString(), differenceHandler);
modelPropertyNames.remove(astPropertyName);
} else {
const QVariant variantValue = context->convertToVariant(astValue, script->qualifiedId);
if (variantValue.isValid()) {
AbstractProperty modelProperty = modelNode.property(astPropertyName);
syncVariantProperty(modelProperty, variantValue, QString(), differenceHandler);
modelPropertyNames.remove(astPropertyName);
} else {
qWarning() << "Skipping invalid variant property" << astPropertyName
<< "for node type" << modelNode.type();
}
}
} else {
// First see if it is a qualified enum:
const QVariant enumValue = context->convertToEnum(script->statement, script->qualifiedId);
if (enumValue.isValid()) {
const QString astPropertyName = flatten(script->qualifiedId);
AbstractProperty modelProperty = modelNode.property(astPropertyName);
syncVariantProperty(modelProperty, enumValue, QString(), differenceHandler);
modelPropertyNames.remove(astPropertyName);
} else {
// apparently not, so:
if (typeName == QLatin1String("Qt/PropertyChanges") || context->lookupProperty(script->qualifiedId)) {
AbstractProperty modelProperty = modelNode.property(astPropertyName);
syncExpressionProperty(modelProperty, astValue, differenceHandler);
modelPropertyNames.remove(astPropertyName);
} else {
qWarning() << "Skipping invalid expression property" << astPropertyName
<< "for node type" << modelNode.type();
}
}
}
modelPropertyNames.remove(syncScriptBinding(modelNode, QString(), script, context, differenceHandler));
} else if (UiPublicMember *property = cast<UiPublicMember *>(member)) {
if (property->type == UiPublicMember::Signal)
continue; // QML designer doesn't support this yet.
......@@ -786,6 +751,73 @@ void TextToModelMerger::syncNode(ModelNode &modelNode,
context->leaveScope();
}
QString TextToModelMerger::syncScriptBinding(ModelNode &modelNode,
const QString &prefix,
UiScriptBinding *script,
ReadingContext *context,
DifferenceHandler &differenceHandler)
{
QString astPropertyName = flatten(script->qualifiedId);
if (!prefix.isEmpty())
astPropertyName.prepend(prefix + QLatin1Char('.'));
QString astValue;
if (script->statement) {
astValue = textAt(context->doc(),
script->statement->firstSourceLocation(),
script->statement->lastSourceLocation());
astValue = astValue.trimmed();
if (astValue.endsWith(QLatin1Char(';')))
astValue = astValue.left(astValue.length() - 1);
astValue = astValue.trimmed();
}
if (astPropertyName == QLatin1String("id")) {
syncNodeId(modelNode, astValue, differenceHandler);
return astPropertyName;
}
if (isSignalPropertyName(astPropertyName))
return QString();
if (isLiteralValue(script)) {
if (modelNode.type() == QLatin1String("Qt/PropertyChanges")) {
AbstractProperty modelProperty = modelNode.property(astPropertyName);
const QVariant variantValue(deEscape(stripQuotes(astValue)));
syncVariantProperty(modelProperty, variantValue, QString(), differenceHandler);
return astPropertyName;
} else {
const QVariant variantValue = context->convertToVariant(astValue, prefix, script->qualifiedId);
if (variantValue.isValid()) {
AbstractProperty modelProperty = modelNode.property(astPropertyName);
syncVariantProperty(modelProperty, variantValue, QString(), differenceHandler);
return astPropertyName;
} else {
qWarning() << "Skipping invalid variant property" << astPropertyName
<< "for node type" << modelNode.type();
return QString();
}
}
}
const QVariant enumValue = context->convertToEnum(script->statement, prefix, script->qualifiedId);
if (enumValue.isValid()) { // It is a qualified enum:
AbstractProperty modelProperty = modelNode.property(astPropertyName);
syncVariantProperty(modelProperty, enumValue, QString(), differenceHandler);
return astPropertyName;
} else { // Not an enum, so:
if (modelNode.type() == QLatin1String("Qt/PropertyChanges") || context->lookupProperty(prefix, script->qualifiedId)) {
AbstractProperty modelProperty = modelNode.property(astPropertyName);
syncExpressionProperty(modelProperty, astValue, differenceHandler);
return astPropertyName;
} else {
qWarning() << "Skipping invalid expression property" << astPropertyName
<< "for node type" << modelNode.type();
return QString();
}
}
}
void TextToModelMerger::syncNodeId(ModelNode &modelNode, const QString &astObjectId,
DifferenceHandler &differenceHandler)
{
......@@ -928,6 +960,27 @@ ModelNode TextToModelMerger::createModelNode(const QString &typeName,
return newNode;
}
QStringList TextToModelMerger::syncGroupedProperties(ModelNode &modelNode,
const QString &name,
UiObjectMemberList *members,
ReadingContext *context,
DifferenceHandler &differenceHandler)
{
QStringList props;
for (UiObjectMemberList *iter = members; iter; iter = iter->next) {
UiObjectMember *member = iter->member;
if (UiScriptBinding *script = cast<UiScriptBinding *>(member)) {
const QString prop = syncScriptBinding(modelNode, name, script, context, differenceHandler);
if (!prop.isEmpty())
props.append(prop);
}
}
return props;
}
void ModelValidator::modelMissesImport(const Import &import)
{
Q_ASSERT(m_merger->view()->model()->imports().contains(import));
......
......@@ -34,8 +34,11 @@
#include "import.h"
#include "nodelistproperty.h"
#include "modelnode.h"
#include <qmljs/qmljsdocument.h>
#include <QtCore/QStringList>
namespace QmlDesigner {
class CORESHARED_EXPORT RewriterView;
......@@ -68,6 +71,11 @@ public:
QmlJS::AST::UiObjectMember *astNode,
ReadingContext *context,
DifferenceHandler &differenceHandler);
QString syncScriptBinding(ModelNode &modelNode,
const QString &prefix,
QmlJS::AST::UiScriptBinding *script,
ReadingContext *context,
DifferenceHandler &differenceHandler);
void syncNodeId(ModelNode &modelNode, const QString &astObjectId,
DifferenceHandler &differenceHandler);
void syncNodeProperty(AbstractProperty &modelProperty,
......@@ -95,6 +103,11 @@ public:
QmlJS::AST::UiObjectMember *astNode,
ReadingContext *context,
DifferenceHandler &differenceHandler);
QStringList syncGroupedProperties(ModelNode &modelNode,
const QString &name,
QmlJS::AST::UiObjectMemberList *members,
ReadingContext *context,
DifferenceHandler &differenceHandler);
private:
void setupComponent(const ModelNode &node);
......
......@@ -535,6 +535,38 @@ void TestCore::testRewriterDynamicProperties()
// QVERIFY(compareTree(testRewriterView1->rootModelNode(), testRewriterView2->rootModelNode()));
}
void TestCore::testRewriterGroupedProperties()
{
const QLatin1String qmlString("\n"
"import Qt 4.6\n"
"\n"
"Text {\n"
" font {\n"
" pointSize: 10\n"
" underline: true\n"
" }\n"
"}");
QPlainTextEdit textEdit1;
textEdit1.setPlainText(qmlString);
NotIndentingTextEditModifier modifier1(&textEdit1);
QScopedPointer<Model> model1(Model::create("Qt/Text"));
QScopedPointer<TestRewriterView> testRewriterView1(new TestRewriterView());
testRewriterView1->setTextModifier(&modifier1);
model1->attachView(testRewriterView1.data());
QVERIFY(testRewriterView1->errors().isEmpty());
//
// text2model
//
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);
}
void TestCore::loadSubItems()
{
QFile file(QString(QTCREATORDIR) + "/tests/auto/qml/qmldesigner/data/fx/topitem.qml");
......
......@@ -119,6 +119,7 @@ private slots:
void testRewriterNodeSliding();
void testRewriterExceptionHandling();
void testRewriterDynamicProperties();
void testRewriterGroupedProperties();
//
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment