summaryrefslogtreecommitdiff
path: root/tools/xmlpatterns/main.cpp
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commite1b2c9deb5943faae2b29be6a5c006f75bb73f06 (patch)
treefc79e45367c0a8fc71185e9afc33f7503a58653c /tools/xmlpatterns/main.cpp
downloadqtxmlpatterns-e1b2c9deb5943faae2b29be6a5c006f75bb73f06.tar.gz
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
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..552a13f
--- /dev/null
+++ b/tools/xmlpatterns/main.cpp
@@ -0,0 +1,386 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtXmlPatterns module of the Qt Toolkit.
+**
+** $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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@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 <QtCore/QCoreApplication>
+
+#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"
+
+#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 QVariant::fromValue(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 QVariant::fromValue(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 QVariant::fromValue(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. */
+ _setmode(_fileno(stdout), _O_BINARY);
+ m_stdout = _wfdopen(_fileno(stdout), L"wb");
+ out->open(m_stdout, QIODevice::WriteOnly);
+#else
+ out->open(stdout, QIODevice::WriteOnly);
+#endif
+
+ return QVariant::fromValue(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(qvariant_cast<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(qvariant_cast<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(qvariant_cast<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;
+}
+