~bzoltan/kubuntu-packaging/decouple_cmake_plugin

« back to all changes in this revision

Viewing changes to src/libs/qmljs/qmljsimportdependencies.cpp

  • Committer: Timo Jyrinki
  • Date: 2013-12-02 09:16:15 UTC
  • mfrom: (1.1.29)
  • Revision ID: timo.jyrinki@canonical.com-20131202091615-xbj1os1f604ber1m
New upstream release candidate.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
 
4
** Contact: http://www.qt-project.org/legal
 
5
**
 
6
** This file is part of Qt Creator.
 
7
**
 
8
** Commercial License Usage
 
9
** Licensees holding valid commercial Qt licenses may use this file in
 
10
** accordance with the commercial license agreement provided with the
 
11
** Software or, alternatively, in accordance with the terms contained in
 
12
** a written agreement between you and Digia.  For licensing terms and
 
13
** conditions see http://qt.digia.com/licensing.  For further information
 
14
** use the contact form at http://qt.digia.com/contact-us.
 
15
**
 
16
** GNU Lesser General Public License Usage
 
17
** Alternatively, this file may be used under the terms of the GNU Lesser
 
18
** General Public License version 2.1 as published by the Free Software
 
19
** Foundation and appearing in the file LICENSE.LGPL included in the
 
20
** packaging of this file.  Please review the following information to
 
21
** ensure the GNU Lesser General Public License version 2.1 requirements
 
22
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 
23
**
 
24
** In addition, as a special exception, Digia gives you certain additional
 
25
** rights.  These rights are described in the Digia Qt LGPL Exception
 
26
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 
27
**
 
28
****************************************************************************/
 
29
 
 
30
#include "qmljsimportdependencies.h"
 
31
#include "qmljsinterpreter.h"
 
32
#include "qmljsqrcparser.h"
 
33
 
 
34
#include <utils/qtcassert.h>
 
35
#include <utils/function.h>
 
36
 
 
37
#include <QCryptographicHash>
 
38
 
 
39
#include <algorithm>
 
40
 
 
41
namespace QmlJS {
 
42
 
 
43
static bool debugImportDependencies = false;
 
44
 
 
45
ImportKind::Enum toImportKind(ImportType::Enum type)
 
46
{
 
47
    switch (type) {
 
48
    case ImportType::Invalid:
 
49
        break;
 
50
    case ImportType::Library:
 
51
        return ImportKind::Library;
 
52
    case ImportType::ImplicitDirectory:
 
53
    case ImportType::File:
 
54
    case ImportType::Directory:
 
55
    case ImportType::UnknownFile:
 
56
        return ImportKind::Path;
 
57
    case ImportType::QrcFile:
 
58
    case ImportType::QrcDirectory:
 
59
        return ImportKind::QrcPath;
 
60
    }
 
61
    return ImportKind::Invalid;
 
62
}
 
63
 
 
64
ImportMatchStrength::ImportMatchStrength(QList<int> match)
 
65
    : m_match(match)
 
66
{ }
 
67
 
 
68
int ImportMatchStrength::compareMatch(const ImportMatchStrength &o) const
 
69
{
 
70
    int len1 = m_match.size();
 
71
    int len2 = o.m_match.size();
 
72
    int len = ((len1 < len2) ? len1 : len2);
 
73
    for (int i = 0; i < len; ++ i) {
 
74
        int v1 = m_match.at(i);
 
75
        int v2 = o.m_match.at(i);
 
76
        if (v1 < v2)
 
77
            return -1;
 
78
        if (v2 > v1)
 
79
            return 1;
 
80
    }
 
81
    if (len1 < len2)
 
82
        return -1;
 
83
    if (len1 > len2)
 
84
        return 1;
 
85
    return 0;
 
86
}
 
87
 
 
88
bool ImportMatchStrength::hasNoMatch()
 
89
{
 
90
    return m_match.isEmpty();
 
91
}
 
92
 
 
93
bool ImportMatchStrength::hasMatch()
 
94
{
 
95
    return !m_match.isEmpty();
 
96
}
 
97
 
 
98
bool operator ==(const ImportMatchStrength &m1, const ImportMatchStrength &m2)
 
99
{
 
100
    return m1.m_match == m2.m_match;
 
101
}
 
102
 
 
103
bool operator !=(const ImportMatchStrength &m1, const ImportMatchStrength &m2)
 
104
{
 
105
    return !(m1 == m2);
 
106
}
 
107
 
 
108
bool operator <(const ImportMatchStrength &m1, const ImportMatchStrength &m2)
 
109
{
 
110
    return m1.compareMatch(m2) < 0;
 
111
}
 
112
 
 
113
ImportKey::ImportKey()
 
114
    : type(ImportType::Invalid),
 
115
      majorVersion(LanguageUtils::ComponentVersion::NoVersion),
 
116
      minorVersion(LanguageUtils::ComponentVersion::NoVersion)
 
117
{ }
 
118
 
 
119
ImportKey::ImportKey(const ImportInfo &info)
 
120
    : type(info.type())
 
121
    , majorVersion(info.version().majorVersion())
 
122
    , minorVersion(info.version().minorVersion())
 
123
{
 
124
    splitPath = QFileInfo(info.path()).canonicalFilePath().split(QLatin1Char('/'),
 
125
                                                                 QString::KeepEmptyParts);
 
126
}
 
127
 
 
128
ImportKey::ImportKey(ImportType::Enum type, const QString &path, int majorVersion, int minorVersion)
 
129
    : type(type)
 
130
    , majorVersion(majorVersion)
 
131
    , minorVersion(minorVersion)
 
132
{
 
133
    switch (type) {
 
134
    case ImportType::Library:
 
135
        splitPath = path.split(QLatin1Char('.'));
 
136
        break;
 
137
    case ImportType::ImplicitDirectory:
 
138
    case ImportType::Directory:
 
139
        splitPath = path.split(QLatin1Char('/'));
 
140
        if (splitPath.length() > 1 && splitPath.last().isEmpty())
 
141
            splitPath.removeLast();
 
142
        break;
 
143
    case ImportType::File:
 
144
    case ImportType::QrcFile:
 
145
        splitPath = QrcParser::normalizedQrcFilePath(path).split(QLatin1Char('/'));
 
146
        break;
 
147
    case ImportType::QrcDirectory:
 
148
        splitPath = QrcParser::normalizedQrcDirectoryPath(path).split(QLatin1Char('/'));
 
149
        if (splitPath.length() > 1 && splitPath.last().isEmpty())
 
150
            splitPath.removeLast();
 
151
        break;
 
152
    case ImportType::Invalid:
 
153
    case ImportType::UnknownFile:
 
154
        splitPath = path.split(QLatin1Char('/'));
 
155
        break;
 
156
    }
 
157
}
 
158
 
 
159
void ImportKey::addToHash(QCryptographicHash &hash) const
 
160
{
 
161
    hash.addData(reinterpret_cast<const char *>(&type), sizeof(type));
 
162
    hash.addData(reinterpret_cast<const char *>(&majorVersion), sizeof(majorVersion));
 
163
    hash.addData(reinterpret_cast<const char *>(&minorVersion), sizeof(minorVersion));
 
164
    foreach (const QString &s, splitPath) {
 
165
        hash.addData("/", 1);
 
166
        hash.addData(reinterpret_cast<const char *>(s.constData()), sizeof(QChar) * s.size());
 
167
    }
 
168
    hash.addData("/", 1);
 
169
}
 
170
 
 
171
ImportKey ImportKey::flatKey() const {
 
172
    switch (type) {
 
173
    case ImportType::Invalid:
 
174
        return *this;
 
175
    case ImportType::ImplicitDirectory:
 
176
    case ImportType::Library:
 
177
    case ImportType::File:
 
178
    case ImportType::Directory:
 
179
    case ImportType::QrcFile:
 
180
    case ImportType::QrcDirectory:
 
181
    case ImportType::UnknownFile:
 
182
        break;
 
183
    }
 
184
    QStringList flatPath = splitPath;
 
185
    int i = 0;
 
186
    while (i < flatPath.size()) {
 
187
        if (flatPath.at(i).startsWith(QLatin1Char('+')))
 
188
            flatPath.removeAt(i);
 
189
        else
 
190
            ++i;
 
191
    }
 
192
    if (flatPath.size() == splitPath.size())
 
193
        return *this;
 
194
    ImportKey res = *this;
 
195
    res.splitPath = flatPath;
 
196
    return res;
 
197
}
 
198
 
 
199
QString ImportKey::path() const
 
200
{
 
201
    QString res = splitPath.join(QString::fromLatin1("/"));
 
202
    if (res.isEmpty() && !splitPath.isEmpty())
 
203
        return QLatin1String("/");
 
204
    return res;
 
205
}
 
206
 
 
207
ImportMatchStrength ImportKey::matchImport(const ImportKey &o, const ViewerContext &vContext) const
 
208
{
 
209
    if (majorVersion != o.majorVersion || minorVersion > o.minorVersion)
 
210
        return ImportMatchStrength();
 
211
    bool dirToFile = false;
 
212
    switch (o.type) {
 
213
    case ImportType::Invalid:
 
214
        return ImportMatchStrength();
 
215
    case ImportType::ImplicitDirectory:
 
216
    case ImportType::Directory:
 
217
        switch (type) {
 
218
        case ImportType::File:
 
219
        case ImportType::UnknownFile:
 
220
            dirToFile = true;
 
221
            break;
 
222
        case ImportType::ImplicitDirectory:
 
223
        case ImportType::Directory:
 
224
            break;
 
225
        default:
 
226
            return ImportMatchStrength();
 
227
        }
 
228
        break;
 
229
    case ImportType::Library:
 
230
        if (type != ImportType::Library)
 
231
            return ImportMatchStrength();
 
232
        break;
 
233
    case ImportType::QrcDirectory:
 
234
        switch (type) {
 
235
        case ImportType::QrcFile:
 
236
            dirToFile = true;
 
237
            break;
 
238
        case ImportType::QrcDirectory:
 
239
            break;
 
240
        default:
 
241
            return ImportMatchStrength();
 
242
        }
 
243
        break;
 
244
    case ImportType::QrcFile:
 
245
        if (type != ImportType::QrcFile)
 
246
            return ImportMatchStrength();
 
247
    case ImportType::UnknownFile:
 
248
    case ImportType::File:
 
249
        switch (type) {
 
250
        case ImportType::UnknownFile:
 
251
        case ImportType::File:
 
252
            break;
 
253
        default:
 
254
            return ImportMatchStrength();
 
255
        }
 
256
        break;
 
257
    }
 
258
 
 
259
    QList<int> res;
 
260
    int iPath1 = 0;
 
261
    int lenPath1 = splitPath.size();
 
262
    int iPath2 = 0;
 
263
    int lenPath2 = o.splitPath.size();
 
264
    if (dirToFile)
 
265
        --lenPath1;
 
266
    int iSelector = 0;
 
267
    int nSelectors = vContext.selectors.size();
 
268
    while (iPath1 < lenPath1) {
 
269
        if (lenPath2 - iPath2 > lenPath1 - iPath1)
 
270
            return ImportMatchStrength();
 
271
        QString p1 = splitPath.at(iPath1);
 
272
        if (iPath2 < lenPath2) {
 
273
            QString p2 = splitPath.at(iPath2);
 
274
            if (p1 == p2) {
 
275
                ++iPath1;
 
276
                ++iPath2;
 
277
                continue;
 
278
            }
 
279
        }
 
280
        if (!p1.startsWith(QLatin1Char('+')))
 
281
            return QList<int>();
 
282
        QStringRef selectorAtt(&p1, 1, p1.size()-1);
 
283
        while (iSelector < nSelectors) {
 
284
            if (selectorAtt == vContext.selectors.at(iSelector))
 
285
                break;
 
286
            ++iSelector;
 
287
        }
 
288
        if (iSelector == nSelectors)
 
289
            return QList<int>();
 
290
        res << (nSelectors - iSelector);
 
291
        ++iSelector;
 
292
        ++iPath1;
 
293
    }
 
294
    if (iPath2 != lenPath2)
 
295
        return QList<int>();
 
296
    if (res.isEmpty())
 
297
        res << 0;
 
298
    return  ImportMatchStrength(res);
 
299
}
 
300
 
 
301
int ImportKey::compare(const ImportKey &other) const
 
302
{
 
303
    ImportKind::Enum k1 = toImportKind(type);
 
304
    ImportKind::Enum k2 = toImportKind(other.type);
 
305
    if (k1 < k2)
 
306
        return -1;
 
307
    if (k1 > k2)
 
308
        return 1;
 
309
    int len1 = splitPath.size();
 
310
    int len2 = other.splitPath.size();
 
311
    int len = ((len1 < len2) ? len1 : len2);
 
312
    for (int i = 0; i < len; ++ i) {
 
313
        QString v1 = splitPath.at(i);
 
314
        QString v2 = other.splitPath.at(i);
 
315
        if (v1 < v2)
 
316
            return -1;
 
317
        if (v1 > v2)
 
318
            return 1;
 
319
    }
 
320
    if (len1 < len2)
 
321
        return -1;
 
322
    if (len1 > len2)
 
323
        return 1;
 
324
    if (majorVersion < other.majorVersion)
 
325
        return -1;
 
326
    if (majorVersion > other.majorVersion)
 
327
        return 1;
 
328
    if (minorVersion < other.minorVersion)
 
329
        return -1;
 
330
    if (minorVersion > other.minorVersion)
 
331
        return 1;
 
332
    if (type < other.type)
 
333
        return -1;
 
334
    if (type > other.type)
 
335
        return 1;
 
336
    return 0;
 
337
}
 
338
 
 
339
bool ImportKey::isDirectoryLike() const
 
340
{
 
341
    switch (type) {
 
342
    case ImportType::Directory:
 
343
    case ImportType::ImplicitDirectory:
 
344
    case ImportType::QrcDirectory:
 
345
        return true;
 
346
    default:
 
347
        return false;
 
348
    }
 
349
}
 
350
 
 
351
ImportKey::DirCompareInfo ImportKey::compareDir(const ImportKey &superDir) const
 
352
{
 
353
    // assumes dir/+selectors/file (i.e. no directories inside selectors)
 
354
    switch (superDir.type) {
 
355
    case ImportType::UnknownFile:
 
356
    case ImportType::File:
 
357
    case ImportType::Directory:
 
358
    case ImportType::ImplicitDirectory:
 
359
        if (type != ImportType::File && type != ImportType::ImplicitDirectory
 
360
                && type != ImportType::Directory && type != ImportType::UnknownFile)
 
361
            return Incompatible;
 
362
        break;
 
363
    case ImportType::QrcDirectory:
 
364
    case ImportType::QrcFile:
 
365
        if (type != ImportType::QrcDirectory && type != ImportType::QrcFile)
 
366
            return Incompatible;
 
367
        break;
 
368
    case ImportType::Invalid:
 
369
    case ImportType::Library:
 
370
        return Incompatible;
 
371
    }
 
372
    bool isDir1 = isDirectoryLike();
 
373
    bool isDir2 = superDir.isDirectoryLike();
 
374
    int len1 = splitPath.size();
 
375
    int len2 = superDir.splitPath.size();
 
376
    if (isDir1 && len1 > 0)
 
377
        --len1;
 
378
    if (isDir2 && len2 > 0)
 
379
        --len2;
 
380
 
 
381
    int i1 = 0;
 
382
    int i2 = 0;
 
383
    while (i1 < len1 && i2 < len2) {
 
384
        QString p1 = splitPath.at(i1);
 
385
        QString p2 = superDir.splitPath.at(i2);
 
386
        if (p1 == p2) {
 
387
            ++i1;
 
388
            ++i2;
 
389
            continue;
 
390
        }
 
391
        if (p1.startsWith(QLatin1Char('+'))) {
 
392
            if (p2.startsWith(QLatin1Char('+')))
 
393
                return SameDir;
 
394
            return SecondInFirst;
 
395
        }
 
396
        if (p2.startsWith(QLatin1Char('+')))
 
397
            return FirstInSecond;
 
398
        return Different;
 
399
    }
 
400
    if (i1 < len1) {
 
401
        if (splitPath.at(i1).startsWith(QLatin1Char('+')))
 
402
            return SameDir;
 
403
        return SecondInFirst;
 
404
    }
 
405
    if (i2 < len2) {
 
406
        if (superDir.splitPath.at(i2).startsWith(QLatin1Char('+')))
 
407
            return SameDir;
 
408
        return SecondInFirst;
 
409
    }
 
410
    return SameDir;
 
411
}
 
412
 
 
413
QString ImportKey::toString() const
 
414
{
 
415
    QString res;
 
416
    switch (type) {
 
417
    case ImportType::UnknownFile:
 
418
    case ImportType::File:
 
419
        res = path();
 
420
        break;
 
421
    case ImportType::Directory:
 
422
    case ImportType::ImplicitDirectory:
 
423
        res = path() + QLatin1Char('/');
 
424
        break;
 
425
    case ImportType::QrcDirectory:
 
426
        res = QLatin1String("qrc:") + path() + QLatin1Char('/');
 
427
        break;
 
428
    case ImportType::QrcFile:
 
429
        res = QLatin1String("qrc:") + path() + QLatin1Char('/');
 
430
        break;
 
431
    case ImportType::Invalid:
 
432
        res = path();
 
433
        break;
 
434
    case ImportType::Library:
 
435
        res = splitPath.join(QLatin1String("."));
 
436
        break;
 
437
    }
 
438
 
 
439
    if (majorVersion != LanguageUtils::ComponentVersion::NoVersion
 
440
            || minorVersion != LanguageUtils::ComponentVersion::NoVersion)
 
441
        return res + QLatin1Char(' ') + QString::number(majorVersion)
 
442
                + QLatin1Char('.') + QString::number(minorVersion);
 
443
 
 
444
    return res;
 
445
}
 
446
 
 
447
uint qHash(const ImportKey &info)
 
448
{
 
449
    uint res = ::qHash(info.type) ^
 
450
            ::qHash(info.majorVersion) ^ ::qHash(info.minorVersion);
 
451
    foreach (const QString &s, info.splitPath)
 
452
        res = res ^ ::qHash(s);
 
453
    return res;
 
454
}
 
455
 
 
456
bool operator==(const ImportKey &i1, const ImportKey &i2)
 
457
{
 
458
    return i1.type == i2.type
 
459
            && i1.splitPath == i2.splitPath
 
460
            && i1.majorVersion == i2.majorVersion
 
461
            && i1.minorVersion == i2.minorVersion;
 
462
}
 
463
 
 
464
bool operator !=(const ImportKey &i1, const ImportKey &i2)
 
465
{
 
466
    return ! (i1 == i2);
 
467
}
 
468
 
 
469
bool operator <(const ImportKey &i1, const ImportKey &i2)
 
470
{
 
471
    return i1.compare(i2) < 0;
 
472
}
 
473
 
 
474
Export::Export()
 
475
    : intrinsic(false)
 
476
{ }
 
477
 
 
478
Export::Export(ImportKey exportName, QString pathRequired, bool intrinsic)
 
479
    : exportName(exportName), pathRequired(pathRequired), intrinsic(intrinsic)
 
480
{ }
 
481
 
 
482
bool Export::visibleInVContext(const ViewerContext &vContext) const
 
483
{
 
484
    return pathRequired.isEmpty() || vContext.paths.contains(pathRequired);
 
485
}
 
486
 
 
487
bool operator ==(const Export &i1, const Export &i2)
 
488
{
 
489
    return i1.exportName == i2.exportName
 
490
            && i1.pathRequired == i2.pathRequired
 
491
            && i1.intrinsic == i2.intrinsic;
 
492
}
 
493
 
 
494
bool operator !=(const Export &i1, const Export &i2)
 
495
{
 
496
    return !(i1 == i2);
 
497
}
 
498
 
 
499
CoreImport::CoreImport() : language(Language::Qml) { }
 
500
 
 
501
CoreImport::CoreImport(const QString &importId, const QList<Export> &possibleExports,
 
502
                       Language::Enum language, const QByteArray &fingerprint)
 
503
    : importId(importId), possibleExports(possibleExports), language(language),
 
504
      fingerprint(fingerprint)
 
505
{ }
 
506
 
 
507
bool CoreImport::valid() {
 
508
    return !fingerprint.isEmpty();
 
509
}
 
510
 
 
511
QByteArray DependencyInfo::calculateFingerprint(const ImportDependencies &deps)
 
512
{
 
513
    QCryptographicHash hash(QCryptographicHash::Sha1);
 
514
    rootImport.addToHash(hash);
 
515
    QStringList coreImports = allCoreImports.toList();
 
516
    coreImports.sort();
 
517
    foreach (const QString importId, coreImports) {
 
518
        hash.addData(reinterpret_cast<const char*>(importId.constData()), importId.size() * sizeof(QChar));
 
519
        QByteArray coreImportFingerprint = deps.coreImport(importId).fingerprint;
 
520
        hash.addData(coreImportFingerprint);
 
521
    }
 
522
    hash.addData("/", 1);
 
523
    QList<ImportKey> imports(allImports.toList());
 
524
    std::sort(imports.begin(), imports.end());
 
525
    foreach (const ImportKey &k, imports)
 
526
        k.addToHash(hash);
 
527
    return hash.result();
 
528
}
 
529
 
 
530
MatchedImport::MatchedImport()
 
531
{ }
 
532
 
 
533
MatchedImport::MatchedImport(ImportMatchStrength matchStrength, ImportKey importKey,
 
534
                             const QString &coreImportId)
 
535
    : matchStrength(matchStrength), importKey(importKey), coreImportId(coreImportId)
 
536
{ }
 
537
 
 
538
int MatchedImport::compare(const MatchedImport &o) const {
 
539
    int res = matchStrength.compareMatch(o.matchStrength);
 
540
    if (res != 0)
 
541
        return res;
 
542
    res = importKey.compare(o.importKey);
 
543
    if (res != 0)
 
544
        return res;
 
545
    if (coreImportId < o.coreImportId)
 
546
        return -1;
 
547
    if (coreImportId > o.coreImportId)
 
548
        return 1;
 
549
    return 0;
 
550
}
 
551
 
 
552
bool operator ==(const MatchedImport &m1, const MatchedImport &m2)
 
553
{
 
554
    return m1.compare(m2) == 0;
 
555
}
 
556
 
 
557
bool operator !=(const MatchedImport &m1, const MatchedImport &m2)
 
558
{
 
559
    return m1.compare(m2) != 0;
 
560
}
 
561
 
 
562
bool operator <(const MatchedImport &m1, const MatchedImport &m2)
 
563
{
 
564
    return m1.compare(m2) < 0;
 
565
}
 
566
 
 
567
ImportDependencies::ImportDependencies()
 
568
{ }
 
569
 
 
570
ImportDependencies::~ImportDependencies()
 
571
{ }
 
572
 
 
573
void ImportDependencies::filter(const ViewerContext &vContext)
 
574
{
 
575
    QMap<QString, CoreImport> newCoreImports;
 
576
    QMap<ImportKey, QStringList> newImportCache;
 
577
    QMapIterator<QString, CoreImport> j(m_coreImports);
 
578
    bool hasChanges = false;
 
579
    while (j.hasNext()) {
 
580
        j.next();
 
581
        const CoreImport &cImport = j.value();
 
582
        if (vContext.languageIsCompatible(cImport.language)) {
 
583
            QList<Export> newExports;
 
584
            foreach (const Export &e, cImport.possibleExports) {
 
585
                if (e.visibleInVContext(vContext)) {
 
586
                    newExports.append(e);
 
587
                    QStringList &candidateImports = newImportCache[e.exportName];
 
588
                    if (!candidateImports.contains(cImport.importId))
 
589
                        candidateImports.append(cImport.importId);
 
590
                }
 
591
            }
 
592
            if (newExports.size() == cImport.possibleExports.size()) {
 
593
                newCoreImports.insert(cImport.importId, cImport);
 
594
            } else if (newExports.length() > 0) {
 
595
                CoreImport newCImport = cImport;
 
596
                newCImport.possibleExports = newExports;
 
597
                newCoreImports.insert(newCImport.importId, newCImport);
 
598
                hasChanges = true;
 
599
            } else {
 
600
                hasChanges = true;
 
601
            }
 
602
        } else {
 
603
            hasChanges = true;
 
604
        }
 
605
    }
 
606
    if (!hasChanges)
 
607
        return;
 
608
    m_coreImports = newCoreImports;
 
609
    m_importCache = newImportCache;
 
610
}
 
611
 
 
612
CoreImport ImportDependencies::coreImport(const QString &importId) const
 
613
{
 
614
    return m_coreImports.value(importId);
 
615
}
 
616
 
 
617
void ImportDependencies::iterateOnCandidateImports(
 
618
        const ImportKey &key, const ViewerContext &vContext,
 
619
        Utils::function<bool (const ImportMatchStrength &,const Export &,const CoreImport &)>
 
620
        const &iterF) const
 
621
{
 
622
    switch (key.type) {
 
623
    case ImportType::Directory:
 
624
    case ImportType::QrcDirectory:
 
625
    case ImportType::ImplicitDirectory:
 
626
        break;
 
627
    default:
 
628
    {
 
629
        const QStringList imp = m_importCache.value(key.flatKey());
 
630
        foreach (const QString &cImportName, imp) {
 
631
            CoreImport cImport = coreImport(cImportName);
 
632
            if (vContext.languageIsCompatible(cImport.language)) {
 
633
                foreach (const Export e, cImport.possibleExports) {
 
634
                    if (e.visibleInVContext(vContext)) {
 
635
                        ImportMatchStrength m = e.exportName.matchImport(key, vContext);
 
636
                        if (m.hasMatch()) {
 
637
                            if (!iterF(m, e, cImport))
 
638
                                return;
 
639
                        }
 
640
                    }
 
641
                }
 
642
            }
 
643
        }
 
644
        return;
 
645
    }
 
646
    }
 
647
    QMap<ImportKey, QStringList>::const_iterator lb = m_importCache.lowerBound(key.flatKey());
 
648
    QMap<ImportKey, QStringList>::const_iterator end = m_importCache.constEnd();
 
649
    while (lb != end) {
 
650
        ImportKey::DirCompareInfo c = key.compareDir(lb.key());
 
651
        if (c == ImportKey::SameDir) {
 
652
            foreach (const QString &cImportName, lb.value()) {
 
653
                CoreImport cImport = coreImport(cImportName);
 
654
                if (vContext.languageIsCompatible(cImport.language)) {
 
655
                    foreach (const Export e, cImport.possibleExports) {
 
656
                        if (e.visibleInVContext(vContext)) {
 
657
                            ImportMatchStrength m = e.exportName.matchImport(key, vContext);
 
658
                            if (m.hasMatch()) {
 
659
                                if (!iterF(m, e, cImport))
 
660
                                    return;
 
661
                            }
 
662
                        }
 
663
                    }
 
664
                }
 
665
            }
 
666
        } else if (c != ImportKey::SecondInFirst) {
 
667
            break;
 
668
        }
 
669
        ++lb;
 
670
    }
 
671
}
 
672
 
 
673
class CollectCandidateImports
 
674
{
 
675
public:
 
676
    ImportDependencies::ImportElements &res;
 
677
 
 
678
    CollectCandidateImports(ImportDependencies::ImportElements & res)
 
679
        : res(res)
 
680
    { }
 
681
 
 
682
    bool operator ()(const ImportMatchStrength &m, const Export &e, const CoreImport &cI) const
 
683
    {
 
684
        ImportKey flatName = e.exportName.flatKey();
 
685
        res[flatName].append(MatchedImport(m, e.exportName, cI.importId));
 
686
        return true;
 
687
    }
 
688
};
 
689
 
 
690
ImportDependencies::ImportElements ImportDependencies::candidateImports(
 
691
        const ImportKey &key,
 
692
        const ViewerContext &vContext) const
 
693
{
 
694
    ImportDependencies::ImportElements res;
 
695
    CollectCandidateImports collector(res);
 
696
    iterateOnCandidateImports(key, vContext, collector);
 
697
    typedef QMap<ImportKey, QList<MatchedImport> >::iterator iter_t;
 
698
    iter_t i = res.begin();
 
699
    iter_t end = res.end();
 
700
    while (i != end) {
 
701
        std::sort(i.value().begin(), i.value().end());
 
702
        ++i;
 
703
    }
 
704
    return res;
 
705
}
 
706
 
 
707
QList<DependencyInfo::ConstPtr> ImportDependencies::createDependencyInfos(
 
708
        const ImportKey &mainDoc, const ViewerContext &vContext) const
 
709
{
 
710
    Q_UNUSED(mainDoc);
 
711
    Q_UNUSED(vContext);
 
712
    QList<DependencyInfo::ConstPtr> res;
 
713
    QTC_CHECK(false);
 
714
    return res;
 
715
}
 
716
 
 
717
void ImportDependencies::addCoreImport(const CoreImport &import)
 
718
{
 
719
    CoreImport newImport = import;
 
720
    if (m_coreImports.contains(import.importId)) {
 
721
        CoreImport oldVal = m_coreImports.value(import.importId);
 
722
        foreach (const Export &e, oldVal.possibleExports) {
 
723
            if (e.intrinsic)
 
724
                removeImportCacheEntry(e.exportName, import.importId);
 
725
            else
 
726
                newImport.possibleExports.append(e);
 
727
        }
 
728
    }
 
729
    foreach (const Export &e, import.possibleExports)
 
730
        m_importCache[e.exportName].append(import.importId);
 
731
    m_coreImports.insert(newImport.importId, newImport);
 
732
    if (debugImportDependencies) {
 
733
        QDebug dbg(qDebug());
 
734
        dbg << "added import "<< newImport.importId << " for";
 
735
        foreach (const Export &e, newImport.possibleExports)
 
736
            dbg << " " << e.exportName.toString() << "(" << e.pathRequired << ")";
 
737
    }
 
738
}
 
739
 
 
740
void ImportDependencies::removeCoreImport(const QString &importId)
 
741
{
 
742
    if (!m_coreImports.contains(importId)) {
 
743
        qDebug() << "missing importId in removeCoreImport(" << importId << ")";
 
744
        return;
 
745
    }
 
746
    CoreImport &cImport = m_coreImports[importId];
 
747
    QList<Export> newExports;
 
748
    foreach (const Export &e, cImport.possibleExports)
 
749
        if (e.intrinsic)
 
750
            removeImportCacheEntry(e.exportName, importId);
 
751
        else
 
752
            newExports.append(e);
 
753
    if (newExports.size()>0)
 
754
        cImport.possibleExports = newExports;
 
755
    else
 
756
        m_coreImports.remove(importId);
 
757
 
 
758
    if (debugImportDependencies)
 
759
        qDebug() << "removed import with id:"<< importId;
 
760
}
 
761
 
 
762
void ImportDependencies::removeImportCacheEntry(const ImportKey &importKey, const QString &importId)
 
763
{
 
764
    QStringList &cImp = m_importCache[importKey];
 
765
    if (!cImp.removeOne(importId)) {
 
766
        qDebug() << "missing possibleExport backpointer for " << importKey.toString() << " to "
 
767
                 << importId;
 
768
    }
 
769
    if (cImp.isEmpty())
 
770
        m_importCache.remove(importKey);
 
771
}
 
772
 
 
773
void ImportDependencies::addExport(const QString &importId, const ImportKey &importKey,
 
774
                                   const QString &requiredPath)
 
775
{
 
776
    if (!m_coreImports.contains(importId)) {
 
777
        CoreImport newImport(importId);
 
778
        newImport.language = Language::Unknown;
 
779
        newImport.possibleExports.append(Export(importKey, requiredPath, false));
 
780
        m_coreImports.insert(newImport.importId, newImport);
 
781
        m_importCache[importKey].append(importId);
 
782
        return;
 
783
    }
 
784
    CoreImport &importValue = m_coreImports[importId];
 
785
    importValue.possibleExports.append(Export(importKey, requiredPath, false));
 
786
    m_importCache[importKey].append(importId);
 
787
    if (debugImportDependencies)
 
788
        qDebug() << "added export "<< importKey.toString() << " for id " <<importId
 
789
                 << " (" << requiredPath << ")";
 
790
}
 
791
 
 
792
void ImportDependencies::removeExport(const QString &importId, const ImportKey &importKey,
 
793
                                      const QString &requiredPath)
 
794
{
 
795
    if (!m_coreImports.contains(importId)) {
 
796
        qDebug() << "non existing core import for removeExport(" << importId << ", "
 
797
                 << importKey.toString() << ")";
 
798
    } else {
 
799
        CoreImport &importValue = m_coreImports[importId];
 
800
        if (!importValue.possibleExports.removeOne(Export(importKey, requiredPath, false))) {
 
801
            qDebug() << "non existing export for removeExport(" << importId << ", "
 
802
                     << importKey.toString() << ")";
 
803
        }
 
804
        if (importValue.possibleExports.isEmpty() && importValue.fingerprint.isEmpty())
 
805
            m_coreImports.remove(importId);
 
806
    }
 
807
    if (!m_importCache.contains(importKey)) {
 
808
        qDebug() << "missing possibleExport for " << importKey.toString() << " when removing export of "
 
809
                 << importId;
 
810
    } else {
 
811
        removeImportCacheEntry(importKey, importId);
 
812
    }
 
813
    if (debugImportDependencies)
 
814
        qDebug() << "removed export "<< importKey.toString() << " for id " << importId
 
815
                 << " (" << requiredPath << ")";
 
816
}
 
817
 
 
818
void ImportDependencies::iterateOnCoreImports(
 
819
        const ViewerContext &vContext,
 
820
        Utils::function<bool (const CoreImport &)> const &iterF) const
 
821
{
 
822
    QMapIterator<QString, CoreImport> i(m_coreImports);
 
823
    while (i.hasNext()) {
 
824
        i.next();
 
825
        if (vContext.languageIsCompatible(i.value().language))
 
826
            iterF(i.value()); // check also that at least one export is visible?
 
827
    }
 
828
}
 
829
 
 
830
void ImportDependencies::iterateOnLibraryImports(
 
831
        const ViewerContext &vContext,
 
832
        Utils::function<bool (const ImportMatchStrength &,
 
833
                              const Export &,
 
834
                              const CoreImport &)> const &iterF) const
 
835
{
 
836
    typedef QMap<ImportKey, QStringList>::const_iterator iter_t;
 
837
    ImportKey firstLib;
 
838
    firstLib.type = ImportType::Library;
 
839
    iter_t i = m_importCache.lowerBound(firstLib);
 
840
    iter_t end = m_importCache.constEnd();
 
841
    while (i != end && i.key().type == ImportType::Library) {
 
842
        if (debugImportDependencies)
 
843
            qDebug() << "libloop:" << i.key().toString() << i.value();
 
844
        foreach (const QString &cImportName, i.value()) {
 
845
            CoreImport cImport = coreImport(cImportName);
 
846
            if (vContext.languageIsCompatible(cImport.language)) {
 
847
                foreach (const Export &e, cImport.possibleExports) {
 
848
                    if (e.visibleInVContext(vContext) && e.exportName.type == ImportType::Library) {
 
849
                        ImportMatchStrength m = e.exportName.matchImport(i.key(), vContext);
 
850
                        if (m.hasMatch()) {
 
851
                            if (debugImportDependencies)
 
852
                                qDebug() << "import iterate:" << e.exportName.toString()
 
853
                                         << " (" << e.pathRequired << "), id:" << cImport.importId;
 
854
                            if (!iterF(m, e, cImport))
 
855
                                return;
 
856
                        }
 
857
                    }
 
858
                }
 
859
            }
 
860
        }
 
861
        ++i;
 
862
    }
 
863
}
 
864
 
 
865
void ImportDependencies::iterateOnSubImports(
 
866
        const ImportKey &baseKey,
 
867
        const ViewerContext &vContext,
 
868
        Utils::function<bool (const ImportMatchStrength &,
 
869
                              const Export &,
 
870
                              const CoreImport &)> const &iterF) const
 
871
{
 
872
    typedef QMap<ImportKey, QStringList>::const_iterator iter_t;
 
873
    iter_t i = m_importCache.lowerBound(baseKey);
 
874
    iter_t end = m_importCache.constEnd();
 
875
    while (i != end) {
 
876
        ImportKey::DirCompareInfo c = baseKey.compareDir(i.key());
 
877
        if (c != ImportKey::SameDir && c != ImportKey::SecondInFirst)
 
878
            break;
 
879
        foreach (const QString &cImportName, i.value()) {
 
880
            CoreImport cImport = coreImport(cImportName);
 
881
            if (vContext.languageIsCompatible(cImport.language)) {
 
882
                foreach (const Export &e, cImport.possibleExports) {
 
883
                    if (e.visibleInVContext(vContext)) {
 
884
                        ImportMatchStrength m = e.exportName.matchImport(i.key(), vContext);
 
885
                        if (m.hasMatch()) {
 
886
                            if (!iterF(m, e, cImport))
 
887
                                return;
 
888
                        }
 
889
                    }
 
890
                }
 
891
            }
 
892
        }
 
893
        ++i;
 
894
    }
 
895
}
 
896
 
 
897
class CollectImportKeys {
 
898
public:
 
899
    QSet<ImportKey> &imports;
 
900
    CollectImportKeys(QSet<ImportKey> &imports)
 
901
        : imports(imports)
 
902
    { }
 
903
    bool operator()(const ImportMatchStrength &m,
 
904
                    const Export &e,
 
905
                    const CoreImport &cI) const
 
906
    {
 
907
        Q_UNUSED(m);
 
908
        Q_UNUSED(cI);
 
909
        imports.insert(e.exportName.flatKey());
 
910
        return true;
 
911
    }
 
912
};
 
913
 
 
914
QSet<ImportKey> ImportDependencies::libraryImports(const ViewerContext &viewContext) const
 
915
{
 
916
    QSet<ImportKey> res;
 
917
    CollectImportKeys importCollector(res);
 
918
    iterateOnLibraryImports(viewContext, importCollector);
 
919
    return res;
 
920
}
 
921
 
 
922
QSet<ImportKey> ImportDependencies::subdirImports(
 
923
        const ImportKey &baseKey, const ViewerContext &viewContext) const
 
924
{
 
925
    QSet<ImportKey> res;
 
926
    CollectImportKeys importCollector(res);
 
927
    iterateOnSubImports(baseKey, viewContext, importCollector);
 
928
    return res;
 
929
}
 
930
 
 
931
void ImportDependencies::checkConsistency() const
 
932
{
 
933
    QMapIterator<ImportKey, QStringList> j(m_importCache);
 
934
    while (j.hasNext()) {
 
935
        j.next();
 
936
        foreach (const QString &s, j.value()) {
 
937
            bool found = false;
 
938
            foreach (const Export &e, m_coreImports.value(s).possibleExports)
 
939
                if (e.exportName == j.key())
 
940
                    found = true;
 
941
            Q_ASSERT(found);
 
942
        }
 
943
    }
 
944
    QMapIterator<QString,CoreImport> i(m_coreImports);
 
945
    while (i.hasNext()) {
 
946
        i.next();
 
947
        foreach (const Export &e, i.value().possibleExports) {
 
948
            if (!m_importCache.value(e.exportName).contains(i.key())) {
 
949
                qDebug() << e.exportName.toString();
 
950
                qDebug() << i.key();
 
951
 
 
952
                QMapIterator<ImportKey, QStringList> j(m_importCache);
 
953
                while (j.hasNext()) {
 
954
                    j.next();
 
955
                    qDebug() << j.key().toString() << j.value();
 
956
                }
 
957
                qDebug() << m_importCache.contains(e.exportName);
 
958
                qDebug() << m_importCache.value(e.exportName);
 
959
            }
 
960
            Q_ASSERT(m_importCache.value(e.exportName).contains(i.key()));
 
961
        }
 
962
    }
 
963
}
 
964
 
 
965
} // namespace QmlJS