Merge branch 'master' of git://labs.trolltech.com/qtscriptgenerator
[qtscriptgenerator/amarok.git] / generator / typesystem.cpp
blobc76bfea3c172dd371a7bd118ea73e3a7a87535ce
1 /****************************************************************************
2 **
3 ** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
4 **
5 ** This file is part of the Qt Script Generator project on Trolltech Labs.
6 **
7 ** This file may be used under the terms of the GNU General Public
8 ** License version 2.0 as published by the Free Software Foundation
9 ** and appearing in the file LICENSE.GPL included in the packaging of
10 ** this file. Please review the following information to ensure GNU
11 ** General Public Licensing requirements will be met:
12 ** http://www.trolltech.com/products/qt/opensource.html
14 ** If you are unsure which license is appropriate for your use, please
15 ** review the following information:
16 ** http://www.trolltech.com/products/qt/licensing.html or contact the
17 ** sales department at sales@trolltech.com.
19 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
20 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22 ****************************************************************************/
24 #include "typesystem.h"
25 #include "generator.h"
27 #include "customtypes.h"
29 #include <reporthandler.h>
31 #include <memory>
33 #include <QtXml>
35 QString strings_Object = QLatin1String("Object");
36 QString strings_String = QLatin1String("String");
37 QString strings_Thread = QLatin1String("Thread");
38 QString strings_char = QLatin1String("char");
39 QString strings_java_lang = QLatin1String("java.lang");
40 QString strings_jchar = QLatin1String("jchar");
41 QString strings_jobject = QLatin1String("jobject");
43 static void addRemoveFunctionToTemplates(TypeDatabase *db);
45 class StackElement
47 public:
48 enum ElementType {
49 None = 0x0,
51 // Type tags (0x1, ... , 0xff)
52 ObjectTypeEntry = 0x1,
53 ValueTypeEntry = 0x2,
54 InterfaceTypeEntry = 0x3,
55 NamespaceTypeEntry = 0x4,
56 ComplexTypeEntryMask = 0xf,
58 // Non-complex type tags (0x10, 0x20, ... , 0xf0)
59 PrimitiveTypeEntry = 0x10,
60 EnumTypeEntry = 0x20,
61 TypeEntryMask = 0xff,
63 // Simple tags (0x100, 0x200, ... , 0xf00)
64 ExtraIncludes = 0x100,
65 Include = 0x200,
66 ModifyFunction = 0x300,
67 ModifyField = 0x400,
68 Root = 0x500,
69 CustomMetaConstructor = 0x600,
70 CustomMetaDestructor = 0x700,
71 ArgumentMap = 0x800,
72 SuppressedWarning = 0x900,
73 Rejection = 0xa00,
74 LoadTypesystem = 0xb00,
75 RejectEnumValue = 0xc00,
76 Template = 0xd00,
77 TemplateInstanceEnum = 0xe00,
78 Replace = 0xf00,
79 SimpleMask = 0xf00,
81 // Code snip tags (0x1000, 0x2000, ... , 0xf000)
82 InjectCode = 0x1000,
83 InjectCodeInFunction = 0x2000,
84 CodeSnipMask = 0xf000,
86 // Function modifier tags (0x010000, 0x020000, ... , 0xf00000)
87 Access = 0x010000,
88 Removal = 0x020000,
89 Rename = 0x040000,
90 ModifyArgument = 0x080000,
91 FunctionModifiers = 0xff0000,
93 // Argument modifier tags (0x01000000 ... 0xf0000000)
94 ConversionRule = 0x01000000,
95 ReplaceType = 0x02000000,
96 ReplaceDefaultExpression = 0x04000000,
97 RemoveArgument = 0x08000000,
98 DefineOwnership = 0x10000000,
99 RemoveDefaultExpression = 0x20000000,
100 NoNullPointers = 0x40000000,
101 ReferenceCount = 0x80000000,
102 ArgumentModifiers = 0xff000000
105 StackElement(StackElement *p) : entry(0), type(None), parent(p){ }
107 TypeEntry *entry;
108 ElementType type;
109 StackElement *parent;
111 union {
112 TemplateInstance *templateInstance;
113 TemplateEntry *templateEntry;
114 CustomFunction *customFunction;
115 } value;
118 class Handler : public QXmlDefaultHandler
120 public:
121 Handler(TypeDatabase *database, bool generate)
122 : m_database(database), m_generate(generate ? TypeEntry::GenerateAll : TypeEntry::GenerateForSubclass)
124 m_current_enum = 0;
125 current = 0;
127 tagNames["rejection"] = StackElement::Rejection;
128 tagNames["primitive-type"] = StackElement::PrimitiveTypeEntry;
129 tagNames["object-type"] = StackElement::ObjectTypeEntry;
130 tagNames["value-type"] = StackElement::ValueTypeEntry;
131 tagNames["interface-type"] = StackElement::InterfaceTypeEntry;
132 tagNames["namespace-type"] = StackElement::NamespaceTypeEntry;
133 tagNames["enum-type"] = StackElement::EnumTypeEntry;
134 tagNames["extra-includes"] = StackElement::ExtraIncludes;
135 tagNames["include"] = StackElement::Include;
136 tagNames["inject-code"] = StackElement::InjectCode;
137 tagNames["modify-function"] = StackElement::ModifyFunction;
138 tagNames["modify-field"] = StackElement::ModifyField;
139 tagNames["access"] = StackElement::Access;
140 tagNames["remove"] = StackElement::Removal;
141 tagNames["rename"] = StackElement::Rename;
142 tagNames["typesystem"] = StackElement::Root;
143 tagNames["custom-constructor"] = StackElement::CustomMetaConstructor;
144 tagNames["custom-destructor"] = StackElement::CustomMetaDestructor;
145 tagNames["argument-map"] = StackElement::ArgumentMap;
146 tagNames["suppress-warning"] = StackElement::SuppressedWarning;
147 tagNames["load-typesystem"] = StackElement::LoadTypesystem;
148 tagNames["define-ownership"] = StackElement::DefineOwnership;
149 tagNames["replace-default-expression"] = StackElement::ReplaceDefaultExpression;
150 tagNames["reject-enum-value"] = StackElement::RejectEnumValue;
151 tagNames["replace-type"] = StackElement::ReplaceType;
152 tagNames["conversion-rule"] = StackElement::ConversionRule;
153 tagNames["modify-argument"] = StackElement::ModifyArgument;
154 tagNames["remove-argument"] = StackElement::RemoveArgument;
155 tagNames["remove-default-expression"] = StackElement::RemoveDefaultExpression;
156 tagNames["template"] = StackElement::Template;
157 tagNames["insert-template"] = StackElement::TemplateInstanceEnum;
158 tagNames["replace"] = StackElement::Replace;
159 tagNames["no-null-pointer"] = StackElement::NoNullPointers;
160 tagNames["reference-count"] = StackElement::ReferenceCount;
163 bool startElement(const QString &namespaceURI, const QString &localName,
164 const QString &qName, const QXmlAttributes &atts);
165 bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName);
167 QString errorString() const { return m_error; }
168 bool error(const QXmlParseException &exception);
169 bool fatalError(const QXmlParseException &exception);
170 bool warning(const QXmlParseException &exception);
172 bool characters(const QString &ch);
174 private:
175 void fetchAttributeValues(const QString &name, const QXmlAttributes &atts,
176 QHash<QString, QString> *acceptedAttributes);
178 bool importFileElement(const QXmlAttributes &atts);
179 bool convertBoolean(const QString &, const QString &, bool);
181 TypeDatabase *m_database;
182 StackElement* current;
183 QString m_defaultPackage;
184 QString m_defaultSuperclass;
185 QString m_error;
186 TypeEntry::CodeGeneration m_generate;
188 EnumTypeEntry *m_current_enum;
190 CodeSnipList m_code_snips;
191 FunctionModificationList m_function_mods;
192 FieldModificationList m_field_mods;
194 QHash<QString, StackElement::ElementType> tagNames;
197 bool Handler::error(const QXmlParseException &e)
199 qWarning("Error: line=%d, column=%d, message=%s\n",
200 e.lineNumber(), e.columnNumber(), qPrintable(e.message()));
201 return false;
204 bool Handler::fatalError(const QXmlParseException &e)
206 qWarning("Fatal error: line=%d, column=%d, message=%s\n",
207 e.lineNumber(), e.columnNumber(), qPrintable(e.message()));
209 return false;
212 bool Handler::warning(const QXmlParseException &e)
214 qWarning("Warning: line=%d, column=%d, message=%s\n",
215 e.lineNumber(), e.columnNumber(), qPrintable(e.message()));
217 return false;
220 void Handler::fetchAttributeValues(const QString &name, const QXmlAttributes &atts,
221 QHash<QString, QString> *acceptedAttributes)
223 Q_ASSERT(acceptedAttributes != 0);
225 for (int i=0; i<atts.length(); ++i) {
226 QString key = atts.localName(i).toLower();
227 QString val = atts.value(i);
229 if (!acceptedAttributes->contains(key)) {
230 ReportHandler::warning(QString("Unknown attribute for '%1': '%2'").arg(name).arg(key));
231 } else {
232 (*acceptedAttributes)[key] = val;
237 bool Handler::endElement(const QString &, const QString &localName, const QString &)
239 QString tagName = localName.toLower();
240 if(tagName == "import-file")
241 return true;
243 if (!current)
244 return true;
246 switch (current->type) {
247 case StackElement::ObjectTypeEntry:
248 case StackElement::ValueTypeEntry:
249 case StackElement::InterfaceTypeEntry:
250 case StackElement::NamespaceTypeEntry:
252 ComplexTypeEntry *centry = static_cast<ComplexTypeEntry *>(current->entry);
253 centry->setFunctionModifications(m_function_mods);
254 centry->setFieldModifications(m_field_mods);
255 centry->setCodeSnips(m_code_snips);
257 if (centry->designatedInterface()) {
258 centry->designatedInterface()->setCodeSnips(m_code_snips);
259 centry->designatedInterface()->setFunctionModifications(m_function_mods);
261 m_code_snips = CodeSnipList();
262 m_function_mods = FunctionModificationList();
263 m_field_mods = FieldModificationList();
265 break;
266 case StackElement::CustomMetaConstructor:
268 current->entry->setCustomConstructor(*current->value.customFunction);
269 delete current->value.customFunction;
271 break;
272 case StackElement::CustomMetaDestructor:
274 current->entry->setCustomDestructor(*current->value.customFunction);
275 delete current->value.customFunction;
277 break;
278 case StackElement::EnumTypeEntry:
279 m_current_enum = 0;
280 break;
281 case StackElement::Template:
282 m_database->addTemplate(current->value.templateEntry);
283 break;
284 case StackElement::TemplateInstanceEnum:
285 if(current->parent->type == StackElement::InjectCode){
286 m_code_snips.last().addTemplateInstance(current->value.templateInstance);
287 }else if(current->parent->type == StackElement::Template){
288 current->parent->value.templateEntry->addTemplateInstance(current->value.templateInstance);
289 }else if(current->parent->type == StackElement::CustomMetaConstructor || current->parent->type == StackElement::CustomMetaConstructor){
290 current->parent->value.customFunction->addTemplateInstance(current->value.templateInstance);
291 }else if(current->parent->type == StackElement::ConversionRule){
292 m_function_mods.last().argument_mods.last().conversion_rules.last().addTemplateInstance(current->value.templateInstance);
293 }else if(current->parent->type == StackElement::InjectCodeInFunction){
294 m_function_mods.last().snips.last().addTemplateInstance(current->value.templateInstance);
296 break;
297 default:
298 break;
301 StackElement *child = current;
302 current=current->parent;
303 delete(child);
305 return true;
308 bool Handler::characters(const QString &ch)
310 if(current->type == StackElement::Template){
311 current->value.templateEntry->addCode(ch);
312 return true;
315 if (current->type == StackElement::CustomMetaConstructor || current->type == StackElement::CustomMetaDestructor){
316 current->value.customFunction->addCode(ch);
317 return true;
320 if (current->type == StackElement::ConversionRule){
321 m_function_mods.last().argument_mods.last().conversion_rules.last().addCode(ch);
322 return true;
325 if (current->parent){
326 if ((current->type & StackElement::CodeSnipMask) != 0) {
327 switch (current->parent->type) {
328 case StackElement::Root:
329 ((TypeSystemTypeEntry *) current->parent->entry)->snips.last().addCode(ch);
330 break;
331 case StackElement::ModifyFunction:
332 m_function_mods.last().snips.last().addCode(ch);
333 break;
334 case StackElement::NamespaceTypeEntry:
335 case StackElement::ObjectTypeEntry:
336 case StackElement::ValueTypeEntry:
337 case StackElement::InterfaceTypeEntry:
338 m_code_snips.last().addCode(ch);
339 break;
340 default:
341 Q_ASSERT(false);
343 return true;
347 return true;
350 bool Handler::importFileElement(const QXmlAttributes &atts)
352 QString fileName = atts.value("name");
353 if(fileName.isEmpty()){
354 m_error = "Required attribute 'name' missing for include-file tag.";
355 return false;
358 QFile file(fileName);
359 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
360 file.setFileName(":/trolltech/generator/" + fileName);
361 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
362 m_error = QString("Could not open file: '%1'").arg(fileName);
363 return false;
367 QString quoteFrom = atts.value("quote-after-line");
368 bool foundFromOk = quoteFrom.isEmpty();
369 bool from = quoteFrom.isEmpty();
371 QString quoteTo = atts.value("quote-before-line");
372 bool foundToOk = quoteTo.isEmpty();
373 bool to = true;
375 QTextStream in(&file);
376 while (!in.atEnd()) {
377 QString line = in.readLine();
378 if(from && to && line.contains(quoteTo)) {
379 to = false;
380 foundToOk = true;
381 break;
383 if(from && to)
384 characters(line + "\n");
385 if(!from && line.contains(quoteFrom)) {
386 from = true;
387 foundFromOk = true;
390 if(!foundFromOk || !foundToOk){
391 QString fromError = QString("Could not find quote-after-line='%1' in file '%2'.").arg(quoteFrom).arg(fileName);
392 QString toError = QString("Could not find quote-before-line='%1' in file '%2'.").arg(quoteTo).arg(fileName);
394 if(!foundToOk)
395 m_error = toError;
396 if(!foundFromOk)
397 m_error = fromError;
398 if(!foundFromOk && !foundToOk)
399 m_error = fromError + " " + toError;
400 return false;
403 return true;
406 bool Handler::convertBoolean(const QString &_value, const QString &attributeName, bool defaultValue)
408 QString value = _value.toLower();
409 if (value == "true" || value == "yes") {
410 return true;
411 } else if (value == "false" || value == "no") {
412 return false;
413 } else {
414 QString warn = QString("Boolean value '%1' not supported in attribute '%2'. Use 'yes' or 'no'. Defaulting to '%3'.")
415 .arg(value).arg(attributeName).arg(defaultValue ? "yes" : "no");
417 ReportHandler::warning(warn);
418 return defaultValue;
422 bool Handler::startElement(const QString &, const QString &n,
423 const QString &, const QXmlAttributes &atts)
425 QString tagName = n.toLower();
426 if(tagName == "import-file"){
427 return importFileElement(atts);
430 std::auto_ptr<StackElement> element(new StackElement(current));
432 if (!tagNames.contains(tagName)) {
433 m_error = QString("Unknown tag name: '%1'").arg(tagName);
434 return false;
437 element->type = tagNames[tagName];
438 if (element->type & StackElement::TypeEntryMask) {
439 if (current->type != StackElement::Root) {
440 m_error = "Nested types not supported";
441 return false;
444 QHash<QString, QString> attributes;
445 attributes["name"] = QString();
447 switch (element->type) {
448 case StackElement::PrimitiveTypeEntry:
449 attributes["java-name"] = QString();
450 attributes["jni-name"] = QString();
451 attributes["preferred-conversion"] = "yes";
452 attributes["preferred-java-type"] = "yes";
453 break;
454 case StackElement::EnumTypeEntry:
455 attributes["flags"] = "no";
456 attributes["upper-bound"] = QString();
457 attributes["lower-bound"] = QString();
458 attributes["force-integer"] = "no";
459 attributes["extensible"] = "no";
461 break;
463 case StackElement::ObjectTypeEntry:
464 case StackElement::ValueTypeEntry:
465 attributes["force-abstract"] = QString("no");
466 attributes["deprecated"] = QString("no");
467 // fall throooough
468 case StackElement::InterfaceTypeEntry:
469 attributes["default-superclass"] = m_defaultSuperclass;
470 attributes["polymorphic-id-expression"] = QString();
471 attributes["delete-in-main-thread"] = QString("no");
472 // fall through
473 case StackElement::NamespaceTypeEntry:
474 attributes["java-name"] = QString();
475 attributes["package"] = m_defaultPackage;
476 attributes["expense-cost"] = "1";
477 attributes["expense-limit"] = "none";
478 attributes["polymorphic-base"] = QString("no");
479 attributes["generate"] = QString("yes");
480 attributes["target-type"] = QString();
481 attributes["generic-class"] = QString("no");
482 break;
483 default:
484 ; // nada
487 fetchAttributeValues(tagName, atts, &attributes);
489 QString name = attributes["name"];
491 // We need to be able to have duplicate primitive type entries, or it's not possible to
492 // cover all primitive java types (which we need to do in order to support fake
493 // meta objects)
494 if (element->type != StackElement::PrimitiveTypeEntry) {
495 TypeEntry *tmp = m_database->findType(name);
496 if (tmp != 0) {
497 ReportHandler::warning(QString("Duplicate type entry: '%1'").arg(name));
501 if (name.isEmpty()) {
502 m_error = "no 'name' attribute specified";
503 return false;
505 switch (element->type) {
506 case StackElement::PrimitiveTypeEntry:
508 QString java_name = attributes["java-name"];
509 QString jni_name = attributes["jni-name"];
510 QString preferred_conversion = attributes["preferred-conversion"].toLower();
511 QString preferred_java_type = attributes["preferred-java-type"].toLower();
513 if (java_name.isEmpty())
514 java_name = name;
515 if (jni_name.isEmpty())
516 jni_name = name;
518 PrimitiveTypeEntry *type = new PrimitiveTypeEntry(name);
519 type->setCodeGeneration(m_generate);
520 type->setTargetLangName(java_name);
521 type->setJniName(jni_name);
523 type->setPreferredConversion(convertBoolean(preferred_conversion, "preferred-conversion", true));
524 type->setPreferredTargetLangType(convertBoolean(preferred_java_type, "preferred-java-type", true));
526 element->entry = type;
528 break;
529 case StackElement::EnumTypeEntry: {
530 QStringList names = name.split(QLatin1String("::"));
532 if (names.size() == 1) {
533 m_current_enum = new EnumTypeEntry(QString(), name);
535 else
536 m_current_enum =
537 new EnumTypeEntry(QStringList(names.mid(0, names.size() - 1)).join("::"),
538 names.last());
539 element->entry = m_current_enum;
540 m_current_enum->setCodeGeneration(m_generate);
541 m_current_enum->setTargetLangPackage(m_defaultPackage);
542 m_current_enum->setUpperBound(attributes["upper-bound"]);
543 m_current_enum->setLowerBound(attributes["lower-bound"]);
544 m_current_enum->setForceInteger(convertBoolean(attributes["force-integer"], "force-integer", false));
545 m_current_enum->setExtensible(convertBoolean(attributes["extensible"], "extensible", false));
547 // put in the flags parallel...
548 if (!attributes["flags"].isEmpty() && attributes["flags"].toLower() != "no") {
549 FlagsTypeEntry *ftype = new FlagsTypeEntry("QFlags<" + name + ">");
550 ftype->setOriginator(m_current_enum);
551 ftype->setOriginalName(attributes["flags"]);
552 ftype->setCodeGeneration(m_generate);
553 QString origname = ftype->originalName();
555 QStringList lst = origname.split("::");
556 if (QStringList(lst.mid(0, lst.size() - 1)).join("::") != m_current_enum->javaQualifier()) {
557 ReportHandler::warning(QString("enum %1 and flags %2 differ in qualifiers")
558 .arg(m_current_enum->javaQualifier())
559 .arg(lst.at(0)));
562 ftype->setFlagsName(lst.last());
563 m_current_enum->setFlags(ftype);
565 m_database->addFlagsType(ftype);
566 m_database->addType(ftype);
569 break;
571 case StackElement::InterfaceTypeEntry:
573 ObjectTypeEntry *otype = new ObjectTypeEntry(name);
574 QString javaName = attributes["java-name"];
575 if (javaName.isEmpty())
576 javaName = name;
577 InterfaceTypeEntry *itype =
578 new InterfaceTypeEntry(InterfaceTypeEntry::interfaceName(javaName));
580 if (!convertBoolean(attributes["generate"], "generate", true))
581 itype->setCodeGeneration(TypeEntry::GenerateForSubclass);
582 else
583 itype->setCodeGeneration(m_generate);
584 otype->setDesignatedInterface(itype);
585 itype->setOrigin(otype);
586 element->entry = otype;
588 // fall through
589 case StackElement::NamespaceTypeEntry:
590 if (element->entry == 0) {
591 element->entry = new NamespaceTypeEntry(name);
593 // fall through
594 case StackElement::ObjectTypeEntry:
595 if (element->entry == 0) {
596 element->entry = new ObjectTypeEntry(name);
598 // fall through
599 case StackElement::ValueTypeEntry:
601 if (element->entry == 0) {
602 element->entry = new ValueTypeEntry(name);
605 ComplexTypeEntry *ctype = static_cast<ComplexTypeEntry *>(element->entry);
606 ctype->setTargetLangPackage(attributes["package"]);
607 ctype->setDefaultSuperclass(attributes["default-superclass"]);
608 ctype->setGenericClass(convertBoolean(attributes["generic-class"], "generic-class", false));
610 if (!convertBoolean(attributes["generate"], "generate", true))
611 element->entry->setCodeGeneration(TypeEntry::GenerateForSubclass);
612 else
613 element->entry->setCodeGeneration(m_generate);
615 QString javaName = attributes["java-name"];
616 if (!javaName.isEmpty())
617 ctype->setTargetLangName(javaName);
619 // The expense policy
620 QString limit = attributes["expense-limit"];
621 if (!limit.isEmpty() && limit != "none") {
622 ExpensePolicy ep;
623 ep.limit = limit.toInt();
624 ep.cost = attributes["expense-cost"];
625 ctype->setExpensePolicy(ep);
628 ctype->setIsPolymorphicBase(convertBoolean(attributes["polymorphic-base"], "polymorphic-base", false));
629 ctype->setPolymorphicIdValue(attributes["polymorphic-id-expression"]);
631 if (element->type == StackElement::ObjectTypeEntry || element->type == StackElement::ValueTypeEntry) {
632 if (convertBoolean(attributes["force-abstract"], "force-abstract", false))
633 ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::ForceAbstract);
634 if (convertBoolean(attributes["deprecated"], "deprecated", false))
635 ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::Deprecated);
638 if (element->type == StackElement::InterfaceTypeEntry ||
639 element->type == StackElement::ValueTypeEntry ||
640 element->type == StackElement::ObjectTypeEntry) {
641 if (convertBoolean(attributes["delete-in-main-thread"], "delete-in-main-thread", false))
642 ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::DeleteInMainThread);
645 QString targetType = attributes["target-type"];
646 if (!targetType.isEmpty() && element->entry->isComplex())
647 static_cast<ComplexTypeEntry *>(element->entry)->setTargetType(targetType);
649 // ctype->setInclude(Include(Include::IncludePath, ctype->name()));
650 ctype = ctype->designatedInterface();
651 if (ctype != 0)
652 ctype->setTargetLangPackage(attributes["package"]);
655 break;
656 default:
657 Q_ASSERT(false);
660 if (element->entry)
661 m_database->addType(element->entry);
662 else
663 ReportHandler::warning(QString("Type: %1 was rejected by typesystem").arg(name));
665 } else if (element->type != StackElement::None) {
666 bool topLevel = element->type == StackElement::Root
667 || element->type == StackElement::SuppressedWarning
668 || element->type == StackElement::Rejection
669 || element->type == StackElement::LoadTypesystem
670 || element->type == StackElement::InjectCode
671 || element->type == StackElement::Template;
673 if (!topLevel && current->type == StackElement::Root) {
674 m_error = QString("Tag requires parent: '%1'").arg(tagName);
675 return false;
678 StackElement topElement = current==0 ? StackElement(0) : *current;
679 element->entry = topElement.entry;
681 QHash<QString, QString> attributes;
682 switch (element->type) {
683 case StackElement::Root:
684 attributes["package"] = QString();
685 attributes["default-superclass"] = QString();
686 break;
687 case StackElement::LoadTypesystem:
688 attributes["name"] = QString();
689 attributes["generate"] = "yes";
690 break;
691 case StackElement::NoNullPointers:
692 attributes["default-value"] = QString();
693 break;
694 case StackElement::SuppressedWarning:
695 attributes["text"] = QString();
696 break;
697 case StackElement::ReplaceDefaultExpression:
698 attributes["with"] = QString();
699 break;
700 case StackElement::DefineOwnership:
701 attributes["class"] = "java";
702 attributes["owner"] = "";
703 break;
704 case StackElement::ModifyFunction:
705 attributes["signature"] = QString();
706 attributes["access"] = QString();
707 attributes["remove"] = QString();
708 attributes["rename"] = QString();
709 attributes["deprecated"] = QString("no");
710 attributes["associated-to"] = QString();
711 attributes["virtual-slot"] = QString("no");
712 break;
713 case StackElement::ModifyArgument:
714 attributes["index"] = QString();
715 attributes["replace-value"] = QString();
716 attributes["invalidate-after-use"] = QString("no");
717 break;
718 case StackElement::ModifyField:
719 attributes["name"] = QString();
720 attributes["write"] = "true";
721 attributes["read"] = "true";
722 break;
723 case StackElement::Access:
724 attributes["modifier"] = QString();
725 break;
726 case StackElement::Include:
727 attributes["file-name"] = QString();
728 attributes["location"] = QString();
729 break;
730 case StackElement::CustomMetaConstructor:
731 attributes["name"] = topElement.entry->name().toLower() + "_create";
732 attributes["param-name"] = "copy";
733 break;
734 case StackElement::CustomMetaDestructor:
735 attributes["name"] = topElement.entry->name().toLower() + "_delete";
736 attributes["param-name"] = "copy";
737 break;
738 case StackElement::ReplaceType:
739 attributes["modified-type"] = QString();
740 break;
741 case StackElement::InjectCode:
742 attributes["class"] = "java";
743 attributes["position"] = "beginning";
744 break;
745 case StackElement::ConversionRule:
746 attributes["class"] = "";
747 break;
748 case StackElement::RejectEnumValue:
749 attributes["name"] = "";
750 break;
751 case StackElement::ArgumentMap:
752 attributes["index"] = "1";
753 attributes["meta-name"] = QString();
754 break;
755 case StackElement::Rename:
756 attributes["to"] = QString();
757 break;
758 case StackElement::Rejection:
759 attributes["class"] = "*";
760 attributes["function-name"] = "*";
761 attributes["field-name"] = "*";
762 attributes["enum-name"] = "*";
763 break;
764 case StackElement::Removal:
765 attributes["class"] = "all";
766 break;
767 case StackElement::Template:
768 attributes["name"] = QString();
769 break;
770 case StackElement::TemplateInstanceEnum:
771 attributes["name"] = QString();
772 break;
773 case StackElement::Replace:
774 attributes["from"] = QString();
775 attributes["to"] = QString();
776 break;
777 case StackElement::ReferenceCount:
778 attributes["action"] = QString();
779 attributes["variable-name"] = QString();
780 attributes["thread-safe"] = QString("no");
781 attributes["declare-variable"] = QString();
782 attributes["access"] = QString("private");
783 attributes["conditional"] = QString("");
784 break;
785 default:
786 ; // nada
789 if (attributes.count() > 0)
790 fetchAttributeValues(tagName, atts, &attributes);
792 switch (element->type) {
793 case StackElement::Root:
794 m_defaultPackage = attributes["package"];
795 m_defaultSuperclass = attributes["default-superclass"];
796 element->type = StackElement::Root;
797 element->entry = new TypeSystemTypeEntry(m_defaultPackage);
798 TypeDatabase::instance()->addType(element->entry);
799 break;
800 case StackElement::LoadTypesystem:
802 QString name = attributes["name"];
803 if (name.isEmpty()) {
804 m_error = "No typesystem name specified";
805 return false;
808 if (!m_database->parseFile(name, convertBoolean(attributes["generate"], "generate", true))) {
809 m_error = QString("Failed to parse: '%1'").arg(name);
810 return false;
813 break;
814 case StackElement::RejectEnumValue: {
815 if (!m_current_enum) {
816 m_error = "<reject-enum-value> node must be used inside a <enum-type> node";
817 return false;
819 QString name = attributes["name"];
821 bool added = false;
822 if (!name.isEmpty()) {
823 added = true;
824 m_current_enum->addEnumValueRejection(name);
827 } break;
828 case StackElement::ReplaceType:
830 if (topElement.type != StackElement::ModifyArgument) {
831 m_error = "Type replacement can only be specified for argument modifications";
832 return false;
835 if (attributes["modified-type"].isEmpty()) {
836 m_error = "Type replacement requires 'modified-type' attribute";
837 return false;
840 m_function_mods.last().argument_mods.last().modified_type = attributes["modified-type"];
842 break;
843 case StackElement::ConversionRule:
845 if (topElement.type != StackElement::ModifyArgument) {
846 m_error = "Conversion rules can only be specified for argument modification";
847 return false;
850 static QHash<QString, TypeSystem::Language> languageNames;
851 if (languageNames.isEmpty()) {
852 languageNames["native"] = TypeSystem::NativeCode;
853 languageNames["shell"] = TypeSystem::ShellCode;
856 CodeSnip snip;
857 QString languageAttribute = attributes["class"].toLower();
858 TypeSystem::Language lang = languageNames.value(languageAttribute, TypeSystem::NoLanguage);
859 if (lang == TypeSystem::NoLanguage) {
860 m_error = QString("unsupported class attribute: '%1'").arg(languageAttribute);
861 return false;
864 snip.language = lang;
865 m_function_mods.last().argument_mods.last().conversion_rules.append(snip);
868 break;
869 case StackElement::ModifyArgument:
871 if (topElement.type != StackElement::ModifyFunction) {
872 m_error = QString::fromLatin1("argument modification requires function"
873 " modification as parent, was %1")
874 .arg(topElement.type, 0, 16);
875 return false;
878 QString index = attributes["index"];
879 if (index == "return")
880 index = "0";
881 else if (index == "this")
882 index = "-1";
884 bool ok = false;
885 int idx = index.toInt(&ok);
886 if (!ok) {
887 m_error = QString("Cannot convert '%1' to integer").arg(index);
888 return false;
891 QString replace_value = attributes["replace-value"];
893 if (!replace_value.isEmpty() && idx != 0) {
894 m_error = QString("replace-value is only supported for return values (index=0).");
895 return false;
898 ArgumentModification argumentModification = ArgumentModification(idx);
899 argumentModification.replace_value = replace_value;
900 argumentModification.reset_after_use = convertBoolean(attributes["invalidate-after-use"], "invalidate-after-use", false);
901 m_function_mods.last().argument_mods.append(argumentModification);
903 break;
904 case StackElement::NoNullPointers:
906 if (topElement.type != StackElement::ModifyArgument) {
907 m_error = "no-null-pointer requires argument modification as parent";
908 return false;
911 m_function_mods.last().argument_mods.last().no_null_pointers = true;
912 if (m_function_mods.last().argument_mods.last().index == 0) {
913 m_function_mods.last().argument_mods.last().null_pointer_default_value = attributes["default-value"];
914 } else if (!attributes["default-value"].isEmpty()) {
915 ReportHandler::warning("default values for null pointer guards are only effective for return values");
918 break;
919 case StackElement::DefineOwnership:
921 if (topElement.type != StackElement::ModifyArgument) {
922 m_error = "define-ownership requires argument modification as parent";
923 return false;
926 static QHash<QString, TypeSystem::Language> languageNames;
927 if (languageNames.isEmpty()) {
928 languageNames["java"] = TypeSystem::TargetLangCode;
929 languageNames["shell"] = TypeSystem::ShellCode;
932 QString classAttribute = attributes["class"].toLower();
933 TypeSystem::Language lang = languageNames.value(classAttribute, TypeSystem::NoLanguage);
934 if (lang == TypeSystem::NoLanguage) {
935 m_error = QString("unsupported class attribute: '%1'").arg(classAttribute);
936 return false;
939 static QHash<QString, TypeSystem::Ownership> ownershipNames;
940 if (ownershipNames.isEmpty()) {
941 ownershipNames["java"] = TypeSystem::TargetLangOwnership;
942 ownershipNames["c++"] = TypeSystem::CppOwnership;
943 ownershipNames["default"] = TypeSystem::DefaultOwnership;
946 QString ownershipAttribute = attributes["owner"].toLower();
947 TypeSystem::Ownership owner = ownershipNames.value(ownershipAttribute, TypeSystem::InvalidOwnership);
948 if (owner == TypeSystem::InvalidOwnership) {
949 m_error = QString("unsupported owner attribute: '%1'").arg(ownershipAttribute);
950 return false;
953 m_function_mods.last().argument_mods.last().ownerships[lang] = owner;
955 break;
956 case StackElement::SuppressedWarning:
957 if (attributes["text"].isEmpty())
958 ReportHandler::warning("Suppressed warning with no text specified");
959 else
960 m_database->addSuppressedWarning(attributes["text"]);
961 break;
962 case StackElement::ArgumentMap:
964 if (!(topElement.type & StackElement::CodeSnipMask)) {
965 m_error = "Argument maps requires code injection as parent";
966 return false;
969 bool ok;
970 int pos = attributes["index"].toInt(&ok);
971 if (!ok) {
972 m_error = QString("Can't convert position '%1' to integer")
973 .arg(attributes["position"]);
974 return false;
977 if (pos <= 0) {
978 m_error = QString("Argument position %1 must be a positive number").arg(pos);
979 return false;
982 QString meta_name = attributes["meta-name"];
983 if (meta_name.isEmpty()) {
984 ReportHandler::warning("Empty meta name in argument map");
987 if (topElement.type == StackElement::InjectCodeInFunction) {
988 m_function_mods.last().snips.last().argumentMap[pos] = meta_name;
989 } else {
990 ReportHandler::warning("Argument maps are only useful for injection of code "
991 "into functions.");
994 break;
995 case StackElement::Removal:
997 if (topElement.type != StackElement::ModifyFunction) {
998 m_error = "Function modification parent required";
999 return false;
1002 static QHash<QString, TypeSystem::Language> languageNames;
1003 if (languageNames.isEmpty()) {
1004 languageNames["java"] = TypeSystem::TargetLangAndNativeCode;
1005 languageNames["all"] = TypeSystem::All;
1008 QString languageAttribute = attributes["class"].toLower();
1009 TypeSystem::Language lang = languageNames.value(languageAttribute, TypeSystem::NoLanguage);
1010 if (lang == TypeSystem::NoLanguage) {
1011 m_error = QString("unsupported class attribute: '%1'").arg(languageAttribute);
1012 return false;
1015 m_function_mods.last().removal = lang;
1017 break;
1018 case StackElement::Rename:
1019 case StackElement::Access:
1021 if (topElement.type != StackElement::ModifyField
1022 && topElement.type != StackElement::ModifyFunction) {
1023 m_error = "Function or field modification parent required";
1024 return false;
1027 Modification *mod = 0;
1028 if (topElement.type == StackElement::ModifyFunction)
1029 mod = &m_function_mods.last();
1030 else
1031 mod = &m_field_mods.last();
1033 QString modifier;
1034 if (element->type == StackElement::Rename) {
1035 modifier = "rename";
1036 QString renamed_to = attributes["to"];
1037 if (renamed_to.isEmpty()) {
1038 m_error = "Rename modifier requires 'to' attribute";
1039 return false;
1042 if (topElement.type == StackElement::ModifyFunction)
1043 mod->setRenamedTo(renamed_to);
1044 else
1045 mod->setRenamedTo(renamed_to);
1046 } else {
1047 modifier = attributes["modifier"].toLower();
1050 if (modifier.isEmpty()) {
1051 m_error = "No access modification specified";
1052 return false;
1055 static QHash<QString, FunctionModification::Modifiers> modifierNames;
1056 if (modifierNames.isEmpty()) {
1057 modifierNames["private"] = Modification::Private;
1058 modifierNames["public"] = Modification::Public;
1059 modifierNames["protected"] = Modification::Protected;
1060 modifierNames["friendly"] = Modification::Friendly;
1061 modifierNames["rename"] = Modification::Rename;
1062 modifierNames["final"] = Modification::Final;
1063 modifierNames["non-final"] = Modification::NonFinal;
1066 if (!modifierNames.contains(modifier)) {
1067 m_error = QString("Unknown access modifier: '%1'").arg(modifier);
1068 return false;
1071 mod->modifiers |= modifierNames[modifier];
1073 break;
1074 case StackElement::RemoveArgument:
1075 if (topElement.type != StackElement::ModifyArgument) {
1076 m_error = "Removing argument requires argument modification as parent";
1077 return false;
1080 m_function_mods.last().argument_mods.last().removed = true;
1082 break;
1084 case StackElement::ModifyField:
1086 QString name = attributes["name"];
1087 if (name.isEmpty())
1088 break;
1089 FieldModification fm;
1090 fm.name = name;
1091 fm.modifiers = 0;
1093 QString read = attributes["read"];
1094 QString write = attributes["write"];
1096 if (read == "true") fm.modifiers |= FieldModification::Readable;
1097 if (write == "true") fm.modifiers |= FieldModification::Writable;
1099 m_field_mods << fm;
1101 break;
1102 case StackElement::ModifyFunction:
1104 if (!(topElement.type & StackElement::ComplexTypeEntryMask)) {
1105 m_error = QString::fromLatin1("Modify function requires complex type as parent"
1106 ", was=%1").arg(topElement.type, 0, 16);
1107 return false;
1109 QString signature = attributes["signature"];
1111 signature = QMetaObject::normalizedSignature(signature.toLocal8Bit().constData());
1112 if (signature.isEmpty()) {
1113 m_error = "No signature for modified function";
1114 return false;
1117 FunctionModification mod;
1118 mod.signature = signature;
1120 QString access = attributes["access"].toLower();
1121 if (!access.isEmpty()) {
1122 if (access == QLatin1String("private"))
1123 mod.modifiers |= Modification::Private;
1124 else if (access == QLatin1String("protected"))
1125 mod.modifiers |= Modification::Protected;
1126 else if (access == QLatin1String("public"))
1127 mod.modifiers |= Modification::Public;
1128 else if (access == QLatin1String("final"))
1129 mod.modifiers |= Modification::Final;
1130 else if (access == QLatin1String("non-final"))
1131 mod.modifiers |= Modification::NonFinal;
1132 else {
1133 m_error = QString::fromLatin1("Bad access type '%1'").arg(access);
1134 return false;
1138 if (convertBoolean(attributes["deprecated"], "deprecated", false)) {
1139 mod.modifiers |= Modification::Deprecated;
1142 QString remove = attributes["remove"].toLower();
1143 if (!remove.isEmpty()) {
1144 if (remove == QLatin1String("all"))
1145 mod.removal = TypeSystem::All;
1146 else if (remove == QLatin1String("java"))
1147 mod.removal = TypeSystem::TargetLangAndNativeCode;
1148 else {
1149 m_error = QString::fromLatin1("Bad removal type '%1'").arg(remove);
1150 return false;
1154 QString rename = attributes["rename"];
1155 if (!rename.isEmpty()) {
1156 mod.renamedToName = rename;
1157 mod.modifiers |= Modification::Rename;
1160 QString association = attributes["associated-to"];
1161 if (!association.isEmpty())
1162 mod.association = association;
1164 mod.modifiers |= (convertBoolean(attributes["virtual-slot"], "virtual-slot", false) ? Modification::VirtualSlot : 0);
1166 m_function_mods << mod;
1168 break;
1169 case StackElement::ReplaceDefaultExpression:
1170 if (!(topElement.type & StackElement::ModifyArgument)) {
1171 m_error = "Replace default expression only allowed as child of argument modification";
1172 return false;
1175 if (attributes["with"].isEmpty()) {
1176 m_error = "Default expression replaced with empty string. Use remove-default-expression instead.";
1177 return false;
1180 m_function_mods.last().argument_mods.last().replaced_default_expression = attributes["with"];
1181 break;
1182 case StackElement::RemoveDefaultExpression:
1183 m_function_mods.last().argument_mods.last().removed_default_expression = true;
1184 break;
1185 case StackElement::CustomMetaConstructor:
1186 case StackElement::CustomMetaDestructor:
1188 CustomFunction *func = new CustomFunction(attributes["name"]);
1189 func->param_name = attributes["param-name"];
1190 element->value.customFunction = func;
1192 break;
1193 case StackElement::ReferenceCount:
1195 if (topElement.type != StackElement::ModifyArgument) {
1196 m_error = "reference-count must be child of modify-argument";
1197 return false;
1200 ReferenceCount rc;
1201 rc.threadSafe = convertBoolean(attributes["thread-safe"], "thread-safe", false);
1203 static QHash<QString, ReferenceCount::Action> actions;
1204 if (actions.isEmpty()) {
1205 actions["add"] = ReferenceCount::Add;
1206 actions["add-all"] = ReferenceCount::AddAll;
1207 actions["remove"] = ReferenceCount::Remove;
1208 actions["set"] = ReferenceCount::Set;
1209 actions["ignore"] = ReferenceCount::Ignore;
1211 rc.action = actions.value(attributes["action"].toLower(), ReferenceCount::Invalid);
1213 rc.variableName = attributes["variable-name"];
1214 if (rc.action != ReferenceCount::Ignore && rc.variableName.isEmpty()) {
1215 m_error = "variable-name attribute must be specified";
1216 return false;
1219 rc.declareVariable = attributes["declare-variable"];
1220 rc.conditional = attributes["conditional"];
1222 static QHash<QString, int> accessRights;
1223 if (accessRights.isEmpty()) {
1224 accessRights["private"] = ReferenceCount::Private;
1225 accessRights["public"] = ReferenceCount::Public;
1226 accessRights["protected"] = ReferenceCount::Protected;
1227 accessRights["friendly"] = ReferenceCount::Friendly;
1229 rc.access = accessRights.value(attributes["access"].toLower(), 0);
1230 if (rc.access == 0) {
1231 m_error = "unrecognized access value: " + attributes["access"];
1232 return false;
1235 if (rc.action == ReferenceCount::Invalid) {
1236 m_error = "unrecognized value for action attribute. supported actions:";
1237 foreach (QString action, actions.keys())
1238 m_error += " " + action;
1241 m_function_mods.last().argument_mods.last().referenceCounts.append(rc);
1243 break;
1244 case StackElement::InjectCode:
1246 if (((topElement.type & StackElement::ComplexTypeEntryMask) == 0)
1247 && (topElement.type != StackElement::ModifyFunction)
1248 && (topElement.type != StackElement::Root)) {
1249 m_error = "wrong parent type for code injection";
1250 return false;
1253 static QHash<QString, TypeSystem::Language> languageNames;
1254 if (languageNames.isEmpty()) {
1255 languageNames["java"] = TypeSystem::TargetLangCode;
1256 languageNames["native"] = TypeSystem::NativeCode;
1257 languageNames["shell"] = TypeSystem::ShellCode;
1258 languageNames["shell-declaration"] = TypeSystem::ShellDeclaration;
1259 languageNames["library-initializer"] = TypeSystem::PackageInitializer;
1260 languageNames["destructor-function"] = TypeSystem::DestructorFunction;
1261 languageNames["constructors"] = TypeSystem::Constructors;
1262 languageNames["interface"] = TypeSystem::Interface;
1265 QString className = attributes["class"].toLower();
1266 if (!languageNames.contains(className)) {
1267 m_error = QString("Invalid class specifier: '%1'").arg(className);
1268 return false;
1272 static QHash<QString, CodeSnip::Position> positionNames;
1273 if (positionNames.isEmpty()) {
1274 positionNames["beginning"] = CodeSnip::Beginning;
1275 positionNames["end"] = CodeSnip::End;
1276 // QtScript
1277 positionNames["prototype-initialization"] = CodeSnip::PrototypeInitialization;
1278 positionNames["constructor-initialization"] = CodeSnip::ConstructorInitialization;
1279 positionNames["constructor"] = CodeSnip::Constructor;
1282 QString position = attributes["position"].toLower();
1283 if (!positionNames.contains(position)) {
1284 m_error = QString("Invalid position: '%1'").arg(position);
1285 return false;
1288 CodeSnip snip;
1289 snip.language = languageNames[className];
1290 snip.position = positionNames[position];
1292 if (snip.language == TypeSystem::Interface && topElement.type != StackElement::InterfaceTypeEntry) {
1293 m_error = "Interface code injections must be direct child of an interface type entry";
1294 return false;
1297 if (topElement.type == StackElement::ModifyFunction) {
1298 FunctionModification mod = m_function_mods.last();
1299 if (snip.language == TypeSystem::ShellDeclaration) {
1300 m_error = "no function implementation in shell declaration in which to inject code";
1301 return false;
1304 m_function_mods.last().snips << snip;
1305 element->type = StackElement::InjectCodeInFunction;
1306 } else if (topElement.type == StackElement::Root) {
1307 ((TypeSystemTypeEntry *) element->entry)->snips << snip;
1309 } else if (topElement.type != StackElement::Root) {
1310 m_code_snips << snip;
1313 break;
1314 case StackElement::Include:
1316 QString location = attributes["location"].toLower();
1318 static QHash<QString, Include::IncludeType> locationNames;
1319 if (locationNames.isEmpty()) {
1320 locationNames["global"] = Include::IncludePath;
1321 locationNames["local"] = Include::LocalPath;
1322 locationNames["java"] = Include::TargetLangImport;
1325 if (!locationNames.contains(location)) {
1326 m_error = QString("Location not recognized: '%1'").arg(location);
1327 return false;
1330 Include::IncludeType loc = locationNames[location];
1331 Include inc(loc, attributes["file-name"]);
1333 ComplexTypeEntry *ctype = static_cast<ComplexTypeEntry *>(element->entry);
1334 if (topElement.type & StackElement::ComplexTypeEntryMask) {
1335 ctype->setInclude(inc);
1336 } else if (topElement.type == StackElement::ExtraIncludes) {
1337 ctype->addExtraInclude(inc);
1338 } else {
1339 m_error = "Only supported parents are complex types and extra-includes";
1340 return false;
1343 inc = ctype->include();
1344 IncludeList lst = ctype->extraIncludes();
1345 ctype = ctype->designatedInterface();
1346 if (ctype != 0) {
1347 ctype->setExtraIncludes(lst);
1348 ctype->setInclude(inc);
1351 break;
1352 case StackElement::Rejection:
1354 QString cls = attributes["class"];
1355 QString function = attributes["function-name"];
1356 QString field = attributes["field-name"];
1357 QString enum_ = attributes["enum-name"];
1358 if (cls == "*" && function == "*" && field == "*" && enum_ == "*") {
1359 m_error = "bad reject entry, neither 'class', 'function-name' nor "
1360 "'field' specified";
1361 return false;
1363 m_database->addRejection(cls, function, field, enum_);
1365 break;
1366 case StackElement::Template:
1367 element->value.templateEntry = new TemplateEntry(attributes["name"]);
1368 break;
1369 case StackElement::TemplateInstanceEnum:
1370 if (!(topElement.type & StackElement::CodeSnipMask) &&
1371 (topElement.type != StackElement::Template) &&
1372 (topElement.type != StackElement::CustomMetaConstructor) &&
1373 (topElement.type != StackElement::CustomMetaDestructor) &&
1374 (topElement.type != StackElement::ConversionRule))
1376 m_error = "Can only insert templates into code snippets, templates, custom-constructors, custom-destructors or conversion-rule.";
1377 return false;
1379 element->value.templateInstance = new TemplateInstance(attributes["name"]);
1380 break;
1381 case StackElement::Replace:
1382 if (topElement.type != StackElement::TemplateInstanceEnum) {
1383 m_error = "Can only insert replace rules into insert-template.";
1384 return false;
1386 element->parent->value.templateInstance->addReplaceRule(attributes["from"],attributes["to"]);
1387 break;
1388 default:
1389 break; // nada
1393 current = element.release();
1394 return true;
1397 TypeDatabase *TypeDatabase::instance()
1399 static TypeDatabase *db = new TypeDatabase();
1400 return db;
1403 TypeDatabase::TypeDatabase() : m_suppressWarnings(true)
1405 addType(new StringTypeEntry("QString"));
1407 StringTypeEntry *e = new StringTypeEntry("QLatin1String");
1408 e->setPreferredConversion(false);
1409 addType(e);
1411 e = new StringTypeEntry("QStringRef");
1412 e->setPreferredConversion(false);
1413 addType(e);
1415 e = new StringTypeEntry("QXmlStreamStringRef");
1416 e->setPreferredConversion(false);
1417 addType(e);
1419 addType(new CharTypeEntry("QChar"));
1421 CharTypeEntry *c = new CharTypeEntry("QLatin1Char");
1422 c->setPreferredConversion(false);
1423 addType(c);
1426 VariantTypeEntry *qvariant = new VariantTypeEntry("QVariant");
1427 qvariant->setCodeGeneration(TypeEntry::GenerateNothing);
1428 addType(qvariant);
1432 JObjectWrapperTypeEntry *wrapper = new JObjectWrapperTypeEntry("JObjectWrapper");
1433 wrapper->setCodeGeneration(TypeEntry::GenerateNothing);
1434 addType(wrapper);
1437 addType(new ThreadTypeEntry());
1438 addType(new VoidTypeEntry());
1440 // Predefined containers...
1441 addType(new ContainerTypeEntry("QList", ContainerTypeEntry::ListContainer));
1442 addType(new ContainerTypeEntry("QStringList", ContainerTypeEntry::StringListContainer));
1443 addType(new ContainerTypeEntry("QLinkedList", ContainerTypeEntry::LinkedListContainer));
1444 addType(new ContainerTypeEntry("QVector", ContainerTypeEntry::VectorContainer));
1445 addType(new ContainerTypeEntry("QStack", ContainerTypeEntry::StackContainer));
1446 addType(new ContainerTypeEntry("QSet", ContainerTypeEntry::SetContainer));
1447 addType(new ContainerTypeEntry("QMap", ContainerTypeEntry::MapContainer));
1448 addType(new ContainerTypeEntry("QHash", ContainerTypeEntry::HashContainer));
1449 addType(new ContainerTypeEntry("QPair", ContainerTypeEntry::PairContainer));
1450 addType(new ContainerTypeEntry("QQueue", ContainerTypeEntry::QueueContainer));
1451 addType(new ContainerTypeEntry("QMultiMap", ContainerTypeEntry::MultiMapContainer));
1453 // Custom types...
1454 addType(new QModelIndexTypeEntry());
1456 addRemoveFunctionToTemplates(this);
1459 bool TypeDatabase::parseFile(const QString &filename, bool generate)
1461 QFile file(filename);
1462 Q_ASSERT(file.exists());
1463 QXmlInputSource source(&file);
1465 int count = m_entries.size();
1467 QXmlSimpleReader reader;
1468 Handler handler(this, generate);
1470 reader.setContentHandler(&handler);
1471 reader.setErrorHandler(&handler);
1473 bool ok = reader.parse(&source, false);
1475 int newCount = m_entries.size();
1477 ReportHandler::debugSparse(QString::fromLatin1("Parsed: '%1', %2 new entries")
1478 .arg(filename)
1479 .arg(newCount - count));
1481 return ok;
1484 QString PrimitiveTypeEntry::javaObjectName() const
1486 static QHash<QString, QString> table;
1487 if (table.isEmpty()) {
1488 table["boolean"] = "Boolean";
1489 table["byte"] = "Byte";
1490 table["char"] = "Character";
1491 table["short"] = "Short";
1492 table["int"] = "Integer";
1493 table["long"] = "Long";
1494 table["float"] = "Float";
1495 table["double"] = "Double";
1497 Q_ASSERT(table.contains(targetLangName()));
1498 return table[targetLangName()];
1501 ContainerTypeEntry *TypeDatabase::findContainerType(const QString &name)
1503 QString template_name = name;
1505 int pos = name.indexOf('<');
1506 if (pos > 0)
1507 template_name = name.left(pos);
1509 TypeEntry *type_entry = findType(template_name);
1510 if (type_entry && type_entry->isContainer())
1511 return static_cast<ContainerTypeEntry *>(type_entry);
1512 return 0;
1515 PrimitiveTypeEntry *TypeDatabase::findTargetLangPrimitiveType(const QString &java_name)
1517 foreach (QList<TypeEntry *> entries, m_entries.values()) {
1518 foreach (TypeEntry *e, entries) {
1519 if (e && e->isPrimitive()) {
1520 PrimitiveTypeEntry *pe = static_cast<PrimitiveTypeEntry *>(e);
1521 if (pe->targetLangName() == java_name && pe->preferredConversion())
1522 return pe;
1527 return 0;
1530 IncludeList TypeDatabase::extraIncludes(const QString &className)
1532 ComplexTypeEntry *typeEntry = findComplexType(className);
1533 if (typeEntry != 0)
1534 return typeEntry->extraIncludes();
1535 else
1536 return IncludeList();
1541 QString Include::toString() const
1543 if (type == IncludePath)
1544 return "#include <" + name + '>';
1545 else if (type == LocalPath)
1546 return "#include \"" + name + "\"";
1547 else
1548 return "import " + name + ";";
1551 QString Modification::accessModifierString() const
1553 if (isPrivate()) return "private";
1554 if (isProtected()) return "protected";
1555 if (isPublic()) return "public";
1556 if (isFriendly()) return "friendly";
1557 return QString();
1560 FunctionModificationList ComplexTypeEntry::functionModifications(const QString &signature) const
1562 FunctionModificationList lst;
1563 for (int i=0; i<m_function_mods.count(); ++i) {
1564 FunctionModification mod = m_function_mods.at(i);
1565 if (mod.signature == signature) {
1566 lst << mod;
1570 return lst;
1573 FieldModification ComplexTypeEntry::fieldModification(const QString &name) const
1575 for (int i=0; i<m_field_mods.size(); ++i)
1576 if (m_field_mods.at(i).name == name)
1577 return m_field_mods.at(i);
1578 FieldModification mod;
1579 mod.name = name;
1580 mod.modifiers = FieldModification::Readable | FieldModification::Writable;
1581 return mod;
1584 QString ContainerTypeEntry::javaPackage() const
1586 if (m_type == PairContainer)
1587 return "com.trolltech.qt";
1588 return "java.util";
1591 QString ContainerTypeEntry::targetLangName() const
1594 switch (m_type) {
1595 case StringListContainer: return "List";
1596 case ListContainer: return "List";
1597 case LinkedListContainer: return "LinkedList";
1598 case VectorContainer: return "List";
1599 case StackContainer: return "Stack";
1600 case QueueContainer: return "Queue";
1601 case SetContainer: return "Set";
1602 case MapContainer: return "SortedMap";
1603 case MultiMapContainer: return "SortedMap";
1604 case HashContainer: return "HashMap";
1605 // case MultiHashCollectio: return "MultiHash";
1606 case PairContainer: return "QPair";
1607 default:
1608 qWarning("bad type... %d", m_type);
1609 break;
1611 return QString();
1614 QString ContainerTypeEntry::qualifiedCppName() const
1616 if (m_type == StringListContainer)
1617 return "QStringList";
1618 return ComplexTypeEntry::qualifiedCppName();
1621 QString EnumTypeEntry::javaQualifier() const
1623 TypeEntry *te = TypeDatabase::instance()->findType(m_qualifier);
1624 if (te != 0)
1625 return te->targetLangName();
1626 else
1627 return m_qualifier;
1630 QString EnumTypeEntry::jniName() const
1632 return "jint";
1635 QString FlagsTypeEntry::jniName() const
1637 return "jint";
1640 void EnumTypeEntry::addEnumValueRedirection(const QString &rejected, const QString &usedValue)
1642 m_enum_redirections << EnumValueRedirection(rejected, usedValue);
1645 QString EnumTypeEntry::enumValueRedirection(const QString &value) const
1647 for (int i=0; i<m_enum_redirections.size(); ++i)
1648 if (m_enum_redirections.at(i).rejected == value)
1649 return m_enum_redirections.at(i).used;
1650 return QString();
1653 QString FlagsTypeEntry::qualifiedTargetLangName() const
1655 return javaPackage() + "." + m_enum->javaQualifier() + "." + targetLangName();
1659 void TypeDatabase::addRejection(const QString &class_name, const QString &function_name,
1660 const QString &field_name, const QString &enum_name)
1662 TypeRejection r;
1663 r.class_name = class_name;
1664 r.function_name = function_name;
1665 r.field_name = field_name;
1666 r.enum_name = enum_name;
1668 m_rejections << r;
1671 bool TypeDatabase::isClassRejected(const QString &class_name)
1673 if (!m_rebuild_classes.isEmpty())
1674 return !m_rebuild_classes.contains(class_name);
1676 foreach (const TypeRejection &r, m_rejections)
1677 if (r.class_name == class_name && r.function_name == "*" && r.field_name == "*" && r.enum_name == "*") {
1678 return true;
1680 return false;
1683 bool TypeDatabase::isEnumRejected(const QString &class_name, const QString &enum_name)
1685 foreach (const TypeRejection &r, m_rejections) {
1686 if (r.enum_name == enum_name
1687 && (r.class_name == class_name || r.class_name == "*")) {
1688 return true;
1692 return false;
1695 bool TypeDatabase::isFunctionRejected(const QString &class_name, const QString &function_name)
1697 foreach (const TypeRejection &r, m_rejections)
1698 if (r.function_name == function_name &&
1699 (r.class_name == class_name || r.class_name == "*"))
1700 return true;
1701 return false;
1705 bool TypeDatabase::isFieldRejected(const QString &class_name, const QString &field_name)
1707 foreach (const TypeRejection &r, m_rejections)
1708 if (r.field_name == field_name &&
1709 (r.class_name == class_name || r.class_name == "*"))
1710 return true;
1711 return false;
1714 FlagsTypeEntry *TypeDatabase::findFlagsType(const QString &name) const
1716 FlagsTypeEntry *fte = (FlagsTypeEntry *) findType(name);
1717 return fte ? fte : (FlagsTypeEntry *) m_flags_entries.value(name);
1720 QString TypeDatabase::globalNamespaceClassName(const TypeEntry * /*entry*/) {
1721 return QLatin1String("Global");
1726 * The Visual Studio 2002 compiler doesn't support these symbols,
1727 * which our typedefs unforntuatly expand to.
1729 QString fixCppTypeName(const QString &name)
1731 if (name == "long long") return "qint64";
1732 else if (name == "unsigned long long") return "quint64";
1733 return name;
1736 QString formattedCodeHelper(QTextStream &s, Indentor &indentor, QStringList &lines) {
1737 bool multilineComment = false;
1738 bool lastEmpty = true;
1739 QString lastLine;
1740 while (!lines.isEmpty()) {
1741 const QString line = lines.takeFirst().trimmed();
1742 if (line.isEmpty()) {
1743 if (!lastEmpty)
1744 s << endl;
1745 lastEmpty = true;
1746 continue;
1747 } else {
1748 lastEmpty = false;
1750 if (line.startsWith("/*"))
1751 multilineComment = true;
1753 if (multilineComment) {
1754 s << indentor;
1755 if (line.startsWith("*"))
1756 s << " ";
1757 s << line << endl;
1758 if (line.endsWith("*/"))
1759 multilineComment = false;
1760 } else if (line.startsWith("}")) {
1761 return line;
1762 } else if (line.endsWith("}")) {
1763 s << indentor << line << endl;
1764 return 0;
1765 } else if(line.endsWith("{")) {
1766 s << indentor << line << endl;
1767 QString tmp;
1769 Indentation indent(indentor);
1770 tmp = formattedCodeHelper(s, indentor, lines);
1772 if (!tmp.isNull()) {
1773 s << indentor << tmp << endl;
1775 lastLine = tmp;
1776 continue;
1777 } else {
1778 s << indentor;
1779 if (!lastLine.isEmpty() &&
1780 !lastLine.endsWith(";") &&
1781 !line.startsWith("@") &&
1782 !line.startsWith("//") &&
1783 !lastLine.startsWith("//") &&
1784 !lastLine.endsWith("}") &&
1785 !line.startsWith("{"))
1786 s << " ";
1787 s << line << endl;
1789 lastLine = line;
1791 return 0;
1795 QTextStream &CodeSnip::formattedCode(QTextStream &s, Indentor &indentor) const
1797 QStringList lst(code().split("\n"));
1798 while (!lst.isEmpty()) {
1799 QString tmp = formattedCodeHelper(s, indentor, lst);
1800 if (!tmp.isNull()) {
1801 s << indentor << tmp << endl;
1804 s.flush();
1805 return s;
1808 QString TemplateInstance::expandCode() const{
1809 TemplateEntry *templateEntry = TypeDatabase::instance()->findTemplate(m_name);
1810 if(templateEntry){
1811 QString res = templateEntry->code();
1812 foreach(QString key, replaceRules.keys()){
1813 res.replace(key, replaceRules[key]);
1815 return "// TEMPLATE - " + m_name + " - START" + res + "// TEMPLATE - " + m_name + " - END";
1817 else{
1818 ReportHandler::warning("insert-template referring to non-existing template '" + m_name + "'");
1820 return QString();
1824 QString CodeSnipAbstract::code() const{
1825 QString res;
1826 foreach(CodeSnipFragment *codeFrag, codeList){
1827 res.append(codeFrag->code());
1829 return res;
1832 QString CodeSnipFragment::code() const{
1833 if(m_instance)
1834 return m_instance->expandCode();
1835 else
1836 return m_code;
1839 QString FunctionModification::toString() const
1841 QString str = signature + QLatin1String("->");
1842 if (modifiers & AccessModifierMask) {
1843 switch (modifiers & AccessModifierMask) {
1844 case Private: str += QLatin1String("private"); break;
1845 case Protected: str += QLatin1String("protected"); break;
1846 case Public: str += QLatin1String("public"); break;
1847 case Friendly: str += QLatin1String("friendly"); break;
1851 if (modifiers & Final) str += QLatin1String("final");
1852 if (modifiers & NonFinal) str += QLatin1String("non-final");
1854 if (modifiers & Readable) str += QLatin1String("readable");
1855 if (modifiers & Writable) str += QLatin1String("writable");
1857 if (modifiers & CodeInjection) {
1858 foreach (CodeSnip s, snips) {
1859 str += QLatin1String("\n//code injection:\n");
1860 str += s.code();
1864 if (modifiers & Rename) str += QLatin1String("renamed:") + renamedToName;
1866 if (modifiers & Deprecated) str += QLatin1String("deprecate");
1868 if (modifiers & ReplaceExpression) str += QLatin1String("replace-expression");
1870 return str;
1873 static void removeFunction(ComplexTypeEntry *e, const char *signature)
1875 FunctionModification mod;
1876 mod.signature = QMetaObject::normalizedSignature(signature);
1877 mod.removal = TypeSystem::All;
1879 e->addFunctionModification(mod);
1885 static void injectCode(ComplexTypeEntry *e,
1886 const char *signature,
1887 const QByteArray &code,
1888 const ArgumentMap &args)
1890 CodeSnip snip;
1891 snip.language = TypeSystem::NativeCode;
1892 snip.position = CodeSnip::Beginning;
1893 snip.addCode(QString::fromLatin1(code));
1894 snip.argumentMap = args;
1896 FunctionModification mod;
1897 mod.signature = QMetaObject::normalizedSignature(signature);
1898 mod.snips << snip;
1899 mod.modifiers = Modification::CodeInjection;
1900 e->addFunctionModification(mod);
1904 static void addRemoveFunctionToTemplates(TypeDatabase *db)
1906 ContainerTypeEntry *qvector = db->findContainerType(QLatin1String("QVector"));
1907 removeFunction(qvector, "constData() const");
1908 removeFunction(qvector, "data() const");
1909 removeFunction(qvector, "data()");
1910 removeFunction(qvector, "first()");
1911 removeFunction(qvector, "last()");
1912 removeFunction(qvector, "operator[](int)");
1913 removeFunction(qvector, "operator[](int) const");
1914 removeFunction(qvector, "operator=(QVector<T>)");
1916 ContainerTypeEntry *qlist = db->findContainerType(QLatin1String("QList"));
1917 removeFunction(qlist, "constData() const");
1918 removeFunction(qlist, "data() const");
1919 removeFunction(qlist, "data()");
1920 removeFunction(qlist, "back()");
1921 removeFunction(qlist, "front()");
1922 removeFunction(qlist, "first()");
1923 removeFunction(qlist, "last()");
1924 removeFunction(qlist, "operator[](int)");
1925 removeFunction(qlist, "operator[](int) const");
1926 removeFunction(qlist, "operator=(QList<T>)");
1928 ContainerTypeEntry *qqueue = db->findContainerType(QLatin1String("QQueue"));
1929 removeFunction(qqueue, "head() const");
1931 // QtScript: The next part is Java-specific, skip it for now...
1932 return;
1934 ArgumentMap args1;
1935 args1[1] = QLatin1String("$1");
1936 ArgumentMap args2 = args1;
1937 args2[2] = QLatin1String("$2");
1939 QByteArray code =
1940 "\nif ($1 >= __qt_this->size() || $1 < 0) {"
1941 "\n __jni_env->ThrowNew(__jni_env->FindClass(\"java/lang/IndexOutOfBoundsException\"),"
1942 "\n QString::fromLatin1(\"Accessing container of size %3 at %4\")"
1943 "\n .arg(__qt_this->size()).arg($1).toLatin1());"
1944 "\n return;"
1945 "\n}";
1947 QByteArray code_with_return = QByteArray(code).replace("return;", "return 0;");
1949 QByteArray code_index_length =
1950 "\nif ($1 < 0 || $2 < 0 || ($1 + $2) >= __qt_this->size()) {"
1951 "\n __jni_env->ThrowNew(__jni_env->FindClass(\"java/lang/IndexOutOfBoundsException\"),"
1952 "\n QString::fromLatin1(\"Accessing container of size %3 from %4 to %5\")"
1953 "\n .arg(__qt_this->size()).arg($1).arg($1+$2).toLatin1());"
1954 "\n return;"
1955 "\n}";
1957 QByteArray code_non_empty =
1958 "\nif (__qt_this->isEmpty()) {"
1959 "\n __jni_env->ThrowNew(__jni_env->FindClass(\"java/lang/IndexOutOfBoundsException\"),"
1960 "\n QString::fromLatin1(\"Accessing empty container...\").toLatin1());"
1961 "\n return;"
1962 "\n}";
1964 QByteArray code_two_indices =
1965 "\nif ($1 < 0 || $2 < 0 || $1 >= __qt_this->size() || $2 >= __qt_this->size()) {"
1966 "\n __jni_env->ThrowNew(__jni_env->FindClass(\"java/lang/IndexOutOfBoundsException\"),"
1967 "\n QString::fromLatin1(\"Accessing container of size %3 from %4 to %5\")"
1968 "\n .arg(__qt_this->size()).arg($1).arg($1+$2).toLatin1());"
1969 "\n return;"
1970 "\n}";
1972 { // QVector safty...
1973 injectCode(qvector, "at(int) const", code_with_return, args1);
1974 injectCode(qvector, "replace(int,T)", code, args1);
1975 injectCode(qvector, "remove(int)", code, args1);
1976 injectCode(qvector, "remove(int, int)", code_index_length, args2);
1977 injectCode(qvector, "pop_back()", code_non_empty, ArgumentMap());
1978 injectCode(qvector, "pop_front()", code_non_empty, ArgumentMap());
1981 { // QList safty...
1982 injectCode(qlist, "at(int) const", code_with_return, args1);
1983 injectCode(qlist, "replace(int, T)", code, args1);
1984 injectCode(qlist, "pop_back()", code_non_empty, ArgumentMap());
1985 injectCode(qlist, "pop_front()", code_non_empty, ArgumentMap());
1986 injectCode(qlist, "swap(int, int)", code_two_indices, args2);
1987 injectCode(qlist, "move(int, int)", code_two_indices, args2);
1988 injectCode(qlist, "removeAt(int)", code, args1);
1989 injectCode(qlist, "takeAt(int)", code_with_return, args1);