aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2019-04-11 11:19:10 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2019-04-16 19:25:07 +0000
commitf21206eb95572af8759366fb249c868659c5674c (patch)
tree642d30620ba6c73fd099d5ddde136a6c85e6133e
parent38a9dc6cb2ba07be4b2c174a10fb60f1cdfba19a (diff)
shiboken: Enable including typesystem XML snippets via entities
Implement a QXmlStreamEntityResolver which resolves undeclared entities to file names in the type system include path. This is a work-around for the missing handling of standard externally parsed entities of QXmlStreamReader. Task-number: PYSIDE-955 Change-Id: I68ebae8ad465ae460c3a0eeadaef22dca2101e6c Reviewed-by: Christian Tismer <tismer@stackless.com>
-rw-r--r--sources/shiboken2/ApiExtractor/messages.cpp6
-rw-r--r--sources/shiboken2/ApiExtractor/messages.h2
-rw-r--r--sources/shiboken2/ApiExtractor/typesystem.cpp66
-rw-r--r--sources/shiboken2/ApiExtractor/typesystem_p.h6
-rw-r--r--sources/shiboken2/doc/typesystem_specifying_types.rst24
5 files changed, 104 insertions, 0 deletions
diff --git a/sources/shiboken2/ApiExtractor/messages.cpp b/sources/shiboken2/ApiExtractor/messages.cpp
index fa4c75743..fdae2359b 100644
--- a/sources/shiboken2/ApiExtractor/messages.cpp
+++ b/sources/shiboken2/ApiExtractor/messages.cpp
@@ -167,6 +167,12 @@ QString msgSkippingFunction(const FunctionModelItem &functionItem,
return result;
}
+QString msgCannotResolveEntity(const QString &name, const QString &reason)
+{
+ return QLatin1String("Cannot resolve entity \"") + name
+ + QLatin1String("\": ") + reason;
+}
+
QString msgCannotSetArrayUsage(const QString &function, int i, const QString &reason)
{
return function + QLatin1String(": Cannot use parameter ")
diff --git a/sources/shiboken2/ApiExtractor/messages.h b/sources/shiboken2/ApiExtractor/messages.h
index 539332aef..30b13fbf8 100644
--- a/sources/shiboken2/ApiExtractor/messages.h
+++ b/sources/shiboken2/ApiExtractor/messages.h
@@ -68,6 +68,8 @@ QString msgUnmatchedReturnType(const FunctionModelItem &functionItem,
QString msgSkippingFunction(const FunctionModelItem &functionItem,
const QString &signature, const QString &why);
+QString msgCannotResolveEntity(const QString &name, const QString &reason);
+
QString msgCannotSetArrayUsage(const QString &function, int i, const QString &reason);
QString msgUnableToTranslateType(const QString &t, const QString &why);
diff --git a/sources/shiboken2/ApiExtractor/typesystem.cpp b/sources/shiboken2/ApiExtractor/typesystem.cpp
index 52dc2a82f..318a52e2e 100644
--- a/sources/shiboken2/ApiExtractor/typesystem.cpp
+++ b/sources/shiboken2/ApiExtractor/typesystem.cpp
@@ -40,6 +40,7 @@
#include <QtCore/QStringAlgorithms>
#include <QtCore/QXmlStreamAttributes>
#include <QtCore/QXmlStreamReader>
+#include <QtCore/QXmlStreamEntityResolver>
#include <algorithm>
@@ -439,12 +440,75 @@ static QString msgUnusedAttributes(const QStringRef &tag, const QXmlStreamAttrib
return result;
}
+// QXmlStreamEntityResolver::resolveEntity(publicId, systemId) is not
+// implemented; resolve via undeclared entities instead.
+class TypeSystemEntityResolver : public QXmlStreamEntityResolver
+{
+public:
+ explicit TypeSystemEntityResolver(const QString &currentPath) :
+ m_currentPath(currentPath) {}
+
+ QString resolveUndeclaredEntity(const QString &name) override;
+
+private:
+ QString readFile(const QString &entityName, QString *errorMessage) const;
+
+ const QString m_currentPath;
+ QHash<QString, QString> m_cache;
+};
+
+QString TypeSystemEntityResolver::readFile(const QString &entityName, QString *errorMessage) const
+{
+ QString fileName = entityName;
+ if (!fileName.contains(QLatin1Char('.')))
+ fileName += QLatin1String(".xml");
+ QString path = TypeDatabase::instance()->modifiedTypesystemFilepath(fileName, m_currentPath);
+ if (!QFileInfo::exists(path)) // PySide2-specific hack
+ fileName.prepend(QLatin1String("typesystem_"));
+ path = TypeDatabase::instance()->modifiedTypesystemFilepath(fileName, m_currentPath);
+ if (!QFileInfo::exists(path)) {
+ *errorMessage = QLatin1String("Unable to resolve: ") + entityName;
+ return QString();
+ }
+ QFile file(path);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ *errorMessage = msgCannotOpenForReading(file);
+ return QString();
+ }
+ QString result = QString::fromUtf8(file.readAll()).trimmed();
+ // Remove license header comments on which QXmlStreamReader chokes
+ if (result.startsWith(QLatin1String("<!--"))) {
+ const int commentEnd = result.indexOf(QLatin1String("-->"));
+ if (commentEnd != -1) {
+ result.remove(0, commentEnd + 3);
+ result = result.trimmed();
+ }
+ }
+ return result;
+}
+
+QString TypeSystemEntityResolver::resolveUndeclaredEntity(const QString &name)
+{
+ auto it = m_cache.find(name);
+ if (it == m_cache.end()) {
+ QString errorMessage;
+ it = m_cache.insert(name, readFile(name, &errorMessage));
+ if (it.value().isEmpty()) { // The parser will fail and display the line number.
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgCannotResolveEntity(name, errorMessage)));
+ }
+ }
+ return it.value();
+}
+
Handler::Handler(TypeDatabase *database, bool generate) :
m_database(database),
m_generate(generate ? TypeEntry::GenerateAll : TypeEntry::GenerateForSubclass)
{
}
+Handler::~Handler() = default;
+
static QString readerFileName(const QXmlStreamReader &reader)
{
const QFile *file = qobject_cast<const QFile *>(reader.device());
@@ -584,6 +648,8 @@ bool Handler::parse(QXmlStreamReader &reader)
const QString fileName = readerFileName(reader);
if (!fileName.isEmpty())
m_currentPath = QFileInfo(fileName).absolutePath();
+ m_entityResolver.reset(new TypeSystemEntityResolver(m_currentPath));
+ reader.setEntityResolver(m_entityResolver.data());
while (!reader.atEnd()) {
switch (reader.readNext()) {
diff --git a/sources/shiboken2/ApiExtractor/typesystem_p.h b/sources/shiboken2/ApiExtractor/typesystem_p.h
index df1390cc3..f6b0717f4 100644
--- a/sources/shiboken2/ApiExtractor/typesystem_p.h
+++ b/sources/shiboken2/ApiExtractor/typesystem_p.h
@@ -29,11 +29,13 @@
#define TYPESYSTEM_P_H
#include <QStack>
+#include <QtCore/QScopedPointer>
#include "typesystem.h"
QT_FORWARD_DECLARE_CLASS(QXmlStreamAttributes)
QT_FORWARD_DECLARE_CLASS(QXmlStreamReader)
+class TypeSystemEntityResolver;
class TypeDatabase;
class StackElement
{
@@ -138,7 +140,10 @@ struct StackElementContext
class Handler
{
public:
+ Q_DISABLE_COPY(Handler)
+
Handler(TypeDatabase* database, bool generate);
+ ~Handler();
bool parse(QXmlStreamReader &reader);
@@ -256,6 +261,7 @@ private:
QString m_currentSignature;
QString m_currentPath;
+ QScopedPointer<TypeSystemEntityResolver> m_entityResolver;
};
#endif
diff --git a/sources/shiboken2/doc/typesystem_specifying_types.rst b/sources/shiboken2/doc/typesystem_specifying_types.rst
index f4016bc2c..ac1121461 100644
--- a/sources/shiboken2/doc/typesystem_specifying_types.rst
+++ b/sources/shiboken2/doc/typesystem_specifying_types.rst
@@ -3,6 +3,30 @@ Specifying Types
.. _typesystem:
+Including Snippets
+^^^^^^^^^^^^^^^^^^
+
+There might be repetitive XML code, for example function modifications that
+need to be done on classes that are not related by type inheritance.
+It is possible to split out such snippets and include them via an entity reference.
+
+.. code-block:: xml
+
+ <typesystem>
+ <object-type name="A">
+ &common_function_modifications;
+ </object-type>
+ <object-type name="B">
+ &common_function_modifications;
+ </object-type>
+ </typesystem>
+
+The entity name is interpreted as file name (with suffix **xml**) appended and resolved
+in the type system paths passed as command line argument.
+
+Note that this is not a standard externally parsed entity due to the limitations
+of the underlying parser.
+
typesystem
^^^^^^^^^^