texttomodelmerger.cpp 51.6 KB
Newer Older
1
2
3
4
/**************************************************************************
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "abstractproperty.h"
#include "bindingproperty.h"
32
33
#include "filemanager/firstdefinitionfinder.h"
#include "filemanager/objectlengthcalculator.h"
34
35
#include "filemanager/qmlrefactoring.h"
#include "rewriteaction.h"
36
#include "nodeproperty.h"
37
#include "propertyparser.h"
38
#include "textmodifier.h"
39
40
41
#include "texttomodelmerger.h"
#include "rewriterview.h"
#include "variantproperty.h"
42

43
#include <qmljs/qmljsevaluate.h>
44
#include <qmljs/qmljsinterpreter.h>
45
46
#include <qmljs/qmljslink.h>
#include <qmljs/qmljsscopebuilder.h>
47
#include <qmljs/parser/qmljsast_p.h>
48

49
50
51
#include <QtDeclarative/QDeclarativeComponent>
#include <QtDeclarative/QDeclarativeEngine>
#include <QtCore/QSet>
Thomas Hartmann's avatar
Thomas Hartmann committed
52
#include <QtGui/QMessageBox>
53

54
55
56
57
58
59
60
61
62
63
64
65
66
67
using namespace QmlJS;
using namespace QmlJS::AST;

namespace {

static inline QString stripQuotes(const QString &str)
{
    if ((str.startsWith(QLatin1Char('"')) && str.endsWith(QLatin1Char('"')))
            || (str.startsWith(QLatin1Char('\'')) && str.endsWith(QLatin1Char('\''))))
        return str.mid(1, str.length() - 2);

    return str;
}

68
static inline QString deEscape(const QString &value)
69
70
71
72
73
74
75
76
77
78
79
{
    QString result = value;

    result.replace(QLatin1String("\\\\"), QLatin1String("\\"));
    result.replace(QLatin1String("\\\""), QLatin1String("\""));
    result.replace(QLatin1String("\\\t"), QLatin1String("\t"));
    result.replace(QLatin1String("\\\r"), QLatin1String("\\\r"));
    result.replace(QLatin1String("\\\n"), QLatin1String("\n"));

    return result;
}
80
81
82
 
static inline int fixUpMajorVersionForQtQuick(const QString &value, int i)
{
Thomas Hartmann's avatar
Thomas Hartmann committed
83
    if (i == 1 && value == "QtQuick")
84
85
86
87
88
89
        return 4;
    else return i;
}

static inline int fixUpMinorVersionForQtQuick(const QString &value, int i)
{
Thomas Hartmann's avatar
Thomas Hartmann committed
90
    if (i == 0 && value == "QtQuick")
91
92
93
        return 7;
    else return i;
}
94

95
96
97
98
99
100
static inline QString fixUpPackeNameForQtQuick(const QString &value)
{
    if (value == "QtQuick")
        return "Qt";
    return value;
}
101

102
103
104
105
106
107
108
static inline bool isSignalPropertyName(const QString &signalName)
{
    // see QmlCompiler::isSignalPropertyName
    return signalName.length() >= 3 && signalName.startsWith(QLatin1String("on")) &&
           signalName.at(2).isLetter();
}

109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
static inline QVariant cleverConvert(const QString &value)
{
    if (value == "true")
        return QVariant(true);
    if (value == "false")
        return QVariant(false);
    bool flag;
    int i = value.toInt(&flag);
    if (flag)
        return QVariant(i);
    double d = value.toDouble(&flag);
    if (flag)
        return QVariant(d);
    return QVariant(value);
}

125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
static QString flatten(UiQualifiedId *qualifiedId)
{
    QString result;

    for (UiQualifiedId *iter = qualifiedId; iter; iter = iter->next) {
        if (!iter->name)
            continue;

        if (!result.isEmpty())
            result.append(QLatin1Char('.'));

        result.append(iter->name->asString());
    }

    return result;
}

static bool isLiteralValue(ExpressionNode *expr)
{
    if (cast<NumericLiteral*>(expr))
        return true;
    else if (cast<StringLiteral*>(expr))
        return true;
    else if (UnaryPlusExpression *plusExpr = cast<UnaryPlusExpression*>(expr))
        return isLiteralValue(plusExpr->expression);
    else if (UnaryMinusExpression *minusExpr = cast<UnaryMinusExpression*>(expr))
        return isLiteralValue(minusExpr->expression);
    else if (cast<TrueLiteral*>(expr))
        return true;
    else if (cast<FalseLiteral*>(expr))
        return true;
    else
        return false;
}

static inline bool isLiteralValue(UiScriptBinding *script)
{
    if (!script || !script->statement)
        return false;

165
166
167
168
169
    ExpressionStatement *exprStmt = cast<ExpressionStatement *>(script->statement);
    if (exprStmt)
        return isLiteralValue(exprStmt->expression);
    else
        return false;
170
171
172
173
174
175
176
177
178
179
180
181
182
183
}

static inline int propertyType(const QString &typeName)
{
    if (typeName == QLatin1String("bool"))
        return QMetaType::type("bool");
    else if (typeName == QLatin1String("color"))
        return QMetaType::type("QColor");
    else if (typeName == QLatin1String("date"))
        return QMetaType::type("QDate");
    else if (typeName == QLatin1String("int"))
        return QMetaType::type("int");
    else if (typeName == QLatin1String("real"))
        return QMetaType::type("double");
184
185
    else if (typeName == QLatin1String("double"))
        return QMetaType::type("double");
186
187
188
189
    else if (typeName == QLatin1String("string"))
        return QMetaType::type("QString");
    else if (typeName == QLatin1String("url"))
        return QMetaType::type("QUrl");
190
    else if (typeName == QLatin1String("var") || typeName == QLatin1String("variant"))
191
192
193
194
195
        return QMetaType::type("QVariant");
    else
        return -1;
}

196
197
198
static inline QVariant convertDynamicPropertyValueToVariant(const QString &astValue,
                                                            const QString &astType)
{
199
    const QString cleanedValue = deEscape(stripQuotes(astValue.trimmed()));
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216

    if (astType.isEmpty())
        return QString();

    const int type = propertyType(astType);
    if (type == QMetaType::type("QVariant")) {
        if (cleanedValue.isNull()) // Explicitly isNull, NOT isEmpty!
            return QVariant(static_cast<QVariant::Type>(type));
        else
            return QVariant(cleanedValue);
    } else {
        QVariant value = QVariant(cleanedValue);
        value.convert(static_cast<QVariant::Type>(type));
        return value;
    }
}

217
218
} // anonymous namespace

219
220
221
222
223
224
225
226
227
228
namespace QmlDesigner {
namespace Internal {

class ReadingContext
{
public:
    ReadingContext(const Snapshot &snapshot, const Document::Ptr &doc,
                   const QStringList importPaths)
        : m_snapshot(snapshot)
        , m_doc(doc)
229
        , m_context(new Interpreter::Context)
230
        , m_link(m_context, doc, snapshot, importPaths)
231
        , m_scopeBuilder(m_context, doc, snapshot)
232
233
234
235
    {
    }

    ~ReadingContext()
236
    { delete m_context; }
237
238
239
240
241
242
243
244
245
246
247
248
249
250

    Document::Ptr doc() const
    { return m_doc; }

    void enterScope(Node *node)
    { m_scopeBuilder.push(node); }

    void leaveScope()
    { m_scopeBuilder.pop(); }

    void lookup(UiQualifiedId *astTypeNode, QString &typeName, int &majorVersion,
                int &minorVersion, QString &defaultPropertyName)
    {
        const Interpreter::ObjectValue *value = m_context->lookupType(m_doc.data(), astTypeNode);
251
252
        defaultPropertyName = m_context->defaultPropertyName(value);

253
254
        const Interpreter::QmlObjectValue * qmlValue = dynamic_cast<const Interpreter::QmlObjectValue *>(value);
        if (qmlValue) {
255
256
257
258
259
            typeName = fixUpPackeNameForQtQuick(qmlValue->packageName()) + QLatin1String("/") + qmlValue->className();

            //### todo this is just a hack to support QtQuick 1.0
            majorVersion = fixUpMajorVersionForQtQuick(qmlValue->packageName(), qmlValue->version().major());
            minorVersion = fixUpMinorVersionForQtQuick(qmlValue->packageName(), qmlValue->version().minor());
260
        } else {
261
262
263
            for (UiQualifiedId *iter = astTypeNode; iter; iter = iter->next)
                if (!iter->next && iter->name)
                    typeName = iter->name->asString();
264
265
            majorVersion = ComponentVersion::NoVersion;
            minorVersion = ComponentVersion::NoVersion;
266
267
268
269
270
271
        }
    }

    /// When something is changed here, also change Check::checkScopeObjectMember in
    /// qmljscheck.cpp
    /// ### Maybe put this into the context as a helper method.
272
    bool lookupProperty(const QString &prefix, const UiQualifiedId *id, const Interpreter::Value **property = 0, const Interpreter::ObjectValue **parentObject = 0, QString *name = 0)
273
274
275
276
277
278
279
280
281
282
283
    {
        QList<const Interpreter::ObjectValue *> scopeObjects = m_context->scopeChain().qmlScopeObjects;
        if (scopeObjects.isEmpty())
            return false;

        if (! id)
            return false; // ### error?

        if (! id->name) // possible after error recovery
            return false;

284
285
286
287
288
289
        QString propertyName;
        if (prefix.isEmpty())
            propertyName = id->name->asString();
        else
            propertyName = prefix;

290
291
292
293
294
295
296
297
298
299
        if (name)
            *name = propertyName;

        if (propertyName == QLatin1String("id") && ! id->next)
            return false; // ### should probably be a special value

        // attached properties
        bool isAttachedProperty = false;
        if (! propertyName.isEmpty() && propertyName[0].isUpper()) {
            isAttachedProperty = true;
300
301
            if (const Interpreter::ObjectValue *qmlTypes = m_context->scopeChain().qmlTypes)
                scopeObjects += qmlTypes;
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
        }

        if (scopeObjects.isEmpty())
            return false;

        // global lookup for first part of id
        const Interpreter::ObjectValue *objectValue = 0;
        const Interpreter::Value *value = 0;
        for (int i = scopeObjects.size() - 1; i >= 0; --i) {
            objectValue = scopeObjects[i];
            value = objectValue->lookupMember(propertyName, m_context);
            if (value)
                break;
        }
        if (parentObject)
            *parentObject = objectValue;
        if (!value) {
            qWarning() << "Skipping invalid property name" << propertyName;
            return false;
        }

        // can't look up members for attached properties
        if (isAttachedProperty)
            return false;

        // member lookup
        const UiQualifiedId *idPart = id;
329
330
331
        if (prefix.isEmpty())
            idPart = idPart->next;
        for (; idPart; idPart = idPart->next) {
332
333
334
335
336
337
338
339
340
341
            objectValue = Interpreter::value_cast<const Interpreter::ObjectValue *>(value);
            if (! objectValue) {
//                if (idPart->name)
//                    qDebug() << idPart->name->asString() << "has no property named"
//                             << propertyName;
                return false;
            }
            if (parentObject)
                *parentObject = objectValue;

342
            if (! idPart->name) {
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
                // somebody typed "id." and error recovery still gave us a valid tree,
                // so just bail out here.
                return false;
            }

            propertyName = idPart->name->asString();
            if (name)
                *name = propertyName;

            value = objectValue->lookupMember(propertyName, m_context);
            if (! value) {
//                if (idPart->name)
//                    qDebug() << "In" << idPart->name->asString() << ":"
//                             << objectValue->className() << "has no property named"
//                             << propertyName;
                return false;
            }
        }

        if (property)
            *property = value;
        return true;
    }

367
    bool isArrayProperty(const Interpreter::Value *value, const Interpreter::ObjectValue *containingObject, const QString &name)
368
369
370
371
    {
        if (!value)
            return false;
        const Interpreter::ObjectValue *objectValue = value->asObjectValue();
372
        if (objectValue && objectValue->prototype(m_context) == m_context->engine()->arrayPrototype())
373
            return true;
374

375
        for (const Interpreter::ObjectValue *iter = containingObject; iter; iter = iter->prototype(m_context)) {
376
            if (iter->property(name, m_context) == m_context->engine()->arrayPrototype())
377
378
379
380
381
382
383
                return true;
            if (const Interpreter::QmlObjectValue *qmlIter = dynamic_cast<const Interpreter::QmlObjectValue *>(iter)) {
                if (qmlIter->isListProperty(name))
                    return true;
            }
        }
        return false;
384
385
    }

386
    QVariant convertToVariant(const QString &astValue, const QString &propertyPrefix, UiQualifiedId *propertyId)
387
    {
388
        const bool hasQuotes = astValue.trimmed().left(1) == QLatin1String("\"") && astValue.trimmed().right(1) == QLatin1String("\"");
389
        const QString cleanedValue = deEscape(stripQuotes(astValue.trimmed()));
390
391
392
        const Interpreter::Value *property = 0;
        const Interpreter::ObjectValue *containingObject = 0;
        QString name;
393
394
        if (!lookupProperty(propertyPrefix, propertyId, &property, &containingObject, &name)) {
            qWarning() << "Unknown property" << propertyPrefix + QLatin1Char('.') + flatten(propertyId)
395
396
                       << "on line" << propertyId->identifierToken.startLine
                       << "column" << propertyId->identifierToken.startColumn;
397
            return hasQuotes ? QVariant(cleanedValue) : cleverConvert(cleanedValue);
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
        }

        for (const Interpreter::ObjectValue *iter = containingObject; iter; iter = iter->prototype(m_context)) {
            if (iter->lookupMember(name, m_context, false)) {
                containingObject = iter;
                break;
            }
        }

        if (const Interpreter::QmlObjectValue * qmlObject = dynamic_cast<const Interpreter::QmlObjectValue *>(containingObject)) {
            const QString typeName = qmlObject->propertyType(name);
            if (qmlObject->isEnum(typeName)) {
                return QVariant(cleanedValue);
            } else {
                int type = QMetaType::type(typeName.toUtf8().constData());
                QVariant result;
                if (type)
                    result = PropertyParser::read(type, cleanedValue);
                if (result.isValid())
                    return result;
            }
        }

        QVariant v(cleanedValue);
        if (property->asBooleanValue()) {
            v.convert(QVariant::Bool);
        } else if (property->asColorValue()) {
            v.convert(QVariant::Color);
        } else if (property->asNumberValue()) {
            v.convert(QVariant::Double);
        } else if (property->asStringValue()) {
            // nothing to do
430
431
432
        } else { //property alias et al
            if (!hasQuotes)
                return cleverConvert(cleanedValue);
433
434
435
436
        }
        return v;
    }

437
    QVariant convertToEnum(Statement *rhs, const QString &propertyPrefix, UiQualifiedId *propertyId)
438
439
440
441
442
443
444
    {
        ExpressionStatement *eStmt = cast<ExpressionStatement *>(rhs);
        if (!eStmt || !eStmt->expression)
            return QVariant();

        const Interpreter::ObjectValue *containingObject = 0;
        QString name;
445
        if (!lookupProperty(propertyPrefix, propertyId, 0, &containingObject, &name)) {
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
            return QVariant();
        }

        for (const Interpreter::ObjectValue *iter = containingObject; iter; iter = iter->prototype(m_context)) {
            if (iter->lookupMember(name, m_context, false)) {
                containingObject = iter;
                break;
            }
        }
        const Interpreter::QmlObjectValue * lhsQmlObject = dynamic_cast<const Interpreter::QmlObjectValue *>(containingObject);
        if (!lhsQmlObject)
            return QVariant();
        const QString lhsPropertyTypeName = lhsQmlObject->propertyType(name);

        const Interpreter::ObjectValue *rhsValueObject = 0;
        QString rhsValueName;
        if (IdentifierExpression *idExp = cast<IdentifierExpression *>(eStmt->expression)) {
            if (!m_context->scopeChain().qmlScopeObjects.isEmpty())
                rhsValueObject = m_context->scopeChain().qmlScopeObjects.last();
            if (idExp->name)
                rhsValueName = idExp->name->asString();
        } else if (FieldMemberExpression *memberExp = cast<FieldMemberExpression *>(eStmt->expression)) {
            Evaluate evaluate(m_context);
            const Interpreter::Value *result = evaluate(memberExp->base);
            rhsValueObject = result->asObjectValue();

            if (memberExp->name)
                rhsValueName = memberExp->name->asString();
        }

        if (!rhsValueObject)
            return QVariant();

        for (const Interpreter::ObjectValue *iter = rhsValueObject; iter; iter = iter->prototype(m_context)) {
            if (iter->lookupMember(rhsValueName, m_context, false)) {
                rhsValueObject = iter;
                break;
            }
        }

        const Interpreter::QmlObjectValue *rhsQmlObjectValue = dynamic_cast<const Interpreter::QmlObjectValue *>(rhsValueObject);
        if (!rhsQmlObjectValue)
            return QVariant();

        if (rhsQmlObjectValue->enumContainsKey(lhsPropertyTypeName, rhsValueName))
            return QVariant(rhsValueName);
        else
            return QVariant();
    }

496
497
498
499
500
501
502
503
504
505
506
507
508
509
private:
    Snapshot m_snapshot;
    Document::Ptr m_doc;
    Interpreter::Context *m_context;
    Link m_link;
    ScopeBuilder m_scopeBuilder;
};

} // namespace Internal
} // namespace QmlDesigner

using namespace QmlDesigner;
using namespace QmlDesigner::Internal;

510
511
512
513
514
515
516
517
518
519
520
521
static inline bool equals(const QVariant &a, const QVariant &b)
{
    if (a.type() == QVariant::Double && b.type() == QVariant::Double)
        return qFuzzyCompare(a.toDouble(), b.toDouble());
    else
        return a == b;
}

TextToModelMerger::TextToModelMerger(RewriterView *reWriterView):
        m_rewriterView(reWriterView),
        m_isActive(false)
{
522
    Q_ASSERT(reWriterView);
523
524
525
526
527
528
529
530
531
532
533
534
}

void TextToModelMerger::setActive(bool active)
{
    m_isActive = active;
}

bool TextToModelMerger::isActive() const
{
    return m_isActive;
}

535
void TextToModelMerger::setupImports(const Document::Ptr &doc,
536
                                     DifferenceHandler &differenceHandler)
537
{
538
    QList<Import> existingImports = m_rewriterView->model()->imports();
539

540
541
542
543
544
545
546
547
548
549
550
551
552
553
    for (UiImportList *iter = doc->qmlProgram()->imports; iter; iter = iter->next) {
        UiImport *import = iter->import;
        if (!import)
            continue;

        QString version;
        if (import->versionToken.isValid())
            version = textAt(doc, import->versionToken);
        QString as;
        if (import->importId)
            as = import->importId->asString();

        if (import->fileName) {
            const QString strippedFileName = stripQuotes(import->fileName->asString());
554
555
            const Import newImport = Import::createFileImport(strippedFileName,
                                                              version, as);
556

557
            if (!existingImports.removeOne(newImport))
558
559
                differenceHandler.modelMissesImport(newImport);
        } else {
560
            const Import newImport =
561
                    Import::createLibraryImport(flatten(import->importUri), version, as);
562

563
            if (!existingImports.removeOne(newImport))
564
                differenceHandler.modelMissesImport(newImport);
565
566
        }
    }
567
568

    foreach (const Import &import, existingImports)
569
        differenceHandler.importAbsentInQMl(import);
570
571
}

572
bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceHandler)
573
{
574
575
//    qDebug() << "TextToModelMerger::load with data:" << data;

576
577
    const QUrl url = m_rewriterView->model()->fileUrl();
    const QStringList importPaths = m_rewriterView->textModifier()->importPaths();
578
579
    setActive(true);

580
    { // Have the QML engine check if the document is valid:
581
        QDeclarativeEngine engine;
582
        engine.setOutputWarningsToStandardError(false);
583
584
585
        foreach (const QString &importPath, importPaths)
            engine.addImportPath(importPath);
        QDeclarativeComponent comp(&engine);
586
        comp.setData(data.toUtf8(), url);
587
588
589
590
591
592
593
594
595
        if (comp.status() == QDeclarativeComponent::Error) {
            QList<RewriterView::Error> errors;
            foreach (const QDeclarativeError &error, comp.errors())
                errors.append(RewriterView::Error(error));
            m_rewriterView->setErrors(errors);
            setActive(false);
            return false;
        } else if (comp.status() == QDeclarativeComponent::Loading) {
            // Probably loading remote components. Previous DOM behaviour was:
596
            QList<RewriterView::Error> errors;
597
            errors.append(RewriterView::Error());
598
            m_rewriterView->setErrors(errors);
599
600
            setActive(false);
            return false;
601
        }
602
603
604
605
606
    }

    try {
        Snapshot snapshot = m_rewriterView->textModifier()->getSnapshot();
        const QString fileName = url.toLocalFile();
Erik Verbruggen's avatar
Erik Verbruggen committed
607
        Document::Ptr doc = Document::create(fileName.isEmpty() ? QLatin1String("<internal>") : fileName);
608
        doc->setSource(data);
609
610
611
612
613
614
615
616
617
618
619
620
621
622
        doc->parseQml();
        snapshot.insert(doc);
        ReadingContext ctxt(snapshot, doc, importPaths);

        setupImports(doc, differenceHandler);

        UiObjectMember *astRootNode = 0;
        if (UiProgram *program = doc->qmlProgram())
            if (program->members)
                astRootNode = program->members->member;
        ModelNode modelRootNode = m_rewriterView->rootModelNode();
        syncNode(modelRootNode, astRootNode, &ctxt, differenceHandler);
        m_rewriterView->positionStorage()->cleanupInvalidOffsets();
        m_rewriterView->clearErrors();
623
624

        setActive(false);
625
        return true;
626
    } catch (Exception &e) {
627
        RewriterView::Error error(&e);
628
629
630
631
        // Somehow, the error below gets eaten in upper levels, so printing the
        // exception info here for debugging purposes:
        qDebug() << "*** An exception occurred while reading the QML file:"
                 << error.toString();
632
        m_rewriterView->addError(error);
633
634
635

        setActive(false);

636
        return false;
637
638
639
    }
}

640
void TextToModelMerger::syncNode(ModelNode &modelNode,
641
642
                                 UiObjectMember *astNode,
                                 ReadingContext *context,
643
                                 DifferenceHandler &differenceHandler)
644
{
645
646
647
648
649
650
651
652
653
654
655
656
657
    UiQualifiedId *astObjectType = 0;
    UiObjectInitializer *astInitializer = 0;
    if (UiObjectDefinition *def = cast<UiObjectDefinition *>(astNode)) {
        astObjectType = def->qualifiedTypeNameId;
        astInitializer = def->initializer;
    } else if (UiObjectBinding *bin = cast<UiObjectBinding *>(astNode)) {
        astObjectType = bin->qualifiedTypeNameId;
        astInitializer = bin->initializer;
    }

    if (!astObjectType || !astInitializer)
        return;

Erik Verbruggen's avatar
Erik Verbruggen committed
658
    m_rewriterView->positionStorage()->setNodeOffset(modelNode, astObjectType->identifierToken.offset);
659

660
    QString typeName, defaultPropertyName;
661
662
    int majorVersion;
    int minorVersion;
663
    context->lookup(astObjectType, typeName, majorVersion, minorVersion, defaultPropertyName);
664
665
666
667
668

    if (typeName.isEmpty()) {
        qWarning() << "Skipping node with unknown type" << flatten(astObjectType);
        return;
    }
669

670
671
672
    if (modelNode.type() != typeName
            /*|| modelNode.majorVersion() != domObject.objectTypeMajorVersion()
            || modelNode.minorVersion() != domObject.objectTypeMinorVersion()*/) {
673
        const bool isRootNode = m_rewriterView->rootModelNode() == modelNode;
674
675
676
        differenceHandler.typeDiffers(isRootNode, modelNode, typeName,
                                      majorVersion, minorVersion,
                                      astNode, context);
677
678
679
680
        if (!isRootNode)
            return; // the difference handler will create a new node, so we're done.
    }

681
682
    context->enterScope(astNode);

683
    QSet<QString> modelPropertyNames = QSet<QString>::fromList(modelNode.propertyNames());
684
685
    if (!modelNode.id().isEmpty())
        modelPropertyNames.insert(QLatin1String("id"));
686
    QList<UiObjectMember *> defaultPropertyItems;
687

688
689
690
    for (UiObjectMemberList *iter = astInitializer->members; iter; iter = iter->next) {
        UiObjectMember *member = iter->member;
        if (!member)
691
692
            continue;

693
694
        if (UiArrayBinding *array = cast<UiArrayBinding *>(member)) {
            const QString astPropertyName = flatten(array->qualifiedId);
695
            if (typeName == QLatin1String("Qt/PropertyChanges") || context->lookupProperty(QString(), array->qualifiedId)) {
696
                AbstractProperty modelProperty = modelNode.property(astPropertyName);
Erik Verbruggen's avatar
Erik Verbruggen committed
697
698
699
700
701
702
                QList<UiObjectMember *> arrayMembers;
                for (UiArrayMemberList *iter = array->members; iter; iter = iter->next)
                    if (UiObjectMember *member = iter->member)
                        arrayMembers.append(member);

                syncArrayProperty(modelProperty, arrayMembers, context, differenceHandler);
703
704
705
706
707
                modelPropertyNames.remove(astPropertyName);
            } else {
                qWarning() << "Skipping invalid array property" << astPropertyName
                           << "for node type" << modelNode.type();
            }
708
709
710
711
712
713
714
715
716
717
718
719
720
        } 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);
            }
721
722
723
724
725
        } else if (UiObjectBinding *binding = cast<UiObjectBinding *>(member)) {
            const QString astPropertyName = flatten(binding->qualifiedId);
            if (binding->hasOnToken) {
                // skip value sources
            } else {
726
727
728
                const Interpreter::Value *propertyType = 0;
                const Interpreter::ObjectValue *containingObject = 0;
                QString name;
729
                if (context->lookupProperty(QString(), binding->qualifiedId, &propertyType, &containingObject, &name) || typeName == QLatin1String("Qt/PropertyChanges")) {
730
                    AbstractProperty modelProperty = modelNode.property(astPropertyName);
731
                    if (context->isArrayProperty(propertyType, containingObject, name)) {
Erik Verbruggen's avatar
Erik Verbruggen committed
732
                        syncArrayProperty(modelProperty, QList<QmlJS::AST::UiObjectMember*>() << member, context, differenceHandler);
733
734
735
                    } else {
                        syncNodeProperty(modelProperty, binding, context, differenceHandler);
                    }
736
737
738
739
740
741
742
                    modelPropertyNames.remove(astPropertyName);
                } else {
                    qWarning() << "Skipping invalid node property" << astPropertyName
                               << "for node type" << modelNode.type();
                }
            }
        } else if (UiScriptBinding *script = cast<UiScriptBinding *>(member)) {
743
            modelPropertyNames.remove(syncScriptBinding(modelNode, QString(), script, context, differenceHandler));
744
        } else if (UiPublicMember *property = cast<UiPublicMember *>(member)) {
745
746
747
748
749
750
            if (property->type == UiPublicMember::Signal)
                continue; // QML designer doesn't support this yet.

            if (!property->name || !property->memberType)
                continue; // better safe than sorry.

751
752
753
            const QString astName = property->name->asString();
            QString astValue;
            if (property->expression)
754
                astValue = textAt(context->doc(),
755
756
757
758
                                  property->expression->firstSourceLocation(),
                                  property->expression->lastSourceLocation());
            const QString astType = property->memberType->asString();
            AbstractProperty modelProperty = modelNode.property(astName);
759
760
761
762
            if (!property->expression || isLiteralValue(property->expression)) {
                const QVariant variantValue = convertDynamicPropertyValueToVariant(astValue, astType);
                syncVariantProperty(modelProperty, variantValue, astType, differenceHandler);
            } else {
763
                syncExpressionProperty(modelProperty, astValue, differenceHandler);
764
            }
765
766
            modelPropertyNames.remove(astName);
        } else {
dt's avatar
dt committed
767
            qWarning() << "Found an unknown QML value.";
768
769
770
        }
    }

771
772
    if (!defaultPropertyItems.isEmpty()) {
        if (defaultPropertyName.isEmpty()) {
773
            if (modelNode.type() != QLatin1String("Qt/Component"))
Erik Verbruggen's avatar
Erik Verbruggen committed
774
                qWarning() << "No default property for node type" << modelNode.type() << ", ignoring child items.";
775
776
777
778
779
780
        } else {
            AbstractProperty modelProperty = modelNode.property(defaultPropertyName);
            if (modelProperty.isNodeListProperty()) {
                NodeListProperty nodeListProperty = modelProperty.toNodeListProperty();
                syncNodeListProperty(nodeListProperty, defaultPropertyItems, context,
                                     differenceHandler);
781
            } else {
782
783
784
                differenceHandler.shouldBeNodeListProperty(modelProperty,
                                                           defaultPropertyItems,
                                                           context);
785
            }
786
            modelPropertyNames.remove(defaultPropertyName);
787
788
789
790
791
792
        }
    }

    foreach (const QString &modelPropertyName, modelPropertyNames) {
        AbstractProperty modelProperty = modelNode.property(modelPropertyName);

793
        // property deleted.
794
795
796
797
        if (modelPropertyName == QLatin1String("id"))
            differenceHandler.idsDiffer(modelNode, QString());
        else
            differenceHandler.propertyAbsentFromQml(modelProperty);
798
    }
799
800

    context->leaveScope();
801
}
802

803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
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)) {
833
        if (modelNode.type() == QLatin1String("Qt/PropertyChanges")) {
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
            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:
858
        if (modelNode.type() == QLatin1String("Qt/PropertyChanges") || context->lookupProperty(prefix, script->qualifiedId)) {
859
860
861
862
863
864
865
866
867
868
869
            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();
        }
    }
}

870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
void TextToModelMerger::syncNodeId(ModelNode &modelNode, const QString &astObjectId,
                                   DifferenceHandler &differenceHandler)
{
    if (astObjectId.isEmpty()) {
        if (!modelNode.id().isEmpty()) {
            ModelNode existingNodeWithId = m_rewriterView->modelNodeForId(astObjectId);
            if (existingNodeWithId.isValid())
                existingNodeWithId.setId(QString());
            differenceHandler.idsDiffer(modelNode, astObjectId);
        }
    } else {
        if (modelNode.id() != astObjectId) {
            ModelNode existingNodeWithId = m_rewriterView->modelNodeForId(astObjectId);
            if (existingNodeWithId.isValid())
                existingNodeWithId.setId(QString());
            differenceHandler.idsDiffer(modelNode, astObjectId);
886
887
888
889
        }
    }
}

890
891
892
893
void TextToModelMerger::syncNodeProperty(AbstractProperty &modelProperty,
                                         UiObjectBinding *binding,
                                         ReadingContext *context,
                                         DifferenceHandler &differenceHandler)
894
{
895
    QString typeName, dummy;
896
897
    int majorVersion;
    int minorVersion;
898
    context->lookup(binding->qualifiedTypeNameId, typeName, majorVersion, minorVersion, dummy);
899

900
901
902
903
    if (typeName.isEmpty()) {
        qWarning() << "Skipping node with unknown type" << flatten(binding->qualifiedTypeNameId);
        return;
    }
904

905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
    if (modelProperty.isNodeProperty()) {
        ModelNode nodePropertyNode = modelProperty.toNodeProperty().modelNode();
        syncNode(nodePropertyNode, binding, context, differenceHandler);
    } else {
        differenceHandler.shouldBeNodeProperty(modelProperty,
                                               typeName,
                                               majorVersion,
                                               minorVersion,
                                               binding, context);
    }
}

void TextToModelMerger::syncExpressionProperty(AbstractProperty &modelProperty,
                                               const QString &javascript,
                                               DifferenceHandler &differenceHandler)
{
    if (modelProperty.isBindingProperty()) {
        BindingProperty bindingProperty = modelProperty.toBindingProperty();
        if (bindingProperty.expression() != javascript) {
            differenceHandler.bindingExpressionsDiffer(bindingProperty, javascript);
925
        }
926
927
928
929
930
931
    } else {
        differenceHandler.shouldBeBindingProperty(modelProperty, javascript);
    }
}

void TextToModelMerger::syncArrayProperty(AbstractProperty &modelProperty,
Erik Verbruggen's avatar
Erik Verbruggen committed
932
                                          const QList<UiObjectMember *> &arrayMembers,
933
934
935
936
937
938
939
940
941
942
943
944
945
946
                                          ReadingContext *context,
                                          DifferenceHandler &differenceHandler)
{
    if (modelProperty.isNodeListProperty()) {
        NodeListProperty nodeListProperty = modelProperty.toNodeListProperty();
        syncNodeListProperty(nodeListProperty, arrayMembers, context, differenceHandler);
    } else {
        differenceHandler.shouldBeNodeListProperty(modelProperty,
                                                   arrayMembers,
                                                   context);
    }
}

void TextToModelMerger::syncVariantProperty(AbstractProperty &modelProperty,
947
                                            const QVariant &astValue,
948
949
950
951
952
953
                                            const QString &astType,
                                            DifferenceHandler &differenceHandler)
{
    if (modelProperty.isVariantProperty()) {
        VariantProperty modelVariantProperty = modelProperty.toVariantProperty();

954
        if (!equals(modelVariantProperty.value(), astValue)
955
956
957
                || !astType.isEmpty() != modelVariantProperty.isDynamic()
                || astType != modelVariantProperty.dynamicTypeName()) {
            differenceHandler.variantValuesDiffer(modelVariantProperty,
958
                                                  astValue,
959
                                                  astType);
960
961
        }
    } else {
962
        differenceHandler.shouldBeVariantProperty(modelProperty,
963
                                                  astValue,
964
                                                  astType);
965
966
967
    }
}

968
969
970
971
void TextToModelMerger::syncNodeListProperty(NodeListProperty &modelListProperty,
                                             const QList<UiObjectMember *> arrayMembers,
                                             ReadingContext *context,
                                             DifferenceHandler &differenceHandler)
972
973
974
{
    QList<ModelNode> modelNodes = modelListProperty.toModelNodeList();
    int i = 0;
975
976
977
    for (; i < modelNodes.size() && i < arrayMembers.size(); ++i) {
        ModelNode modelNode = modelNodes.at(i);
        syncNode(modelNode, arrayMembers.at(i), context, differenceHandler);
978
979
    }

980
    for (int j = i; j < arrayMembers.size(); ++j) {
981
        // more elements in the dom-list, so add them to the model
982
983
984
985
986
        UiObjectMember *arrayMember = arrayMembers.at(j);
        const ModelNode newNode = differenceHandler.listPropertyMissingModelNode(modelListProperty, context, arrayMember);
        QString name;
        if (UiObjectDefinition *definition = cast<UiObjectDefinition *>(arrayMember))
            name = flatten(definition->qualifiedTypeNameId);
987
        if (name == QLatin1String("Qt/Component"))
988
            setupComponent(newNode);
989
990
991
992
993
994
995
996
997
    }

    for (int j = i; j < modelNodes.size(); ++j) {
        // more elements in the model, so remove them.
        ModelNode modelNode = modelNodes.at(j);
        differenceHandler.modelNodeAbsentFromQml(modelNode);
    }
}

998
999
1000
1001
1002
1003
ModelNode TextToModelMerger::createModelNode(const QString &typeName,
                                             int majorVersion,
                                             int minorVersion,
                                             UiObjectMember *astNode,
                                             ReadingContext *context,
                                             DifferenceHandler &differenceHandler)
1004
{
1005
1006
1007
1008
    ModelNode newNode = m_rewriterView->createModelNode(typeName,
                                                        majorVersion,
                                                        minorVersion);
    syncNode(newNode, astNode, context, differenceHandler);
1009
1010
1011
    return newNode;
}

1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
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;
}

1033
void ModelValidator::modelMissesImport(const Import &import)
1034
{
1035
    Q_ASSERT(m_merger->view()->model()->imports().contains(import));
1036
1037
}

1038
void ModelValidator::importAbsentInQMl(const Import &import)
1039
{
1040
    Q_ASSERT(! m_merger->view()->model()->imports().contains(import));
1041
1042
}

1043
1044
void ModelValidator::bindingExpressionsDiffer(BindingProperty &modelProperty,
                                              const QString &javascript)
1045
{
1046
    Q_ASSERT(modelProperty.expression() == javascript);
1047
1048
1049
    Q_ASSERT(0);
}

1050
1051
void ModelValidator::shouldBeBindingProperty(AbstractProperty &modelProperty,
                                             const QString &/*javascript*/)
1052
1053
1054
1055
1056
{
    Q_ASSERT(modelProperty.isBindingProperty());
    Q_ASSERT(0);
}

1057
1058
1059
void ModelValidator::shouldBeNodeListProperty(AbstractProperty &modelProperty,
                                              const QList<UiObjectMember *> /*arrayMembers*/,
                                              ReadingContext * /*context*/)
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
{
    Q_ASSERT(modelProperty.isNodeListProperty());
    Q_ASSERT(0);
}

void ModelValidator::variantValuesDiffer(VariantProperty &modelProperty, const QVariant &qmlVariantValue, const QString &dynamicTypeName)
{
    Q_ASSERT(modelProperty.isDynamic() == !dynamicTypeName.isEmpty());
    if (modelProperty.isDynamic()) {
        Q_ASSERT(modelProperty.dynamicTypeName() == dynamicTypeName);
    }

    Q_ASSERT(equals(modelProperty.value(), qmlVariantValue));
    Q_ASSERT(0);
}

void ModelValidator::shouldBeVariantProperty(AbstractProperty &modelProperty, const QVariant &/*qmlVariantValue*/, const QString &/*dynamicTypeName*/)
{
    Q_ASSERT(modelProperty.isVariantProperty());
    Q_ASSERT(0);
}

1082
1083
1084
1085
1086
1087
void ModelValidator::shouldBeNodeProperty(AbstractProperty &modelProperty,
                                          const QString &/*typeName*/,
                                          int /*majorVersion*/,
                                          int /*minorVersion*/,
                                          UiObjectMember * /*astNode*/,
                                          ReadingContext * /*context*/)
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
{
    Q_ASSERT(modelProperty.isNodeProperty());
    Q_ASSERT(0);
}

void ModelValidator::modelNodeAbsentFromQml(ModelNode &modelNode)
{
    Q_ASSERT(!modelNode.isValid());
    Q_ASSERT(0);
}

1099
1100
1101
ModelNode ModelValidator::listPropertyMissingModelNode(NodeListProperty &/*modelProperty*/,
                                                       ReadingContext * /*context*/,
                                                       UiObjectMember * /*arrayMember*/)
1102
1103
{
    Q_ASSERT(0);
1104
    return ModelNode();
1105
1106
}

1107
1108
1109
1110
1111
1112
1113
void ModelValidator::typeDiffers(bool /*isRootNode*/,
                                 ModelNode &modelNode,
                                 const QString &typeName,
                                 int majorVersion,
                                 int minorVersion,
                                 QmlJS::AST::UiObjectMember * /*astNode*/,
                                 ReadingContext * /*context*/)
1114
{
1115
1116
1117
    Q_ASSERT(modelNode.type() == typeName);
    Q_ASSERT(modelNode.majorVersion() == majorVersion);
    Q_ASSERT(modelNode.minorVersion() == minorVersion);
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
    Q_ASSERT(0);
}

void ModelValidator::propertyAbsentFromQml(AbstractProperty &modelProperty)
{
    Q_ASSERT(!modelProperty.isValid());
    Q_ASSERT(0);
}

void ModelValidator::idsDiffer(ModelNode &modelNode, const QString &qmlId)
{
    Q_ASSERT(modelNode.id() == qmlId);
    Q_ASSERT(0);
}

1133
void ModelAmender::modelMissesImport(const Import &import)
1134
{
1135
    m_merger->view()->model()->addImport(import);
1136
1137
}

1138
void ModelAmender::importAbsentInQMl(const Import &import)
1139
{
1140
    m_merger->view()->model()->removeImport(import);
1141
1142
}

1143
1144
void ModelAmender::bindingExpressionsDiffer(BindingProperty &modelProperty,
                                            const QString &javascript)
1145
{
1146
    modelProperty.toBindingProperty().setExpression(javascript);
1147
1148
}

1149
1150
void ModelAmender::shouldBeBindingProperty(AbstractProperty &modelProperty,
                                           const QString &javascript)
1151
1152
1153
{
    ModelNode theNode = modelProperty.parentModelNode();
    BindingProperty newModelProperty = theNode.bindingProperty(modelProperty.name());
1154
    newModelProperty.setExpression(javascript);
1155
1156
}

1157
1158
1159
void ModelAmender::shouldBeNodeListProperty(AbstractProperty &modelProperty,
                                            const QList<UiObjectMember *> arrayMembers,
                                            ReadingContext *context)
1160
1161
1162
{
    ModelNode theNode = modelProperty.parentModelNode();
    NodeListProperty newNodeListProperty = theNode.nodeListProperty(modelProperty.name());
1163
1164
1165
1166
    m_merger->syncNodeListProperty(newNodeListProperty,
                                   arrayMembers,
                                   context,
                                   *this);
1167
1168
}

1169
1170


1171
1172
void ModelAmender::variantValuesDiffer(VariantProperty &modelProperty, const QVariant &qmlVariantValue, const QString &dynamicType)
{
1173
1174
1175
1176
//    qDebug()<< "ModelAmender::variantValuesDiffer for property"<<modelProperty.name()
//            << "in node" << modelProperty.parentModelNode().id()
//            << ", old value:" << modelProperty.value()
//            << "new value:" << qmlVariantValue;
1177

1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
    if (dynamicType.isEmpty())
        modelProperty.setValue(qmlVariantValue);
    else
        modelProperty.setDynamicTypeNameAndValue(dynamicType, qmlVariantValue);
}

void ModelAmender::shouldBeVariantProperty(AbstractProperty &modelProperty, const QVariant &qmlVariantValue, const QString &dynamicTypeName)
{
    ModelNode theNode = modelProperty.parentModelNode();
    VariantProperty newModelProperty = theNode.variantProperty(modelProperty.name());

    if (dynamicTypeName.isEmpty())
        newModelProperty.setValue(qmlVariantValue);
    else
        newModelProperty.setDynamicTypeNameAndValue(dynamicTypeName, qmlVariantValue);
}

1195
1196
1197
1198
1199
1200
void ModelAmender::shouldBeNodeProperty(AbstractProperty &modelProperty,
                                        const QString &typeName,
                                        int majorVersion,
                                        int minorVersion,
                                        UiObjectMember *astNode,
                                        ReadingContext *context)
1201
1202
1203
{
    ModelNode theNode = modelProperty.parentModelNode();
    NodeProperty newNodeProperty = theNode.nodeProperty(modelProperty.name());
1204
1205
1206
1207
1208
1209
    newNodeProperty.setModelNode(m_merger->createModelNode(typeName,
                                                           majorVersion,
                                                           minorVersion,
                                                           astNode,
                                                           context,
                                                           *this));
1210
1211
1212
1213
1214
1215
1216
}

void ModelAmender::modelNodeAbsentFromQml(ModelNode &modelNode)
{
    modelNode.destroy();
}

1217
1218
1219
ModelNode ModelAmender::listPropertyMissingModelNode(NodeListProperty &modelProperty,
                                                     ReadingContext *context,
                                                     UiObjectMember *arrayMember)
1220
{
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
    UiQualifiedId *astObjectType = 0;
    UiObjectInitializer *astInitializer = 0;
    if (UiObjectDefinition *def = cast<UiObjectDefinition *>(arrayMember)) {
        astObjectType = def->qualifiedTypeNameId;
        astInitializer = def->initializer;
    } else if (UiObjectBinding *bin = cast<UiObjectBinding *>(arrayMember)) {
        astObjectType = bin->qualifiedTypeNameId;
        astInitializer = bin->initializer;
    }

    if (!astObjectType || !astInitializer)
        return ModelNode();

1234
    QString typeName, dummy;
1235
1236
    int majorVersion;
    int minorVersion;
1237
    context->lookup(astObjectType, typeName, majorVersion, minorVersion, dummy);
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249

    if (typeName.isEmpty()) {
        qWarning() << "Skipping node with unknown type" << flatten(astObjectType);
        return ModelNode();
    }

    const ModelNode &newNode = m_merger->createModelNode(typeName,
                                                         majorVersion,
                                                         minorVersion,
                                                         arrayMember,
                                                         context,
                                                         *this);
1250
1251
    modelProperty.reparentHere(newNode);
    return newNode;
1252
1253
}

1254
1255
1256
1257
1258
1259
1260
void ModelAmender::typeDiffers(bool isRootNode,
                               ModelNode &modelNode,
                               const QString &typeName,
                               int majorVersion,
                               int minorVersion,
                               QmlJS::AST::UiObjectMember *astNode,
                               ReadingContext *context)
1261
{
1262
    if (isRootNode) {
1263
        modelNode.view()->changeRootNodeType(typeName, majorVersion, minorVersion);
1264
1265
1266
1267
1268
1269
1270
    } else {
        NodeAbstractProperty parentProperty = modelNode.parentProperty();
        int nodeIndex = -1;
        if (parentProperty.isNodeListProperty()) {
            nodeIndex = parentProperty.toNodeListProperty().toModelNodeList().indexOf(modelNode);
            Q_ASSERT(nodeIndex >= 0);
        }
1271

1272
1273
        modelNode.destroy();

1274
1275
1276
1277
1278
1279
        const ModelNode &newNode = m_merger->createModelNode(typeName,
                                                             majorVersion,
                                                             minorVersion,
                                                             astNode,
                                                             context,
                                                             *this);
1280
1281
1282
1283
1284
1285
1286
        parentProperty.reparentHere(newNode);
        if (nodeIndex >= 0) {
            int currentIndex = parentProperty.toNodeListProperty().toModelNodeList().indexOf(newNode);
            if (nodeIndex != currentIndex)
                parentProperty.toNodeListProperty().slide(currentIndex, nodeIndex);
        }
    }
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
}

void ModelAmender::propertyAbsentFromQml(AbstractProperty &modelProperty)
{
    modelProperty.parentModelNode().removeProperty(modelProperty.name());
}

void ModelAmender::idsDiffer(ModelNode &modelNode, const QString &qmlId)
{
    modelNode.setId(qmlId);
}

1299
1300
void TextToModelMerger::setupComponent(const ModelNode &node)
{
1301
    Q_ASSERT(node.type() == QLatin1String("Qt/Component"));