summaryrefslogtreecommitdiffstats
path: root/tools/xmlpatterns/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/xmlpatterns/main.cpp')
-rw-r--r--tools/xmlpatterns/main.cpp386
1 files changed, 386 insertions, 0 deletions
diff --git a/tools/xmlpatterns/main.cpp b/tools/xmlpatterns/main.cpp
new file mode 100644
index 0000000..ceb5f75
--- /dev/null
+++ b/tools/xmlpatterns/main.cpp
@@ -0,0 +1,386 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the Patternist project on Trolltech Labs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QDir>
+#include <QtCore/QtDebug>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QStringList>
+#include <QtCore/QTextCodec>
+#include <QtCore/QTextStream>
+#include <QtCore/QUrl>
+#include <QtCore/QVariant>
+#include <QtCore/QVector>
+
+#include <QtXmlPatterns/QXmlFormatter>
+#include <QtXmlPatterns/QXmlItem>
+#include <QtXmlPatterns/QXmlQuery>
+#include <QtXmlPatterns/QXmlSerializer>
+
+#include "private/qautoptr_p.h"
+#include "qapplicationargument_p.h"
+#include "qapplicationargumentparser_p.h"
+#include "qcoloringmessagehandler_p.h"
+
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+/* Needed for opening stdout with _fdopen & friends. io.h seems to not be
+ * needed on MinGW though. */
+#include <io.h>
+#include <fcntl.h>
+#endif
+
+#include "main.h"
+
+QT_USE_NAMESPACE
+
+/* The two Q_DECLARE_METATYPE macros must appear before the code
+ * on a couple of HPUX platforms. */
+
+/*!
+ \internal
+ \since 4.4
+ Represents the name and value found in "-param name=value".
+ */
+typedef QPair<QString, QString> Parameter;
+Q_DECLARE_METATYPE(Parameter);
+
+/*!
+ \internal
+ \since 4.4
+ For the -output switch.
+ */
+Q_DECLARE_METATYPE(QIODevice *);
+
+/*!
+ \class PatternistApplicationParser
+ \brief Subclass to handle -param name=value
+ \internal
+ \since 4.4
+ \reentrant
+ */
+class PatternistApplicationParser : public QApplicationArgumentParser
+{
+public:
+ inline PatternistApplicationParser(int argc, char **argv,
+ const QXmlNamePool &np) : QApplicationArgumentParser(argc, argv)
+ , m_namePool(np)
+#ifdef Q_OS_WIN
+ , m_stdout(0)
+#endif
+ {
+ }
+
+#ifdef Q_OS_WIN
+ virtual ~PatternistApplicationParser()
+ {
+ /* QFile::~QFile() nor QFile::close() frees the handle when
+ * we use QFile::open() so we have to do it manually.
+ *
+ * "If stream is NULL, the invalid parameter handler is invoked," so
+ * lets try to avoid that. */
+ if(m_stdout)
+ fclose(m_stdout);
+ }
+#endif
+
+protected:
+ virtual QVariant convertToValue(const QApplicationArgument &arg,
+ const QString &input) const
+ {
+ if(arg.name() == QLatin1String("param"))
+ {
+ const int assign = input.indexOf(QLatin1Char('='));
+
+ if(assign == -1)
+ {
+ message(QXmlPatternistCLI::tr("Each binding must contain an equal sign."));
+ return QVariant();
+ }
+
+ const QString name(input.left(assign));
+ const QString value(input.mid(assign + 1));
+
+ if(!QXmlName::isNCName(name))
+ {
+ message(QXmlPatternistCLI::tr("The variable name must be a valid NCName, which %1 isn't.").arg(name));
+ return QVariant();
+ }
+
+ /* The value.isNull() check ensures we can bind variables whose value is an empty string. */
+ return qVariantFromValue(Parameter(name, value.isNull() ? QString(QLatin1String("")) : value ));
+ }
+ else if(arg.name() == QLatin1String("output"))
+ {
+ QFile *const f = new QFile(input);
+
+ if(f->open(QIODevice::WriteOnly))
+ return qVariantFromValue(static_cast<QIODevice *>(f));
+ else
+ {
+ message(QXmlPatternistCLI::tr("Failed to open file %1 for writing: %2").arg(f->fileName(), f->errorString()));
+ return QVariant();
+ }
+ }
+ else if(arg.name() == QLatin1String("initial-template"))
+ {
+ const QXmlName name(QXmlName::fromClarkName(input, m_namePool));
+ if(name.isNull())
+ {
+ message(QXmlPatternistCLI::tr("%1 is an invalid Clark Name").arg(input));
+ return QVariant();
+ }
+ else
+ return qVariantFromValue(name);
+ }
+ else
+ return QApplicationArgumentParser::convertToValue(arg, input);
+ }
+
+ virtual QString typeToName(const QApplicationArgument &argument) const
+ {
+ if(argument.name() == QLatin1String("param"))
+ return QLatin1String("name=value");
+ else if(argument.name() == QLatin1String("output"))
+ return QLatin1String("local file");
+ else
+ return QApplicationArgumentParser::typeToName(argument);
+ }
+
+ virtual QVariant defaultValue(const QApplicationArgument &argument) const
+ {
+ if(argument.name() == QLatin1String("output"))
+ {
+ QFile *const out = new QFile();
+
+#ifdef Q_OS_WIN
+ /* If we don't open stdout in "binary" mode on Windows, it will translate
+ * 0xA into 0xD 0xA. See Trolltech task 173619, for an example. */
+ _setmode(_fileno(stdout), _O_BINARY);
+ m_stdout = QT_WA_INLINE(_wfdopen(_fileno(stdout), L"wb"),_fdopen(_fileno(stdout), "wb"));
+ out->open(m_stdout, QIODevice::WriteOnly);
+#else
+ out->open(stdout, QIODevice::WriteOnly);
+#endif
+
+ return qVariantFromValue(static_cast<QIODevice *>(out));
+ }
+ else
+ return QApplicationArgumentParser::defaultValue(argument);
+ }
+
+private:
+ QXmlNamePool m_namePool;
+#ifdef Q_OS_WIN
+ mutable FILE * m_stdout;
+#endif
+};
+
+static inline QUrl finalizeURI(const QApplicationArgumentParser &parser,
+ const QApplicationArgument &isURI,
+ const QApplicationArgument &arg)
+{
+ QUrl userURI;
+ {
+ const QString stringURI(parser.value(arg).toString());
+
+ if(parser.has(isURI))
+ userURI = QUrl::fromEncoded(stringURI.toLatin1());
+ else
+ userURI = QUrl::fromLocalFile(stringURI);
+ }
+
+ return QUrl::fromLocalFile(QDir::current().absolutePath() + QLatin1Char('/')).resolved(userURI);
+}
+
+int main(int argc, char **argv)
+{
+ enum ExitCode
+ {
+ /**
+ * We start from 2, because QApplicationArgumentParser
+ * uses 1.
+ */
+ QueryFailure = 2,
+ StdOutFailure
+ };
+
+ const QCoreApplication app(argc, argv);
+ QCoreApplication::setApplicationName(QLatin1String("xmlpatterns"));
+
+ QXmlNamePool namePool;
+ PatternistApplicationParser parser(argc, argv, namePool);
+ parser.setApplicationDescription(QLatin1String("A tool for running XQuery queries."));
+ parser.setApplicationVersion(QLatin1String("0.1"));
+
+ QApplicationArgument param(QLatin1String("param"),
+ QXmlPatternistCLI::tr("Binds an external variable. The value is directly available using the variable reference: $name."),
+ qMetaTypeId<Parameter>());
+ param.setMaximumOccurrence(-1);
+ parser.addArgument(param);
+
+ const QApplicationArgument noformat(QLatin1String("no-format"),
+ QXmlPatternistCLI::tr("By default output is formatted for readability. When specified, strict serialization is performed."));
+ parser.addArgument(noformat);
+
+ const QApplicationArgument isURI(QLatin1String("is-uri"),
+ QXmlPatternistCLI::tr("If specified, all filenames on the command line are interpreted as URIs instead of a local filenames."));
+ parser.addArgument(isURI);
+
+ const QApplicationArgument initialTemplateName(QLatin1String("initial-template"),
+ QXmlPatternistCLI::tr("The name of the initial template to call as a Clark Name."),
+ QVariant::String);
+ parser.addArgument(initialTemplateName);
+
+ /* The temporary object is required to compile with g++ 3.3. */
+ QApplicationArgument queryURI = QApplicationArgument(QLatin1String("query/stylesheet"),
+ QXmlPatternistCLI::tr("A local filename pointing to the query to run. If the name ends with .xsl it's assumed "
+ "to be an XSL-T stylesheet. If it ends with .xq, it's assumed to be an XQuery query. (In "
+ "other cases it's also assumed to be an XQuery query, but that interpretation may "
+ "change in a future release of Qt.)"),
+ QVariant::String);
+ queryURI.setMinimumOccurrence(1);
+ queryURI.setNameless(true);
+ parser.addArgument(queryURI);
+
+ QApplicationArgument focus = QApplicationArgument(QLatin1String("focus"),
+ QXmlPatternistCLI::tr("The document to use as focus. Mandatory "
+ "in case a stylesheet is used. This option is "
+ "also affected by the is-uris option."),
+ QVariant::String);
+ focus.setMinimumOccurrence(0);
+ focus.setNameless(true);
+ parser.addArgument(focus);
+
+ QApplicationArgument output(QLatin1String("output"),
+ QXmlPatternistCLI::tr("A local file to which the output should be written. "
+ "The file is overwritten, or if not exist, created. "
+ "If absent, stdout is used."),
+ qMetaTypeId<QIODevice *>());
+ parser.addArgument(output);
+
+ if(!parser.parse())
+ return parser.exitCode();
+
+ /* Get the query URI. */
+ const QUrl effectiveURI(finalizeURI(parser, isURI, queryURI));
+
+ QXmlQuery::QueryLanguage lang;
+
+ if(effectiveURI.toString().endsWith(QLatin1String(".xsl")))
+ lang = QXmlQuery::XSLT20;
+ else
+ lang = QXmlQuery::XQuery10;
+
+ if(lang == QXmlQuery::XQuery10 && parser.has(initialTemplateName))
+ {
+ parser.message(QXmlPatternistCLI::tr("An initial template name cannot be specified when running an XQuery."));
+ return QApplicationArgumentParser::ParseError;
+ }
+
+ QXmlQuery query(lang, namePool);
+
+ query.setInitialTemplateName(qVariantValue<QXmlName>(parser.value(initialTemplateName)));
+
+ /* Bind external variables. */
+ {
+ const QVariantList parameters(parser.values(param));
+ const int len = parameters.count();
+
+ /* For tracking duplicates. */
+ QSet<QString> usedParameters;
+
+ for(int i = 0; i < len; ++i)
+ {
+ const Parameter p(qVariantValue<Parameter>(parameters.at(i)));
+
+ if(usedParameters.contains(p.first))
+ {
+ parser.message(QXmlPatternistCLI::tr("Each parameter must be unique, %1 is specified at least twice.").arg(p.first));
+ return QApplicationArgumentParser::ParseError;
+ }
+ else
+ {
+ usedParameters.insert(p.first);
+ query.bindVariable(p.first, QXmlItem(p.second));
+ }
+ }
+ }
+
+ if(parser.has(focus))
+ {
+ if(!query.setFocus(finalizeURI(parser, isURI, focus)))
+ return QueryFailure;
+ }
+ else if(lang == QXmlQuery::XSLT20 && !parser.has(initialTemplateName))
+ {
+ parser.message(QXmlPatternistCLI::tr("When a stylesheet is used, a "
+ "document must be specified as a focus, or an "
+ "initial template name must be specified, or both."));
+ return QApplicationArgumentParser::ParseError;
+ }
+
+ query.setQuery(effectiveURI);
+
+ const QPatternist::AutoPtr<QIODevice> outDevice(qVariantValue<QIODevice *>(parser.value(output)));
+ Q_ASSERT(outDevice);
+ Q_ASSERT(outDevice->isWritable());
+
+ if(query.isValid())
+ {
+ typedef QPatternist::AutoPtr<QAbstractXmlReceiver> RecPtr;
+ RecPtr receiver;
+
+ if(parser.has(noformat))
+ receiver = RecPtr(new QXmlSerializer(query, outDevice.data()));
+ else
+ receiver = RecPtr(new QXmlFormatter(query, outDevice.data()));
+
+ const bool success = query.evaluateTo(receiver.data());
+
+ if(success)
+ return parser.exitCode();
+ else
+ return QueryFailure;
+ }
+ else
+ return QueryFailure;
+}
+