1
/*
2
 * This file is part of the API Extractor project.
3
 *
4
 * Copyright (C) 2009 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 "typesystem.h"
25
#include "typesystem_p.h"
26
#include "typedatabase.h"
27
#include "reporthandler.h"
28
#include <QtXml>
29
30
static QString strings_Object = QLatin1String("Object");
31
static QString strings_String = QLatin1String("String");
32
static QString strings_char = QLatin1String("char");
33
static QString strings_jchar = QLatin1String("jchar");
34
static QString strings_jobject = QLatin1String("jobject");
35
36
static QList<CustomConversion*> customConversionsForReview = QList<CustomConversion*>();
37
38
Handler::Handler(TypeDatabase* database, bool generate)
39
            : m_database(database), m_generate(generate ? TypeEntry::GenerateAll : TypeEntry::GenerateForSubclass)
40
{
41
    m_currentEnum = 0;
42
    m_current = 0;
43
    m_currentDroppedEntry = 0;
44
    m_currentDroppedEntryDepth = 0;
45
    m_ignoreDepth = 0;
46
47
    tagNames["rejection"] = StackElement::Rejection;
48
    tagNames["custom-type"] = StackElement::CustomTypeEntry;
49
    tagNames["primitive-type"] = StackElement::PrimitiveTypeEntry;
50
    tagNames["container-type"] = StackElement::ContainerTypeEntry;
51
    tagNames["object-type"] = StackElement::ObjectTypeEntry;
52
    tagNames["value-type"] = StackElement::ValueTypeEntry;
53
    tagNames["interface-type"] = StackElement::InterfaceTypeEntry;
54
    tagNames["namespace-type"] = StackElement::NamespaceTypeEntry;
55
    tagNames["enum-type"] = StackElement::EnumTypeEntry;
56
    tagNames["function"] = StackElement::FunctionTypeEntry;
57
    tagNames["extra-includes"] = StackElement::ExtraIncludes;
58
    tagNames["include"] = StackElement::Include;
59
    tagNames["inject-code"] = StackElement::InjectCode;
60
    tagNames["modify-function"] = StackElement::ModifyFunction;
61
    tagNames["modify-field"] = StackElement::ModifyField;
62
    tagNames["access"] = StackElement::Access;
63
    tagNames["remove"] = StackElement::Removal;
64
    tagNames["rename"] = StackElement::Rename;
65
    tagNames["typesystem"] = StackElement::Root;
66
    tagNames["custom-constructor"] = StackElement::CustomMetaConstructor;
67
    tagNames["custom-destructor"] = StackElement::CustomMetaDestructor;
68
    tagNames["argument-map"] = StackElement::ArgumentMap;
69
    tagNames["suppress-warning"] = StackElement::SuppressedWarning;
70
    tagNames["load-typesystem"] = StackElement::LoadTypesystem;
71
    tagNames["define-ownership"] = StackElement::DefineOwnership;
72
    tagNames["replace-default-expression"] = StackElement::ReplaceDefaultExpression;
73
    tagNames["reject-enum-value"] = StackElement::RejectEnumValue;
74
    tagNames["replace-type"] = StackElement::ReplaceType;
75
    tagNames["conversion-rule"] = StackElement::ConversionRule;
76
    tagNames["native-to-target"] = StackElement::NativeToTarget;
77
    tagNames["target-to-native"] = StackElement::TargetToNative;
78
    tagNames["add-conversion"] = StackElement::AddConversion;
79
    tagNames["modify-argument"] = StackElement::ModifyArgument;
80
    tagNames["remove-argument"] = StackElement::RemoveArgument;
81
    tagNames["remove-default-expression"] = StackElement::RemoveDefaultExpression;
82
    tagNames["template"] = StackElement::Template;
83
    tagNames["insert-template"] = StackElement::TemplateInstanceEnum;
84
    tagNames["replace"] = StackElement::Replace;
85
    tagNames["no-null-pointer"] = StackElement::NoNullPointers;
86
    tagNames["reference-count"] = StackElement::ReferenceCount;
87
    tagNames["parent"] = StackElement::ParentOwner;
88
    tagNames["inject-documentation"] = StackElement::InjectDocumentation;
89
    tagNames["modify-documentation"] = StackElement::ModifyDocumentation;
90
    tagNames["add-function"] = StackElement::AddFunction;
91
}
92
93
bool Handler::error(const QXmlParseException &e)
94
{
95
    qWarning("Error: line=%d, column=%d, message=%s\n",
96
             e.lineNumber(), e.columnNumber(), qPrintable(e.message()));
97
    return false;
98
}
99
100
bool Handler::fatalError(const QXmlParseException &e)
101
{
102
    qWarning("Fatal error: line=%d, column=%d, message=%s\n",
103
             e.lineNumber(), e.columnNumber(), qPrintable(e.message()));
104
105
    return false;
106
}
107
108
bool Handler::warning(const QXmlParseException &e)
109
{
110
    qWarning("Warning: line=%d, column=%d, message=%s\n",
111
             e.lineNumber(), e.columnNumber(), qPrintable(e.message()));
112
113
    return false;
114
}
115
116
void Handler::fetchAttributeValues(const QString &name, const QXmlAttributes &atts,
117
                                   QHash<QString, QString> *acceptedAttributes)
118
{
119
    Q_ASSERT(acceptedAttributes);
120
121
    for (int i = 0; i < atts.length(); ++i) {
122
        QString key = atts.localName(i).toLower();
123
        QString val = atts.value(i);
124
125
        if (!acceptedAttributes->contains(key))
126
            ReportHandler::warning(QString("Unknown attribute for '%1': '%2'").arg(name).arg(key));
127
         else
128
            (*acceptedAttributes)[key] = val;
129
130
    }
131
}
132
133
bool Handler::endElement(const QString &, const QString &localName, const QString &)
134
{
135
    if (m_ignoreDepth) {
136
        --m_ignoreDepth;
137
        return true;
138
    }
139
140
    if (m_currentDroppedEntry) {
141
        if (m_currentDroppedEntryDepth == 1) {
142
            m_current = m_currentDroppedEntry->parent;
143
            delete m_currentDroppedEntry;
144
            m_currentDroppedEntry = 0;
145
            m_currentDroppedEntryDepth = 0;
146
        } else {
147
            ++m_currentDroppedEntryDepth;
148
        }
149
        return true;
150
    }
151
152
    QString tagName = localName.toLower();
153
    if (tagName == "import-file")
154
        return true;
155
156
    if (!m_current)
157
        return true;
158
159
    switch (m_current->type) {
160
    case StackElement::Root:
161
        if (m_generate == TypeEntry::GenerateAll) {
162
            TypeDatabase::instance()->addGlobalUserFunctions(m_contextStack.top()->addedFunctions);
163
            TypeDatabase::instance()->addGlobalUserFunctionModifications(m_contextStack.top()->functionMods);
164
            foreach (CustomConversion* customConversion, customConversionsForReview) {
165
                foreach (CustomConversion::TargetToNativeConversion* toNative, customConversion->targetToNativeConversions())
166
                    toNative->setSourceType(m_database->findType(toNative->sourceTypeName()));
167
            }
168
        }
169
        break;
170
    case StackElement::ObjectTypeEntry:
171
    case StackElement::ValueTypeEntry:
172
    case StackElement::InterfaceTypeEntry:
173
    case StackElement::NamespaceTypeEntry: {
174
        ComplexTypeEntry *centry = static_cast<ComplexTypeEntry *>(m_current->entry);
175
        centry->setAddedFunctions(m_contextStack.top()->addedFunctions);
176
        centry->setFunctionModifications(m_contextStack.top()->functionMods);
177
        centry->setFieldModifications(m_contextStack.top()->fieldMods);
178
        centry->setCodeSnips(m_contextStack.top()->codeSnips);
179
        centry->setDocModification(m_contextStack.top()->docModifications);
180
181
        if (centry->designatedInterface()) {
182
            centry->designatedInterface()->setCodeSnips(m_contextStack.top()->codeSnips);
183
            centry->designatedInterface()->setFunctionModifications(m_contextStack.top()->functionMods);
184
        }
185
    }
186
    break;
187
    case StackElement::NativeToTarget:
188
    case StackElement::AddConversion: {
189
        CustomConversion* customConversion = static_cast<TypeEntry*>(m_current->entry)->customConversion();
190
        if (!customConversion) {
191
            m_error = "CustomConversion object is missing.";
192
            return false;
193
        }
194
195
        QString code = m_contextStack.top()->codeSnips.takeLast().code();
196
        if (m_current->type == StackElement::AddConversion) {
197
            if (customConversion->targetToNativeConversions().isEmpty()) {
198
                m_error = "CustomConversion's target to native conversions missing.";
199
                return false;
200
            }
201
            customConversion->targetToNativeConversions().last()->setConversion(code);
202
        } else {
203
            customConversion->setNativeToTargetConversion(code);
204
        }
205
    }
206
    break;
207
    case StackElement::CustomMetaConstructor: {
208
        m_current->entry->setCustomConstructor(*m_current->value.customFunction);
209
        delete m_current->value.customFunction;
210
    }
211
    break;
212
    case StackElement::CustomMetaDestructor: {
213
        m_current->entry->setCustomDestructor(*m_current->value.customFunction);
214
        delete m_current->value.customFunction;
215
    }
216
    break;
217
    case StackElement::EnumTypeEntry:
218
        m_current->entry->setDocModification(m_contextStack.top()->docModifications);
219
        m_contextStack.top()->docModifications = DocModificationList();
220
        m_currentEnum = 0;
221
        break;
222
    case StackElement::Template:
223
        m_database->addTemplate(m_current->value.templateEntry);
224
        break;
225
    case StackElement::TemplateInstanceEnum:
226
        switch (m_current->parent->type) {
227
        case StackElement::InjectCode:
228
            if (m_current->parent->parent->type == StackElement::Root) {
229
                CodeSnipList snips = m_current->parent->entry->codeSnips();
230
                CodeSnip snip = snips.takeLast();
231
                snip.addTemplateInstance(m_current->value.templateInstance);
232
                snips.append(snip);
233
                m_current->parent->entry->setCodeSnips(snips);
234
                break;
235
            }
236
        case StackElement::NativeToTarget:
237
        case StackElement::AddConversion:
238
            m_contextStack.top()->codeSnips.last().addTemplateInstance(m_current->value.templateInstance);
239
            break;
240
        case StackElement::Template:
241
            m_current->parent->value.templateEntry->addTemplateInstance(m_current->value.templateInstance);
242
            break;
243
        case StackElement::CustomMetaConstructor:
244
        case StackElement::CustomMetaDestructor:
245
            m_current->parent->value.customFunction->addTemplateInstance(m_current->value.templateInstance);
246
            break;
247
        case StackElement::ConversionRule:
248
            m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.last().addTemplateInstance(m_current->value.templateInstance);
249
            break;
250
        case StackElement::InjectCodeInFunction:
251
            m_contextStack.top()->functionMods.last().snips.last().addTemplateInstance(m_current->value.templateInstance);
252
            break;
253
        default:
254
            break; // nada
255
        };
256
        break;
257
    default:
258
        break;
259
    }
260
261
    if (m_current->type == StackElement::Root
262
        || m_current->type == StackElement::NamespaceTypeEntry
263
        || m_current->type == StackElement::InterfaceTypeEntry
264
        || m_current->type == StackElement::ObjectTypeEntry
265
        || m_current->type == StackElement::ValueTypeEntry
266
        || m_current->type == StackElement::PrimitiveTypeEntry) {
267
        StackElementContext* context = m_contextStack.pop();
268
        delete context;
269
    }
270
271
    StackElement *child = m_current;
272
    m_current = m_current->parent;
273
    delete(child);
274
275
    return true;
276
}
277
278
bool Handler::characters(const QString &ch)
279
{
280
    if (m_currentDroppedEntry || m_ignoreDepth)
281
        return true;
282
283
    if (m_current->type == StackElement::Template) {
284
        m_current->value.templateEntry->addCode(ch);
285
        return true;
286
    }
287
288
    if (m_current->type == StackElement::CustomMetaConstructor || m_current->type == StackElement::CustomMetaDestructor) {
289
        m_current->value.customFunction->addCode(ch);
290
        return true;
291
    }
292
293
    if (m_current->type == StackElement::ConversionRule
294
        && m_current->parent->type == StackElement::ModifyArgument) {
295
        m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.last().addCode(ch);
296
        return true;
297
    }
298
299
    if (m_current->type == StackElement::NativeToTarget || m_current->type == StackElement::AddConversion) {
300
       m_contextStack.top()->codeSnips.last().addCode(ch);
301
       return true;
302
    }
303
304
    if (m_current->parent) {
305
        if ((m_current->type & StackElement::CodeSnipMask)) {
306
            CodeSnipList snips;
307
            switch (m_current->parent->type) {
308
            case StackElement::Root:
309
                snips = m_current->parent->entry->codeSnips();
310
                snips.last().addCode(ch);
311
                m_current->parent->entry->setCodeSnips(snips);
312
                break;
313
            case StackElement::ModifyFunction:
314
            case StackElement::AddFunction:
315
                m_contextStack.top()->functionMods.last().snips.last().addCode(ch);
316
                m_contextStack.top()->functionMods.last().modifiers |= FunctionModification::CodeInjection;
317
                break;
318
            case StackElement::NamespaceTypeEntry:
319
            case StackElement::ObjectTypeEntry:
320
            case StackElement::ValueTypeEntry:
321
            case StackElement::InterfaceTypeEntry:
322
                m_contextStack.top()->codeSnips.last().addCode(ch);
323
                break;
324
            default:
325
                Q_ASSERT(false);
326
            };
327
            return true;
328
        }
329
    }
330
331
    if (m_current->type & StackElement::DocumentationMask)
332
        m_contextStack.top()->docModifications.last().setCode(ch);
333
334
    return true;
335
}
336
337
bool Handler::importFileElement(const QXmlAttributes &atts)
338
{
339
    QString fileName = atts.value("name");
340
    if (fileName.isEmpty()) {
341
        m_error = "Required attribute 'name' missing for include-file tag.";
342
        return false;
343
    }
344
345
    QFile file(fileName);
346
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
347
        file.setFileName(":/trolltech/generator/" + fileName);
348
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
349
            m_error = QString("Could not open file: '%1'").arg(fileName);
350
            return false;
351
        }
352
    }
353
354
    QString quoteFrom = atts.value("quote-after-line");
355
    bool foundFromOk = quoteFrom.isEmpty();
356
    bool from = quoteFrom.isEmpty();
357
358
    QString quoteTo = atts.value("quote-before-line");
359
    bool foundToOk = quoteTo.isEmpty();
360
    bool to = true;
361
362
    QTextStream in(&file);
363
    while (!in.atEnd()) {
364
        QString line = in.readLine();
365
        if (from && to && line.contains(quoteTo)) {
366
            to = false;
367
            foundToOk = true;
368
            break;
369
        }
370
        if (from && to)
371
            characters(line + "\n");
372
        if (!from && line.contains(quoteFrom)) {
373
            from = true;
374
            foundFromOk = true;
375
        }
376
    }
377
    if (!foundFromOk || !foundToOk) {
378
        QString fromError = QString("Could not find quote-after-line='%1' in file '%2'.").arg(quoteFrom).arg(fileName);
379
        QString toError = QString("Could not find quote-before-line='%1' in file '%2'.").arg(quoteTo).arg(fileName);
380
381
        if (!foundToOk)
382
            m_error = toError;
383
        if (!foundFromOk)
384
            m_error = fromError;
385
        if (!foundFromOk && !foundToOk)
386
            m_error = fromError + " " + toError;
387
        return false;
388
    }
389
390
    return true;
391
}
392
393
bool Handler::convertBoolean(const QString &_value, const QString &attributeName, bool defaultValue)
394
{
395
    QString value = _value.toLower();
396
    if (value == "true" || value == "yes")
397
        return true;
398
     else if (value == "false" || value == "no")
399
        return false;
400
     else {
401
        QString warn = QString("Boolean value '%1' not supported in attribute '%2'. Use 'yes' or 'no'. Defaulting to '%3'.")
402
                       .arg(value).arg(attributeName).arg(defaultValue ? "yes" : "no");
403
404
        ReportHandler::warning(warn);
405
        return defaultValue;
406
    }
407
}
408
409
static bool convertRemovalAttribute(const QString& removalAttribute, Modification& mod, QString& errorMsg)
410
{
411
    QString remove = removalAttribute.toLower();
412
    if (!remove.isEmpty()) {
413
        if (remove == QLatin1String("all")) {
414
            mod.removal = TypeSystem::All;
415
        } else if (remove == QLatin1String("target")) {
416
            mod.removal = TypeSystem::TargetLangAndNativeCode;
417
        } else {
418
            errorMsg = QString::fromLatin1("Bad removal type '%1'").arg(remove);
419
            return false;
420
        }
421
    }
422
    return true;
423
}
424
425
static void getNamePrefixRecursive(StackElement* element, QStringList& names)
426
{
427
    if (!element->parent || !element->parent->entry)
428
        return;
429
    getNamePrefixRecursive(element->parent, names);
430
    names << element->parent->entry->name();
431
}
432
433
static QString getNamePrefix(StackElement* element)
434
{
435
    QStringList names;
436
    getNamePrefixRecursive(element, names);
437
    return names.join(".");
438
}
439
440
// Returns empty string if there's no error.
441
static QString checkSignatureError(const QString& signature, const QString& tag)
442
{
443
    QString funcName = signature.left(signature.indexOf('(')).trimmed();
444
    static QRegExp whiteSpace("\\s");
445
    if (!funcName.startsWith("operator ") && funcName.contains(whiteSpace)) {
446
        return QString("Error in <%1> tag signature attribute '%2'.\n"
447
                       "White spaces aren't allowed in function names, "
448
                       "and return types should not be part of the signature.")
449
                  .arg(tag)
450
                  .arg(signature);
451
    }
452
    return QString();
453
}
454
455
bool Handler::startElement(const QString &, const QString &n,
456
                           const QString &, const QXmlAttributes &atts)
457
{
458
    if (m_ignoreDepth) {
459
        ++m_ignoreDepth;
460
        return true;
461
    }
462
463
    if (!m_defaultPackage.isEmpty() && atts.index("since") != -1) {
464
        TypeDatabase* td = TypeDatabase::instance();
465
        if (!td->checkApiVersion(m_defaultPackage, atts.value("since").toAscii())) {
466
            ++m_ignoreDepth;
467
            return true;
468
        }
469
    }
470
471
472
    QString tagName = n.toLower();
473
    if (tagName == "import-file")
474
        return importFileElement(atts);
475
476
    if (!tagNames.contains(tagName)) {
477
        m_error = QString("Unknown tag name: '%1'").arg(tagName);
478
        return false;
479
    }
480
481
    if (m_currentDroppedEntry) {
482
        ++m_currentDroppedEntryDepth;
483
        return true;
484
    }
485
486
    StackElement* element = new StackElement(m_current);
487
    element->type = tagNames[tagName];
488
489
    if (element->type == StackElement::Root && m_generate == TypeEntry::GenerateAll)
490
        customConversionsForReview.clear();
491
492
    if (element->type == StackElement::Root
493
        || element->type == StackElement::NamespaceTypeEntry
494
        || element->type == StackElement::InterfaceTypeEntry
495
        || element->type == StackElement::ObjectTypeEntry
496
        || element->type == StackElement::ValueTypeEntry
497
        || element->type == StackElement::PrimitiveTypeEntry) {
498
        m_contextStack.push(new StackElementContext());
499
    }
500
501
    if (element->type & StackElement::TypeEntryMask) {
502
        QHash<QString, QString> attributes;
503
        attributes["name"] = QString();
504
        attributes["since"] = QString("0");
505
        attributes["revision"] = QString("0");
506
507
        switch (element->type) {
508
        case StackElement::PrimitiveTypeEntry:
509
            attributes["target-lang-name"] = QString();
510
            attributes["target-lang-api-name"] = QString();
511
            attributes["preferred-conversion"] = "yes";
512
            attributes["preferred-target-lang-type"] = "yes";
513
            attributes["default-constructor"] = QString();
514
            break;
515
        case StackElement::ContainerTypeEntry:
516
            attributes["type"] = QString();
517
            break;
518
        case StackElement::EnumTypeEntry:
519
            attributes["flags"] = QString();
520
            attributes["flags-revision"] = QString();
521
            attributes["upper-bound"] = QString();
522
            attributes["lower-bound"] = QString();
523
            attributes["force-integer"] = "no";
524
            attributes["extensible"] = "no";
525
            attributes["identified-by-value"] = QString();
526
            break;
527
        case StackElement::ValueTypeEntry:
528
            attributes["default-constructor"] = QString();
529
            // fall throooough
530
        case StackElement::ObjectTypeEntry:
531
            attributes["force-abstract"] = QString("no");
532
            attributes["deprecated"] = QString("no");
533
            attributes["hash-function"] = QString("");
534
            attributes["stream"] = QString("no");
535
            // fall throooough
536
        case StackElement::InterfaceTypeEntry:
537
            attributes["default-superclass"] = m_defaultSuperclass;
538
            attributes["polymorphic-id-expression"] = QString();
539
            attributes["delete-in-main-thread"] = QString("no");
540
            attributes["held-type"] = QString();
541
            attributes["copyable"] = QString();
542
            // fall through
543
        case StackElement::NamespaceTypeEntry:
544
            attributes["target-lang-name"] = QString();
545
            attributes["package"] = m_defaultPackage;
546
            attributes["expense-cost"] = "1";
547
            attributes["expense-limit"] = "none";
548
            attributes["polymorphic-base"] = QString("no");
549
            attributes["generate"] = QString("yes");
550
            attributes["target-type"] = QString();
551
            attributes["generic-class"] = QString("no");
552
            break;
553
        case StackElement::FunctionTypeEntry:
554
            attributes["signature"] = QString();
555
            attributes["rename"] = QString();
556
            break;
557
        default:
558
            { } // nada
559
        };
560
561
        fetchAttributeValues(tagName, atts, &attributes);
562
        QString name = attributes["name"];
563
        double since = attributes["since"].toDouble();
564
565
        if (m_database->hasDroppedTypeEntries()) {
566
            QString identifier = getNamePrefix(element) + '.';
567
            identifier += (element->type == StackElement::FunctionTypeEntry ? attributes["signature"] : name);
568
            if (m_database->shouldDropTypeEntry(identifier)) {
569
                m_currentDroppedEntry = element;
570
                m_currentDroppedEntryDepth = 1;
571
                ReportHandler::debugSparse(QString("Type system entry '%1' was intentionally dropped from generation.").arg(identifier));
572
                return true;
573
            }
574
        }
575
576
        // The top level tag 'function' has only the 'signature' tag
577
        // and we should extract the 'name' value from it.
578
        if (element->type == StackElement::FunctionTypeEntry) {
579
            QString signature = attributes["signature"];
580
            name = signature.left(signature.indexOf('(')).trimmed();
581
            QString errorString = checkSignatureError(signature, "function");
582
            if (!errorString.isEmpty()) {
583
                m_error = errorString;
584
                return false;
585
            }
586
            QString rename = attributes["rename"];
587
            if (!rename.isEmpty()) {
588
                static QRegExp functionNameRegExp("^[a-zA-Z_][a-zA-Z0-9_]*$");
589
                if (!functionNameRegExp.exactMatch(rename)) {
590
                    m_error = "can not rename '" + signature + "', '" + rename + "' is not a valid function name";
591
                    return false;
592
                }
593
                FunctionModification mod(since);
594
                mod.signature = signature;
595
                mod.renamedToName = attributes["rename"];
596
                mod.modifiers |= Modification::Rename;
597
                m_contextStack.top()->functionMods << mod;
598
            }
599
        }
600
601
        // We need to be able to have duplicate primitive type entries,
602
        // or it's not possible to cover all primitive target language
603
        // types (which we need to do in order to support fake meta objects)
604
        if (element->type != StackElement::PrimitiveTypeEntry
605
            && element->type != StackElement::FunctionTypeEntry) {
606
            TypeEntry *tmp = m_database->findType(name);
607
            if (tmp)
608
                ReportHandler::warning(QString("Duplicate type entry: '%1'").arg(name));
609
        }
610
611
        if (element->type == StackElement::EnumTypeEntry) {
612
            if (name.isEmpty()) {
613
                name = attributes["identified-by-value"];
614
            } else if (!attributes["identified-by-value"].isEmpty()) {
615
                m_error = "can't specify both 'name' and 'identified-by-value' attributes";
616
                return false;
617
            }
618
        }
619
620
        // Fix type entry name using nesting information.
621
        if (element->type & StackElement::TypeEntryMask
622
            && element->parent && element->parent->type != StackElement::Root) {
623
            name = element->parent->entry->name() + "::" + name;
624
        }
625
626
627
        if (name.isEmpty()) {
628
            m_error = "no 'name' attribute specified";
629
            return false;
630
        }
631
632
        switch (element->type) {
633
        case StackElement::CustomTypeEntry:
634
            element->entry = new TypeEntry(name, TypeEntry::CustomType, since);
635
            break;
636
        case StackElement::PrimitiveTypeEntry: {
637
            QString targetLangName = attributes["target-lang-name"];
638
            QString targetLangApiName = attributes["target-lang-api-name"];
639
            QString preferredConversion = attributes["preferred-conversion"].toLower();
640
            QString preferredTargetLangType = attributes["preferred-target-lang-type"].toLower();
641
            QString defaultConstructor = attributes["default-constructor"];
642
643
            if (targetLangName.isEmpty())
644
                targetLangName = name;
645
            if (targetLangApiName.isEmpty())
646
                targetLangApiName = name;
647
648
            PrimitiveTypeEntry *type = new PrimitiveTypeEntry(name, since);
649
            type->setCodeGeneration(m_generate);
650
            type->setTargetLangName(targetLangName);
651
            type->setTargetLangApiName(targetLangApiName);
652
            type->setTargetLangPackage(m_defaultPackage);
653
            type->setDefaultConstructor(defaultConstructor);
654
655
            bool preferred;
656
            preferred = convertBoolean(preferredConversion, "preferred-conversion", true);
657
            type->setPreferredConversion(preferred);
658
            preferred = convertBoolean(preferredTargetLangType,
659
                                       "preferred-target-lang-type", true);
660
            type->setPreferredTargetLangType(preferred);
661
662
            element->entry = type;
663
            }
664
            break;
665
        case StackElement::ContainerTypeEntry:
666
            {
667
                QString typeName = attributes["type"];
668
                ContainerTypeEntry::Type containerType =
669
                        ContainerTypeEntry::containerTypeFromString(typeName);
670
                if (typeName.isEmpty()) {
671
                    m_error = "no 'type' attribute specified";
672
                    return false;
673
                } else if (containerType == ContainerTypeEntry::NoContainer) {
674
                    m_error = "there is no container of type " + containerType;
675
                    return false;
676
                }
677
678
                ContainerTypeEntry *type = new ContainerTypeEntry(name, containerType, since);
679
                type->setCodeGeneration(m_generate);
680
                element->entry = type;
681
            }
682
            break;
683
        case StackElement::EnumTypeEntry: {
684
            QStringList names = name.split(QLatin1String("::"));
685
            if (names.size() == 1)
686
                m_currentEnum = new EnumTypeEntry(QString(), name, since);
687
             else
688
                m_currentEnum =
689
                    new EnumTypeEntry(QStringList(names.mid(0, names.size() - 1)).join("::"),
690
                                      names.last(), since);
691
            m_currentEnum->setAnonymous(!attributes["identified-by-value"].isEmpty());
692
            element->entry = m_currentEnum;
693
            m_currentEnum->setCodeGeneration(m_generate);
694
            m_currentEnum->setTargetLangPackage(m_defaultPackage);
695
            m_currentEnum->setUpperBound(attributes["upper-bound"]);
696
            m_currentEnum->setLowerBound(attributes["lower-bound"]);
697
            m_currentEnum->setForceInteger(convertBoolean(attributes["force-integer"], "force-integer", false));
698
            m_currentEnum->setExtensible(convertBoolean(attributes["extensible"], "extensible", false));
699
700
            // put in the flags parallel...
701
            QString flagName = attributes["flags"];
702
            if (!flagName.isEmpty()) {
703
                FlagsTypeEntry *ftype = new FlagsTypeEntry("QFlags<" + name + ">", since);
704
                ftype->setOriginator(m_currentEnum);
705
                // Try to get the guess the qualified flag name
706
                if (!flagName.contains("::") && names.count() > 1) {
707
                    QStringList cpy(names);
708
                    cpy.removeLast();
709
                    cpy.append(flagName);
710
                    flagName = cpy.join("::");
711
                }
712
713
                ftype->setOriginalName(flagName);
714
                ftype->setCodeGeneration(m_generate);
715
                QString n = ftype->originalName();
716
717
                QStringList lst = n.split("::");
718
                if (QStringList(lst.mid(0, lst.size() - 1)).join("::") != m_currentEnum->targetLangQualifier()) {
719
                    ReportHandler::warning(QString("enum %1 and flags %2 differ in qualifiers")
720
                                           .arg(m_currentEnum->targetLangQualifier())
721
                                           .arg(lst.at(0)));
722
                }
723
724
                ftype->setFlagsName(lst.last());
725
                m_currentEnum->setFlags(ftype);
726
727
                m_database->addFlagsType(ftype);
728
                m_database->addType(ftype);
729
730
                QString revision = attributes["flags-revision"].isEmpty() ? attributes["revision"] : attributes["flags-revision"];
731
                setTypeRevision(ftype, revision.toInt());
732
            }
733
        }
734
        break;
735
736
        case StackElement::InterfaceTypeEntry: {
737
            ObjectTypeEntry *otype = new ObjectTypeEntry(name, since);
738
            QString targetLangName = attributes["target-lang-name"];
739
            if (targetLangName.isEmpty())
740
                targetLangName = name;
741
            InterfaceTypeEntry *itype =
742
                new InterfaceTypeEntry(InterfaceTypeEntry::interfaceName(targetLangName), since);
743
744
            if (!convertBoolean(attributes["generate"], "generate", true))
745
                itype->setCodeGeneration(TypeEntry::GenerateForSubclass);
746
            else
747
                itype->setCodeGeneration(m_generate);
748
            otype->setDesignatedInterface(itype);
749
            itype->setOrigin(otype);
750
            element->entry = otype;
751
        }
752
        // fall through
753
        case StackElement::ValueTypeEntry: {
754
            if (!element->entry) {
755
                ValueTypeEntry* typeEntry = new ValueTypeEntry(name, since);
756
                QString defaultConstructor = attributes["default-constructor"];
757
                if (!defaultConstructor.isEmpty())
758
                    typeEntry->setDefaultConstructor(defaultConstructor);
759
                element->entry = typeEntry;
760
            }
761
762
        // fall through
763
        case StackElement::NamespaceTypeEntry:
764
            if (!element->entry)
765
                element->entry = new NamespaceTypeEntry(name, since);
766
767
        // fall through
768
        case StackElement::ObjectTypeEntry:
769
            if (!element->entry)
770
                element->entry = new ObjectTypeEntry(name, since);
771
772
            element->entry->setStream(attributes["stream"] == QString("yes"));
773
774
            ComplexTypeEntry *ctype = static_cast<ComplexTypeEntry *>(element->entry);
775
            ctype->setTargetLangPackage(attributes["package"]);
776
            ctype->setDefaultSuperclass(attributes["default-superclass"]);
777
            ctype->setGenericClass(convertBoolean(attributes["generic-class"], "generic-class", false));
778
779
            if (!convertBoolean(attributes["generate"], "generate", true))
780
                element->entry->setCodeGeneration(TypeEntry::GenerateForSubclass);
781
            else
782
                element->entry->setCodeGeneration(m_generate);
783
784
            QString targetLangName = attributes["target-lang-name"];
785
            if (!targetLangName.isEmpty())
786
                ctype->setTargetLangName(targetLangName);
787
788
            // The expense policy
789
            QString limit = attributes["expense-limit"];
790
            if (!limit.isEmpty() && limit != "none") {
791
                ExpensePolicy ep;
792
                ep.limit = limit.toInt();
793
                ep.cost = attributes["expense-cost"];
794
                ctype->setExpensePolicy(ep);
795
            }
796
797
798
            ctype->setIsPolymorphicBase(convertBoolean(attributes["polymorphic-base"], "polymorphic-base", false));
799
            ctype->setPolymorphicIdValue(attributes["polymorphic-id-expression"]);
800
            //Copyable
801
            if (attributes["copyable"].isEmpty())
802
                ctype->setCopyable(ComplexTypeEntry::Unknown);
803
             else {
804
                if (convertBoolean(attributes["copyable"], "copyable", false))
805
                    ctype->setCopyable(ComplexTypeEntry::CopyableSet);
806
                 else
807
                    ctype->setCopyable(ComplexTypeEntry::NonCopyableSet);
808
809
            }
810
811
            if (element->type == StackElement::ObjectTypeEntry || element->type == StackElement::ValueTypeEntry)
812
                ctype->setHashFunction(attributes["hash-function"]);
813
814
815
            ctype->setHeldType(attributes["held-type"]);
816
817
            if (element->type == StackElement::ObjectTypeEntry
818
                || element->type == StackElement::ValueTypeEntry) {
819
                if (convertBoolean(attributes["force-abstract"], "force-abstract", false))
820
                    ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::ForceAbstract);
821
                if (convertBoolean(attributes["deprecated"], "deprecated", false))
822
                    ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::Deprecated);
823
            }
824
825
            if (element->type == StackElement::InterfaceTypeEntry
826
                || element->type == StackElement::ValueTypeEntry
827
                || element->type == StackElement::ObjectTypeEntry) {
828
                if (convertBoolean(attributes["delete-in-main-thread"], "delete-in-main-thread", false))
829
                    ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::DeleteInMainThread);
830
            }
831
832
            QString targetType = attributes["target-type"];
833
            if (!targetType.isEmpty() && element->entry->isComplex())
834
                static_cast<ComplexTypeEntry *>(element->entry)->setTargetType(targetType);
835
836
            // ctype->setInclude(Include(Include::IncludePath, ctype->name()));
837
            ctype = ctype->designatedInterface();
838
            if (ctype)
839
                ctype->setTargetLangPackage(attributes["package"]);
840
841
        }
842
        break;
843
        case StackElement::FunctionTypeEntry: {
844
            QString signature = attributes["signature"];
845
            signature = TypeDatabase::normalizedSignature(signature.toLatin1().constData());
846
            element->entry = m_database->findType(name);
847
            if (element->entry) {
848
                if (element->entry->type() == TypeEntry::FunctionType) {
849
                    reinterpret_cast<FunctionTypeEntry*>(element->entry)->addSignature(signature);
850
                } else {
851
                    m_error = QString("%1 expected to be a function, but isn't! Maybe it was already declared as a class or something else.").arg(name);
852
                    return false;
853
                }
854
            } else {
855
                element->entry = new FunctionTypeEntry(name, signature, since);
856
                element->entry->setCodeGeneration(m_generate);
857
            }
858
        }
859
        break;
860
        default:
861
            Q_ASSERT(false);
862
        };
863
864
        if (element->entry) {
865
            m_database->addType(element->entry);
866
            setTypeRevision(element->entry, attributes["revision"].toInt());
867
        } else {
868
            ReportHandler::warning(QString("Type: %1 was rejected by typesystem").arg(name));
869
        }
870
871
    } else if (element->type == StackElement::InjectDocumentation) {
872
        // check the XML tag attributes
873
        QHash<QString, QString> attributes;
874
        attributes["mode"] = "replace";
875
        attributes["format"] = "native";
876
        attributes["since"] = QString("0");
877
878
        fetchAttributeValues(tagName, atts, &attributes);
879
        double since = attributes["since"].toDouble();
880
881
        const int validParent = StackElement::TypeEntryMask
882
                                | StackElement::ModifyFunction
883
                                | StackElement::ModifyField;
884
        if (m_current->parent && m_current->parent->type & validParent) {
885
            QString modeName = attributes["mode"];
886
            DocModification::Mode mode;
887
            if (modeName == "append") {
888
                mode = DocModification::Append;
889
            } else if (modeName == "prepend") {
890
                mode = DocModification::Prepend;
891
            } else if (modeName == "replace") {
892
                mode = DocModification::Replace;
893
            } else {
894
                m_error = "Unknow documentation injection mode: " + modeName;
895
                return false;
896
            }
897
898
            static QHash<QString, TypeSystem::Language> languageNames;
899
            if (languageNames.isEmpty()) {
900
                languageNames["target"] = TypeSystem::TargetLangCode;
901
                languageNames["native"] = TypeSystem::NativeCode;
902
            }
903
904
            QString format = attributes["format"].toLower();
905
            TypeSystem::Language lang = languageNames.value(format, TypeSystem::NoLanguage);
906
            if (lang == TypeSystem::NoLanguage) {
907
                m_error = QString("unsupported class attribute: '%1'").arg(format);
908
                return false;
909
            }
910
911
            QString signature = m_current->type & StackElement::TypeEntryMask ? QString() : m_currentSignature;
912
            DocModification mod(mode, signature, since);
913
            mod.format = lang;
914
            m_contextStack.top()->docModifications << mod;
915
        } else {
916
            m_error = "inject-documentation must be inside modify-function, "
917
                      "modify-field or other tags that creates a type";
918
            return false;
919
        }
920
    } else if (element->type == StackElement::ModifyDocumentation) {
921
        // check the XML tag attributes
922
        QHash<QString, QString> attributes;
923
        attributes["xpath"] = QString();
924
        attributes["since"] = QString("0");
925
        fetchAttributeValues(tagName, atts, &attributes);
926
        double since = attributes["since"].toDouble();
927
928
        const int validParent = StackElement::TypeEntryMask
929
                                | StackElement::ModifyFunction
930
                                | StackElement::ModifyField;
931
        if (m_current->parent && m_current->parent->type & validParent) {
932
            QString signature = (m_current->type & StackElement::TypeEntryMask) ? QString() : m_currentSignature;
933
            m_contextStack.top()->docModifications << DocModification(attributes["xpath"], signature, since);
934
        } else {
935
            m_error = "modify-documentation must be inside modify-function, "
936
                      "modify-field or other tags that creates a type";
937
            return false;
938
        }
939
    } else if (element->type != StackElement::None) {
940
        bool topLevel = element->type == StackElement::Root
941
                        || element->type == StackElement::SuppressedWarning
942
                        || element->type == StackElement::Rejection
943
                        || element->type == StackElement::LoadTypesystem
944
                        || element->type == StackElement::InjectCode
945
                        || element->type == StackElement::ExtraIncludes
946
                        || element->type == StackElement::ConversionRule
947
                        || element->type == StackElement::AddFunction
948
                        || element->type == StackElement::Template;
949
950
        if (!topLevel && m_current->type == StackElement::Root) {
951
            m_error = QString("Tag requires parent: '%1'").arg(tagName);
952
            return false;
953
        }
954
955
        StackElement topElement = !m_current ? StackElement(0) : *m_current;
956
        element->entry = topElement.entry;
957
958
        QHash<QString, QString> attributes;
959
        attributes["since"] = QString("0");
960
        switch (element->type) {
961
        case StackElement::Root:
962
            attributes["package"] = QString();
963
            attributes["default-superclass"] = QString();
964
            break;
965
        case StackElement::LoadTypesystem:
966
            attributes["name"] = QString();
967
            attributes["generate"] = "yes";
968
            break;
969
        case StackElement::NoNullPointers:
970
            attributes["default-value"] = QString();
971
            break;
972
        case StackElement::SuppressedWarning:
973
            attributes["text"] = QString();
974
            break;
975
        case StackElement::ReplaceDefaultExpression:
976
            attributes["with"] = QString();
977
            break;
978
        case StackElement::DefineOwnership:
979
            attributes["class"] = "target";
980
            attributes["owner"] = "";
981
            break;
982
        case StackElement::AddFunction:
983
            attributes["signature"] = QString();
984
            attributes["return-type"] = QString("void");
985
            attributes["access"] = QString("public");
986
            attributes["static"] = QString("no");
987
            break;
988
        case StackElement::ModifyFunction:
989
            attributes["signature"] = QString();
990
            attributes["access"] = QString();
991
            attributes["remove"] = QString();
992
            attributes["rename"] = QString();
993
            attributes["deprecated"] = QString("no");
994
            attributes["associated-to"] = QString();
995
            attributes["virtual-slot"] = QString("no");
996
            attributes["thread"] = QString("no");
997
            attributes["allow-thread"] = QString("no");
998
            break;
999
        case StackElement::ModifyArgument:
1000
            attributes["index"] = QString();
1001
            attributes["replace-value"] = QString();
1002
            attributes["invalidate-after-use"] = QString("no");
1003
            break;
1004
        case StackElement::ModifyField:
1005
            attributes["name"] = QString();
1006
            attributes["write"] = "true";
1007
            attributes["read"] = "true";
1008
            attributes["remove"] = QString();
1009
            break;
1010
        case StackElement::Access:
1011
            attributes["modifier"] = QString();
1012
            break;
1013
        case StackElement::Include:
1014
            attributes["file-name"] = QString();
1015
            attributes["location"] = QString();
1016
            break;
1017
        case StackElement::CustomMetaConstructor:
1018
            attributes["name"] = topElement.entry->name().toLower() + "_create";
1019
            attributes["param-name"] = "copy";
1020
            break;
1021
        case StackElement::CustomMetaDestructor:
1022
            attributes["name"] = topElement.entry->name().toLower() + "_delete";
1023
            attributes["param-name"] = "copy";
1024
            break;
1025
        case StackElement::ReplaceType:
1026
            attributes["modified-type"] = QString();
1027
            break;
1028
        case StackElement::InjectCode:
1029
            attributes["class"] = "target";
1030
            attributes["position"] = "beginning";
1031
            attributes["file"] = QString();
1032
            break;
1033
        case StackElement::ConversionRule:
1034
            attributes["class"] = QString();
1035
            attributes["file"] = QString();
1036
            break;
1037
        case StackElement::TargetToNative:
1038
            attributes["replace"] = QString("yes");
1039
            break;
1040
        case StackElement::AddConversion:
1041
            attributes["type"] = QString();
1042
            attributes["check"] = QString();
1043
            break;
1044
        case StackElement::RejectEnumValue:
1045
            attributes["name"] = "";
1046
            break;
1047
        case StackElement::ArgumentMap:
1048
            attributes["index"] = "1";
1049
            attributes["meta-name"] = QString();
1050
            break;
1051
        case StackElement::Rename:
1052
            attributes["to"] = QString();
1053
            break;
1054
        case StackElement::Rejection:
1055
            attributes["class"] = "*";
1056
            attributes["function-name"] = "*";
1057
            attributes["field-name"] = "*";
1058
            attributes["enum-name"] = "*";
1059
            break;
1060
        case StackElement::Removal:
1061
            attributes["class"] = "all";
1062
            break;
1063
        case StackElement::Template:
1064
            attributes["name"] = QString();
1065
            break;
1066
        case StackElement::TemplateInstanceEnum:
1067
            attributes["name"] = QString();
1068
            break;
1069
        case StackElement::Replace:
1070
            attributes["from"] = QString();
1071
            attributes["to"] = QString();
1072
            break;
1073
        case StackElement::ReferenceCount:
1074
            attributes["action"] = QString();
1075
            attributes["variable-name"] = QString();
1076
            break;
1077
        case StackElement::ParentOwner:
1078
            attributes["index"] = QString();
1079
            attributes["action"] = QString();
1080
        default:
1081
            { };
1082
        };
1083
1084
        double since = 0;
1085
        if (attributes.count() > 0) {
1086
            fetchAttributeValues(tagName, atts, &attributes);
1087
            since = attributes["since"].toDouble();
1088
        }
1089
1090
        switch (element->type) {
1091
        case StackElement::Root:
1092
            m_defaultPackage = attributes["package"];
1093
            m_defaultSuperclass = attributes["default-superclass"];
1094
            element->type = StackElement::Root;
1095
            {
1096
                TypeSystemTypeEntry* moduleEntry = reinterpret_cast<TypeSystemTypeEntry*>(
1097
                        m_database->findType(m_defaultPackage));
1098
                element->entry = moduleEntry ? moduleEntry : new TypeSystemTypeEntry(m_defaultPackage, since);
1099
                element->entry->setCodeGeneration(m_generate);
1100
            }
1101
1102
            if ((m_generate == TypeEntry::GenerateForSubclass ||
1103
                 m_generate == TypeEntry::GenerateNothing) && m_defaultPackage != "")
1104
                TypeDatabase::instance()->addRequiredTargetImport(m_defaultPackage);
1105
1106
            if (!element->entry->qualifiedCppName().isEmpty())
1107
                m_database->addType(element->entry);
1108
            break;
1109
        case StackElement::LoadTypesystem: {
1110
            QString name = attributes["name"];
1111
            if (name.isEmpty()) {
1112
                m_error = "No typesystem name specified";
1113
                return false;
1114
            }
1115
            bool generateChild = (convertBoolean(attributes["generate"], "generate", true) && (m_generate == TypeEntry::GenerateAll));
1116
            if (!m_database->parseFile(name, generateChild)) {
1117
                m_error = QString("Failed to parse: '%1'").arg(name);
1118
                return false;
1119
            }
1120
        }
1121
        break;
1122
        case StackElement::RejectEnumValue: {
1123
            if (!m_currentEnum) {
1124
                m_error = "<reject-enum-value> node must be used inside a <enum-type> node";
1125
                return false;
1126
            }
1127
            QString name = attributes["name"];
1128
        } break;
1129
        case StackElement::ReplaceType: {
1130
            if (topElement.type != StackElement::ModifyArgument) {
1131
                m_error = "Type replacement can only be specified for argument modifications";
1132
                return false;
1133
            }
1134
1135
            if (attributes["modified-type"].isEmpty()) {
1136
                m_error = "Type replacement requires 'modified-type' attribute";
1137
                return false;
1138
            }
1139
1140
            m_contextStack.top()->functionMods.last().argument_mods.last().modified_type = attributes["modified-type"];
1141
        }
1142
        break;
1143
        case StackElement::ConversionRule: {
1144
            if (topElement.type != StackElement::ModifyArgument
1145
                && topElement.type != StackElement::ValueTypeEntry
1146
                && topElement.type != StackElement::PrimitiveTypeEntry
1147
                && topElement.type != StackElement::ContainerTypeEntry) {
1148
                m_error = "Conversion rules can only be specified for argument modification, "
1149
                          "value-type, primitive-type or container-type conversion.";
1150
                return false;
1151
            }
1152
1153
            static QHash<QString, TypeSystem::Language> languageNames;
1154
            if (languageNames.isEmpty()) {
1155
                languageNames["target"] = TypeSystem::TargetLangCode;
1156
                languageNames["native"] = TypeSystem::NativeCode;
1157
            }
1158
1159
            QString languageAttribute = attributes["class"].toLower();
1160
            TypeSystem::Language lang = languageNames.value(languageAttribute, TypeSystem::NoLanguage);
1161
1162
            if (topElement.type == StackElement::ModifyArgument) {
1163
               if (lang == TypeSystem::NoLanguage) {
1164
                    m_error = QString("unsupported class attribute: '%1'").arg(lang);
1165
                    return false;
1166
                }
1167
1168
                CodeSnip snip(since);
1169
                snip.language = lang;
1170
                m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.append(snip);
1171
            } else {
1172
                if (topElement.entry->hasConversionRule() || topElement.entry->hasCustomConversion()) {
1173
                    m_error = "Types can have only one conversion rule";
1174
                    return false;
1175
                }
1176
1177
                // The old conversion rule tag that uses a file containing the conversion
1178
                // will be kept temporarily for compatibility reasons.
1179
                QString sourceFile = attributes["file"];
1180
                if (!sourceFile.isEmpty()) {
1181
                    if (m_generate != TypeEntry::GenerateForSubclass
1182
                        && m_generate != TypeEntry::GenerateNothing) {
1183
1184
                        const char* conversionFlag = NATIVE_CONVERSION_RULE_FLAG;
1185
                        if (lang == TypeSystem::TargetLangCode)
1186
                            conversionFlag = TARGET_CONVERSION_RULE_FLAG;
1187
1188
                        QFile conversionSource(sourceFile);
1189
                        if (conversionSource.open(QIODevice::ReadOnly | QIODevice::Text)) {
1190
                            topElement.entry->setConversionRule(conversionFlag + QString::fromUtf8(conversionSource.readAll()));
1191
                        } else {
1192
                            ReportHandler::warning("File containing conversion code for "
1193
                                                   + topElement.entry->name()
1194
                                                   + " type does not exist or is not readable: "
1195
                                                   + sourceFile);
1196
                        }
1197
                    }
1198
                }
1199
1200
                CustomConversion* customConversion = new CustomConversion(static_cast<TypeEntry*>(m_current->entry));
1201
                customConversionsForReview.append(customConversion);
1202
            }
1203
        }
1204
        break;
1205
        case StackElement::NativeToTarget: {
1206
            if (topElement.type != StackElement::ConversionRule) {
1207
                m_error = "Native to Target conversion code can only be specified for custom conversion rules.";
1208
                return false;
1209
            }
1210
            m_contextStack.top()->codeSnips << CodeSnip(0);
1211
        }
1212
        break;
1213
        case StackElement::TargetToNative: {
1214
            if (topElement.type != StackElement::ConversionRule) {
1215
                m_error = "Target to Native conversions can only be specified for custom conversion rules.";
1216
                return false;
1217
            }
1218
            bool replace = attributes["replace"] == "yes";
1219
            static_cast<TypeEntry*>(m_current->entry)->customConversion()->setReplaceOriginalTargetToNativeConversions(replace);
1220
        }
1221
        break;
1222
        case StackElement::AddConversion: {
1223
            if (topElement.type != StackElement::TargetToNative) {
1224
                m_error = "Target to Native conversions can only be added inside 'target-to-native' tags.";
1225
                return false;
1226
            }
1227
            QString sourceTypeName = attributes["type"];
1228
            if (sourceTypeName.isEmpty()) {
1229
                m_error = "Target to Native conversions must specify the input type with the 'type' attribute.";
1230
                return false;
1231
            }
1232
            QString typeCheck = attributes["check"];
1233
            static_cast<TypeEntry*>(m_current->entry)->customConversion()->addTargetToNativeConversion(sourceTypeName, typeCheck);
1234
            m_contextStack.top()->codeSnips << CodeSnip(0);
1235
        }
1236
        break;
1237
        case StackElement::ModifyArgument: {
1238
            if (topElement.type != StackElement::ModifyFunction
1239
                && topElement.type != StackElement::AddFunction) {
1240
                m_error = QString::fromLatin1("argument modification requires function"
1241
                                              " modification as parent, was %1")
1242
                          .arg(topElement.type, 0, 16);
1243
                return false;
1244
            }
1245
1246
            QString index = attributes["index"];
1247
            if (index == "return")
1248
                index = "0";
1249
            else if (index == "this")
1250
                index = "-1";
1251
1252
            bool ok = false;
1253
            int idx = index.toInt(&ok);
1254
            if (!ok) {
1255
                m_error = QString("Cannot convert '%1' to integer").arg(index);
1256
                return false;
1257
            }
1258
1259
            QString replace_value = attributes["replace-value"];
1260
1261
            if (!replace_value.isEmpty() && idx) {
1262
                m_error = QString("replace-value is only supported for return values (index=0).");
1263
                return false;
1264
            }
1265
1266
            ArgumentModification argumentModification = ArgumentModification(idx, since);
1267
            argumentModification.replace_value = replace_value;
1268
            argumentModification.resetAfterUse = convertBoolean(attributes["invalidate-after-use"], "invalidate-after-use", false);
1269
            m_contextStack.top()->functionMods.last().argument_mods.append(argumentModification);
1270
        }
1271
        break;
1272
        case StackElement::NoNullPointers: {
1273
            if (topElement.type != StackElement::ModifyArgument) {
1274
                m_error = "no-null-pointer requires argument modification as parent";
1275
                return false;
1276
            }
1277
1278
            m_contextStack.top()->functionMods.last().argument_mods.last().noNullPointers = true;
1279
            if (!m_contextStack.top()->functionMods.last().argument_mods.last().index)
1280
                m_contextStack.top()->functionMods.last().argument_mods.last().nullPointerDefaultValue = attributes["default-value"];
1281
             else if (!attributes["default-value"].isEmpty())
1282
                ReportHandler::warning("default values for null pointer guards are only effective for return values");
1283
1284
        }
1285
        break;
1286
        case StackElement::DefineOwnership: {
1287
            if (topElement.type != StackElement::ModifyArgument) {
1288
                m_error = "define-ownership requires argument modification as parent";
1289
                return false;
1290
            }
1291
1292
            static QHash<QString, TypeSystem::Language> languageNames;
1293
            if (languageNames.isEmpty()) {
1294
                languageNames["target"] = TypeSystem::TargetLangCode;
1295
                languageNames["native"] = TypeSystem::NativeCode;
1296
            }
1297
1298
            QString classAttribute = attributes["class"].toLower();
1299
            TypeSystem::Language lang = languageNames.value(classAttribute, TypeSystem::NoLanguage);
1300
            if (lang == TypeSystem::NoLanguage) {
1301
                m_error = QString("unsupported class attribute: '%1'").arg(classAttribute);
1302
                return false;
1303
            }
1304
1305
            static QHash<QString, TypeSystem::Ownership> ownershipNames;
1306
            if (ownershipNames.isEmpty()) {
1307
                ownershipNames["target"] = TypeSystem::TargetLangOwnership;
1308
                ownershipNames["c++"] = TypeSystem::CppOwnership;
1309
                ownershipNames["default"] = TypeSystem::DefaultOwnership;
1310
            }
1311
1312
            QString ownershipAttribute = attributes["owner"].toLower();
1313
            TypeSystem::Ownership owner = ownershipNames.value(ownershipAttribute, TypeSystem::InvalidOwnership);
1314
            if (owner == TypeSystem::InvalidOwnership) {
1315
                m_error = QString("unsupported owner attribute: '%1'").arg(ownershipAttribute);
1316
                return false;
1317
            }
1318
1319
            m_contextStack.top()->functionMods.last().argument_mods.last().ownerships[lang] = owner;
1320
        }
1321
        break;
1322
        case StackElement::SuppressedWarning:
1323
            if (attributes["text"].isEmpty())
1324
                ReportHandler::warning("Suppressed warning with no text specified");
1325
            else
1326
                m_database->addSuppressedWarning(attributes["text"]);
1327
            break;
1328
        case StackElement::ArgumentMap: {
1329
            if (!(topElement.type & StackElement::CodeSnipMask)) {
1330
                m_error = "Argument maps requires code injection as parent";
1331
                return false;
1332
            }
1333
1334
            bool ok;
1335
            int pos = attributes["index"].toInt(&ok);
1336
            if (!ok) {
1337
                m_error = QString("Can't convert position '%1' to integer")
1338
                          .arg(attributes["position"]);
1339
                return false;
1340
            }
1341
1342
            if (pos <= 0) {
1343
                m_error = QString("Argument position %1 must be a positive number").arg(pos);
1344
                return false;
1345
            }
1346
1347
            QString meta_name = attributes["meta-name"];
1348
            if (meta_name.isEmpty())
1349
                ReportHandler::warning("Empty meta name in argument map");
1350
1351
1352
            if (topElement.type == StackElement::InjectCodeInFunction)
1353
                m_contextStack.top()->functionMods.last().snips.last().argumentMap[pos] = meta_name;
1354
             else {
1355
                ReportHandler::warning("Argument maps are only useful for injection of code "
1356
                                       "into functions.");
1357
            }
1358
        }
1359
        break;
1360
        case StackElement::Removal: {
1361
            if (topElement.type != StackElement::ModifyFunction) {
1362
                m_error = "Function modification parent required";
1363
                return false;
1364
            }
1365
1366
            static QHash<QString, TypeSystem::Language> languageNames;
1367
            if (languageNames.isEmpty()) {
1368
                languageNames["target"] = TypeSystem::TargetLangAndNativeCode;
1369
                languageNames["all"] = TypeSystem::All;
1370
            }
1371
1372
            QString languageAttribute = attributes["class"].toLower();
1373
            TypeSystem::Language lang = languageNames.value(languageAttribute, TypeSystem::NoLanguage);
1374
            if (lang == TypeSystem::NoLanguage) {
1375
                m_error = QString("unsupported class attribute: '%1'").arg(languageAttribute);
1376
                return false;
1377
            }
1378
1379
            m_contextStack.top()->functionMods.last().removal = lang;
1380
        }
1381
        break;
1382
        case StackElement::Rename:
1383
        case StackElement::Access: {
1384
            if (topElement.type != StackElement::ModifyField
1385
                && topElement.type != StackElement::ModifyFunction
1386
                && topElement.type != StackElement::ModifyArgument) {
1387
                m_error = "Function, field  or argument modification parent required";
1388
                return false;
1389
            }
1390
1391
            Modification *mod = 0;
1392
            if (topElement.type == StackElement::ModifyFunction)
1393
                mod = &m_contextStack.top()->functionMods.last();
1394
            else if (topElement.type == StackElement::ModifyField)
1395
                mod = &m_contextStack.top()->fieldMods.last();
1396
1397
            QString modifier;
1398
            if (element->type == StackElement::Rename) {
1399
                modifier = "rename";
1400
                QString renamed_to = attributes["to"];
1401
                if (renamed_to.isEmpty()) {
1402
                    m_error = "Rename modifier requires 'to' attribute";
1403
                    return false;
1404
                }
1405
1406
                if (topElement.type == StackElement::ModifyFunction)
1407
                    mod->setRenamedTo(renamed_to);
1408
                else if (topElement.type == StackElement::ModifyField)
1409
                    mod->setRenamedTo(renamed_to);
1410
                else
1411
                    m_contextStack.top()->functionMods.last().argument_mods.last().renamed_to = renamed_to;
1412
            } else
1413
                modifier = attributes["modifier"].toLower();
1414
1415
1416
            if (modifier.isEmpty()) {
1417
                m_error = "No access modification specified";
1418
                return false;
1419
            }
1420
1421
            static QHash<QString, FunctionModification::Modifiers> modifierNames;
1422
            if (modifierNames.isEmpty()) {
1423
                modifierNames["private"] = Modification::Private;
1424
                modifierNames["public"] = Modification::Public;
1425
                modifierNames["protected"] = Modification::Protected;
1426
                modifierNames["friendly"] = Modification::Friendly;
1427
                modifierNames["rename"] = Modification::Rename;
1428
                modifierNames["final"] = Modification::Final;
1429
                modifierNames["non-final"] = Modification::NonFinal;
1430
            }
1431
1432
            if (!modifierNames.contains(modifier)) {
1433
                m_error = QString("Unknown access modifier: '%1'").arg(modifier);
1434
                return false;
1435
            }
1436
1437
            if (mod)
1438
                mod->modifiers |= modifierNames[modifier];
1439
        }
1440
        break;
1441
        case StackElement::RemoveArgument:
1442
            if (topElement.type != StackElement::ModifyArgument) {
1443
                m_error = "Removing argument requires argument modification as parent";
1444
                return false;
1445
            }
1446
1447
            m_contextStack.top()->functionMods.last().argument_mods.last().removed = true;
1448
            break;
1449
1450
        case StackElement::ModifyField: {
1451
            QString name = attributes["name"];
1452
            if (name.isEmpty())
1453
                break;
1454
            FieldModification fm;
1455
            fm.name = name;
1456
            fm.modifiers = 0;
1457
1458
            if (!convertRemovalAttribute(attributes["remove"], fm, m_error))
1459
                return false;
1460
1461
            QString read = attributes["read"];
1462
            QString write = attributes["write"];
1463
1464
            if (read == "true") fm.modifiers |= FieldModification::Readable;
1465
            if (write == "true") fm.modifiers |= FieldModification::Writable;
1466
1467
            m_contextStack.top()->fieldMods << fm;
1468
        }
1469
        break;
1470
        case StackElement::AddFunction: {
1471
            if (!(topElement.type & (StackElement::ComplexTypeEntryMask | StackElement::Root))) {
1472
                m_error = QString::fromLatin1("Add function requires a complex type or a root tag as parent"
1473
                                              ", was=%1").arg(topElement.type, 0, 16);
1474
                return false;
1475
            }
1476
            QString signature = attributes["signature"];
1477
1478
            signature = TypeDatabase::normalizedSignature(signature.toLocal8Bit().constData());
1479
            if (signature.isEmpty()) {
1480
                m_error = "No signature for the added function";
1481
                return false;
1482
            }
1483
1484
            QString errorString = checkSignatureError(signature, "add-function");
1485
            if (!errorString.isEmpty()) {
1486
                m_error = errorString;
1487
                return false;
1488
            }
1489
1490
            AddedFunction func(signature, attributes["return-type"], since);
1491
            func.setStatic(attributes["static"] == "yes");
1492
            if (!signature.contains("("))
1493
                signature += "()";
1494
            m_currentSignature = signature;
1495
1496
            QString access = attributes["access"].toLower();
1497
            if (!access.isEmpty()) {
1498
                if (access == QLatin1String("protected")) {
1499
                    func.setAccess(AddedFunction::Protected);
1500
                } else if (access == QLatin1String("public")) {
1501
                    func.setAccess(AddedFunction::Public);
1502
                } else {
1503
                    m_error = QString::fromLatin1("Bad access type '%1'").arg(access);
1504
                    return false;
1505
                }
1506
            }
1507
1508
            m_contextStack.top()->addedFunctions << func;
1509
1510
            FunctionModification mod(since);
1511
            mod.signature = m_currentSignature;
1512
            m_contextStack.top()->functionMods << mod;
1513
        }
1514
        break;
1515
        case StackElement::ModifyFunction: {
1516
            if (!(topElement.type & StackElement::ComplexTypeEntryMask)) {
1517
                m_error = QString::fromLatin1("Modify function requires complex type as parent"
1518
                                              ", was=%1").arg(topElement.type, 0, 16);
1519
                return false;
1520
            }
1521
            QString signature = attributes["signature"];
1522
1523
            signature = TypeDatabase::normalizedSignature(signature.toLocal8Bit().constData());
1524
            if (signature.isEmpty()) {
1525
                m_error = "No signature for modified function";
1526
                return false;
1527
            }
1528
1529
            QString errorString = checkSignatureError(signature, "modify-function");
1530
            if (!errorString.isEmpty()) {
1531
                m_error = errorString;
1532
                return false;
1533
            }
1534
1535
            FunctionModification mod(since);
1536
            m_currentSignature = mod.signature = signature;
1537
1538
            QString access = attributes["access"].toLower();
1539
            if (!access.isEmpty()) {
1540
                if (access == QLatin1String("private"))
1541
                    mod.modifiers |= Modification::Private;
1542
                else if (access == QLatin1String("protected"))
1543
                    mod.modifiers |= Modification::Protected;
1544
                else if (access == QLatin1String("public"))
1545
                    mod.modifiers |= Modification::Public;
1546
                else if (access == QLatin1String("final"))
1547
                    mod.modifiers |= Modification::Final;
1548
                else if (access == QLatin1String("non-final"))
1549
                    mod.modifiers |= Modification::NonFinal;
1550
                else {
1551
                    m_error = QString::fromLatin1("Bad access type '%1'").arg(access);
1552
                    return false;
1553
                }
1554
            }
1555
1556
            if (convertBoolean(attributes["deprecated"], "deprecated", false))
1557
                mod.modifiers |= Modification::Deprecated;
1558
1559
            if (!convertRemovalAttribute(attributes["remove"], mod, m_error))
1560
                return false;
1561
1562
            QString rename = attributes["rename"];
1563
            if (!rename.isEmpty()) {
1564
                mod.renamedToName = rename;
1565
                mod.modifiers |= Modification::Rename;
1566
            }
1567
1568
            QString association = attributes["associated-to"];
1569
            if (!association.isEmpty())
1570
                mod.association = association;
1571
1572
            mod.setIsThread(convertBoolean(attributes["thread"], "thread", false));
1573
            mod.setAllowThread(convertBoolean(attributes["allow-thread"], "allow-thread", false));
1574
1575
            mod.modifiers |= (convertBoolean(attributes["virtual-slot"], "virtual-slot", false) ? Modification::VirtualSlot : 0);
1576
1577
            m_contextStack.top()->functionMods << mod;
1578
        }
1579
        break;
1580
        case StackElement::ReplaceDefaultExpression:
1581
            if (!(topElement.type & StackElement::ModifyArgument)) {
1582
                m_error = "Replace default expression only allowed as child of argument modification";
1583
                return false;
1584
            }
1585
1586
            if (attributes["with"].isEmpty()) {
1587
                m_error = "Default expression replaced with empty string. Use remove-default-expression instead.";
1588
                return false;
1589
            }
1590
1591
            m_contextStack.top()->functionMods.last().argument_mods.last().replacedDefaultExpression = attributes["with"];
1592
            break;
1593
        case StackElement::RemoveDefaultExpression:
1594
            m_contextStack.top()->functionMods.last().argument_mods.last().removedDefaultExpression = true;
1595
            break;
1596
        case StackElement::CustomMetaConstructor:
1597
        case StackElement::CustomMetaDestructor: {
1598
            CustomFunction *func = new CustomFunction(attributes["name"]);
1599
            func->paramName = attributes["param-name"];
1600
            element->value.customFunction = func;
1601
        }
1602
        break;
1603
        case StackElement::ReferenceCount: {
1604
            if (topElement.type != StackElement::ModifyArgument) {
1605
                m_error = "reference-count must be child of modify-argument";
1606
                return false;
1607
            }
1608
1609
            ReferenceCount rc;
1610
1611
            static QHash<QString, ReferenceCount::Action> actions;
1612
            if (actions.isEmpty()) {
1613
                actions["add"] = ReferenceCount::Add;
1614
                actions["add-all"] = ReferenceCount::AddAll;
1615
                actions["remove"] = ReferenceCount::Remove;
1616
                actions["set"] = ReferenceCount::Set;
1617
                actions["ignore"] = ReferenceCount::Ignore;
1618
            }
1619
            rc.action = actions.value(attributes["action"].toLower(), ReferenceCount::Invalid);
1620
            rc.varName = attributes["variable-name"];
1621
1622
            if (rc.action == ReferenceCount::Invalid) {
1623
                m_error = "unrecognized value for action attribute. supported actions:";
1624
                foreach (QString action, actions.keys())
1625
                    m_error += " " + action;
1626
            }
1627
1628
            m_contextStack.top()->functionMods.last().argument_mods.last().referenceCounts.append(rc);
1629
        }
1630
        break;
1631
1632
        case StackElement::ParentOwner: {
1633
            if (topElement.type != StackElement::ModifyArgument) {
1634
                m_error = "parent-policy must be child of modify-argument";
1635
                return false;
1636
            }
1637
1638
            ArgumentOwner ao;
1639
1640
            QString index = attributes["index"];
1641
            if (index == "return")
1642
                index = "0";
1643
            else if (index == "this")
1644
                index = "-1";
1645
1646
            bool ok = false;
1647
            int idx = index.toInt(&ok);
1648
            if (!ok) {
1649
                m_error = QString("Cannot convert '%1' to integer").arg(index);
1650
                return false;
1651
            }
1652
1653
            static QHash<QString, ArgumentOwner::Action> actions;
1654
            if (actions.isEmpty()) {
1655
                actions["add"] = ArgumentOwner::Add;
1656
                actions["remove"] = ArgumentOwner::Remove;
1657
            }
1658
1659
            ao.action = actions.value(attributes["action"].toLower(), ArgumentOwner::Invalid);
1660
            if (!ao.action) {
1661
                m_error = QString("Invalid parent actionr");
1662
                return false;
1663
            }
1664
            ao.index = idx;
1665
            m_contextStack.top()->functionMods.last().argument_mods.last().owner = ao;
1666
        }
1667
        break;
1668
1669
1670
        case StackElement::InjectCode: {
1671
            if (!(topElement.type & StackElement::ComplexTypeEntryMask)
1672
                && (topElement.type != StackElement::AddFunction)
1673
                && (topElement.type != StackElement::ModifyFunction)
1674
                && (topElement.type != StackElement::Root)) {
1675
                m_error = "wrong parent type for code injection";
1676
                return false;
1677
            }
1678
1679
            static QHash<QString, TypeSystem::Language> languageNames;
1680
            if (languageNames.isEmpty()) {
1681
                languageNames["target"] = TypeSystem::TargetLangCode; // em algum lugar do cpp
1682
                languageNames["native"] = TypeSystem::NativeCode; // em algum lugar do cpp
1683
                languageNames["shell"] = TypeSystem::ShellCode; // coloca no header, mas antes da declaracao da classe
1684
                languageNames["shell-declaration"] = TypeSystem::ShellDeclaration; // coloca no header, dentro da declaracao da classe
1685
                languageNames["library-initializer"] = TypeSystem::PackageInitializer;
1686
                languageNames["destructor-function"] = TypeSystem::DestructorFunction;
1687
                languageNames["constructors"] = TypeSystem::Constructors;
1688
                languageNames["interface"] = TypeSystem::Interface;
1689
            }
1690
1691
            QString className = attributes["class"].toLower();
1692
            if (!languageNames.contains(className)) {
1693
                m_error = QString("Invalid class specifier: '%1'").arg(className);
1694
                return false;
1695
            }
1696
1697
1698
            static QHash<QString, CodeSnip::Position> positionNames;
1699
            if (positionNames.isEmpty()) {
1700
                positionNames["beginning"] = CodeSnip::Beginning;
1701
                positionNames["end"] = CodeSnip::End;
1702
                // QtScript
1703
                positionNames["declaration"] = CodeSnip::Declaration;
1704
                positionNames["prototype-initialization"] = CodeSnip::PrototypeInitialization;
1705
                positionNames["constructor-initialization"] = CodeSnip::ConstructorInitialization;
1706
                positionNames["constructor"] = CodeSnip::Constructor;
1707
            }
1708
1709
            QString position = attributes["position"].toLower();
1710
            if (!positionNames.contains(position)) {
1711
                m_error = QString("Invalid position: '%1'").arg(position);
1712
                return false;
1713
            }
1714
1715
            CodeSnip snip(since);
1716
            snip.language = languageNames[className];
1717
            snip.position = positionNames[position];
1718
            bool in_file = false;
1719
1720
            QString file_name = attributes["file"];
1721
1722
            //Handler constructor....
1723
            if (m_generate != TypeEntry::GenerateForSubclass &&
1724
                m_generate != TypeEntry::GenerateNothing &&
1725
                !file_name.isEmpty()) {
1726
                if (QFile::exists(file_name)) {
1727
                    QFile codeFile(file_name);
1728
                    if (codeFile.open(QIODevice::Text | QIODevice::ReadOnly)) {
1729
                        QString content = QString::fromUtf8(codeFile.readAll());
1730
                        content.prepend("// ========================================================================\n"
1731
                                        "// START of custom code block [file: " + file_name + "]\n");
1732
                        content.append("\n// END of custom code block [file: " + file_name + "]\n"
1733
                                       "// ========================================================================\n");
1734
                        snip.addCode(content);
1735
                        in_file = true;
1736
                    }
1737
                } else
1738
                    ReportHandler::warning("File for inject code not exist: " + file_name);
1739
1740
            }
1741
1742
            if (snip.language == TypeSystem::Interface && topElement.type != StackElement::InterfaceTypeEntry) {
1743
                m_error = "Interface code injections must be direct child of an interface type entry";
1744
                return false;
1745
            }
1746
1747
            if (topElement.type == StackElement::ModifyFunction || topElement.type == StackElement::AddFunction) {
1748
                FunctionModification mod = m_contextStack.top()->functionMods.last();
1749
                if (snip.language == TypeSystem::ShellDeclaration) {
1750
                    m_error = "no function implementation in shell declaration in which to inject code";
1751
                    return false;
1752
                }
1753
1754
                m_contextStack.top()->functionMods.last().snips << snip;
1755
                if (in_file)
1756
                    m_contextStack.top()->functionMods.last().modifiers |= FunctionModification::CodeInjection;
1757
                element->type = StackElement::InjectCodeInFunction;
1758
            } else if (topElement.type == StackElement::Root) {
1759
                element->entry->addCodeSnip(snip);
1760
            } else if (topElement.type != StackElement::Root) {
1761
                m_contextStack.top()->codeSnips << snip;
1762
            }
1763
1764
        }
1765
        break;
1766
        case StackElement::Include: {
1767
            QString location = attributes["location"].toLower();
1768
1769
            static QHash<QString, Include::IncludeType> locationNames;
1770
            if (locationNames.isEmpty()) {
1771
                locationNames["global"] = Include::IncludePath;
1772
                locationNames["local"] = Include::LocalPath;
1773
                locationNames["target"] = Include::TargetLangImport;
1774
            }
1775
1776
            if (!locationNames.contains(location)) {
1777
                m_error = QString("Location not recognized: '%1'").arg(location);
1778
                return false;
1779
            }
1780
1781
            Include::IncludeType loc = locationNames[location];
1782
            Include inc(loc, attributes["file-name"]);
1783
1784
            ComplexTypeEntry *ctype = static_cast<ComplexTypeEntry *>(element->entry);
1785
            if (topElement.type & (StackElement::ComplexTypeEntryMask | StackElement::PrimitiveTypeEntry)) {
1786
                element->entry->setInclude(inc);
1787
            } else if (topElement.type == StackElement::ExtraIncludes) {
1788
                element->entry->addExtraInclude(inc);
1789
            } else {
1790
                m_error = "Only supported parent tags are primitive-type, complex types or extra-includes";
1791
                return false;
1792
            }
1793
1794
            inc = ctype->include();
1795
            IncludeList lst = ctype->extraIncludes();
1796
            ctype = ctype->designatedInterface();
1797
            if (ctype) {
1798
                ctype->setExtraIncludes(lst);
1799
                ctype->setInclude(inc);
1800
            }
1801
        }
1802
        break;
1803
        case StackElement::Rejection: {
1804
            QString cls = attributes["class"];
1805
            QString function = attributes["function-name"];
1806
            QString field = attributes["field-name"];
1807
            QString enum_ = attributes["enum-name"];
1808
            if (cls == "*" && function == "*" && field == "*" && enum_ == "*") {
1809
                m_error = "bad reject entry, neither 'class', 'function-name' nor "
1810
                          "'field' specified";
1811
                return false;
1812
            }
1813
            m_database->addRejection(cls, function, field, enum_);
1814
        }
1815
        break;
1816
        case StackElement::Template:
1817
            element->value.templateEntry = new TemplateEntry(attributes["name"], since);
1818
            break;
1819
        case StackElement::TemplateInstanceEnum:
1820
            if (!(topElement.type & StackElement::CodeSnipMask) &&
1821
                (topElement.type != StackElement::Template) &&
1822
                (topElement.type != StackElement::CustomMetaConstructor) &&
1823
                (topElement.type != StackElement::CustomMetaDestructor) &&
1824
                (topElement.type != StackElement::NativeToTarget) &&
1825
                (topElement.type != StackElement::AddConversion) &&
1826
                (topElement.type != StackElement::ConversionRule)) {
1827
                m_error = "Can only insert templates into code snippets, templates, custom-constructors, "\
1828
                          "custom-destructors, conversion-rule, native-to-target or add-conversion tags.";
1829
                return false;
1830
            }
1831
            element->value.templateInstance = new TemplateInstance(attributes["name"], since);
1832
            break;
1833
        case StackElement::Replace:
1834
            if (topElement.type != StackElement::TemplateInstanceEnum) {
1835
                m_error = "Can only insert replace rules into insert-template.";
1836
                return false;
1837
            }
1838
            element->parent->value.templateInstance->addReplaceRule(attributes["from"], attributes["to"]);
1839
            break;
1840
        default:
1841
            break; // nada
1842
        };
1843
    }
1844
1845
    m_current = element;
1846
    return true;
1847
}
1848
1849
PrimitiveTypeEntry* PrimitiveTypeEntry::basicAliasedTypeEntry() const
1850
{
1851
    if (!m_aliasedTypeEntry)
1852
        return 0;
1853
1854
    PrimitiveTypeEntry* baseAliasTypeEntry = m_aliasedTypeEntry->basicAliasedTypeEntry();
1855
    if (baseAliasTypeEntry)
1856
        return baseAliasTypeEntry;
1857
    else
1858
        return m_aliasedTypeEntry;
1859
}
1860
1861
typedef QHash<const PrimitiveTypeEntry*, QString> PrimitiveTypeEntryTargetLangPackageMap;
1862
Q_GLOBAL_STATIC(PrimitiveTypeEntryTargetLangPackageMap, primitiveTypeEntryTargetLangPackages);
1863
1864
void PrimitiveTypeEntry::setTargetLangPackage(const QString& package)
1865
{
1866
    primitiveTypeEntryTargetLangPackages()->insert(this, package);
1867
}
1868
QString PrimitiveTypeEntry::targetLangPackage() const
1869
{
1870
    if (!primitiveTypeEntryTargetLangPackages()->contains(this))
1871
        return this->::TypeEntry::targetLangPackage();
1872
    return primitiveTypeEntryTargetLangPackages()->value(this);
1873
}
1874
1875
CodeSnipList TypeEntry::codeSnips() const
1876
{
1877
    return m_codeSnips;
1878
}
1879
1880
QString Modification::accessModifierString() const
1881
{
1882
    if (isPrivate()) return "private";
1883
    if (isProtected()) return "protected";
1884
    if (isPublic()) return "public";
1885
    if (isFriendly()) return "friendly";
1886
    return QString();
1887
}
1888
1889
FunctionModificationList ComplexTypeEntry::functionModifications(const QString &signature) const
1890
{
1891
    FunctionModificationList lst;
1892
    for (int i = 0; i < m_functionMods.count(); ++i) {
1893
        const FunctionModification &mod = m_functionMods.at(i);
1894
        if (mod.signature == signature)
1895
            lst << mod;
1896
    }
1897
    return lst;
1898
}
1899
1900
FieldModification ComplexTypeEntry::fieldModification(const QString &name) const
1901
{
1902
    for (int i = 0; i < m_fieldMods.size(); ++i)
1903
        if (m_fieldMods.at(i).name == name)
1904
            return m_fieldMods.at(i);
1905
    FieldModification mod;
1906
    mod.name = name;
1907
    mod.modifiers = FieldModification::Readable | FieldModification::Writable;
1908
    return mod;
1909
}
1910
1911
// The things we do not to break the ABI...
1912
typedef QHash<const ComplexTypeEntry*, QString> ComplexTypeEntryDefaultConstructorMap;
1913
Q_GLOBAL_STATIC(ComplexTypeEntryDefaultConstructorMap, complexTypeEntryDefaultConstructors);
1914
1915
void ComplexTypeEntry::setDefaultConstructor(const QString& defaultConstructor)
1916
{
1917
    if (!defaultConstructor.isEmpty())
1918
        complexTypeEntryDefaultConstructors()->insert(this, defaultConstructor);
1919
}
1920
QString ComplexTypeEntry::defaultConstructor() const
1921
{
1922
    if (!complexTypeEntryDefaultConstructors()->contains(this))
1923
        return QString();
1924
    return complexTypeEntryDefaultConstructors()->value(this);
1925
}
1926
bool ComplexTypeEntry::hasDefaultConstructor() const
1927
{
1928
    return complexTypeEntryDefaultConstructors()->contains(this);
1929
}
1930
1931
QString ContainerTypeEntry::targetLangPackage() const
1932
{
1933
    return QString();
1934
}
1935
1936
QString ContainerTypeEntry::targetLangName() const
1937
{
1938
1939
    switch (m_type) {
1940
    case StringListContainer: return "QStringList";
1941
    case ListContainer: return "QList";
1942
    case LinkedListContainer: return "QLinkedList";
1943
    case VectorContainer: return "QVector";
1944
    case StackContainer: return "QStack";
1945
    case QueueContainer: return "QQueue";
1946
    case SetContainer: return "QSet";
1947
    case MapContainer: return "QMap";
1948
    case MultiMapContainer: return "QMultiMap";
1949
    case HashContainer: return "QHash";
1950
    case MultiHashContainer: return "QMultiHash";
1951
    case PairContainer: return "QPair";
1952
    default:
1953
        qWarning("bad type... %d", m_type);
1954
        break;
1955
    }
1956
    return QString();
1957
}
1958
1959
QString ContainerTypeEntry::qualifiedCppName() const
1960
{
1961
    if (m_type == StringListContainer)
1962
        return "QStringList";
1963
    return ComplexTypeEntry::qualifiedCppName();
1964
}
1965
1966
QString EnumTypeEntry::targetLangQualifier() const
1967
{
1968
    TypeEntry *te = TypeDatabase::instance()->findType(m_qualifier);
1969
    if (te)
1970
        return te->targetLangName();
1971
    else
1972
        return m_qualifier;
1973
}
1974
1975
QString EnumTypeEntry::targetLangApiName() const
1976
{
1977
    return "jint";
1978
}
1979
1980
QString FlagsTypeEntry::targetLangApiName() const
1981
{
1982
    return "jint";
1983
}
1984
1985
void EnumTypeEntry::addEnumValueRedirection(const QString &rejected, const QString &usedValue)
1986
{
1987
    m_enumRedirections << EnumValueRedirection(rejected, usedValue);
1988
}
1989
1990
QString EnumTypeEntry::enumValueRedirection(const QString &value) const
1991
{
1992
    for (int i = 0; i < m_enumRedirections.size(); ++i)
1993
        if (m_enumRedirections.at(i).rejected == value)
1994
            return m_enumRedirections.at(i).used;
1995
    return QString();
1996
}
1997
1998
QString FlagsTypeEntry::qualifiedTargetLangName() const
1999
{
2000
    return targetLangPackage() + "." + m_enum->targetLangQualifier() + "." + targetLangName();
2001
}
2002
2003
/*!
2004
 * The Visual Studio 2002 compiler doesn't support these symbols,
2005
 * which our typedefs unforntuatly expand to.
2006
 */
2007
QString fixCppTypeName(const QString &name)
2008
{
2009
    if (name == "long long") return "qint64";
2010
    else if (name == "unsigned long long") return "quint64";
2011
    return name;
2012
}
2013
2014
QString TemplateInstance::expandCode() const
2015
{
2016
    TemplateEntry *templateEntry = TypeDatabase::instance()->findTemplate(m_name);
2017
    if (templateEntry) {
2018
        QString res = templateEntry->code();
2019
        foreach (QString key, replaceRules.keys())
2020
            res.replace(key, replaceRules[key]);
2021
2022
        return "// TEMPLATE - " + m_name + " - START" + res + "// TEMPLATE - " + m_name + " - END";
2023
    } else
2024
        ReportHandler::warning("insert-template referring to non-existing template '" + m_name + "'");
2025
2026
    return QString();
2027
}
2028
2029
2030
QString CodeSnipAbstract::code() const
2031
{
2032
    QString res;
2033
    foreach (CodeSnipFragment codeFrag, codeList)
2034
        res.append(codeFrag.code());
2035
2036
    return res;
2037
}
2038
2039
QString CodeSnipFragment::code() const
2040
{
2041
    if (m_instance)
2042
        return m_instance->expandCode();
2043
    else
2044
        return m_code;
2045
}
2046
2047
QString FunctionModification::toString() const
2048
{
2049
    QString str = signature + QLatin1String("->");
2050
    if (modifiers & AccessModifierMask) {
2051
        switch (modifiers & AccessModifierMask) {
2052
        case Private: str += QLatin1String("private"); break;
2053
        case Protected: str += QLatin1String("protected"); break;
2054
        case Public: str += QLatin1String("public"); break;
2055
        case Friendly: str += QLatin1String("friendly"); break;
2056
        }
2057
    }
2058
2059
    if (modifiers & Final) str += QLatin1String("final");
2060
    if (modifiers & NonFinal) str += QLatin1String("non-final");
2061
2062
    if (modifiers & Readable) str += QLatin1String("readable");
2063
    if (modifiers & Writable) str += QLatin1String("writable");
2064
2065
    if (modifiers & CodeInjection) {
2066
        foreach (CodeSnip s, snips) {
2067
            str += QLatin1String("\n//code injection:\n");
2068
            str += s.code();
2069
        }
2070
    }
2071
2072
    if (modifiers & Rename) str += QLatin1String("renamed:") + renamedToName;
2073
2074
    if (modifiers & Deprecated) str += QLatin1String("deprecate");
2075
2076
    if (modifiers & ReplaceExpression) str += QLatin1String("replace-expression");
2077
2078
    return str;
2079
}
2080
2081
bool FunctionModification::operator!=(const FunctionModification& other) const
2082
{
2083
    return !(*this == other);
2084
}
2085
2086
bool FunctionModification::operator==(const FunctionModification& other) const
2087
{
2088
    if (signature != other.signature)
2089
        return false;
2090
2091
    if (association != other.association)
2092
        return false;
2093
2094
    if (modifiers != other.modifiers)
2095
        return false;
2096
2097
    if (removal != other.removal)
2098
        return false;
2099
2100
    if (m_thread != other.m_thread)
2101
        return false;
2102
2103
    if (m_allowThread != other.m_allowThread)
2104
        return false;
2105
2106
    if (m_version != other.m_version)
2107
        return false;
2108
2109
    return true;
2110
}
2111
2112
static AddedFunction::TypeInfo parseType(const QString& signature, int startPos = 0, int* endPos = 0)
2113
{
2114
    AddedFunction::TypeInfo result;
2115
    QRegExp regex("\\w");
2116
    int length = signature.length();
2117
    int start = signature.indexOf(regex, startPos);
2118
    if (start == -1) {
2119
        if (signature.mid(startPos + 1, 3) == "...") { // varargs
2120
            if (endPos)
2121
                *endPos = startPos + 4;
2122
            result.name = "...";
2123
        } else { // error
2124
            if (endPos)
2125
                *endPos = length;
2126
        }
2127
        return result;
2128
    }
2129
2130
    int cantStop = 0;
2131
    QString paramString;
2132
    QChar c;
2133
    int i = start;
2134
    for (; i < length; ++i) {
2135
        c = signature[i];
2136
        if (c == '<')
2137
            cantStop++;
2138
        if (c == '>')
2139
            cantStop--;
2140
        if (cantStop < 0)
2141
            break; // FIXME: report error?
2142
        if ((c == ')' || c == ',') && !cantStop)
2143
            break;
2144
        paramString += signature[i];
2145
    }
2146
    if (endPos)
2147
        *endPos = i;
2148
2149
    // Check default value
2150
    if (paramString.contains('=')) {
2151
        QStringList lst = paramString.split('=');
2152
        paramString = lst[0].trimmed();
2153
        result.defaultValue = lst[1].trimmed();
2154
    }
2155
2156
    // check constness
2157
    if (paramString.startsWith("const ")) {
2158
        result.isConstant = true;
2159
        paramString.remove(0, sizeof("const")/sizeof(char));
2160
        paramString = paramString.trimmed();
2161
    }
2162
    // check reference
2163
    if (paramString.endsWith("&")) {
2164
        result.isReference = true;
2165
        paramString.chop(1);
2166
        paramString = paramString.trimmed();
2167
    }
2168
    // check Indirections
2169
    while (paramString.endsWith("*")) {
2170
        result.indirections++;
2171
        paramString.chop(1);
2172
        paramString = paramString.trimmed();
2173
    }
2174
    result.name = paramString;
2175
2176
    return result;
2177
}
2178
2179
AddedFunction::AddedFunction(QString signature, QString returnType, double vr) : m_access(Public), m_version(vr)
2180
{
2181
    Q_ASSERT(!returnType.isEmpty());
2182
    m_returnType = parseType(returnType);
2183
    signature = signature.trimmed();
2184
    int endPos = signature.indexOf('(');
2185
    if (endPos < 0) {
2186
        m_isConst = false;
2187
        m_name = signature;
2188
    } else {
2189
        m_name = signature.left(endPos).trimmed();
2190
        int signatureLength = signature.length();
2191
        while (endPos < signatureLength) {
2192
            TypeInfo arg = parseType(signature, endPos, &endPos);
2193
            if (!arg.name.isEmpty())
2194
                m_arguments.append(arg);
2195
            // end of parameters...
2196
            if (signature[endPos] == ')')
2197
                break;
2198
        }
2199
        // is const?
2200
        m_isConst = signature.right(signatureLength - endPos).contains("const");
2201
    }
2202
}
2203
2204
QString ComplexTypeEntry::targetLangApiName() const
2205
{
2206
    return strings_jobject;
2207
}
2208
QString StringTypeEntry::targetLangApiName() const
2209
{
2210
    return strings_jobject;
2211
}
2212
QString StringTypeEntry::targetLangName() const
2213
{
2214
    return strings_String;
2215
}
2216
QString StringTypeEntry::targetLangPackage() const
2217
{
2218
    return QString();
2219
}
2220
QString CharTypeEntry::targetLangApiName() const
2221
{
2222
    return strings_jchar;
2223
}
2224
QString CharTypeEntry::targetLangName() const
2225
{
2226
    return strings_char;
2227
}
2228
QString VariantTypeEntry::targetLangApiName() const
2229
{
2230
    return strings_jobject;
2231
}
2232
QString VariantTypeEntry::targetLangName() const
2233
{
2234
    return strings_Object;
2235
}
2236
QString VariantTypeEntry::targetLangPackage() const
2237
{
2238
    return QString();
2239
}
2240
2241
QString ContainerTypeEntry::typeName() const
2242
{
2243
    switch(m_type) {
2244
        case LinkedListContainer:
2245
            return "linked-list";
2246
        case ListContainer:
2247
            return "list";
2248
        case StringListContainer:
2249
            return "string-list";
2250
        case VectorContainer:
2251
            return "vector";
2252
        case StackContainer:
2253
            return "stack";
2254
        case QueueContainer:
2255
            return "queue";
2256
        case SetContainer:
2257
            return "set";
2258
        case MapContainer:
2259
            return "map";
2260
        case MultiMapContainer:
2261
            return "multi-map";
2262
        case HashContainer:
2263
            return "hash";
2264
        case MultiHashContainer:
2265
            return "multi-hash";
2266
        case PairContainer:
2267
            return "pair";
2268
        case NoContainer:
2269
        default:
2270
            return "?";
2271
    }
2272
}
2273
2274
static bool strLess(const char* a, const char* b)
2275
{
2276
    return ::strcmp(a, b) < 0;
2277
}
2278
2279
bool TypeEntry::isCppPrimitive() const
2280
{
2281
    if (!isPrimitive())
2282
        return false;
2283
2284
    PrimitiveTypeEntry* aliasedType = ((PrimitiveTypeEntry*)this)->basicAliasedTypeEntry();
2285
    QByteArray typeName = (aliasedType ? aliasedType->name() : m_name).toAscii();
2286
2287
    if (typeName.contains(' ') || m_type == VoidType)
2288
        return true;
2289
    // Keep this sorted!!
2290
    static const char* cppTypes[] = { "bool", "char", "double", "float", "int", "long", "long long", "short", "wchar_t" };
2291
    const int N = sizeof(cppTypes)/sizeof(char*);
2292
2293
    const char** res = qBinaryFind(&cppTypes[0], &cppTypes[N], typeName.constData(), strLess);
2294
2295
    return res != &cppTypes[N];
2296
}
2297
2298
// Again, stuff to avoid ABI breakage.
2299
typedef QHash<const TypeEntry*, CustomConversion*> TypeEntryCustomConversionMap;
2300
Q_GLOBAL_STATIC(TypeEntryCustomConversionMap, typeEntryCustomConversionMap);
2301
2302
TypeEntry::~TypeEntry()
2303
{
2304
    if (typeEntryCustomConversionMap()->contains(this)) {
2305
        CustomConversion* customConversion = typeEntryCustomConversionMap()->value(this);
2306
        typeEntryCustomConversionMap()->remove(this);
2307
        delete customConversion;
2308
    }
2309
}
2310
2311
bool TypeEntry::hasCustomConversion() const
2312
{
2313
    return typeEntryCustomConversionMap()->contains(this);
2314
}
2315
void TypeEntry::setCustomConversion(CustomConversion* customConversion)
2316
{
2317
    if (customConversion)
2318
        typeEntryCustomConversionMap()->insert(this, customConversion);
2319
    else if (typeEntryCustomConversionMap()->contains(this))
2320
        typeEntryCustomConversionMap()->remove(this);
2321
}
2322
CustomConversion* TypeEntry::customConversion() const
2323
{
2324
    if (typeEntryCustomConversionMap()->contains(this))
2325
        return typeEntryCustomConversionMap()->value(this);
2326
    return 0;
2327
}
2328
2329
/*
2330
static void injectCode(ComplexTypeEntry *e,
2331
                       const char *signature,
2332
                       const QByteArray &code,
2333
                       const ArgumentMap &args)
2334
{
2335
    CodeSnip snip;
2336
    snip.language = TypeSystem::NativeCode;
2337
    snip.position = CodeSnip::Beginning;
2338
    snip.addCode(QString::fromLatin1(code));
2339
    snip.argumentMap = args;
2340
2341
    FunctionModification mod;
2342
    mod.signature = QMetaObject::normalizedSignature(signature);
2343
    mod.snips << snip;
2344
    mod.modifiers = Modification::CodeInjection;
2345
}
2346
*/
2347
2348
struct CustomConversion::CustomConversionPrivate
2349
{
2350
    CustomConversionPrivate(const TypeEntry* ownerType)
2351
        : ownerType(ownerType), replaceOriginalTargetToNativeConversions(false)
2352
    {
2353
    }
2354
    const TypeEntry* ownerType;
2355
    QString nativeToTargetConversion;
2356
    bool replaceOriginalTargetToNativeConversions;
2357
    TargetToNativeConversions targetToNativeConversions;
2358
};
2359
2360
struct CustomConversion::TargetToNativeConversion::TargetToNativeConversionPrivate
2361
{
2362
    TargetToNativeConversionPrivate()
2363
        : sourceType(0)
2364
    {
2365
    }
2366
    const TypeEntry* sourceType;
2367
    QString sourceTypeName;
2368
    QString sourceTypeCheck;
2369
    QString conversion;
2370
};
2371
2372
CustomConversion::CustomConversion(TypeEntry* ownerType)
2373
{
2374
    m_d = new CustomConversionPrivate(ownerType);
2375
    if (ownerType)
2376
        ownerType->setCustomConversion(this);
2377
}
2378
2379
CustomConversion::~CustomConversion()
2380
{
2381
    foreach (TargetToNativeConversion* targetToNativeConversion, m_d->targetToNativeConversions)
2382
        delete targetToNativeConversion;
2383
    m_d->targetToNativeConversions.clear();
2384
    delete m_d;
2385
}
2386
2387
const TypeEntry* CustomConversion::ownerType() const
2388
{
2389
    return m_d->ownerType;
2390
}
2391
2392
QString CustomConversion::nativeToTargetConversion() const
2393
{
2394
    return m_d->nativeToTargetConversion;
2395
}
2396
2397
void CustomConversion::setNativeToTargetConversion(const QString& nativeToTargetConversion)
2398
{
2399
    m_d->nativeToTargetConversion = nativeToTargetConversion;
2400
}
2401
2402
bool CustomConversion::replaceOriginalTargetToNativeConversions() const
2403
{
2404
    return m_d->replaceOriginalTargetToNativeConversions;
2405
}
2406
2407
void CustomConversion::setReplaceOriginalTargetToNativeConversions(bool replaceOriginalTargetToNativeConversions)
2408
{
2409
    m_d->replaceOriginalTargetToNativeConversions = replaceOriginalTargetToNativeConversions;
2410
}
2411
2412
bool CustomConversion::hasTargetToNativeConversions() const
2413
{
2414
    return !(m_d->targetToNativeConversions.isEmpty());
2415
}
2416
2417
CustomConversion::TargetToNativeConversions& CustomConversion::targetToNativeConversions()
2418
{
2419
    return m_d->targetToNativeConversions;
2420
}
2421
2422
const CustomConversion::TargetToNativeConversions& CustomConversion::targetToNativeConversions() const
2423
{
2424
    return m_d->targetToNativeConversions;
2425
}
2426
2427
void CustomConversion::addTargetToNativeConversion(const QString& sourceTypeName,
2428
                                                   const QString& sourceTypeCheck,
2429
                                                   const QString& conversion)
2430
{
2431
    m_d->targetToNativeConversions.append(new TargetToNativeConversion(sourceTypeName, sourceTypeCheck, conversion));
2432
}
2433
2434
CustomConversion::TargetToNativeConversion::TargetToNativeConversion(const QString& sourceTypeName,
2435
                                                                     const QString& sourceTypeCheck,
2436
                                                                     const QString& conversion)
2437
{
2438
    m_d = new TargetToNativeConversionPrivate;
2439
    m_d->sourceTypeName = sourceTypeName;
2440
    m_d->sourceTypeCheck = sourceTypeCheck;
2441
    m_d->conversion = conversion;
2442
}
2443
2444
CustomConversion::TargetToNativeConversion::~TargetToNativeConversion()
2445
{
2446
    delete m_d;
2447
}
2448
2449
const TypeEntry* CustomConversion::TargetToNativeConversion::sourceType() const
2450
{
2451
    return m_d->sourceType;
2452
}
2453
2454
void CustomConversion::TargetToNativeConversion::setSourceType(const TypeEntry* sourceType)
2455
{
2456
    m_d->sourceType = sourceType;
2457
}
2458
2459
bool CustomConversion::TargetToNativeConversion::isCustomType() const
2460
{
2461
    return !(m_d->sourceType);
2462
}
2463
2464
QString CustomConversion::TargetToNativeConversion::sourceTypeName() const
2465
{
2466
    return m_d->sourceTypeName;
2467
}
2468
2469
QString CustomConversion::TargetToNativeConversion::sourceTypeCheck() const
2470
{
2471
    return m_d->sourceTypeCheck;
2472
}
2473
2474
QString CustomConversion::TargetToNativeConversion::conversion() const
2475
{
2476
    return m_d->conversion;
2477
}
2478
2479
void CustomConversion::TargetToNativeConversion::setConversion(const QString& conversion)
2480
{
2481
    m_d->conversion = conversion;
2482
}