diff options
Diffstat (limited to 'src/lib/corelib/jsextensions/domxml.cpp')
-rw-r--r-- | src/lib/corelib/jsextensions/domxml.cpp | 740 |
1 files changed, 414 insertions, 326 deletions
diff --git a/src/lib/corelib/jsextensions/domxml.cpp b/src/lib/corelib/jsextensions/domxml.cpp index 86e1574c6..35cff186b 100644 --- a/src/lib/corelib/jsextensions/domxml.cpp +++ b/src/lib/corelib/jsextensions/domxml.cpp @@ -38,428 +38,516 @@ ** ****************************************************************************/ +#include "jsextension.h" + #include <language/scriptengine.h> #include <QtCore/qfile.h> -#include <QtCore/qobject.h> #include <QtCore/qvariant.h> -#include <QtScript/qscriptengine.h> -#include <QtScript/qscriptvalue.h> -#include <QtScript/qscriptable.h> - #include <QtXml/qdom.h> namespace qbs { namespace Internal { -class XmlDomDocument; - -class XmlDomNode: public QObject, public QScriptable +template<class C> class XmlDomNode : public JsExtension<XmlDomNode<C>> { - Q_OBJECT public: - static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine); - - Q_INVOKABLE bool isElement() const; - Q_INVOKABLE bool isCDATASection() const; - Q_INVOKABLE bool isText() const; - - Q_INVOKABLE QString attribute(const QString & name, const QString & defValue = QString()); - Q_INVOKABLE void setAttribute(const QString & name, const QString & value); - Q_INVOKABLE bool hasAttribute(const QString & name) const; - Q_INVOKABLE QString tagName() const; - Q_INVOKABLE void setTagName(const QString & name); - - Q_INVOKABLE QString text() const; - - Q_INVOKABLE QString data() const; - Q_INVOKABLE void setData(const QString &v) const; - - Q_INVOKABLE void clear(); - Q_INVOKABLE bool hasAttributes() const; - Q_INVOKABLE bool hasChildNodes() const; - Q_INVOKABLE QScriptValue parentNode() const; - Q_INVOKABLE QScriptValue firstChild(const QString & tagName = QString()); - Q_INVOKABLE QScriptValue lastChild(const QString & tagName = QString()) const; - Q_INVOKABLE QScriptValue previousSibling(const QString & tagName = QString()) const; - Q_INVOKABLE QScriptValue nextSibling(const QString & tagName = QString()) const; - - Q_INVOKABLE QScriptValue appendChild(const QScriptValue &newChild); - Q_INVOKABLE QScriptValue insertBefore(const QScriptValue& newChild, const QScriptValue& refChild); - Q_INVOKABLE QScriptValue insertAfter(const QScriptValue& newChild, const QScriptValue& refChild); - Q_INVOKABLE QScriptValue replaceChild(const QScriptValue& newChild, const QScriptValue& oldChild); - Q_INVOKABLE QScriptValue removeChild(const QScriptValue& oldChild); - -protected: - friend class XmlDomDocument; - XmlDomNode(const QDomNode &other = QDomNode()); - QDomNode m_domNode; -}; + static const char *name(); + XmlDomNode(const C &value) : m_value(value) {} + XmlDomNode(JSContext *, const QString &name); + XmlDomNode(JSContext *) {} -class XmlDomDocument: public XmlDomNode -{ - Q_OBJECT -public: - static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine); - Q_INVOKABLE QScriptValue documentElement(); - Q_INVOKABLE QScriptValue createElement(const QString & tagName); - Q_INVOKABLE QScriptValue createCDATASection(const QString & value); - Q_INVOKABLE QScriptValue createTextNode(const QString & value); - - Q_INVOKABLE bool setContent(const QString & content); - Q_INVOKABLE QString toString(int indent = 1); + static JSValue ctor(JSContext *ctx, JSValueConst, JSValueConst, + int argc, JSValueConst *argv, int); + static void setupMethods(JSContext *ctx, JSValue obj); - Q_INVOKABLE void save(const QString & filePath, int indent = 1); - Q_INVOKABLE void load(const QString & filePath); - -protected: - XmlDomDocument(QScriptContext *context, const QString &name = QString()); + C value() const { return m_value; } private: - QDomDocument m_domDocument; -}; - -QScriptValue XmlDomDocument::ctor(QScriptContext *context, QScriptEngine *engine) -{ - XmlDomDocument *xml = nullptr; - switch (context->argumentCount()) { - case 0: - xml = new XmlDomDocument(context); - break; - case 1: - xml = new XmlDomDocument(context, context->argument(0).toString()); - break; - default: - return context->throwError(QStringLiteral("DomXml(QString file = QLatin1String(\"\"))")); - } - QScriptValue obj = engine->newQObject(xml, QScriptEngine::ScriptOwnership); - static_cast<ScriptEngine *>(engine)->setUsesIo(); - return obj; -} - -QScriptValue XmlDomDocument::documentElement() -{ - return engine()->newQObject(new XmlDomNode(m_domDocument.documentElement()), QScriptEngine::ScriptOwnership); -} - -QScriptValue XmlDomDocument::createElement(const QString &tagName) -{ - return engine()->newQObject(new XmlDomNode(m_domDocument.createElement(tagName)), QScriptEngine::ScriptOwnership); -} - -QScriptValue XmlDomDocument::createCDATASection(const QString &value) -{ - return engine()->newQObject(new XmlDomNode(m_domDocument.createCDATASection(value)), QScriptEngine::ScriptOwnership); -} - -QScriptValue XmlDomDocument::createTextNode(const QString &value) -{ - return engine()->newQObject(new XmlDomNode(m_domDocument.createTextNode(value)), QScriptEngine::ScriptOwnership); -} - -bool XmlDomDocument::setContent(const QString &content) -{ - return m_domDocument.setContent(content); -} - -QString XmlDomDocument::toString(int indent) -{ - return m_domDocument.toString(indent); -} - -void XmlDomDocument::save(const QString &filePath, int indent) -{ - QFile f(filePath); - if (!f.open(QIODevice::WriteOnly)) { - context()->throwError(QStringLiteral("unable to open '%1'") - .arg(filePath)); - return; +#define XML_JS_FWD(name, func, text) DEFINE_JS_FORWARDER_QUAL(XmlDomNode, name, func, text) + + XML_JS_FWD(jsIsElement, &XmlDomNode::isElement, "DomNode.isElement"); + XML_JS_FWD(jsIsCDATASection, &XmlDomNode::isCDATASection, "DomNode.isCDATASection"); + XML_JS_FWD(jsIsText, &XmlDomNode::isText, "DomNode.isText"); + XML_JS_FWD(jsHasAttribute, &XmlDomNode::hasAttribute, "DomNode.hasAttribute"); + XML_JS_FWD(jsTagName, &XmlDomNode::tagName, "DomNode.tagName"); + XML_JS_FWD(jsSetTagName, &XmlDomNode::setTagName, "DomNode.setTagName"); + XML_JS_FWD(jsText, &XmlDomNode::text, "DomNode.text"); + XML_JS_FWD(jsData, &XmlDomNode::data, "DomNode.data"); + XML_JS_FWD(jsSetData, &XmlDomNode::setData, "DomNode.setData"); + XML_JS_FWD(jsClear, &XmlDomNode::clear, "DomNode.clear"); + XML_JS_FWD(jsHasAttributes, &XmlDomNode::hasAttributes, "DomNode.hasAttributes"); + XML_JS_FWD(jsHasChildNodes, &XmlDomNode::hasChildNodes, "DomNode.hasChildNodes"); + XML_JS_FWD(jsSetAttribute, &XmlDomNode::setAttribute, "DomNode.setAttribute"); + XML_JS_FWD(jsAppendChild, &XmlDomNode::appendChild, "DomNode.appendChild"); + XML_JS_FWD(jsInsertBefore, &XmlDomNode::insertBefore, "DomNode.insertBefore"); + XML_JS_FWD(jsInsertAfter, &XmlDomNode::insertAfter, "DomNode.insertAfter"); + XML_JS_FWD(jsReplaceChild, &XmlDomNode::replaceChild, "DomNode.replaceChild"); + XML_JS_FWD(jsRemoveChild, &XmlDomNode::removeChild, "DomNode.removeChild"); + + static JSValue jsAttribute(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) + { + try { + const auto name = JsExtension<XmlDomNode>::template getArgument<QString> + (ctx, "DomNode.attribute", argc, argv); + QString defaultValue; + if (argc > 1) + defaultValue = JsExtension<XmlDomNode>::template fromArg<QString> + (ctx, "DomNode.attribute", 2, argv[1]); + return makeJsString(ctx, JsExtension<XmlDomNode>::fromJsObject + (ctx, this_val)->attribute(name, defaultValue)); + } catch (const QString &error) { + return throwError(ctx, error); + } } - - QByteArray buff(m_domDocument.toByteArray(indent)); - if (buff.size() != f.write(buff)) + static JSValue jsParentNode(JSContext *ctx, JSValueConst this_val, int, JSValueConst *) { - context()->throwError(f.errorString()); - f.close(); - return; + return JsExtension<XmlDomNode<QDomNode>>::createObjectDirect( + ctx, JsExtension<XmlDomNode>::fromJsObject(ctx, this_val)->parentNode()); } - - f.close(); - if (f.error() != QFile::NoError) - context()->throwError(f.errorString()); -} - -void XmlDomDocument::load(const QString &filePath) -{ - QFile f(filePath); - if (!f.open(QIODevice::ReadOnly)) { - context()->throwError(QStringLiteral("unable to open '%1'") - .arg(filePath)); - return; + static JSValue jsFirstChild(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) + { + try { + QString tagName; + if (argc > 0) + tagName = JsExtension<XmlDomNode>::template getArgument<QString> + (ctx, "DomNode.firstChild", argc, argv); + return JsExtension<XmlDomNode<QDomNode>>::createObjectDirect + (ctx, JsExtension<XmlDomNode>::fromJsObject(ctx, this_val)->firstChild(tagName)); + } catch (const QString &error) { + return throwError(ctx, error); + } + } + static JSValue jsLastChild(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) + { + try { + QString tagName; + if (argc > 0) + tagName = JsExtension<XmlDomNode>::template getArgument<QString> + (ctx, "DomNode.lastChild", argc, argv); + return JsExtension<XmlDomNode<QDomNode>>::createObjectDirect + (ctx, JsExtension<XmlDomNode>::fromJsObject(ctx, this_val)->lastChild(tagName)); + } catch (const QString &error) { + return throwError(ctx, error); + } + } + static JSValue jsPreviousSibling(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) + { + try { + QString tagName; + if (argc > 0) + tagName = JsExtension<XmlDomNode>::template getArgument<QString> + (ctx, "DomNode.previousSibling", argc, argv); + return JsExtension<XmlDomNode<QDomNode>>::createObjectDirect(ctx, JsExtension<XmlDomNode>::fromJsObject + (ctx, this_val)->previousSibling(tagName)); + } catch (const QString &error) { + return throwError(ctx, error); + } + } + static JSValue jsNextSibling(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) + { + try { + QString tagName; + if (argc > 0) + tagName = JsExtension<C>::template getArgument<QString> + (ctx, "Xml.DomElement.nextSibling", argc, argv); + return JsExtension<XmlDomNode<QDomNode>>::createObjectDirect + (ctx, JsExtension<XmlDomNode>::fromJsObject(ctx, this_val)->nextSibling(tagName)); + } catch (const QString &error) { + return throwError(ctx, error); + } } - QString errorMsg; - if (!m_domDocument.setContent(&f, &errorMsg)) { - context()->throwError(errorMsg); - return; + // Implemented for QDomDocument only. + static JSValue jsDocumentElement(JSContext *ctx, JSValueConst this_val, int, JSValueConst *); + static JSValue jsCreateElement(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); + static JSValue jsCreateCDATASection(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); + static JSValue jsCreateTextNode(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); + static JSValue jsSetContent(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); + static JSValue jsToString(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); + static JSValue jsSave(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); + static JSValue jsLoad(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); + + bool isElement() const { return m_value.isElement(); } + bool isCDATASection() const { return m_value.isCDATASection(); } + bool isText() const { return m_value.isText(); } + QString attribute(const QString &name, const QString &defaultValue) + { + QDomElement el = m_value.toElement(); + if (el.isNull()) + throw QStringLiteral("Node '%1' is not an element node").arg(m_value.nodeName()); + return el.attribute(name, defaultValue); + } + void setAttribute(const QString &name, const QString &value) + { + QDomElement el = m_value.toElement(); + if (el.isNull()) + throw QStringLiteral("Node '%1' is not an element node").arg(m_value.nodeName()); + el.setAttribute(name, value); + } + bool hasAttribute(const QString &name) const + { + QDomElement el = m_value.toElement(); + if (el.isNull()) + throw QStringLiteral("Node '%1' is not an element node").arg(m_value.nodeName()); + return el.hasAttribute(name); + } + QString tagName() const + { + QDomElement el = m_value.toElement(); + if (el.isNull()) + throw QStringLiteral("Node '%1' is not an element node").arg(m_value.nodeName()); + return el.tagName(); + } + void setTagName(const QString &name) + { + QDomElement el = m_value.toElement(); + if (el.isNull()) + throw QStringLiteral("Node '%1' is not an element node").arg(m_value.nodeName()); + el.setTagName(name); } -} -XmlDomDocument::XmlDomDocument(QScriptContext *context, const QString &name):m_domDocument(name) -{ - Q_UNUSED(context) - m_domNode = m_domDocument; -} + QString text() const + { + QDomElement el = m_value.toElement(); + if (el.isNull()) + throw QStringLiteral("Node '%1' is not an element node").arg(m_value.nodeName()); + return el.text(); + } -QScriptValue XmlDomNode::ctor(QScriptContext *context, QScriptEngine *engine) -{ - Q_UNUSED(context) - return engine->newQObject(new XmlDomNode(), QScriptEngine::ScriptOwnership); -} + QString data() const + { + if (m_value.isText()) + return m_value.toText().data(); + if (m_value.isCDATASection()) + return m_value.toCDATASection().data(); + if (m_value.isCharacterData()) + return m_value.toCharacterData().data(); + throw QStringLiteral("Node '%1' is not a character data node").arg(m_value.nodeName()); + } + void setData(const QString &v) const + { + if (m_value.isText()) + return m_value.toText().setData(v); + if (m_value.isCDATASection()) + return m_value.toCDATASection().setData(v); + if (m_value.isCharacterData()) + return m_value.toCharacterData().setData(v); + throw QStringLiteral("Node '%1' is not a character data node").arg(m_value.nodeName()); + } + void clear() { m_value.clear(); } + bool hasAttributes() const { return m_value.hasAttributes(); } + bool hasChildNodes() const { return m_value.hasChildNodes(); } -bool XmlDomNode::isElement() const -{ - return m_domNode.isElement(); -} + XmlDomNode<QDomNode> *parentNode() const + { + return new XmlDomNode<QDomNode>(m_value.parentNode()); + } + XmlDomNode<QDomNode> *firstChild(const QString &tagName) + { + if (tagName.isEmpty()) + return new XmlDomNode<QDomNode>(m_value.firstChild()); + return new XmlDomNode<QDomNode>(m_value.firstChildElement(tagName)); + } + XmlDomNode<QDomNode> *lastChild(const QString &tagName) const + { + if (tagName.isEmpty()) + return new XmlDomNode<QDomNode>(m_value.lastChild()); + return new XmlDomNode<QDomNode>(m_value.lastChildElement(tagName)); + } + XmlDomNode<QDomNode> *previousSibling(const QString &tagName) const + { + if (tagName.isEmpty()) + return new XmlDomNode<QDomNode>(m_value.previousSibling()); + return new XmlDomNode<QDomNode>(m_value.previousSiblingElement(tagName)); + } + XmlDomNode<QDomNode> *nextSibling(const QString &tagName) const + { + if (tagName.isEmpty()) + return new XmlDomNode<QDomNode>(m_value.nextSibling()); + return new XmlDomNode<QDomNode>(m_value.nextSiblingElement(tagName)); + } + void appendChild(const XmlDomNode<QDomNode> *newChild) + { + m_value.appendChild(newChild->value()); + } + void insertBefore(const XmlDomNode<QDomNode> *newChild, const XmlDomNode<QDomNode> *refChild) + { + m_value.insertBefore(newChild->value(), refChild->value()); + } + void insertAfter(const XmlDomNode<QDomNode> *newChild, const XmlDomNode<QDomNode> *refChild) + { + m_value.insertAfter(newChild->value(), refChild->value()); + } + void replaceChild(const XmlDomNode<QDomNode> *newChild, const XmlDomNode<QDomNode> *oldChild) + { + m_value.replaceChild(newChild->value(), oldChild->value()); + } + void removeChild(const XmlDomNode<QDomNode> *oldChild) + { + m_value.removeChild(oldChild->value()); + } -bool XmlDomNode::isCDATASection() const -{ - return m_domNode.isCDATASection(); -} + // Implemented for QDomDocument only. + XmlDomNode<QDomNode> *documentElement(); + XmlDomNode<QDomNode> *createElement(const QString &tagName); + XmlDomNode<QDomNode> *createCDATASection(const QString &value); + XmlDomNode<QDomNode> *createTextNode(const QString &value); -bool XmlDomNode::isText() const -{ - return m_domNode.isText(); -} + bool setContent(const QString &content); + QString toString(int indent = 1); -QString XmlDomNode::attribute(const QString &name, const QString &defValue) -{ - QDomElement el = m_domNode.toElement(); - if (el.isNull()) { - context()->throwError(QStringLiteral("Node '%1' is not an element node").arg(m_domNode.nodeName())); - return defValue; - } - return el.attribute(name, defValue); -} + void save(const QString &filePath, int indent = 1); + void load(const QString &filePath); -void XmlDomNode::setAttribute(const QString &name, const QString &value) -{ - QDomElement el = m_domNode.toElement(); - if (el.isNull()) { - context()->throwError(QStringLiteral("Node '%1' is not an element node").arg(m_domNode.nodeName())); - return; - } - el.setAttribute(name, value); -} + C m_value; +}; -bool XmlDomNode::hasAttribute(const QString &name) const -{ - QDomElement el = m_domNode.toElement(); - if (el.isNull()) { - context()->throwError(QStringLiteral("Node '%1' is not an element node").arg(m_domNode.nodeName())); - return false; - } - return el.hasAttribute(name); -} +template<> const char *XmlDomNode<QDomNode>::name() { return "DomNode"; } +template<> const char *XmlDomNode<QDomDocument>::name() { return "DomDocument"; } -QString XmlDomNode::tagName() const +template<> JSValue XmlDomNode<QDomNode>::ctor(JSContext *ctx, JSValue, JSValue, int , JSValue *, + int) { - QDomElement el = m_domNode.toElement(); - if (el.isNull()) { - context()->throwError(QStringLiteral("Node '%1' is not an element node").arg(m_domNode.nodeName())); - return {}; - } - return el.tagName(); + const JSValue obj = createObject(ctx); + const auto se = ScriptEngine::engineForContext(ctx); + const DubiousContextList dubiousContexts{ + DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving) + }; + se->checkContext(QStringLiteral("qbs.Xml.DomNode"), dubiousContexts); + se->setUsesIo(); + return obj; } -void XmlDomNode::setTagName(const QString &name) +template<> JSValue XmlDomNode<QDomDocument>::ctor(JSContext *ctx, JSValue, JSValue, + int argc, JSValue *argv, int) { - QDomElement el = m_domNode.toElement(); - if (el.isNull()) { - context()->throwError(QStringLiteral("Node '%1' is not an element node").arg(m_domNode.nodeName())); - return; - } - el.setTagName(name); + try { + JSValue obj; + if (argc == 0) { + obj = createObject(ctx); + } else { + const auto name = getArgument<QString>(ctx, "XmlDomDocument constructor", + argc, argv); + obj = createObject(ctx, name); + } + const auto se = ScriptEngine::engineForContext(ctx); + const DubiousContextList dubiousContexts{ + DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving) + }; + se->checkContext(QStringLiteral("qbs.Xml.DomDocument"), dubiousContexts); + se->setUsesIo(); + return obj; + } catch (const QString &error) { return throwError(ctx, error); } } -QString XmlDomNode::text() const +template<> XmlDomNode<QDomNode> *XmlDomNode<QDomDocument>::documentElement() { - QDomElement el = m_domNode.toElement(); - if (el.isNull()) { - context()->throwError(QStringLiteral("Node '%1' is not an element node").arg(m_domNode.nodeName())); - return {}; - } - return el.text(); + return new XmlDomNode<QDomNode>(m_value.documentElement()); } -QString XmlDomNode::data() const +template<> XmlDomNode<QDomNode> *XmlDomNode<QDomDocument>::createElement(const QString &tagName) { - if (m_domNode.isText()) - return m_domNode.toText().data(); - if (m_domNode.isCDATASection()) - return m_domNode.toCDATASection().data(); - if (m_domNode.isCharacterData()) - return m_domNode.toCharacterData().data(); - context()->throwError(QStringLiteral("Node '%1' is not a character data node").arg(m_domNode.nodeName())); - return {}; + return new XmlDomNode<QDomNode>(m_value.createElement(tagName)); } -void XmlDomNode::setData(const QString &v) const +template<> XmlDomNode<QDomNode> *XmlDomNode<QDomDocument>::createCDATASection(const QString &value) { - if (m_domNode.isText()) - return m_domNode.toText().setData(v); - if (m_domNode.isCDATASection()) - return m_domNode.toCDATASection().setData(v); - if (m_domNode.isCharacterData()) - return m_domNode.toCharacterData().setData(v); - context()->throwError(QStringLiteral("Node '%1' is not a character data node").arg(m_domNode.nodeName())); + return new XmlDomNode<QDomNode>(m_value.createCDATASection(value)); } -void XmlDomNode::clear() +template<> XmlDomNode<QDomNode> *XmlDomNode<QDomDocument>::createTextNode(const QString &value) { - m_domNode.clear(); + return new XmlDomNode<QDomNode>(m_value.createTextNode(value)); } -bool XmlDomNode::hasAttributes() const +template<> bool XmlDomNode<QDomDocument>::setContent(const QString &content) { - return m_domNode.hasAttributes(); + return static_cast<bool>(m_value.setContent(content)); } -bool XmlDomNode::hasChildNodes() const +template<> QString XmlDomNode<QDomDocument>::toString(int indent) { - return m_domNode.hasChildNodes(); + return m_value.toString(indent); } -QScriptValue XmlDomNode::parentNode() const +template<> JSValue XmlDomNode<QDomDocument>::jsDocumentElement(JSContext *ctx, JSValue this_val, + int, JSValue *) { - return engine()->newQObject(new XmlDomNode(m_domNode.parentNode()), QScriptEngine::ScriptOwnership); + return XmlDomNode<QDomNode>::createObjectDirect( + ctx, fromJsObject(ctx, this_val)->documentElement()); } -QScriptValue XmlDomNode::firstChild(const QString &tagName) +template<> JSValue XmlDomNode<QDomDocument>::jsCreateElement(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) { - if (tagName.isEmpty()) - return engine()->newQObject(new XmlDomNode(m_domNode.firstChild()), QScriptEngine::ScriptOwnership); - return engine()->newQObject(new XmlDomNode(m_domNode.firstChildElement(tagName)), QScriptEngine::ScriptOwnership); + try { + const auto tagName = getArgument<QString>(ctx, "DomDocument.createElement", argc, argv); + const JSValue obj = XmlDomNode<QDomNode>::createObjectDirect( + ctx, fromJsObject(ctx, this_val)->createElement(tagName)); + return obj; + } catch (const QString &error) { + return throwError(ctx, error); + } } -QScriptValue XmlDomNode::lastChild(const QString &tagName) const +template<> JSValue XmlDomNode<QDomDocument>::jsCreateCDATASection(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) { - if (tagName.isEmpty()) - return engine()->newQObject(new XmlDomNode(m_domNode.lastChild()), QScriptEngine::ScriptOwnership); - return engine()->newQObject(new XmlDomNode(m_domNode.lastChildElement(tagName)), QScriptEngine::ScriptOwnership); + try { + const auto value = getArgument<QString>(ctx, "DomDocument.createCDATASection", argc, argv); + return XmlDomNode<QDomNode>::createObjectDirect( + ctx, fromJsObject(ctx, this_val)->createCDATASection(value)); + } catch (const QString &error) { + return throwError(ctx, error); + } } -QScriptValue XmlDomNode::previousSibling(const QString &tagName) const +template<> JSValue XmlDomNode<QDomDocument>::jsCreateTextNode(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) { - if (tagName.isEmpty()) - return engine()->newQObject(new XmlDomNode(m_domNode.previousSibling()), QScriptEngine::ScriptOwnership); - return engine()->newQObject(new XmlDomNode(m_domNode.previousSiblingElement(tagName)), QScriptEngine::ScriptOwnership); + try { + const auto value = getArgument<QString>(ctx, "DomDocument.createTextNode", argc, argv); + return XmlDomNode<QDomNode>::createObjectDirect( + ctx, fromJsObject(ctx, this_val)->createTextNode(value)); + } catch (const QString &error) { + return throwError(ctx, error); + } } -QScriptValue XmlDomNode::nextSibling(const QString &tagName) const +template<> JSValue XmlDomNode<QDomDocument>::jsSetContent(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) { - if (tagName.isEmpty()) - return engine()->newQObject(new XmlDomNode(m_domNode.nextSibling()), QScriptEngine::ScriptOwnership); - return engine()->newQObject(new XmlDomNode(m_domNode.nextSiblingElement(tagName)), QScriptEngine::ScriptOwnership); + try { + const auto content = getArgument<QString>(ctx, "DomDocument.setContent", argc, argv); + return JS_NewBool(ctx, fromJsObject(ctx, this_val)->setContent(content)); + } catch (const QString &error) { + return throwError(ctx, error); + } } -QScriptValue XmlDomNode::appendChild(const QScriptValue &newChild) +template<> JSValue XmlDomNode<QDomDocument>::jsToString(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) { - auto newNode = qobject_cast<XmlDomNode*>(newChild.toQObject()); - if (!newNode) { - context()->throwError(QStringLiteral("First argument is not a XmlDomNode object")); - return {}; + try { + qint32 indent = 1; + if (argc > 0) + indent = getArgument<qint32>(ctx, "DomDocument.toString", argc, argv); + return makeJsString(ctx, fromJsObject(ctx, this_val)->toString(indent)); + } catch (const QString &error) { + return throwError(ctx, error); } - return engine()->newQObject(new XmlDomNode(m_domNode.appendChild(newNode->m_domNode)), QScriptEngine::ScriptOwnership); } -QScriptValue XmlDomNode::insertBefore(const QScriptValue &newChild, const QScriptValue &refChild) +template<> void XmlDomNode<QDomDocument>::save(const QString &filePath, int indent) { - auto newNode = qobject_cast<XmlDomNode*>(newChild.toQObject()); - if (!newNode) { - context()->throwError(QStringLiteral("First argument is not a XmlDomNode object")); - return {}; - } + QFile f(filePath); + if (!f.open(QIODevice::WriteOnly)) + throw QStringLiteral("unable to open '%1'").arg(filePath); - auto refNode = qobject_cast<XmlDomNode*>(refChild.toQObject()); - if (!refNode) { - context()->throwError(QStringLiteral("Second argument is not a XmlDomNode object")); - return {}; - } + QByteArray buff(m_value.toByteArray(indent)); + if (buff.size() != f.write(buff)) + throw f.errorString(); - return engine()->newQObject(new XmlDomNode(m_domNode.insertBefore(newNode->m_domNode, refNode->m_domNode)), QScriptEngine::ScriptOwnership); + f.close(); + if (f.error() != QFile::NoError) + throw f.errorString(); } -QScriptValue XmlDomNode::insertAfter(const QScriptValue &newChild, const QScriptValue &refChild) +template<> JSValue XmlDomNode<QDomDocument>::jsSave(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) { - auto newNode = qobject_cast<XmlDomNode*>(newChild.toQObject()); - if (!newNode) { - context()->throwError(QStringLiteral("First argument is not a XmlDomNode object")); - return {}; - } - - auto refNode = qobject_cast<XmlDomNode*>(refChild.toQObject()); - if (!refNode) { - context()->throwError(QStringLiteral("Second argument is not a XmlDomNode object")); - return {}; + try { + const auto filePath = getArgument<QString>(ctx, "DomDocument.save", argc, argv); + qint32 indent = 1; + if (argc > 1) + indent = fromArg<qint32>(ctx, "toString", 2, argv[1]); + fromJsObject(ctx, this_val)->save(filePath, indent); + return JS_UNDEFINED; + } catch (const QString &error) { + return throwError(ctx, error); } - - return engine()->newQObject(new XmlDomNode(m_domNode.insertAfter(newNode->m_domNode, refNode->m_domNode)), QScriptEngine::ScriptOwnership); } -QScriptValue XmlDomNode::replaceChild(const QScriptValue &newChild, const QScriptValue &oldChild) +template<> void XmlDomNode<QDomDocument>::load(const QString &filePath) { - auto newNode = qobject_cast<XmlDomNode*>(newChild.toQObject()); - if (!newNode) { - context()->throwError(QStringLiteral("First argument is not a XmlDomNode object")); - return {}; - } - - auto oldNode = qobject_cast<XmlDomNode*>(oldChild.toQObject()); - if (!oldNode) { - context()->throwError(QStringLiteral("Second argument is not a XmlDomNode object")); - return {}; - } + QFile f(filePath); + if (!f.open(QIODevice::ReadOnly)) + throw QStringLiteral("unable to open '%1'").arg(filePath); - return engine()->newQObject(new XmlDomNode(m_domNode.replaceChild(newNode->m_domNode, oldNode->m_domNode)), QScriptEngine::ScriptOwnership); + QString errorMsg; + if (!m_value.setContent(&f, &errorMsg)) + throw errorMsg; } -QScriptValue XmlDomNode::removeChild(const QScriptValue &oldChild) +template<> JSValue XmlDomNode<QDomDocument>::jsLoad(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) { - auto oldNode = qobject_cast<XmlDomNode*>(oldChild.toQObject()); - if (!oldNode) { - context()->throwError(QStringLiteral("First argument is not a XmlDomNode object")); - return {}; + try { + const auto filePath = getArgument<QString>(ctx, "DomDocument.load", argc, argv); + fromJsObject(ctx, this_val)->load(filePath); + return JS_UNDEFINED; + } catch (const QString &error) { + return throwError(ctx, error); } - - return engine()->newQObject(new XmlDomNode(m_domNode.removeChild(oldNode->m_domNode)), QScriptEngine::ScriptOwnership); } -XmlDomNode::XmlDomNode(const QDomNode &other) -{ - m_domNode = other; +template<> XmlDomNode<QDomDocument>::XmlDomNode(JSContext *, const QString &name) : m_value(name) {} + +template<class C> +void XmlDomNode<C>::setupMethods(JSContext *ctx, JSValue obj) +{ + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "appendChild", &jsAppendChild, 1); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "attribute", &jsAttribute, 2); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "clear", &jsClear, 0); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "data", &jsData, 0); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "firstChild", &jsFirstChild, 1); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "hasAttribute", &jsHasAttribute, 1); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "hasAttributes", &jsHasAttributes, 0); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "hasChildNodes", &jsHasChildNodes, 0); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "insertAfter", &jsInsertAfter, 2); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "insertBefore", &jsInsertBefore, 2); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "isCDATASection", &jsIsCDATASection, 0); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "isElement", &jsIsElement, 0); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "isText", &jsIsText, 0); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "lastChild", &jsLastChild, 1); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "nextSibling", &jsNextSibling, 1); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "parentNode", &jsParentNode, 0); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "previousSibling", &jsPreviousSibling, 1); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "removeChild", &jsRemoveChild, 1); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "replaceChild", &jsReplaceChild, 2); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "setAttribute", &jsSetAttribute, 2); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "setData", &jsSetData, 1); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "setTagName", &jsSetTagName, 1); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "tagName", &jsTagName, 0); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "text", &jsText, 0); + if constexpr (std::is_same_v<C, QDomDocument>) { + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "documentElement", &jsDocumentElement, 0); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "createElement", &jsCreateElement, 1); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "createCDATASection", + &jsCreateCDATASection, 1); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "createTextNode", &jsCreateTextNode, 1); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "setContent", &jsSetContent, 1); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "toString", &jsToString, 1); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "save", &jsSave, 2); + JsExtension<XmlDomNode>::setupMethod(ctx, obj, "load", &jsLoad, 1); + } } } // namespace Internal } // namespace qbs -void initializeJsExtensionXml(QScriptValue extensionObject) +void initializeJsExtensionXml(qbs::Internal::ScriptEngine *engine, JSValue extensionObject) { using namespace qbs::Internal; - QScriptEngine *engine = extensionObject.engine(); - QScriptValue docObj = engine->newQMetaObject(&XmlDomDocument::staticMetaObject, - engine->newFunction(&XmlDomDocument::ctor)); - QScriptValue nodeObj = engine->newQMetaObject(&XmlDomNode::staticMetaObject, - engine->newFunction(&XmlDomNode::ctor)); - QScriptValue contextObject = engine->newObject(); - contextObject.setProperty(QStringLiteral("DomDocument"), docObj); - contextObject.setProperty(QStringLiteral("DomElement"), nodeObj); - - extensionObject.setProperty(QStringLiteral("Xml"), contextObject); + JSValue contextObject = engine->newObject(); + XmlDomNode<QDomNode>::registerClass(engine, contextObject); + XmlDomNode<QDomDocument>::registerClass(engine, contextObject); + setJsProperty(engine->context(), extensionObject, QLatin1String("Xml"), contextObject); } - -Q_DECLARE_METATYPE(qbs::Internal::XmlDomDocument *) -Q_DECLARE_METATYPE(qbs::Internal::XmlDomNode *) - -#include "domxml.moc" |