nodemetainfo.cpp 52.4 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
Eike Ziller's avatar
Eike Ziller committed
3 4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8 9 10 11
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
Eike Ziller's avatar
Eike Ziller committed
12 13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
**
Eike Ziller's avatar
Eike Ziller committed
25 26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
con's avatar
con committed
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
30 31 32 33 34

#include "nodemetainfo.h"
#include "model.h"

#include "metainfo.h"
35
#include <enumeration.h>
36
#include <rewriterview.h>
37
#include <propertyparser.h>
38

39
#include <QDir>
hjk's avatar
hjk committed
40
#include <QDebug>
41

Christian Kamm's avatar
Christian Kamm committed
42
#include <qmljs/qmljsscopechain.h>
43
#include <qmljs/parser/qmljsast_p.h>
44
#include <qmljs/qmljsmodelmanagerinterface.h>
45
#include <qmljs/qmljsvalueowner.h>
46
#include <languageutils/fakemetaobject.h>
47 48 49 50 51

namespace QmlDesigner {

namespace Internal {

52
struct TypeDescription
53
{
54
    TypeName className;
55
    int minorVersion;
56
    int majorVersion;
57 58
};

59
} //Internal
60 61 62 63 64 65 66 67 68 69 70 71

/*!
\class QmlDesigner::NodeMetaInfo
\ingroup CoreModel
\brief The NodeMetaInfo class provides meta information about a qml type.

A NodeMetaInfo object can be created via ModelNode::metaInfo, or MetaInfo::nodeMetaInfo.

The object can be invalid - you can check this by calling isValid().
The object is invalid if you ask for meta information for
an non-existing qml property. Also the node meta info can become invalid
if the enclosing type is deregistered from the meta type system (e.g.
72
a sub component qml file is deleted). Trying to call any accessor functions on an invalid
73 74 75 76 77
NodeMetaInfo object will result in an InvalidMetaInfoException being thrown.

\see QmlDesigner::MetaInfo, QmlDesigner::PropertyMetaInfo, QmlDesigner::EnumeratorMetaInfo
*/

78 79 80 81
namespace Internal {

using namespace QmlJS;

82
typedef QPair<PropertyName, TypeName> PropertyInfo;
83

84
QList<PropertyInfo> getObjectTypes(const ObjectValue *ov, const ContextPtr &context, bool local = false, int rec = 0);
85

86
static TypeName resolveTypeName(const ASTPropertyReference *ref, const ContextPtr &context,  QList<PropertyInfo> &dotProperties)
87
{
88
    TypeName type = "unknown";
89 90

    if (!ref->ast()->memberType.isEmpty()) {
91
        type = ref->ast()->memberType.toUtf8();
92

93
        if (type == "alias") {
94 95 96 97 98 99
            const Value *value = context->lookupReference(ref);

            if (!value)
                return type;

            if (const ASTObjectValue * astObjectValue = value->asAstObjectValue()) {
100
                if (astObjectValue->typeName()) {
101
                    type = astObjectValue->typeName()->name.toUtf8();
102 103 104 105
                    const ObjectValue *  objectValue =  context->lookupType(astObjectValue->document(), astObjectValue->typeName());;
                    if (objectValue)
                        dotProperties = getObjectTypes(objectValue, context);
                }
106
            } else if (const ObjectValue * objectValue = value->asObjectValue()) {
107
                type = objectValue->className().toUtf8();
108
                dotProperties = getObjectTypes(objectValue, context);
109
            } else if (value->asColorValue()) {
110
                type = "color";
111
            } else if (value->asUrlValue()) {
112
                type = "url";
113
            } else if (value->asStringValue()) {
114
                type = "string";
115
            } else if (value->asRealValue()) {
116
                type = "real";
117
            } else if (value->asIntValue()) {
118
                type = "int";
119
            } else if (value->asBooleanValue()) {
120
                type = "boolean";
121 122 123 124 125 126 127
            }
        }
    }

    return type;
}

128 129 130 131 132 133 134 135 136 137 138
static QString qualifiedTypeNameForContext(const ObjectValue *objectValue,
                                           const ViewerContext &vContext,
                                           const ImportDependencies &dep)
{
    QString cppName;
    QStringList packages;
    if (const CppComponentValue *cppComponent = value_cast<CppComponentValue>(objectValue)) {
        QString className = cppComponent->className();
        foreach (const LanguageUtils::FakeMetaObject::Export &e, cppComponent->metaObject()->exports()) {
            if (e.type == className)
                packages << e.package;
139
            if (e.package == CppQmlTypes::cppPackage)
140 141
                cppName = e.type;
        }
142
        if (packages.size() == 1 && packages.at(0) == CppQmlTypes::cppPackage)
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
            return packages.at(0) + QLatin1Char('.') + className;
    }
    // try to recover a "global context name"
    QStringList possibleLibraries;
    QStringList possibleQrcFiles;
    QStringList possibleFiles;
    bool hasQtQuick = false;
    do {
        if (objectValue->originId().isEmpty())
            break;
        CoreImport cImport = dep.coreImport(objectValue->originId());
        if (!cImport.valid())
            break;
        foreach (const Export &e, cImport.possibleExports) {
            if (e.pathRequired.isEmpty() || vContext.paths.contains(e.pathRequired)) {
                switch (e.exportName.type) {
                case ImportType::Library:
                {
                    QString typeName = objectValue->className();
                    if (!e.typeName.isEmpty() && e.typeName != Export::LibraryTypeName) {
                        typeName = e.typeName;
                        if (typeName != objectValue->className())
                            qCWarning(qmljsLog) << "Outdated classname " << objectValue->className()
                                                << " vs " << typeName
                                                << " for " << e.exportName.toString();
                    }
Fawzi Mohamed's avatar
Fawzi Mohamed committed
169
                    if (packages.isEmpty() || packages.contains(e.exportName.libraryQualifiedPath())) {
170 171
                        if (e.exportName.splitPath.value(0) == QLatin1String("QtQuick"))
                            hasQtQuick = true;
Fawzi Mohamed's avatar
Fawzi Mohamed committed
172
                        possibleLibraries.append(e.exportName.libraryQualifiedPath() + '.' + typeName);
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
                    }
                    break;
                }
                case ImportType::File:
                {
                    // remove the search path prefix.
                    // this means that the same relative path wrt. different import paths will clash
                    QString filePath = e.exportName.path();
                    foreach (const QString &path, vContext.paths) {
                        if (filePath.startsWith(path) && filePath.size() > path.size()
                                && filePath.at(path.size()) == QLatin1Char('/'))
                        {
                            filePath = filePath.mid(path.size() + 1);
                            break;
                        }
                    }

                    if (filePath.startsWith(QLatin1Char('/')))
                        filePath = filePath.mid(1);
                    QFileInfo fileInfo(filePath);
                    QStringList splitName = fileInfo.path().split(QLatin1Char('/'));
                    QString typeName = fileInfo.baseName();
                    if (!e.typeName.isEmpty()) {
                        if (e.typeName != fileInfo.baseName())
                            qCWarning(qmljsLog) << "type renaming in file import " << e.typeName
                                                << " for " << e.exportName.path();
                        typeName = e.typeName;
                    }
                    if (typeName != objectValue->className())
                        qCWarning(qmljsLog) << "Outdated classname " << objectValue->className()
                                            << " vs " << typeName
                                            << " for " << e.exportName.toString();
                    splitName.append(typeName);
                    possibleFiles.append(splitName.join(QLatin1Char('.')));
                    break;
                }
                case ImportType::QrcFile:
                {
                    QString filePath = e.exportName.path();
                    if (filePath.startsWith(QLatin1Char('/')))
                        filePath = filePath.mid(1);
                    QFileInfo fileInfo(filePath);
                    QStringList splitName = fileInfo.path().split(QLatin1Char('/'));
                    QString typeName = fileInfo.baseName();
                    if (!e.typeName.isEmpty()) {
                        if (e.typeName != fileInfo.baseName())
                            qCWarning(qmljsLog) << "type renaming in file import " << e.typeName
                                                << " for " << e.exportName.path();
                        typeName = e.typeName;
                    }
                    if (typeName != objectValue->className())
                        qCWarning(qmljsLog) << "Outdated classname " << objectValue->className()
                                            << " vs " << typeName
                                            << " for " << e.exportName.toString();
                    splitName.append(typeName);
                    possibleQrcFiles.append(splitName.join(QLatin1Char('.')));
                    break;
                }
                case ImportType::Invalid:
                case ImportType::UnknownFile:
                    break;
                case ImportType::Directory:
                case ImportType::ImplicitDirectory:
                case ImportType::QrcDirectory:
                    qCWarning(qmljsLog) << "unexpected import type in export "
                                        << e.exportName.toString() << " of coreExport "
                                        << objectValue->originId();
                    break;
                }
            }
        }
        auto optimalName = [] (const QStringList &list) -> QString {
            QString res = list.at(0);
            for (int i = 1; i < list.size(); ++i) {
                const QString &nameNow = list.at(i);
                if (nameNow.size() < res.size()
                        || (nameNow.size() == res.size() && nameNow < res))
                    res = nameNow;
            }
            return res;
        };
        if (!possibleLibraries.isEmpty()) {
            if (hasQtQuick) {
                foreach (const QString &libImport, possibleLibraries)
                    if (!libImport.startsWith(QLatin1String("QtQuick")))
                        possibleLibraries.removeAll(libImport);
            }
            return optimalName(possibleLibraries);
        }
        if (!possibleQrcFiles.isEmpty())
            return optimalName(possibleQrcFiles);
        if (!possibleFiles.isEmpty())
            return optimalName(possibleFiles);
    } while (false);
    if (!cppName.isEmpty())
268
        return CppQmlTypes::cppPackage + QLatin1Char('.') + cppName;
269 270 271 272 273 274 275 276 277 278
    if (const CppComponentValue *cppComponent = value_cast<CppComponentValue>(objectValue)) {
        if (cppComponent->moduleName().isEmpty())
            return cppComponent->className();
        else
            return cppComponent->moduleName() + QLatin1Char('.') + cppComponent->className();
    } else {
        return objectValue->className();
    }
}

279
class PropertyMemberProcessor : public MemberProcessor
280
{
281
public:
282 283
    PropertyMemberProcessor(const ContextPtr &context) : m_context(context)
    {}
Thomas Hartmann's avatar
Thomas Hartmann committed
284
    bool processProperty(const QString &name, const Value *value, const QmlJS::PropertyInfo &) override
285
    {
286
        PropertyName propertyName = name.toUtf8();
Christian Kamm's avatar
Christian Kamm committed
287
        const ASTPropertyReference *ref = value_cast<ASTPropertyReference>(value);
288
        if (ref) {
289
            QList<PropertyInfo> dotProperties;
290 291
            const TypeName type = resolveTypeName(ref, m_context, dotProperties);
            m_properties.append(qMakePair(propertyName, type));
292 293
            if (!dotProperties.isEmpty()) {
                foreach (const PropertyInfo &propertyInfo, dotProperties) {
294 295 296
                    PropertyName dotName = propertyInfo.first;
                    TypeName type = propertyInfo.second;
                    dotName = propertyName + '.' + dotName;
297 298 299
                    m_properties.append(qMakePair(dotName, type));
                }
            }
300
        } else {
301

302
            if (const CppComponentValue * cppComponentValue = value_cast<CppComponentValue>(value)) {
303
                TypeName qualifiedTypeName = qualifiedTypeNameForContext(cppComponentValue,
Fawzi Mohamed's avatar
Fawzi Mohamed committed
304
                    m_context->viewerContext(), *m_context->snapshot().importDependencies()).toUtf8();
305
                m_properties.append(qMakePair(propertyName, qualifiedTypeName));
306
            } else {
307
                TypeId typeId;
308 309
                TypeName typeName = typeId(value).toUtf8();
                if (typeName == "number") {
310
                    if (value->asIntValue()) {
311
                        typeName = "int";
312 313 314
                    } else {
                        typeName = "real";
                    }
315
                }
316
                m_properties.append(qMakePair(propertyName, typeName));
317 318 319 320
            }
        }
        return true;
    }
321

322
    bool processSignal(const QString &name, const Value * /*value*/) override
323 324 325 326 327
    {
        m_signals.append(name.toUtf8());
        return true;
    }

328 329
    QList<PropertyInfo> properties() const { return m_properties; }

330 331
    PropertyNameList signalList() const { return m_signals; }

332 333
private:
    QList<PropertyInfo> m_properties;
334
    PropertyNameList m_signals;
335
    const ContextPtr m_context;
336 337 338
};

static inline bool isValueType(const QString &type)
339
{
340 341 342
    QStringList objectValuesList;
    objectValuesList << "QFont" << "QPoint" << "QPointF" << "QSize" << "QSizeF" << "QVector3D" << "QVector2D";
    return objectValuesList.contains(type);
343 344
}

345
const CppComponentValue *findQmlPrototype(const ObjectValue *ov, const ContextPtr &context)
346
{
347 348 349
    if (!ov)
        return 0;

Christian Kamm's avatar
Christian Kamm committed
350
    const CppComponentValue * qmlValue = value_cast<CppComponentValue>(ov);
351 352 353
    if (qmlValue)
        return qmlValue;

Christian Kamm's avatar
Christian Kamm committed
354
    return findQmlPrototype(ov->prototype(context), context);
355 356
}

357
QStringList prototypes(const ObjectValue *ov, const ContextPtr &context, bool versions = false)
358
{
359 360 361
    QStringList list;
    if (!ov)
        return list;
Christian Kamm's avatar
Christian Kamm committed
362
    ov = ov->prototype(context);
363
    while (ov) {
Christian Kamm's avatar
Christian Kamm committed
364
        const CppComponentValue * qmlValue = value_cast<CppComponentValue>(ov);
365 366
        if (qmlValue) {
            if (versions) {
367 368 369
                list << qmlValue->moduleName() + '.' + qmlValue->className() +
                ' ' + QString::number(qmlValue->componentVersion().majorVersion()) +
                '.' + QString::number(qmlValue->componentVersion().minorVersion());
370
            } else {
371
                list << qmlValue->moduleName() + QLatin1Char('.') + qmlValue->className();
372 373
            }
        } else {
374
            if (versions)
375
                list << ov->className() + " -1.-1";
376
            else
377 378
                list << ov->className();
        }
Christian Kamm's avatar
Christian Kamm committed
379
        ov = ov->prototype(context);
380 381
    }
    return list;
382 383
}

384
QList<PropertyInfo> getQmlTypes(const CppComponentValue *objectValue, const ContextPtr &context, bool local = false, int rec = 0)
385
{
386 387 388 389 390 391
    QList<PropertyInfo> propertyList;

    if (!objectValue)
        return propertyList;
    if (objectValue->className().isEmpty())
        return propertyList;
392

393 394 395
    if (rec > 2)
        return propertyList;

396
    PropertyMemberProcessor processor(context);
397
    objectValue->processMembers(&processor);
398

399 400 401
    QList<PropertyInfo> newList = processor.properties();

    foreach (PropertyInfo property, newList) {
402 403
        PropertyName name = property.first;
        if (!objectValue->isWritable(name) && objectValue->isPointer(name)) {
404
            //dot property
405
            const CppComponentValue * qmlValue = value_cast<CppComponentValue>(objectValue->lookupMember(name, context));
406
            if (qmlValue) {
407
                QList<PropertyInfo> dotProperties = getQmlTypes(qmlValue, context, false, rec + 1);
408
                foreach (const PropertyInfo &propertyInfo, dotProperties) {
409 410
                    PropertyName dotName = propertyInfo.first;
                    TypeName type = propertyInfo.second;
411
                    dotName = name + '.' + dotName;
412
                    propertyList.append(qMakePair(dotName, type));
413 414 415
                }
            }
        }
416 417
        if (isValueType(objectValue->propertyType(name))) {
            const ObjectValue *dotObjectValue = value_cast<ObjectValue>(objectValue->lookupMember(name, context));
418
            if (dotObjectValue) {
419
                QList<PropertyInfo> dotProperties = getObjectTypes(dotObjectValue, context, false, rec + 1);
420
                foreach (const PropertyInfo &propertyInfo, dotProperties) {
421 422
                    PropertyName dotName = propertyInfo.first;
                    TypeName type = propertyInfo.second;
423
                    dotName = name + '.' + dotName;
424
                    propertyList.append(qMakePair(dotName, type));
425 426 427
                }
            }
        }
428 429 430 431
        TypeName type = property.second;
        if (!objectValue->isPointer(name) && !objectValue->isListProperty(name))
            type = objectValue->propertyType(name).toUtf8();
        propertyList.append(qMakePair(name, type));
432 433 434
    }

    if (!local) {
435
        const ObjectValue* prototype = objectValue->prototype(context);
436

Christian Kamm's avatar
Christian Kamm committed
437
        const CppComponentValue * qmlObjectValue = value_cast<CppComponentValue>(prototype);
438

439
        if (qmlObjectValue)
440
            propertyList.append(getQmlTypes(qmlObjectValue, context, false, rec));
441
        else
442
            propertyList.append(getObjectTypes(prototype, context, false, rec));
443 444
    }

445
    return propertyList;
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
PropertyNameList getSignals(const ObjectValue *objectValue, const ContextPtr &context, bool local = false)
{
    PropertyNameList signalList;

     if (!objectValue)
        return signalList;
    if (objectValue->className().isEmpty())
        return signalList;

    PropertyMemberProcessor processor(context);
    objectValue->processMembers(&processor);

    signalList.append(processor.signalList());

    if (!local) {
        const ObjectValue* prototype = objectValue->prototype(context);

        if (prototype == objectValue)
            return signalList;

        signalList.append(getSignals(prototype, context));
    }

    return signalList;
}

474
QList<PropertyInfo> getTypes(const ObjectValue *objectValue, const ContextPtr &context, bool local = false)
475
{
476
    QList<PropertyInfo> propertyList;
477

478
    const CppComponentValue * qmlObjectValue = value_cast<CppComponentValue>(objectValue);
479

480
    if (qmlObjectValue)
481
        propertyList.append(getQmlTypes(qmlObjectValue, context, local));
482
    else
483
        propertyList.append(getObjectTypes(objectValue, context, local));
484

485
    return propertyList;
486 487
}

488
QList<PropertyInfo> getObjectTypes(const ObjectValue *objectValue, const ContextPtr &context, bool local, int rec)
489
{
490 491 492 493 494 495
    QList<PropertyInfo> propertyList;

    if (!objectValue)
        return propertyList;
    if (objectValue->className().isEmpty())
        return propertyList;
Thomas Hartmann's avatar
Thomas Hartmann committed
496

497 498 499
    if (rec > 2)
        return propertyList;

500
    PropertyMemberProcessor processor(context);
501
    objectValue->processMembers(&processor);
502

503
    propertyList.append(processor.properties());
504 505

    if (!local) {
506
        const ObjectValue* prototype = objectValue->prototype(context);
507

508 509
        if (prototype == objectValue)
            return propertyList;
Thomas Hartmann's avatar
Thomas Hartmann committed
510

Christian Kamm's avatar
Christian Kamm committed
511
        const CppComponentValue * qmlObjectValue = value_cast<CppComponentValue>(prototype);
512

513
        if (qmlObjectValue)
514
            propertyList.append(getQmlTypes(qmlObjectValue, context, local, rec));
515
        else
516
            propertyList.append(getObjectTypes(prototype, context, local, rec));
517 518
    }

519
    return propertyList;
520 521
}

522
class NodeMetaInfoPrivate
523
{
524 525 526 527 528 529
public:
    typedef QSharedPointer<NodeMetaInfoPrivate> Pointer;
    NodeMetaInfoPrivate();
    ~NodeMetaInfoPrivate() {}

    bool isValid() const;
Marco Bubke's avatar
Marco Bubke committed
530
    bool isFileComponent() const;
531 532
    PropertyNameList properties() const;
    PropertyNameList localProperties() const;
533
    PropertyNameList signalNames() const;
534 535
    PropertyName defaultPropertyName() const;
    TypeName propertyType(const PropertyName &propertyName) const;
536

537
    void setupPrototypes();
538 539
    QList<TypeDescription> prototypes() const;

540 541 542 543 544
    bool isPropertyWritable(const PropertyName &propertyName) const;
    bool isPropertyPointer(const PropertyName &propertyName) const;
    bool isPropertyList(const PropertyName &propertyName) const;
    bool isPropertyEnum(const PropertyName &propertyName) const;
    QString propertyEnumScope(const PropertyName &propertyName) const;
545 546
    QStringList keysForEnum(const QString &enumName) const;
    bool cleverCheckType(const QString &otherType) const;
547
    QVariant::Type variantTypeId(const PropertyName &properyName) const;
548

Marco Bubke's avatar
Marco Bubke committed
549 550
    int majorVersion() const;
    int minorVersion() const;
551
    TypeName qualfiedTypeName() const;
Marco Bubke's avatar
Marco Bubke committed
552
    Model *model() const;
553

554
    QString cppPackageName() const;
555 556 557

    QString componentSource() const;
    QString componentFileName() const;
558
    QString importDirectoryPath() const;
559

560
    static Pointer create(Model *model, const TypeName &type, int maj = -1, int min = -1);
561

Marco Bubke's avatar
Marco Bubke committed
562 563
    QSet<QString> &prototypeCachePositives();
    QSet<QString> &prototypeCacheNegatives();
564

Marco Bubke's avatar
Marco Bubke committed
565
    static void clearCache();
566

567

568
private:
569
    NodeMetaInfoPrivate(Model *model, TypeName type, int maj = -1, int min = -1);
570

571 572
    const CppComponentValue *getCppComponentValue() const;
    const ObjectValue *getObjectValue() const;
573 574 575 576
    void setupPropertyInfo(QList<PropertyInfo> propertyInfos);
    void setupLocalPropertyInfo(QList<PropertyInfo> propertyInfos);
    QString lookupName() const;
    QStringList lookupNameComponent() const;
577
    const CppComponentValue *getNearestCppComponentValue() const;
578
    QString fullQualifiedImportAliasType() const;
579

580
    TypeName m_qualfiedTypeName;
581 582 583
    int m_majorVersion;
    int m_minorVersion;
    bool m_isValid;
Marco Bubke's avatar
Marco Bubke committed
584
    bool m_isFileComponent;
585
    PropertyNameList m_properties;
586
    PropertyNameList m_signals;
587 588 589
    QList<TypeName> m_propertyTypes;
    PropertyNameList m_localProperties;
    PropertyName m_defaultPropertyName;
590
    QList<TypeDescription> m_prototypes;
591 592
    QSet<QString> m_prototypeCachePositives;
    QSet<QString> m_prototypeCacheNegatives;
593 594

    //storing the pointer would not be save
595
    ContextPtr context() const;
596
    const Document *document() const;
597 598

    QPointer<Model> m_model;
599
    static QHash<QString, Pointer> m_nodeMetaInfoCache;
600 601
};

602 603
QHash<QString, NodeMetaInfoPrivate::Pointer> NodeMetaInfoPrivate::m_nodeMetaInfoCache;

Marco Bubke's avatar
Marco Bubke committed
604 605 606 607 608
bool NodeMetaInfoPrivate::isFileComponent() const
{
    return m_isFileComponent;
}

609
PropertyNameList NodeMetaInfoPrivate::properties() const
Marco Bubke's avatar
Marco Bubke committed
610 611 612 613
{
    return m_properties;
}

614
PropertyNameList NodeMetaInfoPrivate::localProperties() const
Marco Bubke's avatar
Marco Bubke committed
615 616 617 618
{
    return m_localProperties;
}

619 620 621 622 623
PropertyNameList NodeMetaInfoPrivate::signalNames() const
{
    return m_signals;
}

Marco Bubke's avatar
Marco Bubke committed
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
QSet<QString> &NodeMetaInfoPrivate::prototypeCachePositives()
{
    return m_prototypeCachePositives;
}

QSet<QString> &NodeMetaInfoPrivate::prototypeCacheNegatives()
{
    return m_prototypeCacheNegatives;
}

void NodeMetaInfoPrivate::clearCache()
{
    m_nodeMetaInfoCache.clear();
}

639
PropertyName NodeMetaInfoPrivate::defaultPropertyName() const
Marco Bubke's avatar
Marco Bubke committed
640 641 642
{
    if (!m_defaultPropertyName.isEmpty())
        return m_defaultPropertyName;
643
    return PropertyName("data");
Marco Bubke's avatar
Marco Bubke committed
644
}
645

646
static inline QString stringIdentifier( const QString &type, int maj, int min)
647
{
648
    return type + QString::number(maj) + '_' + QString::number(min);
649 650
}

651
NodeMetaInfoPrivate::Pointer NodeMetaInfoPrivate::create(Model *model, const TypeName &type, int major, int minor)
652
{
653 654
    if (m_nodeMetaInfoCache.contains(stringIdentifier(type, major, minor))) {
        const Pointer &info = m_nodeMetaInfoCache.value(stringIdentifier(type, major, minor));
655
        if (info->model() == model)
656
            return info;
657
        else
658
            m_nodeMetaInfoCache.clear();
659
    }
660

661
    Pointer newData(new NodeMetaInfoPrivate(model, type, major, minor));
662
    if (newData->isValid())
663
        m_nodeMetaInfoCache.insert(stringIdentifier(type, major, minor), newData);
664
    return newData;
665 666
}

667 668
NodeMetaInfoPrivate::NodeMetaInfoPrivate() : m_isValid(false)
{
669

670
}
671

672
NodeMetaInfoPrivate::NodeMetaInfoPrivate(Model *model, TypeName type, int maj, int min) :
673
                                        m_qualfiedTypeName(type), m_majorVersion(maj),
Marco Bubke's avatar
Marco Bubke committed
674
                                        m_minorVersion(min), m_isValid(false), m_isFileComponent(false),
Kai Koehne's avatar
Kai Koehne committed
675
                                        m_model(model)
676
{
Christian Kamm's avatar
Christian Kamm committed
677
    if (context()) {
678
        const CppComponentValue *cppObjectValue = getCppComponentValue();
679

680
        if (cppObjectValue) {
681
            if (m_majorVersion == -1 && m_minorVersion == -1) {
682 683
                m_majorVersion = cppObjectValue->componentVersion().majorVersion();
                m_minorVersion = cppObjectValue->componentVersion().minorVersion();
684
            }
685 686 687
            setupPropertyInfo(getTypes(cppObjectValue, context()));
            setupLocalPropertyInfo(getTypes(cppObjectValue, context(), true));
            m_defaultPropertyName = cppObjectValue->defaultPropertyName().toUtf8();
688
            m_isValid = true;
Thomas Hartmann's avatar
Thomas Hartmann committed
689
            setupPrototypes();
690
            m_signals = getSignals(cppObjectValue, context());
691
        } else {
692
            const ObjectValue *objectValue = getObjectValue();
693
            if (objectValue) {
Christian Kamm's avatar
Christian Kamm committed
694
                const CppComponentValue *qmlValue = value_cast<CppComponentValue>(objectValue);
695
                if (qmlValue) {
696 697 698
                    if (m_majorVersion == -1 && m_minorVersion == -1) {
                        m_majorVersion = qmlValue->componentVersion().majorVersion();
                        m_minorVersion = qmlValue->componentVersion().minorVersion();
699
                        m_qualfiedTypeName = qmlValue->moduleName().toUtf8() + '.' + qmlValue->className().toUtf8();
700
                    } else if (m_majorVersion == qmlValue->componentVersion().majorVersion() && m_minorVersion == qmlValue->componentVersion().minorVersion()) {
701
                        m_qualfiedTypeName = qmlValue->moduleName().toUtf8() + '.' + qmlValue->className().toUtf8();
702 703 704
                    } else {
                        return;
                    }
705
                } else {
Marco Bubke's avatar
Marco Bubke committed
706
                    m_isFileComponent = true;
707 708
                    const Imports *imports = context()->imports(document());
                    ImportInfo importInfo = imports->info(lookupNameComponent().last(), context().data());
Fawzi Mohamed's avatar
Fawzi Mohamed committed
709
                    if (importInfo.isValid() && importInfo.type() == ImportType::Library) {
710 711 712
                        m_majorVersion = importInfo.version().majorVersion();
                        m_minorVersion = importInfo.version().minorVersion();
                    }
713
                }
Christian Kamm's avatar
Christian Kamm committed
714 715
                setupPropertyInfo(getTypes(objectValue, context()));
                setupLocalPropertyInfo(getTypes(objectValue, context(), true));
716
                m_defaultPropertyName = context()->defaultPropertyName(objectValue).toUtf8();
717
                m_isValid = true;
Thomas Hartmann's avatar
Thomas Hartmann committed
718
                setupPrototypes();
719
                m_signals = getSignals(objectValue, context());
720
            }
721 722 723 724
        }
    }
}

725
const CppComponentValue *NodeMetaInfoPrivate::getCppComponentValue() const
726
{
727
    const QList<TypeName> nameComponents = m_qualfiedTypeName.split('.');
728
    if (nameComponents.size() < 2)
729
        return 0;
730
    const TypeName type = nameComponents.last();
731

732
    TypeName module;
733 734
    for (int i = 0; i < nameComponents.size() - 1; ++i) {
        if (i != 0)
735
            module += '/';
736
        module += nameComponents.at(i);
737 738
    }

739
    // get the qml object value that's available in the document
740
    foreach (const QmlJS::Import &import, context()->imports(document())->all()) {
741
        if (import.info.path() != QString::fromUtf8(module))
742
            continue;
743
        const Value *lookupResult = import.object->lookupMember(QString::fromUtf8(type), context());
744 745 746 747 748 749
        const CppComponentValue *cppValue = value_cast<CppComponentValue>(lookupResult);
        if (cppValue
                && (m_majorVersion == -1 || m_majorVersion == cppValue->componentVersion().majorVersion())
                && (m_minorVersion == -1 || m_minorVersion == cppValue->componentVersion().minorVersion())
                )
            return cppValue;
750
    }
751

752
    const CppComponentValue *value = value_cast<CppComponentValue>(getObjectValue());
753 754 755 756
    if (value)
        return value;

    // maybe 'type' is a cpp name
757
    const CppComponentValue *cppValue = context()->valueOwner()->cppQmlTypes().objectByCppName(type);
758 759

    return cppValue;
760
}
761

762
const ObjectValue *NodeMetaInfoPrivate::getObjectValue() const
763
{
Christian Kamm's avatar
Christian Kamm committed
764
    return context()->lookupType(document(), lookupNameComponent());
765
}
766

767
ContextPtr NodeMetaInfoPrivate::context() const
768
{
769
    if (m_model && m_model->rewriterView() && m_model->rewriterView()->scopeChain())
Thomas Hartmann's avatar
Thomas Hartmann committed
770
        return m_model->rewriterView()->scopeChain()->context();
771
    return ContextPtr(0);
772
}
773

774
const Document *NodeMetaInfoPrivate::document() const
775
{
776
    if (m_model && m_model->rewriterView())
777 778 779
        return m_model->rewriterView()->document();
    return 0;
}
780