summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcelo Lira <marcelo.lira@openbossa.org>2011-07-25 22:44:53 -0300
committerHugo Parente Lima <hugo.pl@gmail.com>2012-03-09 19:10:19 -0300
commit35ab8b8e722b73a3a3ed9312379f6ab849252e19 (patch)
tree1411f293a32e125874b4b092bb96788b8f583d85
parente7fdca6465740132bd881ffd9d20e61be47472d0 (diff)
Added improved functionality for the 'conversion-rule' tag.
It works for primitive, container and value types. Object types doesn't have conversion rules because they can not have implicit conversions, and the regular conversion is always the same (get C++ object held on Python wrapper, and finding/creating a Python wrapper to a C++ pointer). Unit tests were added. Documentation was updated. Reviewed by Luciano Wolf <luciano.wolf@openbossa.org> Reviewed by Renato Ara├║jo <renato.filho@openbossa.org>
-rw-r--r--doc/typesystem.rst1
-rw-r--r--doc/typesystem_conversionrule.rst113
-rw-r--r--tests/testconversionruletag.cpp177
-rw-r--r--tests/testconversionruletag.h7
-rw-r--r--typesystem.cpp346
-rw-r--r--typesystem.h92
-rw-r--r--typesystem_p.h67
7 files changed, 714 insertions, 89 deletions
diff --git a/doc/typesystem.rst b/doc/typesystem.rst
index 098a9d98..69dda43a 100644
--- a/doc/typesystem.rst
+++ b/doc/typesystem.rst
@@ -23,6 +23,7 @@ can be found in the PySide/<QT_MODULE_NAME> directory of the PySide package.
typesystem_arguments
typesystem_solving_compilation
typesystem_templates
+ typesystem_conversionrule
typesystem_documentation
diff --git a/doc/typesystem_conversionrule.rst b/doc/typesystem_conversionrule.rst
new file mode 100644
index 00000000..c62d5bbf
--- /dev/null
+++ b/doc/typesystem_conversionrule.rst
@@ -0,0 +1,113 @@
+.. _conversion-rule-tag:
+
+Conversion Rule Tag
+-------------------
+
+.. _conversion-rule:
+
+conversion-rule
+^^^^^^^^^^^^^^^
+
+ The **conversion-rule** tag specifies how a **primitive-type**, a **container-type**,
+ or a **value-type** may be converted to and from the native C++ language types to the
+ target language types.
+
+ .. code-block:: xml
+
+ <value-type>
+ <conversion-rule>
+ <native-to-target>
+ // Code to convert a native value to a target language object.
+ </native-to-target>
+ <target-to-native>
+ <add-conversion type='TARGETTYPEA' check='TARGETTYPEA_CHECK(%in)'>
+ // Code to convert target language type object of type TARGETTYPEA
+ // to the C++ native type represented by the value/primitive/container-type.
+ </add-conversion>
+ <add-conversion type='TARGETTYPEB' check='TARGETTYPEB_CHECK(%in)'>
+ // Code to convert target language type object of type TARGETTYPEB
+ // to the C++ native type represented by the value/primitive/container-type.
+ </add-conversion>
+ </target-to-native>
+ </conversion-rule>
+ </value-type>
+
+ The example above show the structure of a complete conversion rule. Each of the
+ child tags comprising the conversion rule are described in their own sections
+ below.
+
+
+.. _native-to-target:
+
+native-to-target
+^^^^^^^^^^^^^^^^
+
+ The **native-to-target** tag tells how to convert a native C++ value to its
+ target language equivalent. The text inside the tag is a C++ code the takes
+ an input value an does what's needed to convert it to the output value.
+ ``insert-template`` tags may be used to insert commonly repeating code.
+
+ .. code-block:: xml
+
+ <conversion-rule>
+ <native-to-target>
+ // Code to convert a native value to a target language object.
+ </native-to-target>
+ </conversion-rule>
+
+ Use the replace node to modify the template code.
+ Notice that the generator must provide type system variables for the input
+ and output values and types, namely **%in**, **%out**, **%INTYPE** and
+ **%OUTTYPE**. In the case of container types, **%INTYPE** refers to the
+ full container type (e.g. **"list<int>"**) and **%INTYPE_0**, **%INTYPE_1**,
+ **%INTYPE_#**, should be replaced by the types used in the container template
+ (e.g. **%INTYPE_0** correspondes to **"int"** for **"list<int>"**).
+
+
+.. _target-to-native:
+
+target-to-native
+^^^^^^^^^^^^^^^^
+
+ The **target-to-native** tag encloses at least one, but usually many, conversions
+ from target language values to C++ native values. The *optional* attribute
+ ``replace`` tells if the target language to C++ conversions will be added to, or if
+ they will replace the implicit conversions collected by *ApiExtractor*. The default
+ value for it is *yes*.
+
+
+ .. code-block:: xml
+
+ <conversion-rule>
+ <target-to-native replace='yes|no'>\
+ // List of target to native conversions meant to replace or expand
+ // the already existing implicit conversions.
+ </target-to-native>
+ </conversion-rule>
+
+
+.. _add-conversion:
+
+add-conversion
+^^^^^^^^^^^^^^
+
+ Each **add-conversion** tag adds a rule for conversion of a target language type,
+ indicated by the ``type`` attribute, to the C++ native type represented by the
+ **primitive-type**, a **container-type**, or **value-type**, to which the parent
+ **conversion-rule** belongs.
+
+ .. code-block:: xml
+
+ <target-to-native>
+ <add-conversion type='TARGETTYPE' check='TARGETTYPECHECK(%in)'>
+ // Code to convert target language type object of type TARGETTYPE_A
+ // to the C++ native type represented by the value/primitive/container-type.
+ </add-conversion>
+ <target-to-native>
+
+ The ``check`` attribute tells how a target value should be checked to see if it belongs to
+ the type expected. This attribute is *optional*, for it can be derived from the ``type``
+ attribute, but it isn't unusual that some special check is needed. The variables
+ **%in**, **%out**, **%INTYPE**, **%INTYPE_#**, and **%OUTTYPE**, must be provided by
+ the generator as in the ``native-to-target`` tag.
+
diff --git a/tests/testconversionruletag.cpp b/tests/testconversionruletag.cpp
index 609a806b..6228ef28 100644
--- a/tests/testconversionruletag.cpp
+++ b/tests/testconversionruletag.cpp
@@ -27,7 +27,7 @@
#include <QFile>
#include <QTemporaryFile>
-void TestConversionRuleTag::testConversionRuleTag()
+void TestConversionRuleTag::testConversionRuleTagWithFile()
{
// temp file used later
const char conversionData[] = "Hi! I'm a conversion rule.";
@@ -38,7 +38,7 @@ void TestConversionRuleTag::testConversionRuleTag()
const char cppCode[] = "struct A {};";
QString xmlCode = "\
- <typesystem package=\"Foo\">\
+ <typesystem package='Foo'>\
<value-type name='A'>\
<conversion-rule file='"+ file.fileName() +"' />\
</value-type>\
@@ -52,6 +52,179 @@ void TestConversionRuleTag::testConversionRuleTag()
QCOMPARE(typeEntry->conversionRule(), QString(conversionData));
}
+void TestConversionRuleTag::testConversionRuleTagReplace()
+{
+ const char cppCode[] = "\
+ struct A {\
+ A();\
+ A(const char*, int);\
+ };\
+ struct B {\
+ A createA();\
+ };\
+ ";
+ const char* xmlCode = "\
+ <typesystem package='Foo'>\
+ <primitive-type name='int'/>\
+ <primitive-type name='char'/>\
+ <primitive-type name='A'>\
+ <conversion-rule>\
+ <native-to-target>\
+ DoThis();\
+ return ConvertFromCppToPython(%IN);\
+ </native-to-target>\
+ <target-to-native>\
+ <add-conversion type='TargetNone' check='%IN == Target_None'>\
+ DoThat();\
+ DoSomething();\
+ %OUT = A();\
+ </add-conversion>\
+ <add-conversion type='B' check='CheckIfInputObjectIsB(%IN)'>\
+ %OUT = %IN.createA();\
+ </add-conversion>\
+ <add-conversion type='String' check='String_Check(%IN)'>\
+ %OUT = new A(String_AsString(%IN), String_GetSize(%IN));\
+ </add-conversion>\
+ </target-to-native>\
+ </conversion-rule>\
+ </primitive-type>\
+ <value-type name='B'/>\
+ </typesystem>";
+
+ TestUtil t(cppCode, xmlCode);
+ TypeDatabase* typeDb = TypeDatabase::instance();
+ PrimitiveTypeEntry* typeA = typeDb->findPrimitiveType("A");
+ QVERIFY(typeA);
+
+ CustomConversion* conversion = typeA->customConversion();
+ QVERIFY(conversion);
+
+ QCOMPARE(typeA, conversion->ownerType());
+ QCOMPARE(conversion->nativeToTargetConversion().trimmed(), QString("DoThis(); return ConvertFromCppToPython(%IN);"));
+
+ QVERIFY(conversion->replaceOriginalTargetToNativeConversions());
+ QVERIFY(conversion->hasTargetToNativeConversions());
+ QCOMPARE(conversion->targetToNativeConversions().size(), 3);
+
+ CustomConversion::TargetToNativeConversion* toNative = conversion->targetToNativeConversions().at(0);
+ QVERIFY(toNative);
+ QCOMPARE(toNative->sourceTypeName(), QString("TargetNone"));
+ QVERIFY(toNative->isCustomType());
+ QCOMPARE(toNative->sourceType(), (const TypeEntry*)0);
+ QCOMPARE(toNative->sourceTypeCheck(), QString("%IN == Target_None"));
+ QCOMPARE(toNative->conversion().trimmed(), QString("DoThat(); DoSomething(); %OUT = A();"));
+
+ toNative = conversion->targetToNativeConversions().at(1);
+ QVERIFY(toNative);
+ QCOMPARE(toNative->sourceTypeName(), QString("B"));
+ QVERIFY(!toNative->isCustomType());
+ TypeEntry* typeB = typeDb->findType("B");
+ QVERIFY(typeB);
+ QCOMPARE(toNative->sourceType(), typeB);
+ QCOMPARE(toNative->sourceTypeCheck(), QString("CheckIfInputObjectIsB(%IN)"));
+ QCOMPARE(toNative->conversion().trimmed(), QString("%OUT = %IN.createA();"));
+
+ toNative = conversion->targetToNativeConversions().at(2);
+ QVERIFY(toNative);
+ QCOMPARE(toNative->sourceTypeName(), QString("String"));
+ QVERIFY(toNative->isCustomType());
+ QCOMPARE(toNative->sourceType(), (const TypeEntry*)0);
+ QCOMPARE(toNative->sourceTypeCheck(), QString("String_Check(%IN)"));
+ QCOMPARE(toNative->conversion().trimmed(), QString("%OUT = new A(String_AsString(%IN), String_GetSize(%IN));"));
+}
+
+void TestConversionRuleTag::testConversionRuleTagAdd()
+{
+ const char cppCode[] = "\
+ struct Date {\
+ Date();\
+ Date(int, int, int);\
+ };\
+ ";
+ const char* xmlCode = "\
+ <typesystem package='Foo'>\
+ <primitive-type name='int'/>\
+ <value-type name='Date'>\
+ <conversion-rule>\
+ <target-to-native replace='no'>\
+ <add-conversion type='TargetDate' check='TargetDate_Check(%IN)'>\
+ if (!TargetDateTimeAPI) TargetDateTime_IMPORT;\
+ %OUT = new Date(TargetDate_Day(%IN), TargetDate_Month(%IN), TargetDate_Year(%IN));\
+ </add-conversion>\
+ </target-to-native>\
+ </conversion-rule>\
+ </value-type>\
+ </typesystem>";
+
+ TestUtil t(cppCode, xmlCode);
+ AbstractMetaClass* classA = t.builder()->classes().findClass("Date");
+ QVERIFY(classA);
+
+ CustomConversion* conversion = classA->typeEntry()->customConversion();
+ QVERIFY(conversion);
+
+ QCOMPARE(conversion->nativeToTargetConversion(), QString());
+
+ QVERIFY(!conversion->replaceOriginalTargetToNativeConversions());
+ QVERIFY(conversion->hasTargetToNativeConversions());
+ QCOMPARE(conversion->targetToNativeConversions().size(), 1);
+
+ CustomConversion::TargetToNativeConversion* toNative = conversion->targetToNativeConversions().first();
+ QVERIFY(toNative);
+ QCOMPARE(toNative->sourceTypeName(), QString("TargetDate"));
+ QVERIFY(toNative->isCustomType());
+ QCOMPARE(toNative->sourceType(), (const TypeEntry*)0);
+ QCOMPARE(toNative->sourceTypeCheck(), QString("TargetDate_Check(%IN)"));
+ QCOMPARE(toNative->conversion().trimmed(), QString("if (!TargetDateTimeAPI) TargetDateTime_IMPORT; %OUT = new Date(TargetDate_Day(%IN), TargetDate_Month(%IN), TargetDate_Year(%IN));"));
+}
+
+void TestConversionRuleTag::testConversionRuleTagWithInsertTemplate()
+{
+ const char cppCode[] = "struct A {};";
+ const char* xmlCode = "\
+ <typesystem package='Foo'>\
+ <primitive-type name='int'/>\
+ <template name='native_to_target'>\
+ return ConvertFromCppToPython(%IN);\
+ </template>\
+ <template name='target_to_native'>\
+ %OUT = %IN.createA();\
+ </template>\
+ <primitive-type name='A'>\
+ <conversion-rule>\
+ <native-to-target>\
+ <insert-template name='native_to_target'/>\
+ </native-to-target>\
+ <target-to-native>\
+ <add-conversion type='TargetType'>\
+ <insert-template name='target_to_native'/>\
+ </add-conversion>\
+ </target-to-native>\
+ </conversion-rule>\
+ </primitive-type>\
+ </typesystem>";
+
+ TestUtil t(cppCode, xmlCode);
+ TypeDatabase* typeDb = TypeDatabase::instance();
+ PrimitiveTypeEntry* typeA = typeDb->findPrimitiveType("A");
+ QVERIFY(typeA);
+
+ CustomConversion* conversion = typeA->customConversion();
+ QVERIFY(conversion);
+
+ QCOMPARE(typeA, conversion->ownerType());
+ QCOMPARE(conversion->nativeToTargetConversion().trimmed(),
+ QString("// TEMPLATE - native_to_target - START return ConvertFromCppToPython(%IN); // TEMPLATE - native_to_target - END"));
+
+ QVERIFY(conversion->hasTargetToNativeConversions());
+ QCOMPARE(conversion->targetToNativeConversions().size(), 1);
+
+ CustomConversion::TargetToNativeConversion* toNative = conversion->targetToNativeConversions().first();
+ QVERIFY(toNative);
+ QCOMPARE(toNative->conversion().trimmed(),
+ QString("// TEMPLATE - target_to_native - START %OUT = %IN.createA(); // TEMPLATE - target_to_native - END"));
+}
+
QTEST_APPLESS_MAIN(TestConversionRuleTag)
#include "testconversionruletag.moc"
diff --git a/tests/testconversionruletag.h b/tests/testconversionruletag.h
index afab6888..8ab1481b 100644
--- a/tests/testconversionruletag.h
+++ b/tests/testconversionruletag.h
@@ -29,7 +29,10 @@ class TestConversionRuleTag : public QObject
{
Q_OBJECT
private slots:
- void testConversionRuleTag();
+ void testConversionRuleTagWithFile();
+ void testConversionRuleTagReplace();
+ void testConversionRuleTagAdd();
+ void testConversionRuleTagWithInsertTemplate();
};
-#endif \ No newline at end of file
+#endif
diff --git a/typesystem.cpp b/typesystem.cpp
index 9976d47f..daffebaa 100644
--- a/typesystem.cpp
+++ b/typesystem.cpp
@@ -33,6 +33,8 @@ static QString strings_char = QLatin1String("char");
static QString strings_jchar = QLatin1String("jchar");
static QString strings_jobject = QLatin1String("jobject");
+static QList<CustomConversion*> customConversionsForReview = QList<CustomConversion*>();
+
Handler::Handler(TypeDatabase* database, bool generate)
: m_database(database), m_generate(generate ? TypeEntry::GenerateAll : TypeEntry::GenerateForSubclass)
{
@@ -70,6 +72,9 @@ Handler::Handler(TypeDatabase* database, bool generate)
tagNames["reject-enum-value"] = StackElement::RejectEnumValue;
tagNames["replace-type"] = StackElement::ReplaceType;
tagNames["conversion-rule"] = StackElement::ConversionRule;
+ tagNames["native-to-target"] = StackElement::NativeToTarget;
+ tagNames["target-to-native"] = StackElement::TargetToNative;
+ tagNames["add-conversion"] = StackElement::AddConversion;
tagNames["modify-argument"] = StackElement::ModifyArgument;
tagNames["remove-argument"] = StackElement::RemoveArgument;
tagNames["remove-default-expression"] = StackElement::RemoveDefaultExpression;
@@ -155,6 +160,10 @@ bool Handler::endElement(const QString &, const QString &localName, const QStrin
if (m_generate == TypeEntry::GenerateAll) {
TypeDatabase::instance()->addGlobalUserFunctions(m_contextStack.top()->addedFunctions);
TypeDatabase::instance()->addGlobalUserFunctionModifications(m_contextStack.top()->functionMods);
+ foreach (CustomConversion* customConversion, customConversionsForReview) {
+ foreach (CustomConversion::TargetToNativeConversion* toNative, customConversion->targetToNativeConversions())
+ toNative->setSourceType(m_database->findType(toNative->sourceTypeName()));
+ }
}
break;
case StackElement::ObjectTypeEntry:
@@ -174,6 +183,26 @@ bool Handler::endElement(const QString &, const QString &localName, const QStrin
}
}
break;
+ case StackElement::NativeToTarget:
+ case StackElement::AddConversion: {
+ CustomConversion* customConversion = static_cast<TypeEntry*>(m_current->entry)->customConversion();
+ if (!customConversion) {
+ m_error = "CustomConversion object is missing.";
+ return false;
+ }
+
+ QString code = m_contextStack.top()->codeSnips.takeLast().code();
+ if (m_current->type == StackElement::AddConversion) {
+ if (customConversion->targetToNativeConversions().isEmpty()) {
+ m_error = "CustomConversion's target to native conversions missing.";
+ return false;
+ }
+ customConversion->targetToNativeConversions().last()->setConversion(code);
+ } else {
+ customConversion->setNativeToTargetConversion(code);
+ }
+ }
+ break;
case StackElement::CustomMetaConstructor: {
m_current->entry->setCustomConstructor(*m_current->value.customFunction);
delete m_current->value.customFunction;
@@ -193,18 +222,28 @@ bool Handler::endElement(const QString &, const QString &localName, const QStrin
m_database->addTemplate(m_current->value.templateEntry);
break;
case StackElement::TemplateInstanceEnum:
- if (m_current->parent->type == StackElement::InjectCode)
+ switch (m_current->parent->type) {
+ case StackElement::InjectCode:
+ case StackElement::NativeToTarget:
+ case StackElement::AddConversion:
m_contextStack.top()->codeSnips.last().addTemplateInstance(m_current->value.templateInstance);
- else if (m_current->parent->type == StackElement::Template)
+ break;
+ case StackElement::Template:
m_current->parent->value.templateEntry->addTemplateInstance(m_current->value.templateInstance);
- else if (m_current->parent->type == StackElement::CustomMetaConstructor
- || m_current->parent->type == StackElement::CustomMetaConstructor)
+ break;
+ case StackElement::CustomMetaConstructor:
+ case StackElement::CustomMetaDestructor:
m_current->parent->value.customFunction->addTemplateInstance(m_current->value.templateInstance);
- else if (m_current->parent->type == StackElement::ConversionRule)
+ break;
+ case StackElement::ConversionRule:
m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.last().addTemplateInstance(m_current->value.templateInstance);
- else if (m_current->parent->type == StackElement::InjectCodeInFunction)
+ break;
+ case StackElement::InjectCodeInFunction:
m_contextStack.top()->functionMods.last().snips.last().addTemplateInstance(m_current->value.templateInstance);
-
+ break;
+ default:
+ break; // nada
+ };
break;
default:
break;
@@ -214,7 +253,8 @@ bool Handler::endElement(const QString &, const QString &localName, const QStrin
|| m_current->type == StackElement::NamespaceTypeEntry
|| m_current->type == StackElement::InterfaceTypeEntry
|| m_current->type == StackElement::ObjectTypeEntry
- || m_current->type == StackElement::ValueTypeEntry) {
+ || m_current->type == StackElement::ValueTypeEntry
+ || m_current->type == StackElement::PrimitiveTypeEntry) {
StackElementContext* context = m_contextStack.pop();
delete context;
}
@@ -247,6 +287,11 @@ bool Handler::characters(const QString &ch)
return true;
}
+ if (m_current->type == StackElement::NativeToTarget || m_current->type == StackElement::AddConversion) {
+ m_contextStack.top()->codeSnips.last().addCode(ch);
+ return true;
+ }
+
if (m_current->parent) {
if ((m_current->type & StackElement::CodeSnipMask)) {
CodeSnipList snips;
@@ -417,11 +462,15 @@ bool Handler::startElement(const QString &, const QString &n,
StackElement* element = new StackElement(m_current);
element->type = tagNames[tagName];
+ if (element->type == StackElement::Root && m_generate == TypeEntry::GenerateAll)
+ customConversionsForReview.clear();
+
if (element->type == StackElement::Root
|| element->type == StackElement::NamespaceTypeEntry
|| element->type == StackElement::InterfaceTypeEntry
|| element->type == StackElement::ObjectTypeEntry
- || element->type == StackElement::ValueTypeEntry) {
+ || element->type == StackElement::ValueTypeEntry
+ || element->type == StackElement::PrimitiveTypeEntry) {
m_contextStack.push(new StackElementContext());
}
@@ -953,6 +1002,13 @@ bool Handler::startElement(const QString &, const QString &n,
attributes["class"] = QString();
attributes["file"] = QString();
break;
+ case StackElement::TargetToNative:
+ attributes["replace"] = QString("yes");
+ break;
+ case StackElement::AddConversion:
+ attributes["type"] = QString();
+ attributes["check"] = QString();
+ break;
case StackElement::RejectEnumValue:
attributes["name"] = "";
break;
@@ -1055,11 +1111,10 @@ bool Handler::startElement(const QString &, const QString &n,
case StackElement::ConversionRule: {
if (topElement.type != StackElement::ModifyArgument
&& topElement.type != StackElement::ValueTypeEntry
- && topElement.type != StackElement::ObjectTypeEntry
&& topElement.type != StackElement::PrimitiveTypeEntry
&& topElement.type != StackElement::ContainerTypeEntry) {
m_error = "Conversion rules can only be specified for argument modification, "
- "value-type, object-type, primitive-type or container-type conversion.";
+ "value-type, primitive-type or container-type conversion.";
return false;
}
@@ -1082,40 +1137,70 @@ bool Handler::startElement(const QString &, const QString &n,
snip.language = lang;
m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.append(snip);
} else {
- if (topElement.entry->hasConversionRule()) {
+ if (topElement.entry->hasConversionRule() || topElement.entry->hasCustomConversion()) {
m_error = "Types can have only one conversion rule";
return false;
}
+ // The old conversion rule tag that uses a file containing the conversion
+ // will be kept temporarily for compatibility reasons.
QString sourceFile = attributes["file"];
- if (sourceFile.isEmpty()) {
- m_error = QString("'file' attribute required; the source file containing the"
- " containing the conversion functions must be provided");
- return false;
- }
-
- //Handler constructor....
- if (m_generate != TypeEntry::GenerateForSubclass
- && m_generate != TypeEntry::GenerateNothing) {
-
- const char* conversionFlag = NATIVE_CONVERSION_RULE_FLAG;
- if (lang == TypeSystem::TargetLangCode)
- conversionFlag = TARGET_CONVERSION_RULE_FLAG;
-
- QFile conversionSource(sourceFile);
- if (conversionSource.open(QIODevice::ReadOnly | QIODevice::Text)) {
- topElement.entry->setConversionRule(conversionFlag + QString::fromUtf8(conversionSource.readAll()));
- } else {
- ReportHandler::warning("File containing conversion code for "
- + topElement.entry->name()
- + " type does not exist or is not readable: "
- + sourceFile);
+ if (!sourceFile.isEmpty()) {
+ if (m_generate != TypeEntry::GenerateForSubclass
+ && m_generate != TypeEntry::GenerateNothing) {
+
+ const char* conversionFlag = NATIVE_CONVERSION_RULE_FLAG;
+ if (lang == TypeSystem::TargetLangCode)
+ conversionFlag = TARGET_CONVERSION_RULE_FLAG;
+
+ QFile conversionSource(sourceFile);
+ if (conversionSource.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ topElement.entry->setConversionRule(conversionFlag + QString::fromUtf8(conversionSource.readAll()));
+ } else {
+ ReportHandler::warning("File containing conversion code for "
+ + topElement.entry->name()
+ + " type does not exist or is not readable: "
+ + sourceFile);
+ }
}
}
+ CustomConversion* customConversion = new CustomConversion(static_cast<TypeEntry*>(m_current->entry));
+ customConversionsForReview.append(customConversion);
}
}
-
+ break;
+ case StackElement::NativeToTarget: {
+ if (topElement.type != StackElement::ConversionRule) {
+ m_error = "Native to Target conversion code can only be specified for custom conversion rules.";
+ return false;
+ }
+ m_contextStack.top()->codeSnips << CodeSnip(0);
+ }
+ break;
+ case StackElement::TargetToNative: {
+ if (topElement.type != StackElement::ConversionRule) {
+ m_error = "Target to Native conversions can only be specified for custom conversion rules.";
+ return false;
+ }
+ bool replace = attributes["replace"] == "yes";
+ static_cast<TypeEntry*>(m_current->entry)->customConversion()->setReplaceOriginalTargetToNativeConversions(replace);
+ }
+ break;
+ case StackElement::AddConversion: {
+ if (topElement.type != StackElement::TargetToNative) {
+ m_error = "Target to Native conversions can only be added inside 'target-to-native' tags.";
+ return false;
+ }
+ QString sourceTypeName = attributes["type"];
+ if (sourceTypeName.isEmpty()) {
+ m_error = "Target to Native conversions must specify the input type with the 'type' attribute.";
+ return false;
+ }
+ QString typeCheck = attributes["check"];
+ static_cast<TypeEntry*>(m_current->entry)->customConversion()->addTargetToNativeConversion(sourceTypeName, typeCheck);
+ m_contextStack.top()->codeSnips << CodeSnip(0);
+ }
break;
case StackElement::ModifyArgument: {
if (topElement.type != StackElement::ModifyFunction
@@ -1692,8 +1777,11 @@ bool Handler::startElement(const QString &, const QString &n,
(topElement.type != StackElement::Template) &&
(topElement.type != StackElement::CustomMetaConstructor) &&
(topElement.type != StackElement::CustomMetaDestructor) &&
+ (topElement.type != StackElement::NativeToTarget) &&
+ (topElement.type != StackElement::AddConversion) &&
(topElement.type != StackElement::ConversionRule)) {
- m_error = "Can only insert templates into code snippets, templates, custom-constructors, custom-destructors or conversion-rule.";
+ m_error = "Can only insert templates into code snippets, templates, custom-constructors, "\
+ "custom-destructors, conversion-rule, native-to-target or add-conversion tags.";
return false;
}
element->value.templateInstance = new TemplateInstance(attributes["name"], since);
@@ -2162,3 +2250,189 @@ bool TypeEntry::isCppPrimitive() const
return res != &cppTypes[N];
}
+
+// Again, stuff to avoid ABI breakage.
+typedef QHash<const TypeEntry*, CustomConversion*> TypeEntryCustomConversionMap;
+Q_GLOBAL_STATIC(TypeEntryCustomConversionMap, typeEntryCustomConversionMap);
+
+TypeEntry::~TypeEntry()
+{
+ if (typeEntryCustomConversionMap()->contains(this)) {
+ CustomConversion* customConversion = typeEntryCustomConversionMap()->value(this);
+ typeEntryCustomConversionMap()->remove(this);
+ delete customConversion;
+ }
+}
+
+bool TypeEntry::hasCustomConversion() const
+{
+ return typeEntryCustomConversionMap()->contains(this);
+}
+void TypeEntry::setCustomConversion(CustomConversion* customConversion)
+{
+ if (customConversion)
+ typeEntryCustomConversionMap()->insert(this, customConversion);
+ else if (typeEntryCustomConversionMap()->contains(this))
+ typeEntryCustomConversionMap()->remove(this);
+}
+CustomConversion* TypeEntry::customConversion() const
+{
+ if (typeEntryCustomConversionMap()->contains(this))
+ return typeEntryCustomConversionMap()->value(this);
+ return 0;
+}
+
+/*
+static void injectCode(ComplexTypeEntry *e,
+ const char *signature,
+ const QByteArray &code,
+ const ArgumentMap &args)
+{
+ CodeSnip snip;
+ snip.language = TypeSystem::NativeCode;
+ snip.position = CodeSnip::Beginning;
+ snip.addCode(QString::fromLatin1(code));
+ snip.argumentMap = args;
+
+ FunctionModification mod;
+ mod.signature = QMetaObject::normalizedSignature(signature);
+ mod.snips << snip;
+ mod.modifiers = Modification::CodeInjection;
+}
+*/
+
+struct CustomConversion::CustomConversionPrivate
+{
+ CustomConversionPrivate(const TypeEntry* ownerType)
+ : ownerType(ownerType), replaceOriginalTargetToNativeConversions(false)
+ {
+ }
+ const TypeEntry* ownerType;
+ QString nativeToTargetConversion;
+ bool replaceOriginalTargetToNativeConversions;
+ TargetToNativeConversions targetToNativeConversions;
+};
+
+struct CustomConversion::TargetToNativeConversion::TargetToNativeConversionPrivate
+{
+ TargetToNativeConversionPrivate()
+ : sourceType(0)
+ {
+ }
+ const TypeEntry* sourceType;
+ QString sourceTypeName;
+ QString sourceTypeCheck;
+ QString conversion;
+};
+
+CustomConversion::CustomConversion(TypeEntry* ownerType)
+{
+ m_d = new CustomConversionPrivate(ownerType);
+ if (ownerType)
+ ownerType->setCustomConversion(this);
+}
+
+CustomConversion::~CustomConversion()
+{
+ foreach (TargetToNativeConversion* targetToNativeConversion, m_d->targetToNativeConversions)
+ delete targetToNativeConversion;
+ m_d->targetToNativeConversions.clear();
+ delete m_d;
+}
+
+const TypeEntry* CustomConversion::ownerType() const
+{
+ return m_d->ownerType;
+}
+
+QString CustomConversion::nativeToTargetConversion() const
+{
+ return m_d->nativeToTargetConversion;
+}
+
+void CustomConversion::setNativeToTargetConversion(const QString& nativeToTargetConversion)
+{
+ m_d->nativeToTargetConversion = nativeToTargetConversion;
+}
+
+bool CustomConversion::replaceOriginalTargetToNativeConversions() const
+{
+ return m_d->replaceOriginalTargetToNativeConversions;
+}
+
+void CustomConversion::setReplaceOriginalTargetToNativeConversions(bool replaceOriginalTargetToNativeConversions)
+{
+ m_d->replaceOriginalTargetToNativeConversions = replaceOriginalTargetToNativeConversions;
+}
+
+bool CustomConversion::hasTargetToNativeConversions() const
+{
+ return !(m_d->targetToNativeConversions.isEmpty());
+}
+
+CustomConversion::TargetToNativeConversions& CustomConversion::targetToNativeConversions()
+{
+ return m_d->targetToNativeConversions;
+}
+
+const CustomConversion::TargetToNativeConversions& CustomConversion::targetToNativeConversions() const
+{
+ return m_d->targetToNativeConversions;
+}
+
+void CustomConversion::addTargetToNativeConversion(const QString& sourceTypeName,
+ const QString& sourceTypeCheck,
+ const QString& conversion)
+{
+ m_d->targetToNativeConversions.append(new TargetToNativeConversion(sourceTypeName, sourceTypeCheck, conversion));
+}
+
+CustomConversion::TargetToNativeConversion::TargetToNativeConversion(const QString& sourceTypeName,
+ const QString& sourceTypeCheck,
+ const QString& conversion)
+{
+ m_d = new TargetToNativeConversionPrivate;
+ m_d->sourceTypeName = sourceTypeName;
+ m_d->sourceTypeCheck = sourceTypeCheck;
+ m_d->conversion = conversion;
+}
+
+CustomConversion::TargetToNativeConversion::~TargetToNativeConversion()
+{
+ delete m_d;
+}
+
+const TypeEntry* CustomConversion::TargetToNativeConversion::sourceType() const
+{
+ return m_d->sourceType;
+}
+
+void CustomConversion::TargetToNativeConversion::setSourceType(const TypeEntry* sourceType)
+{
+ m_d->sourceType = sourceType;
+}
+
+bool CustomConversion::TargetToNativeConversion::isCustomType() const
+{
+ return !(m_d->sourceType);
+}
+
+QString CustomConversion::TargetToNativeConversion::sourceTypeName() const
+{
+ return m_d->sourceTypeName;
+}
+
+QString CustomConversion::TargetToNativeConversion::sourceTypeCheck() const
+{
+ return m_d->sourceTypeCheck;
+}
+
+QString CustomConversion::TargetToNativeConversion::conversion() const
+{
+ return m_d->conversion;
+}
+
+void CustomConversion::TargetToNativeConversion::setConversion(const QString& conversion)
+{
+ m_d->conversion = conversion;
+}
diff --git a/typesystem.h b/typesystem.h
index abba4455..c915cd42 100644
--- a/typesystem.h
+++ b/typesystem.h
@@ -82,7 +82,7 @@ enum Ownership {
};
};
-struct ReferenceCount
+struct APIEXTRACTOR_API ReferenceCount
{
ReferenceCount() {}
enum Action { // 0x01 - 0xff
@@ -102,7 +102,7 @@ struct ReferenceCount
QString varName;
};
-struct ArgumentOwner
+struct APIEXTRACTOR_API ArgumentOwner
{
enum Action {
Invalid = 0x00,
@@ -156,7 +156,7 @@ public:
QList<CodeSnipFragment> codeList;
};
-class CustomFunction : public CodeSnipAbstract
+class APIEXTRACTOR_API CustomFunction : public CodeSnipAbstract
{
public:
CustomFunction(const QString &n = QString()) : name(n) { }
@@ -165,7 +165,7 @@ public:
QString paramName;
};
-class TemplateEntry : public CodeSnipAbstract
+class APIEXTRACTOR_API TemplateEntry : public CodeSnipAbstract
{
public:
TemplateEntry(const QString &name, double vr)
@@ -190,7 +190,7 @@ private:
typedef QHash<QString, TemplateEntry *> TemplateEntryHash;
-class TemplateInstance
+class APIEXTRACTOR_API TemplateInstance
{
public:
TemplateInstance(const QString &name, double vr)
@@ -245,7 +245,7 @@ public:
};
typedef QList<CodeSnip> CodeSnipList;
-struct ArgumentModification
+struct APIEXTRACTOR_API ArgumentModification
{
ArgumentModification(int idx, double vr)
: removedDefaultExpression(false), removed(false),
@@ -436,7 +436,7 @@ private:
};
typedef QList<FunctionModification> FunctionModificationList;
-struct FieldModification: public Modification
+struct APIEXTRACTOR_API FieldModification: public Modification
{
bool isReadable() const
{
@@ -547,7 +547,7 @@ private:
};
typedef QList<AddedFunction> AddedFunctionList;
-struct ExpensePolicy
+struct APIEXTRACTOR_API ExpensePolicy
{
ExpensePolicy() : limit(-1) {}
int limit;
@@ -561,7 +561,7 @@ struct ExpensePolicy
class InterfaceTypeEntry;
class ObjectTypeEntry;
-class DocModification
+class APIEXTRACTOR_API DocModification
{
public:
enum Mode {
@@ -614,6 +614,8 @@ private:
typedef QList<DocModification> DocModificationList;
+class CustomConversion;
+
class APIEXTRACTOR_API TypeEntry
{
public:
@@ -662,7 +664,7 @@ public:
{
};
- virtual ~TypeEntry() { }
+ virtual ~TypeEntry();
Type type() const
{
@@ -949,17 +951,23 @@ public:
return m_version;
}
+ /// TODO-CONVERTER: mark as deprecated
bool hasNativeConversionRule() const
{
return m_conversionRule.startsWith(NATIVE_CONVERSION_RULE_FLAG);
}
+ /// TODO-CONVERTER: mark as deprecated
bool hasTargetConversionRule() const
{
return m_conversionRule.startsWith(TARGET_CONVERSION_RULE_FLAG);
}
bool isCppPrimitive() const;
+
+ bool hasCustomConversion() const;
+ void setCustomConversion(CustomConversion* customConversion);
+ CustomConversion* customConversion() const;
private:
QString m_name;
Type m_type;
@@ -1155,7 +1163,7 @@ private:
typedef QList<const PrimitiveTypeEntry*> PrimitiveTypeEntryList;
-struct EnumValueRedirection
+struct APIEXTRACTOR_API EnumValueRedirection
{
EnumValueRedirection(const QString &rej, const QString &us)
: rejected(rej),
@@ -1729,7 +1737,7 @@ public:
};
-class ValueTypeEntry : public ComplexTypeEntry
+class APIEXTRACTOR_API ValueTypeEntry : public ComplexTypeEntry
{
public:
ValueTypeEntry(const QString &name, double vr) : ComplexTypeEntry(name, BasicValueType, vr) { }
@@ -1749,7 +1757,7 @@ protected:
};
-class StringTypeEntry : public ValueTypeEntry
+class APIEXTRACTOR_API StringTypeEntry : public ValueTypeEntry
{
public:
StringTypeEntry(const QString &name, double vr)
@@ -1768,7 +1776,7 @@ public:
}
};
-class CharTypeEntry : public ValueTypeEntry
+class APIEXTRACTOR_API CharTypeEntry : public ValueTypeEntry
{
public:
CharTypeEntry(const QString &name, double vr) : ValueTypeEntry(name, CharType, vr)
@@ -1789,7 +1797,7 @@ public:
}
};
-class VariantTypeEntry: public ValueTypeEntry
+class APIEXTRACTOR_API VariantTypeEntry: public ValueTypeEntry
{
public:
VariantTypeEntry(const QString &name, double vr) : ValueTypeEntry(name, VariantType, vr) { }
@@ -1889,7 +1897,7 @@ private:
InterfaceTypeEntry *m_interface;
};
-struct TypeRejection
+struct APIEXTRACTOR_API TypeRejection
{
QString class_name;
QString function_name;
@@ -1897,6 +1905,56 @@ struct TypeRejection
QString enum_name;
};
-QString fixCppTypeName(const QString &name);
+APIEXTRACTOR_API QString fixCppTypeName(const QString &name);
+
+class APIEXTRACTOR_API CustomConversion
+{
+public:
+ CustomConversion(TypeEntry* ownerType);
+ ~CustomConversion();
+
+ const TypeEntry* ownerType() const;
+ QString nativeToTargetConversion() const;
+ void setNativeToTargetConversion(const QString& nativeToTargetConversion);
+
+ class APIEXTRACTOR_API TargetToNativeConversion
+ {
+ public:
+ TargetToNativeConversion(const QString& sourceTypeName,
+ const QString& sourceTypeCheck,
+ const QString& conversion = QString());
+ ~TargetToNativeConversion();
+
+ const TypeEntry* sourceType() const;
+ void setSourceType(const TypeEntry* sourceType);
+ bool isCustomType() const;
+ QString sourceTypeName() const;
+ QString sourceTypeCheck() const;
+ QString conversion() const;
+ void setConversion(const QString& conversion);
+ private:
+ struct TargetToNativeConversionPrivate;
+ TargetToNativeConversionPrivate* m_d;
+ };
+
+ /**
+ * Returns true if the target to C++ custom conversions should
+ * replace the original existing ones, and false if the custom
+ * conversions should be added to the original.
+ */
+ bool replaceOriginalTargetToNativeConversions() const;
+ void setReplaceOriginalTargetToNativeConversions(bool replaceOriginalTargetToNativeConversions);
+
+ typedef QList<TargetToNativeConversion*> TargetToNativeConversions;
+ bool hasTargetToNativeConversions() const;
+ TargetToNativeConversions& targetToNativeConversions();
+ const TargetToNativeConversions& targetToNativeConversions() const;
+ void addTargetToNativeConversion(const QString& sourceTypeName,
+ const QString& sourceTypeCheck,
+ const QString& conversion = QString());
+private:
+ struct CustomConversionPrivate;
+ CustomConversionPrivate* m_d;
+};
#endif // TYPESYSTEM_H
diff --git a/typesystem_p.h b/typesystem_p.h
index 0cf57550..371321bc 100644
--- a/typesystem_p.h
+++ b/typesystem_p.h
@@ -35,23 +35,23 @@ class StackElement
None = 0x0,
// Type tags (0x1, ... , 0xff)
- ObjectTypeEntry = 0x1,
- ValueTypeEntry = 0x2,
- InterfaceTypeEntry = 0x3,
- NamespaceTypeEntry = 0x4,
- ComplexTypeEntryMask = 0x7,
+ ObjectTypeEntry = 0x1,
+ ValueTypeEntry = 0x2,
+ InterfaceTypeEntry = 0x3,
+ NamespaceTypeEntry = 0x4,
+ ComplexTypeEntryMask = 0x7,
// Non-complex type tags (0x8, 0x9, ... , 0xf)
- PrimitiveTypeEntry = 0x8,
- EnumTypeEntry = 0x9,
- ContainerTypeEntry = 0xa,
- FunctionTypeEntry = 0xb,
- TypeEntryMask = 0xf,
+ PrimitiveTypeEntry = 0x8,
+ EnumTypeEntry = 0x9,
+ ContainerTypeEntry = 0xa,
+ FunctionTypeEntry = 0xb,
+ TypeEntryMask = 0xf,
// Documentation tags
- InjectDocumentation = 0x10,
- ModifyDocumentation = 0x20,
- DocumentationMask = 0xf0,
+ InjectDocumentation = 0x10,
+ ModifyDocumentation = 0x20,
+ DocumentationMask = 0xf0,
// Simple tags (0x100, 0x200, ... , 0xf00)
ExtraIncludes = 0x0100,
@@ -70,32 +70,35 @@ class StackElement
TemplateInstanceEnum = 0x0e00,
Replace = 0x0f00,
AddFunction = 0x1000,
+ NativeToTarget = 0x1100,
+ TargetToNative = 0x1200,
+ AddConversion = 0x1300,
SimpleMask = 0x3f00,
// Code snip tags (0x1000, 0x2000, ... , 0xf000)
- InjectCode = 0x4000,
- InjectCodeInFunction = 0x8000,
- CodeSnipMask = 0xc000,
+ InjectCode = 0x4000,
+ InjectCodeInFunction = 0x8000,
+ CodeSnipMask = 0xc000,
// Function modifier tags (0x010000, 0x020000, ... , 0xf00000)
- Access = 0x010000,
- Removal = 0x020000,
- Rename = 0x040000,
- ModifyArgument = 0x080000,
- Thread = 0x100000,
- FunctionModifiers = 0xff0000,
+ Access = 0x010000,
+ Removal = 0x020000,
+ Rename = 0x040000,
+ ModifyArgument = 0x080000,
+ Thread = 0x100000,
+ FunctionModifiers = 0xff0000,
// Argument modifier tags (0x01000000 ... 0xf0000000)
- ConversionRule = 0x01000000,
- ReplaceType = 0x02000000,
- ReplaceDefaultExpression = 0x04000000,
- RemoveArgument = 0x08000000,
- DefineOwnership = 0x10000000,
- RemoveDefaultExpression = 0x20000000,
- NoNullPointers = 0x40000000,
- ReferenceCount = 0x80000000,
- ParentOwner = 0x90000000,
- ArgumentModifiers = 0xff000000
+ ConversionRule = 0x01000000,
+ ReplaceType = 0x02000000,
+ ReplaceDefaultExpression = 0x04000000,
+ RemoveArgument = 0x08000000,
+ DefineOwnership = 0x10000000,
+ RemoveDefaultExpression = 0x20000000,
+ NoNullPointers = 0x40000000,
+ ReferenceCount = 0x80000000,
+ ParentOwner = 0x90000000,
+ ArgumentModifiers = 0xff000000
};
StackElement(StackElement *p) : entry(0), type(None), parent(p) { }