XFA: template tree (with attributes)

This commit is contained in:
Jakub Melka 2021-10-28 20:41:28 +02:00
parent 564c4068a5
commit f99c7970f2
6 changed files with 1797 additions and 2 deletions

View File

@ -1399,4 +1399,299 @@ void GeneratedParameter::setParameterDescription(const QString& parameterDescrip
m_parameterDescription = parameterDescription;
}
void XFACodeGenerator::generateCode(const QDomDocument& document, QString headerName, QString sourceName)
{
QString startMark = "/* START GENERATED CODE */";
QString endMark = "/* END GENERATED CODE */";
loadClasses(document);
QFile headerFile(headerName);
if (headerFile.exists())
{
if (headerFile.open(QFile::ReadOnly | QFile::Text))
{
QString utfCode = QString::fromUtf8(headerFile.readAll());
headerFile.close();
int startIndex = utfCode.indexOf(startMark, Qt::CaseSensitive) + startMark.length();
int endIndex = utfCode.indexOf(endMark, Qt::CaseSensitive);
QString frontPart = utfCode.left(startIndex);
QString backPart = utfCode.mid(endIndex);
QString headerGeneratedCode = generateHeader();
QString allCode = frontPart + headerGeneratedCode + backPart;
headerFile.open(QFile::WriteOnly | QFile::Truncate);
headerFile.write(allCode.toUtf8());
headerFile.close();
}
}
QFile sourceFile(sourceName);
if (sourceFile.exists())
{
if (sourceFile.open(QFile::ReadOnly | QFile::Text))
{
QString utfCode = QString::fromUtf8(sourceFile.readAll());
sourceFile.close();
int startIndex = utfCode.indexOf(startMark, Qt::CaseSensitive) + startMark.length();
int endIndex = utfCode.indexOf(endMark, Qt::CaseSensitive);
QString frontPart = utfCode.left(startIndex);
QString backPart = utfCode.mid(endIndex);
QString sourceGeneratedCode = generateSource();
QString allCode = frontPart + sourceGeneratedCode + backPart;
sourceFile.open(QFile::WriteOnly | QFile::Truncate);
sourceFile.write(allCode.toUtf8());
sourceFile.close();
}
}
}
void XFACodeGenerator::loadClasses(const QDomDocument& document)
{
QDomElement xfaElement = document.firstChildElement("xfa");
if (xfaElement.isNull())
{
return;
}
QDomElement element = xfaElement.firstChildElement("template");
if (element.isNull())
{
return;
}
QDomNodeList childNodes = element.elementsByTagName("class");
const int size = childNodes.size();
m_classes.reserve(size);
for (int i = 0; i < size; ++i)
{
QDomNode child = childNodes.item(i);
if (!child.isElement())
{
continue;
}
Class myClass;
QDomElement element = child.toElement();
// Class name
myClass.className = element.attribute("name");
// Attributes
QDomNodeList attributes = element.elementsByTagName("property");
const int attributeCount = attributes.size();
for (int ai = 0; ai < attributeCount; ++ai)
{
QDomElement attributeElement = attributes.item(ai).toElement();
QString name = attributeElement.attribute("name");
QString type = attributeElement.attribute("type");
QString defaultValue = attributeElement.attribute("default");
QString id = QString("%1_%2").arg(name, type);
Attribute attribute;
attribute.attributeName = name;
attribute.defaultValue = defaultValue;
attribute.type = createType(id, name, type);
myClass.attributes.emplace_back(std::move(attribute));
}
m_classes.emplace_back(std::move(myClass));
}
}
const XFACodeGenerator::Type* XFACodeGenerator::createType(QString id, QString name, QString type)
{
QString simpleType;
QString adjustedId = id;
if (type == "cdata" || type == "pcdata")
{
simpleType = "QString";
adjustedId = "QString";
}
else if (type == "0 | 1")
{
simpleType = "bool";
adjustedId = "bool";
}
else if (type.contains("measurement"))
{
simpleType = "XFA_Measurement";
adjustedId = "XFA_Measurement";
}
else if (type.contains("integer"))
{
simpleType = "PDFInteger";
adjustedId = "PDFInteger";
}
else if (type.contains("angle"))
{
simpleType = "PDFReal";
adjustedId = "PDFReal";
}
QString enumValuesString = type;
auto it = m_types.find(adjustedId);
if (it == m_types.end())
{
Type type;
type.id = adjustedId;
type.typeName = simpleType;
if (type.typeName.isEmpty())
{
QString typeName = name.toUpper();
QString finalTypeName = typeName;
int i = 1;
while (m_types.count(finalTypeName))
{
finalTypeName = typeName + QString::number(i++);
}
QString enumValues = enumValuesString.remove(QChar::Space);
type.enumValues = enumValues.split("|");
type.typeName = finalTypeName;
}
it = m_types.insert(std::make_pair(type.id, type)).first;
}
return &it->second;
}
QString XFACodeGenerator::generateSource() const
{
QByteArray ba;
{
QTextStream stream(&ba, QIODevice::WriteOnly);
stream.setCodec("UTF-8");
stream.setRealNumberPrecision(3);
stream.setRealNumberNotation(QTextStream::FixedNotation);
stream << Qt::endl << Qt::endl;
stream << "namespace xfa" << Qt::endl;
stream << "{" << Qt::endl << Qt::endl;
stream << "class XFA_BaseNode : public XFA_AbstractNode" << Qt::endl;
stream << "{" << Qt::endl;
stream << "public:" << Qt::endl;
stream << Qt::endl;
for (const auto& typeItem : m_types)
{
const Type& type = typeItem.second;
if (type.enumValues.isEmpty())
{
continue;
}
stream << QString(" enum class %1").arg(type.typeName) << Qt::endl;
stream << " {" << Qt::endl;
for (const QString& enumValue : type.enumValues)
{
stream << " " << getEnumValueName(enumValue) << "," << Qt::endl;
}
stream << " };" << Qt::endl << Qt::endl;
}
stream << "};" << Qt::endl << Qt::endl;
for (const Class& myClass : m_classes)
{
stream << QString("class XFA_%1 : public XFA_BaseNode").arg(myClass.className) << Qt::endl;
stream << "{" << Qt::endl;
stream << "public:" << Qt::endl;
QStringList attributeGetters;
QStringList attributeDeclarations;
for (const Attribute& attribute : myClass.attributes)
{
QString attributeFieldName = QString("m_%1").arg(attribute.attributeName);
QString attributeGetterName = attribute.attributeName;
attributeGetterName[0] = attributeGetterName.front().toUpper();
QString attributeDeclaration = QString(" XFA_Attribute<%1> %2;").arg(attribute.type->typeName, attributeFieldName);
QString attributeGetter = QString(" const %1* get%2() const { return %3.getValue(); }").arg(attribute.type->typeName, attributeGetterName, attributeFieldName);
attributeDeclarations << attributeDeclaration;
attributeGetters << attributeGetter;
}
for (const QString& getter : attributeGetters)
{
stream << getter << Qt::endl;
}
stream << Qt::endl;
stream << "private:" << Qt::endl;
stream << " /* properties */" << Qt::endl;
for (const QString& getter : attributeDeclarations)
{
stream << getter << Qt::endl;
}
stream << Qt::endl;
stream << "};" << Qt::endl << Qt::endl;
}
stream << "} // namespace xfa" << Qt::endl;
stream << Qt::endl << Qt::endl;
}
return QString::fromUtf8(ba);
}
QString XFACodeGenerator::getEnumValueName(QString enumName) const
{
if (!enumName.isEmpty())
{
enumName[0] = enumName.front().toUpper();
if (enumName.front().isDigit())
{
enumName.push_front("_");
}
enumName.replace("-", "_");
}
return enumName;
}
QString XFACodeGenerator::generateHeader() const
{
QByteArray ba;
{
QTextStream stream(&ba, QIODevice::WriteOnly);
stream.setCodec("UTF-8");
stream.setRealNumberPrecision(3);
stream.setRealNumberNotation(QTextStream::FixedNotation);
stream << Qt::endl << Qt::endl;
stream << "namespace xfa" << Qt::endl;
stream << "{" << Qt::endl;
stream << "} // namespace xfa" << Qt::endl;
stream << Qt::endl << Qt::endl;
}
return QString::fromUtf8(ba);
}
}

View File

@ -448,7 +448,43 @@ private:
class XFACodeGenerator
{
public:
XFACodeGenerator() = default;
void generateCode(const QDomDocument& document, QString headerName, QString sourceName);
private:
struct Type
{
QString id;
QString typeName;
QStringList enumValues;
};
struct Attribute
{
QString attributeName;
const Type* type = nullptr;
QString defaultValue;
};
struct Class
{
QString className;
QString valueType;
std::vector<Attribute> attributes;
};
void loadClasses(const QDomDocument& document);
const Type* createType(QString id, QString name, QString type);
QString generateHeader() const;
QString generateSource() const;
QString getEnumValueName(QString enumName) const;
std::vector<Class> m_classes;
std::map<QString, Type> m_types;
};
} // namespace codegen

View File

@ -560,5 +560,15 @@ void GeneratorMainWindow::on_actionSet_XFA_description_triggered()
void GeneratorMainWindow::on_actionGenerate_XFA_code_triggered()
{
codegen::XFACodeGenerator generator;
QFile file(m_XFAdefinitionFileName);
if (file.open(QFile::ReadOnly))
{
QDomDocument document;
document.setContent(&file);
file.close();
generator.generateCode(document, m_XFAheaderFileName, m_XFAsourceFileName);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -31,7 +31,7 @@ namespace xfa
struct XFA_InplaceTag;
struct XFA_SharedMemoryTag;
template<typename Value, struct Tag>
template<typename Value, typename Tag>
class PDFXFAValueHolder
{
public:
@ -143,6 +143,11 @@ private:
/* START GENERATED CODE */
namespace xfa
{
} // namespace xfa
/* END GENERATED CODE */
} // namespace pdf

View File

@ -3,7 +3,7 @@
<!-- XFA template specification written by Jakub Melka -->
<template>
<class name ="appearanceFilter">
<class name="appearanceFilter">
<property name="id" type="cdata"/>
<property name="type" type="optional | required" default="optional"/>
<property name="use" type="cdata"/>