1
/*
2
 * This file is part of the API Extractor project.
3
 *
4
 * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
5
 *
6
 * Contact: PySide team <contact@pyside.org>
7
 *
8
 * This program is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU General Public License
10
 * version 2 as published by the Free Software Foundation.
11
 *
12
 * This program is distributed in the hope that it will be useful, but
13
 * WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20
 * 02110-1301 USA
21
 *
22
 */
23
24
#include "typedatabase.h"
25
#include "typesystem.h"
26
#include "typesystem_p.h"
27
28
#include <QFile>
29
#include <QXmlInputSource>
30
#include "reporthandler.h"
31
// #include <tr1/tuple>
32
#include <algorithm>
33
34
// package -> api-version
35
typedef QMap<QString, QByteArray> ApiVersionMap;
36
37
Q_GLOBAL_STATIC(ApiVersionMap, apiVersions)
38
39
TypeDatabase::TypeDatabase() : m_suppressWarnings(true), m_apiVersion(0)
40
{
41
    addType(new VoidTypeEntry());
42
    addType(new VarargsTypeEntry());
43
}
44
45
TypeDatabase* TypeDatabase::instance(bool newInstance)
46
{
47
    static TypeDatabase* db = 0;
48
    if (!db || newInstance) {
49
        if (db)
50
            delete db;
51
        db = new TypeDatabase;
52
    }
53
    return db;
54
}
55
56
QString TypeDatabase::normalizedSignature(const char* signature)
57
{
58
    QString normalized = QMetaObject::normalizedSignature(signature);
59
60
    if (!instance() || !QString(signature).contains("unsigned"))
61
        return normalized;
62
63
    QStringList types;
64
    types << "char" << "short" << "int" << "long";
65
    foreach (const QString& type, types) {
66
        if (instance()->findType(QString("u%1").arg(type)))
67
            continue;
68
        normalized.replace(QRegExp(QString("\\bu%1\\b").arg(type)), QString("unsigned %1").arg(type));
69
    }
70
71
    return normalized;
72
}
73
74
QStringList TypeDatabase::requiredTargetImports() const
75
{
76
    return m_requiredTargetImports;
77
}
78
79
void TypeDatabase::addRequiredTargetImport(const QString& moduleName)
80
{
81
    if (!m_requiredTargetImports.contains(moduleName))
82
        m_requiredTargetImports << moduleName;
83
}
84
85
void TypeDatabase::addTypesystemPath(const QString& typesystem_paths)
86
{
87
    #if defined(Q_OS_WIN32)
88
    char* path_splitter = const_cast<char*>(";");
89
    #else
90
    char* path_splitter = const_cast<char*>(":");
91
    #endif
92
    m_typesystemPaths += typesystem_paths.split(path_splitter);
93
}
94
95
IncludeList TypeDatabase::extraIncludes(const QString& className) const
96
{
97
    ComplexTypeEntry* typeEntry = findComplexType(className);
98
    if (typeEntry)
99
        return typeEntry->extraIncludes();
100
    else
101
        return IncludeList();
102
}
103
104
ContainerTypeEntry* TypeDatabase::findContainerType(const QString &name) const
105
{
106
    QString template_name = name;
107
108
    int pos = name.indexOf('<');
109
    if (pos > 0)
110
        template_name = name.left(pos);
111
112
    TypeEntry* type_entry = findType(template_name);
113
    if (type_entry && type_entry->isContainer())
114
        return static_cast<ContainerTypeEntry*>(type_entry);
115
    return 0;
116
}
117
118
FunctionTypeEntry* TypeDatabase::findFunctionType(const QString& name) const
119
{
120
    TypeEntry* entry = findType(name);
121
    if (entry && entry->type() == TypeEntry::FunctionType)
122
        return static_cast<FunctionTypeEntry*>(entry);
123
    return 0;
124
}
125
126
127
PrimitiveTypeEntry* TypeDatabase::findTargetLangPrimitiveType(const QString& targetLangName) const
128
{
129
    foreach (QList<TypeEntry*> entries, m_entries.values()) {
130
        foreach (TypeEntry* e, entries) {
131
            if (e && e->isPrimitive()) {
132
                PrimitiveTypeEntry *pe = static_cast<PrimitiveTypeEntry*>(e);
133
                if (pe->targetLangName() == targetLangName && pe->preferredConversion())
134
                    return pe;
135
            }
136
        }
137
    }
138
139
    return 0;
140
}
141
142
TypeEntry* TypeDatabase::findType(const QString& name) const
143
{
144
    QList<TypeEntry *> entries = findTypes(name);
145
    foreach (TypeEntry *entry, entries) {
146
        if (entry &&
147
            (!entry->isPrimitive() || static_cast<PrimitiveTypeEntry *>(entry)->preferredTargetLangType())) {
148
            return entry;
149
        }
150
    }
151
    return 0;
152
}
153
154
SingleTypeEntryHash TypeDatabase::entries() const
155
{
156
    TypeEntryHash entries = allEntries();
157
158
    SingleTypeEntryHash returned;
159
    QList<QString> keys = entries.keys();
160
161
    foreach (QString key, keys)
162
        returned[key] = findType(key);
163
164
    return returned;
165
}
166
167
QList<const PrimitiveTypeEntry*> TypeDatabase::primitiveTypes() const
168
{
169
    TypeEntryHash entries = allEntries();
170
    QList<const PrimitiveTypeEntry*> returned;
171
    foreach(QString key, entries.keys()) {
172
        foreach(const TypeEntry* typeEntry, entries[key]) {
173
            if (typeEntry->isPrimitive())
174
                returned.append((PrimitiveTypeEntry*) typeEntry);
175
        }
176
    }
177
    return returned;
178
}
179
180
QList<const ContainerTypeEntry*> TypeDatabase::containerTypes() const
181
{
182
    TypeEntryHash entries = allEntries();
183
    QList<const ContainerTypeEntry*> returned;
184
    foreach(QString key, entries.keys()) {
185
        foreach(const TypeEntry* typeEntry, entries[key]) {
186
            if (typeEntry->isContainer())
187
                returned.append((ContainerTypeEntry*) typeEntry);
188
        }
189
    }
190
    return returned;
191
}
192
void TypeDatabase::addRejection(const QString& className, const QString& functionName,
193
                                const QString& fieldName, const QString& enumName)
194
{
195
    TypeRejection r;
196
    r.class_name = className;
197
    r.function_name = functionName;
198
    r.field_name = fieldName;
199
    r.enum_name = enumName;
200
201
    m_rejections << r;
202
}
203
204
bool TypeDatabase::isClassRejected(const QString& className) const
205
{
206
    if (!m_rebuildClasses.isEmpty())
207
        return !m_rebuildClasses.contains(className);
208
209
    foreach (const TypeRejection& r, m_rejections)
210
    if (r.class_name == className && r.function_name == "*" && r.field_name == "*" && r.enum_name == "*")
211
        return true;
212
213
    return false;
214
}
215
216
bool TypeDatabase::isEnumRejected(const QString& className, const QString& enumName) const
217
{
218
    foreach (const TypeRejection& r, m_rejections) {
219
        if (r.enum_name == enumName
220
            && (r.class_name == className || r.class_name == "*")) {
221
            return true;
222
        }
223
    }
224
225
    return false;
226
}
227
228
bool TypeDatabase::isFunctionRejected(const QString& className, const QString& functionName) const
229
{
230
    foreach (const TypeRejection& r, m_rejections)
231
    if (r.function_name == functionName &&
232
        (r.class_name == className || r.class_name == "*"))
233
        return true;
234
    return false;
235
}
236
237
238
bool TypeDatabase::isFieldRejected(const QString& className, const QString& fieldName) const
239
{
240
    foreach (const TypeRejection& r, m_rejections)
241
    if (r.field_name == fieldName &&
242
        (r.class_name == className || r.class_name == "*"))
243
        return true;
244
    return false;
245
}
246
247
FlagsTypeEntry* TypeDatabase::findFlagsType(const QString &name) const
248
{
249
    FlagsTypeEntry* fte = (FlagsTypeEntry*) findType(name);
250
    if (!fte) {
251
        fte = (FlagsTypeEntry*) m_flagsEntries.value(name);
252
        if (!fte) {
253
            //last hope, search for flag without scope  inside of flags hash
254
            foreach(QString key, m_flagsEntries.keys()) {
255
                if (key.endsWith(name)) {
256
                    fte = (FlagsTypeEntry*) m_flagsEntries.value(key);
257
                    break;
258
                }
259
            }
260
        }
261
    }
262
    return fte;
263
}
264
265
AddedFunctionList TypeDatabase::findGlobalUserFunctions(const QString& name) const
266
{
267
    AddedFunctionList addedFunctions;
268
    foreach (AddedFunction func, m_globalUserFunctions) {
269
        if (func.name() == name)
270
            addedFunctions.append(func);
271
    }
272
    return addedFunctions;
273
}
274
275
276
QString TypeDatabase::globalNamespaceClassName(const TypeEntry * /*entry*/)
277
{
278
    return QLatin1String("Global");
279
}
280
281
FunctionModificationList TypeDatabase::functionModifications(const QString& signature) const
282
{
283
    FunctionModificationList lst;
284
    for (int i = 0; i < m_functionMods.count(); ++i) {
285
        const FunctionModification& mod = m_functionMods.at(i);
286
        if (mod.signature == signature)
287
            lst << mod;
288
    }
289
290
    return lst;
291
}
292
293
bool TypeDatabase::isSuppressedWarning(const QString& s) const
294
{
295
    if (!m_suppressWarnings)
296
        return false;
297
298
    foreach (const QString &_warning, m_suppressedWarnings) {
299
        QString warning(QString(_warning).replace("\\*", "&place_holder_for_asterisk;"));
300
301
        QStringList segs = warning.split("*", QString::SkipEmptyParts);
302
        if (!segs.size())
303
            continue;
304
305
        int i = 0;
306
        int pos = s.indexOf(QString(segs.at(i++)).replace("&place_holder_for_asterisk;", "*"));
307
        //qDebug() << "s == " << s << ", warning == " << segs;
308
        while (pos != -1) {
309
            if (i == segs.size())
310
                return true;
311
            pos = s.indexOf(QString(segs.at(i++)).replace("&place_holder_for_asterisk;", "*"), pos);
312
        }
313
    }
314
315
    return false;
316
}
317
318
QString TypeDatabase::modifiedTypesystemFilepath(const QString& tsFile) const
319
{
320
    if (!QFile::exists(tsFile)) {
321
        int idx = tsFile.lastIndexOf('/');
322
        QString fileName = idx >= 0 ? tsFile.right(tsFile.length() - idx - 1) : tsFile;
323
        foreach (const QString &path, m_typesystemPaths) {
324
            QString filepath(path + '/' + fileName);
325
            if (QFile::exists(filepath))
326
                return filepath;
327
        }
328
    }
329
    return tsFile;
330
}
331
332
bool TypeDatabase::parseFile(const QString &filename, bool generate)
333
{
334
    QString filepath = modifiedTypesystemFilepath(filename);
335
    if (m_parsedTypesystemFiles.contains(filepath))
336
        return m_parsedTypesystemFiles[filepath];
337
338
    QFile file(filepath);
339
    if (!file.exists()) {
340
        ReportHandler::warning("Can't find " + filename+", typesystem paths: "+m_typesystemPaths.join(", "));
341
        return false;
342
    }
343
344
    int count = m_entries.size();
345
    bool ok = parseFile(&file, generate);
346
    m_parsedTypesystemFiles[filepath] = ok;
347
    int newCount = m_entries.size();
348
349
    ReportHandler::debugSparse(QString::fromLatin1("Parsed: '%1', %2 new entries")
350
    .arg(filename)
351
    .arg(newCount - count));
352
    return ok;
353
}
354
355
bool TypeDatabase::parseFile(QIODevice* device, bool generate)
356
{
357
    if (m_apiVersion) // backwards compatibility with deprecated API
358
        setApiVersion("*", QByteArray::number(m_apiVersion));
359
360
    QXmlInputSource source(device);
361
    QXmlSimpleReader reader;
362
    Handler handler(this, generate);
363
364
    reader.setContentHandler(&handler);
365
    reader.setErrorHandler(&handler);
366
367
    return reader.parse(&source, false);
368
}
369
370
PrimitiveTypeEntry *TypeDatabase::findPrimitiveType(const QString& name) const
371
{
372
    QList<TypeEntry*> entries = findTypes(name);
373
374
    foreach (TypeEntry* entry, entries) {
375
        if (entry && entry->isPrimitive() && static_cast<PrimitiveTypeEntry*>(entry)->preferredTargetLangType())
376
            return static_cast<PrimitiveTypeEntry*>(entry);
377
    }
378
379
    return 0;
380
}
381
382
ComplexTypeEntry* TypeDatabase::findComplexType(const QString& name) const
383
{
384
    QList<TypeEntry*> entries = findTypes(name);
385
    foreach (TypeEntry* entry, entries) {
386
        if (entry && entry->isComplex())
387
            return static_cast<ComplexTypeEntry*>(entry);
388
    }
389
    return 0;
390
}
391
392
ObjectTypeEntry* TypeDatabase::findObjectType(const QString& name) const
393
{
394
    QList<TypeEntry*> entries = findTypes(name);
395
    foreach (TypeEntry* entry, entries) {
396
        if (entry && entry->isObject())
397
            return static_cast<ObjectTypeEntry*>(entry);
398
    }
399
    return 0;
400
}
401
402
NamespaceTypeEntry* TypeDatabase::findNamespaceType(const QString& name) const
403
{
404
    QList<TypeEntry*> entries = findTypes(name);
405
    foreach (TypeEntry* entry, entries) {
406
        if (entry && entry->isNamespace())
407
            return static_cast<NamespaceTypeEntry*>(entry);
408
    }
409
    return 0;
410
}
411
412
bool TypeDatabase::supportedApiVersion(double version) const
413
{
414
    return version <= m_apiVersion;
415
}
416
417
bool TypeDatabase::shouldDropTypeEntry(const QString& fullTypeName) const
418
{
419
    return m_dropTypeEntries.contains(fullTypeName);
420
}
421
422
void TypeDatabase::setDropTypeEntries(QStringList dropTypeEntries)
423
{
424
    m_dropTypeEntries = dropTypeEntries;
425
    m_dropTypeEntries.sort();
426
}
427
428
// Using std::pair to save some memory
429
// the pair means (revision, typeIndex)
430
// This global variable exists only because we can't break the ABI
431
typedef QHash<const TypeEntry*, std::pair<int, int> > TypeRevisionMap;
432
Q_GLOBAL_STATIC(TypeRevisionMap, typeEntryFields);
433
static bool computeTypeIndexes = true;
434
static int maxTypeIndex;
435
436
int getTypeRevision(const TypeEntry* typeEntry)
437
{
438
    return typeEntryFields()->value(typeEntry).first;
439
}
440
441
void setTypeRevision(TypeEntry* typeEntry, int revision)
442
{
443
    (*typeEntryFields())[typeEntry].first = revision;
444
    computeTypeIndexes = true;
445
}
446
447
static bool compareTypeEntriesByName(const TypeEntry* t1, const TypeEntry* t2)
448
{
449
    return t1->qualifiedCppName() < t2->qualifiedCppName();
450
}
451
452
static void _computeTypeIndexes()
453
{
454
    TypeDatabase* tdb = TypeDatabase::instance();
455
    typedef QMap<int, QList<TypeEntry*> > GroupedTypeEntries;
456
    GroupedTypeEntries groupedEntries;
457
458
    // Group type entries by revision numbers
459
    TypeEntryHash allEntries = tdb->allEntries();
460
    foreach (QList<TypeEntry*> entryList, allEntries) {
461
        foreach (TypeEntry* entry, entryList) {
462
            if (entry->isPrimitive()
463
                || entry->isContainer()
464
                || entry->isFunction()
465
                || !entry->generateCode()
466
                || entry->isEnumValue()
467
                || entry->isVarargs()
468
                || entry->isTypeSystem()
469
                || entry->isVoid()
470
                || entry->isCustom())
471
                continue;
472
            groupedEntries[getTypeRevision(entry)] << entry;
473
        }
474
    }
475
476
    maxTypeIndex = 0;
477
    GroupedTypeEntries::iterator it = groupedEntries.begin();
478
    for (; it != groupedEntries.end(); ++it) {
479
        // Remove duplicates
480
        QList<TypeEntry*>::iterator newEnd = std::unique(it.value().begin(), it.value().end());
481
        it.value().erase(newEnd, it.value().end());
482
        // Sort the type entries by name
483
        qSort(it.value().begin(), newEnd, compareTypeEntriesByName);
484
485
        foreach (TypeEntry* entry, it.value()) {
486
            (*typeEntryFields())[entry].second = maxTypeIndex++;
487
        }
488
    }
489
    computeTypeIndexes = false;
490
}
491
492
int getTypeIndex(const TypeEntry* typeEntry)
493
{
494
    if (computeTypeIndexes)
495
        _computeTypeIndexes();
496
    return typeEntryFields()->value(typeEntry).second;
497
}
498
499
int getMaxTypeIndex()
500
{
501
    if (computeTypeIndexes)
502
        _computeTypeIndexes();
503
    return maxTypeIndex;
504
}
505
506
void TypeDatabase::setApiVersion(const QString& package, const QByteArray& version)
507
{
508
    (*apiVersions())[package.trimmed()] = version.trimmed();
509
}
510
511
/**
512
 * Returns -1, 0 or 1 if v1 is less, equal or greater than v2
513
 */
514
static int versionCheck(const QByteArray& v1, const QByteArray& v2)
515
{
516
    if (v1.isEmpty() || v2.isEmpty())
517
        return 0;
518
519
    QList<QByteArray> v1Components = v1.split('.');
520
    QList<QByteArray> v2Components = v2.split('.');
521
    int numComponents = qMax(v1Components.count(), v2Components.count());
522
    while (v1Components.count() < numComponents)
523
        v1Components.append("0");
524
    while (v2Components.count() < numComponents)
525
        v2Components.append("0");
526
527
    for (int i = 0, max = v1Components.count(); i < max; ++i) {
528
        int v1Comp = v1Components[i].toInt();
529
        int v2Comp = v2Components[i].toInt();
530
        if (v1Comp > v2Comp)
531
            return 1;
532
        else if (v1Comp < v2Comp)
533
            return -1;
534
    }
535
    return 0;
536
}
537
538
bool TypeDatabase::checkApiVersion(const QString& package, const QByteArray& version) const
539
{
540
    ApiVersionMap* vMap = apiVersions();
541
    ApiVersionMap::const_iterator it = vMap->begin();
542
    for (; it != vMap->end(); ++it) {
543
        QRegExp regex(it.key(), Qt::CaseSensitive, QRegExp::Wildcard);
544
        if (regex.exactMatch(package))
545
            return versionCheck(it.value(), version) >= 0;
546
    }
547
    return false;
548
}