summaryrefslogtreecommitdiffstats
path: root/tools/xmlpatterns
diff options
context:
space:
mode:
Diffstat (limited to 'tools/xmlpatterns')
-rw-r--r--tools/xmlpatterns/main.cpp386
-rw-r--r--tools/xmlpatterns/main.h75
-rw-r--r--tools/xmlpatterns/qapplicationargument.cpp344
-rw-r--r--tools/xmlpatterns/qapplicationargument_p.h100
-rw-r--r--tools/xmlpatterns/qapplicationargumentparser.cpp1028
-rw-r--r--tools/xmlpatterns/qapplicationargumentparser_p.h111
-rw-r--r--tools/xmlpatterns/qcoloringmessagehandler.cpp193
-rw-r--r--tools/xmlpatterns/qcoloringmessagehandler_p.h99
-rw-r--r--tools/xmlpatterns/qcoloroutput.cpp350
-rw-r--r--tools/xmlpatterns/qcoloroutput_p.h134
-rw-r--r--tools/xmlpatterns/xmlpatterns.pro31
11 files changed, 2851 insertions, 0 deletions
diff --git a/tools/xmlpatterns/main.cpp b/tools/xmlpatterns/main.cpp
new file mode 100644
index 0000000000..ceb5f75f2b
--- /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;
+}
+
diff --git a/tools/xmlpatterns/main.h b/tools/xmlpatterns/main.h
new file mode 100644
index 0000000000..eaef3bbb60
--- /dev/null
+++ b/tools/xmlpatterns/main.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+ * ** * ** 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$
+ * **
+ * ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ * ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * **
+ * ****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#ifndef Patternist_main_h
+#define Patternist_main_h
+
+#include <QCoreApplication>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QXmlPatternistCLI
+{
+public:
+ Q_DECLARE_TR_FUNCTIONS(QXmlPatternistCLI)
+private:
+ inline QXmlPatternistCLI();
+ Q_DISABLE_COPY(QXmlPatternistCLI)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/tools/xmlpatterns/qapplicationargument.cpp b/tools/xmlpatterns/qapplicationargument.cpp
new file mode 100644
index 0000000000..e88d5f8eda
--- /dev/null
+++ b/tools/xmlpatterns/qapplicationargument.cpp
@@ -0,0 +1,344 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications 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 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 <QHash>
+#include <QString>
+
+#include "qapplicationargument_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QApplicationArgument
+ \brief The QApplicationArgument class is a declared of a command line
+ argument for QApplicationArgumentParser.
+ \reentrant
+ \internal
+ \since 4.4
+
+ QApplicationArgument describes a valid command line argument,
+ by having a set of characteristics:
+
+ \table
+ \header
+ \o Characteristic
+ \o Functions
+ \row
+ \o A name. For instance, "backend"
+ \o setName() and name()
+ \row
+ \o A description, for human consumption.
+ \o setDescription() and description()
+ \row
+ \o How many times the argument can occur. For instance, whether the argument is optional or not.
+ \o setMinimumOccurrence() & minimumOccurrence(), setMaximumOccurrence() & maximumOccurrence()
+ \row
+ \o The type of the argument's value, if it has one. For instance, \c int or \c bool.
+ \o setType() and type()
+ \row
+ \o The value that should be used in case the argument isn't used.
+ \o setDefaultValue() and defaultValue()
+ \endtable
+
+ \sa QApplicationArgumentParser
+ */
+
+class QApplicationArgumentPrivate
+{
+public:
+ inline QApplicationArgumentPrivate(const QString &newName,
+ const QString &desc,
+ const int newType) : name(newName)
+ , description(desc)
+ , type(newType)
+ , minimum(0)
+ , maximum(1)
+ , isNameless(false)
+ {
+ }
+
+ QString name;
+ QString description;
+ int type;
+ QVariant defaultValue;
+ int minimum;
+ int maximum;
+ bool isNameless;
+};
+
+/*!
+ Constructs an invalid QApplicationArgument instance.
+ */
+QApplicationArgument::QApplicationArgument() : d_ptr(new QApplicationArgumentPrivate(QString(), QString(), QVariant::Invalid))
+{
+}
+
+/*!
+ Constructs an QApplicationArgument instance that is a copy of \a other.
+ */
+QApplicationArgument::QApplicationArgument(const QApplicationArgument &other) : d_ptr(new QApplicationArgumentPrivate(*other.d_ptr))
+{
+}
+
+/*!
+ Destructs this QApplicationArgument instance.
+ */
+QApplicationArgument::~QApplicationArgument()
+{
+ delete d_ptr;
+}
+
+/*!
+ Constructs an argument that has the name \a name and is of type
+ \a aType.
+
+ Calling this constructor is equivalent to calling setName() and setType()
+ on a default constructed QApplicationArgument instance.
+
+ \sa setName(), setType()
+ */
+QApplicationArgument::QApplicationArgument(const QString &name,
+ const QString &description,
+ int aType) : d_ptr(new QApplicationArgumentPrivate(name, description, aType))
+{
+}
+
+/*!
+ Assigns \a other to this QApplicationArgument instance.
+ */
+QApplicationArgument &QApplicationArgument::operator=(const QApplicationArgument &other)
+{
+ if(this != &other)
+ *d_ptr = *other.d_ptr;
+
+ return *this;
+}
+
+// TODO is this really what we want?
+/*!
+ Returns true if this QApplicationArgument instance is equal to \a other.
+
+ Equalness is defined to only consider name(). If for instance the type() differs
+ but the names are equal, this operator will return \c true.
+ */
+bool QApplicationArgument::operator==(const QApplicationArgument &other) const
+{
+ return name() == other.name();
+}
+
+/*!
+ \fn qHash(const QApplicationArgument &);
+ \internal
+
+ Returns a hash index of \a argument. This function is used when QApplicationArgument
+ is used with QHash.
+
+ The hash index is computed on name(). The other properties are ignored.
+
+ \relates QApplicationArgument
+ */
+
+/*!
+ Sets this argument's name to \a newName. The name does not
+ include any dash, or other prefix that is used by the parser.
+ */
+void QApplicationArgument::setName(const QString &newName)
+{
+ d_ptr->name = newName;
+}
+
+/*!
+ Returns the name that this argument has.
+
+ \sa setName()
+ */
+QString QApplicationArgument::name() const
+{
+ return d_ptr->name;
+}
+
+/*!
+ Sets the tupe to \a newType.
+
+ If \a newType is QVariant::Invalid, it signals that this
+ argument does not accept a value at all.
+
+ \a newType can be a QVariant::type() value, or QVariant::userType().
+
+ \sa type()
+ */
+void QApplicationArgument::setType(int newType)
+{
+ d_ptr->type = newType;
+}
+
+/*!
+ Returns the type that the value of this argument has. If it
+ is QVariant::Invalid, it means this argument cannot have a value
+ and is a switch only.
+
+ The type is by default QVariant::Invalid.
+\sa setType()
+ */
+int QApplicationArgument::type() const
+{
+ return d_ptr->type;
+}
+
+void QApplicationArgument::setDefaultValue(const QVariant &value)
+{
+ d_ptr->defaultValue = value;
+}
+
+QVariant QApplicationArgument::defaultValue() const
+{
+ return d_ptr->defaultValue;
+}
+
+/*!
+ Sets the minimum amount of times this argument can occur, to \a minimum.
+ For instance, if \a minimum is 2, the argument must be used at least two times.
+
+ If \a minimum is zero, it means the argument is optional.
+
+ \sa minimumOccurrence(), setMaximumOccurrence()
+ */
+void QApplicationArgument::setMinimumOccurrence(int minimum)
+{
+ Q_ASSERT_X(minimum >= 0, Q_FUNC_INFO,
+ "The minimum cannot be less than zero.");
+ d_ptr->minimum = minimum;
+}
+
+/*!
+ Returns the minimum amount of times an an argument must occur.
+
+ The default is 0.
+
+ \sa setMinimumOccurrence(), maximumOccurrence()
+ */
+int QApplicationArgument::minimumOccurrence() const
+{
+ return d_ptr->minimum;
+}
+
+/*!
+ Sets the maximum occurrence to \a maximum.
+
+ If \a maximum is -1, it means the argument can appear an unlimited
+ amount of times. Setting it to zero or less than -1, yields
+ undefined behavior.
+
+\sa maximumOccurrence(), setMinimumOccurrence()
+ */
+void QApplicationArgument::setMaximumOccurrence(int maximum)
+{
+ Q_ASSERT_X(maximum == -1 || maximum >= 1, Q_FUNC_INFO,
+ "The maximum can only be -1 or 1 or larger.");
+ d_ptr->maximum = maximum;
+}
+
+/*!
+ Returns the maximum amount of times this argument can occur. For instance,
+ if the maximum occurrence is 2, it would be an error if 3 were specified
+ on the command line.
+
+ If the maximum occurrence is -1, it signals the argument can appear an unlimited
+ amount of times.
+
+ The default is 1.
+
+ \sa setMaximumOccurrence()
+ */
+int QApplicationArgument::maximumOccurrence() const
+{
+ return d_ptr->maximum;
+}
+
+/*!
+ Sets the description to \a newDescription. The description
+ should describe the argument in a sentence or two. It is used
+ when displaying a help message, if requested.
+
+ The description should be terminated as if it was a paragraph. This
+ typically means a period.
+
+ The description should be translated by wrapping the
+ string literal in a call to tr().
+
+ */
+void QApplicationArgument::setDescription(const QString &newDescription)
+{
+ d_ptr->description = newDescription;
+}
+
+/*!
+ Returns the description of this argument.
+
+ \sa setDescription()
+ */
+QString QApplicationArgument::description() const
+{
+ return d_ptr->description;
+}
+
+/*!
+ \internal
+ \relates QApplicationArgument
+
+ Computes a hash key on \a argument's name and returns it.
+ */
+uint qHash(const QApplicationArgument &argument)
+{
+ return qHash(argument.name());
+}
+
+void QApplicationArgument::setNameless(bool value)
+{
+ d_ptr->isNameless = value;
+}
+
+bool QApplicationArgument::isNameless() const
+{
+ return d_ptr->isNameless;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/xmlpatterns/qapplicationargument_p.h b/tools/xmlpatterns/qapplicationargument_p.h
new file mode 100644
index 0000000000..3232bbcffe
--- /dev/null
+++ b/tools/xmlpatterns/qapplicationargument_p.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+** ** This file is part of the tools applications 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 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$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#ifndef QApplicationArgument_H
+#define QApplicationArgument_H
+
+#include <QtCore/QVariant>
+
+QT_BEGIN_HEADER
+QT_BEGIN_NAMESPACE
+
+class QString;
+class QApplicationArgumentPrivate;
+
+class QApplicationArgument
+{
+public:
+ QApplicationArgument();
+ QApplicationArgument(const QApplicationArgument &other);
+ QApplicationArgument(const QString &name,
+ const QString &description,
+ int aType = QVariant::Invalid);
+ ~QApplicationArgument();
+ QApplicationArgument &operator=(const QApplicationArgument &other);
+ bool operator==(const QApplicationArgument &other) const;
+
+ void setName(const QString &newName);
+ QString name() const;
+ void setDescription(const QString &newDescription);
+ QString description() const;
+
+ int type() const;
+ void setType(int newType);
+ void setDefaultValue(const QVariant &value);
+ QVariant defaultValue() const;
+
+ void setMinimumOccurrence(int minimum);
+ int minimumOccurrence() const;
+ void setMaximumOccurrence(int maximum);
+ int maximumOccurrence() const;
+ void setNameless(bool value);
+ bool isNameless() const;
+
+private:
+ QApplicationArgumentPrivate *d_ptr;
+};
+
+uint qHash(const QApplicationArgument &argument);
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif
+
diff --git a/tools/xmlpatterns/qapplicationargumentparser.cpp b/tools/xmlpatterns/qapplicationargumentparser.cpp
new file mode 100644
index 0000000000..7a9b902e70
--- /dev/null
+++ b/tools/xmlpatterns/qapplicationargumentparser.cpp
@@ -0,0 +1,1028 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the tools applications 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 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 <QtDebug>
+#include <QTextBoundaryFinder>
+#include <QCoreApplication>
+#include <QHash>
+#include <QPair>
+#include <QStringList>
+#include <QTextStream>
+#include <QUrl>
+
+#include "qapplicationargument_p.h"
+
+#include "qapplicationargumentparser_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QApplicationArgumentParser
+ \brief The QApplicationArgumentParser class parses the command
+ line arguments for an application.
+ \reentrant
+ \internal
+ \since 4.4
+
+ QApplicationArgumentParser simplifies writing command line applications by taking care of:
+
+ \list
+ \o Generating help and version arguments
+ \o Taking care of converting arguments to QVariant types, since each argument
+ has a type: QApplicationArgument::type()
+ \o Validates the command line such that the user operates on well-defined input. For instance,
+ that the argument is a valid integer if that is the case, that an argument does not
+ occur more times than allowed, and so on.
+ \o Allows customization through sub-classing.
+ \endlist
+
+ The user declares what arguments that can be given to the application with QApplicationArgument. Provided
+ with that information, QApplicationArgumentParser takes care of parsing the actual
+ command line, appropriately flag errors, generate help messages, and provide
+ convenient access to the values of the arguments.
+
+ The way to use it is to create a set of QApplicationArgument by ones choosing, call
+ addArgument() for each, and subsequently call parse(). If parse() returns \c false,
+ the caller should exit and return exitCode().
+
+ If parse() returns \c true the command line was successfully parsed, its
+ values are well-defined, and they can be spectated with count(),
+ has(), value() and values().
+
+ \snippet doc/src/snippets/code/tools_patternist_qapplicationargumentparser.cpp 0
+
+ For arguments without a name(such as filename passed to the \c ls utility on Linux) add a
+ QApplicationArgument that does not have a name. The minimum and maximum occurrences will be
+ respected as usual and the type applies too.
+
+ QApplicationArgumentParser always has two options builtin: \c version and \c help.
+
+ \section1 Changing Parsing Convention
+
+ QApplicationArgumentParser by default parses the command line in the style
+ of Qt's utilities, where arguments are preceded by a single dash, and identified
+ by a single name. However, in some cases it might be of interest to parse
+ another style, such as the well-established UNIX \c getopt convention(\c -l
+ and \c --long).
+
+ This can be achieved by sub-classing QApplicationArgumentParser and reimplementing
+ parse(). It would do the following:
+
+ \list
+ \o Call input() to retrieve the strings the user specified on the command line.
+ \o Call declaredArguments() to retrieve the arguments that the implementor has
+ decided can be specified.
+ \o Parse and validate the input. Salt and pepper as per taste.
+ \o If an error occurred, call setExitCode() and return \c false.
+ \o Otherwise, call setExitCode(Success), provide access to the
+ arguments by calling setUsedArguments(), and return \c true. If a
+ help message was requested, call setExitCode(Success) and return \c false.
+ \endlist
+
+ \sa QApplicationArgument, QCoreApplication
+*/
+class QApplicationArgumentParserPrivate
+{
+ Q_DECLARE_TR_FUNCTIONS(QApplicationArgumentParserPrivate)
+public:
+ // TODO Isn't it like ten times better with QHash<QApplicationArgument, QList<QVariant> >?
+ // TODO test QApplicationArgument::nameless()
+ typedef QList<QPair<QApplicationArgument, QVariant> > UsedList;
+
+ /*!
+ We initialize exitCode to ParseError such that we consciously flag success.
+ */
+ inline QApplicationArgumentParserPrivate(QApplicationArgumentParser *const master,
+ const QStringList &aInput) : exitCode(QApplicationArgumentParser::ParseError)
+ , input(aInput)
+ , q_ptr(master)
+ {
+ Q_ASSERT(!aInput.isEmpty());
+ }
+
+ QApplicationArgument nextNamelessArgument() const;
+ static QStringList argumentsFromLocal(const int argc, const char *const *const argv);
+
+ bool error(const QString &message);
+ static bool errorMessage(const QString &message);
+ static inline bool isSwitch(const QApplicationArgument &arg);
+ static inline QVariant conversionError(const QString &typeName,
+ const QString &input);
+ int count(const QApplicationArgument &arg) const;
+ bool contains(const QApplicationArgument &arg) const;
+ static inline bool isBuiltinVariant(const int type);
+ void displayVersion() const;
+ void displayHelp() const;
+ void parseNameless();
+ bool parseNamelessArguments(const QString &in);
+
+ QApplicationArgumentParser::ExitCode exitCode;
+ const QStringList input;
+
+ /*!
+ Since the QString is QApplicationArgument::name() anyway, why
+ not use a QSet?
+ */
+ QHash<QString, QApplicationArgument> declaredArguments;
+
+ QList<QApplicationArgument> declaredNamelessArguments;
+
+ UsedList usedArguments;
+ QString applicationDescription;
+ QString applicationVersion;
+
+private:
+ QApplicationArgumentParser *const q_ptr;
+ Q_DECLARE_PUBLIC(QApplicationArgumentParser)
+
+ static QString lineWrap(const QString &input,
+ const int leftIndent,
+ const int width);
+ static QList<QApplicationArgument> builtinArguments();
+};
+
+QApplicationArgument QApplicationArgumentParserPrivate::nextNamelessArgument() const
+{
+ /* Count how many nameless arguments we have so far. */
+ int count = 0;
+
+ for(int i = 0; i < usedArguments.count(); ++i)
+ {
+ if(usedArguments.at(i).first.isNameless())
+ ++count;
+ }
+
+ /* TODO this doesn't work for arguments that have more than one
+ * mandatory value(e.g nameless ones), since several values should
+ * then only count for one argument. */
+ for(int i = 0; i < declaredNamelessArguments.count(); ++i)
+ {
+ if(count)
+ {
+ /* Skip the ones we already have processed. */
+ --count;
+ continue;
+ }
+
+ if(declaredNamelessArguments.at(i).isNameless())
+ return declaredNamelessArguments.at(i);
+ }
+
+ return QApplicationArgument();
+}
+
+int QApplicationArgumentParserPrivate::count(const QApplicationArgument &arg) const
+{
+ const int len = usedArguments.count();
+ int count = 0;
+
+ for(int i = 0; i < len; ++i)
+ {
+ if(usedArguments.at(i).first == arg)
+ ++count;
+ }
+
+ return count;
+}
+
+/*!
+ Returns \c true if \a arg has appeared on the command line, not whether it has been declared.
+ */
+bool QApplicationArgumentParserPrivate::contains(const QApplicationArgument &arg) const
+{
+ const int len = usedArguments.count();
+
+ for(int i = 0; i < len; ++i)
+ {
+ if(usedArguments.at(i).first == arg)
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ Returns always \c false.
+ */
+bool QApplicationArgumentParserPrivate::error(const QString &message)
+{
+ exitCode = QApplicationArgumentParser::ParseError;
+ errorMessage(message);
+ return errorMessage(tr("Pass -help for information about the command line."));
+}
+
+/*!
+ Returns always \c false.
+ */
+bool QApplicationArgumentParserPrivate::errorMessage(const QString &message)
+{
+ QTextStream out(stderr, QIODevice::WriteOnly);
+ out << message << endl;
+ return false;
+}
+
+/*!
+ \internal
+ Determines whether \a arg carries a value or is on/off.
+ */
+bool QApplicationArgumentParserPrivate::isSwitch(const QApplicationArgument &arg)
+{
+ return arg.type() == QVariant::Invalid;
+}
+
+QVariant QApplicationArgumentParserPrivate::conversionError(const QString &typeName,
+ const QString &input)
+{
+ errorMessage(tr("Cannot convert %1 to type %2.").arg(input, typeName));
+ return QVariant();
+}
+
+bool QApplicationArgumentParserPrivate::isBuiltinVariant(const int type)
+{
+ return type < int(QVariant::UserType);
+}
+
+/*!
+ TODO Temporary, replace with a function in QCoreApplication.
+*/
+QStringList QApplicationArgumentParserPrivate::argumentsFromLocal(const int argc, const char *const *const argv)
+{
+ Q_ASSERT(argc >= 1);
+ Q_ASSERT(argv);
+ QStringList result;
+
+ for(int i = 0; i < argc; ++i)
+ result.append(QString::fromLocal8Bit(argv[i]));
+
+ return result;
+}
+
+void QApplicationArgumentParserPrivate::displayVersion() const
+{
+ QTextStream out(stderr);
+
+ out << tr("%1 version %2 using Qt %3").arg(QCoreApplication::applicationName(), applicationVersion, QString::fromAscii(qVersion()))
+ << endl;
+}
+
+/*!
+ \internal
+ \relates QApplicationArgument
+
+ qLess() functor for QApplicationArgument that considers the name.
+ */
+template<>
+class qLess <QApplicationArgument>
+{
+public:
+ inline bool operator()(const QApplicationArgument &o1,
+ const QApplicationArgument &o2) const
+ {
+ return o1.name().compare(o2.name()) < 0;
+ }
+};
+
+void QApplicationArgumentParserPrivate::displayHelp() const
+{
+ enum Constants
+ {
+ /**
+ * When we want to line wrap, 80 minus a couple of characters. This should
+ * be suitable for vt100 compatible terminals.
+ */
+ LineWrapAt = 78,
+
+ /**
+ * The initial " -" for each option.
+ */
+ IndentPadding = 3,
+
+ /**
+ * Pad for the brackets and space we use when we have a type.
+ */
+ ValueArgumentPadding = 4
+ };
+
+ QList<QApplicationArgument> args(declaredArguments.values());
+ args += builtinArguments();
+
+ /* Sort them, such that we get the nameless options at the end, and it
+ * generally looks tidy. */
+ qSort(args);
+
+ /* This is the basic approach:
+ * Switches:
+ * -name description
+ * Value arguments:
+ * -name <name-of-value-type> description
+ *
+ * Nameless arguments
+ * name <type> description
+ *
+ * It all line-wraps at OutputWidth and the description is indented,
+ * where the highest indent is the length of the name plus length of the name
+ * of the type. */
+
+ /* First we find the name with the largest width. */
+ int maxWidth = 0;
+
+ QList<QApplicationArgument> nameless(declaredNamelessArguments);
+ qSort(nameless);
+
+ /* Note, here the nameless arguments appear last, but are sorted
+ * with themselves. */
+ QList<QApplicationArgument> allArgs(args + nameless);
+ const int allArgsCount = allArgs.count();
+
+ for(int i = 0; i < allArgsCount; ++i)
+ {
+ const QApplicationArgument &at = allArgs.at(i);
+ const int nameLength = at.name().length();
+ const QString typeName(q_ptr->typeToName(at));
+ const int typeNameLength = typeName.length();
+ const int padding = at.type() == QVariant::Invalid ? 0 : ValueArgumentPadding;
+ maxWidth = qMax(maxWidth, nameLength + typeNameLength + padding);
+ }
+
+ QTextStream out(stderr);
+ out << endl
+ << QString(IndentPadding, QLatin1Char(' '))
+ << QCoreApplication::applicationName()
+ << QLatin1String(" -- ")
+ << applicationDescription
+ << endl;
+ // TODO synopsis
+
+ /* One extra so we get some space between the overview and the options. */
+ out << endl;
+
+ const int indentWidth = maxWidth + 3;
+
+ /* Ok, print them out. */
+ for(int i = 0; i < allArgsCount; ++i)
+ {
+ const QApplicationArgument &at = allArgs.at(i);
+ /* " -name ". Indent a bit first, inspired by Qt's moc. */
+ const QString &name = at.name();
+ QString prolog(QLatin1String(" "));
+
+ /* We have a special case for the single dash. */
+ if(name == QChar::fromLatin1('-'))
+ prolog.append(name);
+ else
+ {
+ if(!at.isNameless())
+ prolog.append(QLatin1Char('-'));
+
+ prolog.append(name + QLatin1Char(' '));
+ }
+
+ if(at.type() != QVariant::Invalid)
+ {
+ /* It's not a switch, it has a value. */
+
+ /* Do we have a default value? If so, the argument is optional. */
+ const QString typeName(q_ptr->typeToName(at));
+
+ if(at.defaultValue().isValid())
+ prolog.append(QLatin1Char('[') + typeName + QLatin1Char(']'));
+ else
+ prolog.append(QLatin1Char('<') + typeName + QLatin1Char('>'));
+ // TODO Don't we want to display the default value?
+
+ prolog.append(QLatin1Char(' '));
+ }
+
+ prolog = prolog.leftJustified(indentWidth);
+
+ out << prolog
+ << lineWrap(at.description(), indentWidth, LineWrapAt)
+ << endl;
+ }
+}
+
+/*!
+ Line wraps \a input and indents each line with \a leftIndent spaces, such that
+ the width does not go beyond \a maxWidth.
+
+ The addition of line endings is accounted for by the caller.
+
+ With QTextBoundaryFinder our line wrapping is relatively fancy, since it
+ does it the Unicode-way.
+ */
+QString QApplicationArgumentParserPrivate::lineWrap(const QString &input,
+ const int leftIndent,
+ const int maxWidth)
+{
+ const QString indent(QString(leftIndent, QLatin1Char(' ')));
+ const int len = input.length();
+ const int textWidth = maxWidth - leftIndent;
+
+ QString output;
+ QTextBoundaryFinder wrapFinder(QTextBoundaryFinder::Line, input);
+ wrapFinder.setPosition(textWidth);
+
+ if(input.length() + leftIndent <= maxWidth)
+ return input;
+
+ int from = wrapFinder.toPreviousBoundary();
+ output.append(input.left(from));
+
+ while(true)
+ {
+ if((len - from) + leftIndent > maxWidth)
+ {
+ /* We need to line wrap. */
+ wrapFinder.setPosition(from + textWidth);
+ const int currentWidthPos = wrapFinder.toPreviousBoundary();
+
+ output.append(QLatin1Char('\n'));
+ output.append(indent);
+ output.append(input.mid(from, currentWidthPos - from).trimmed());
+ from += (currentWidthPos - from);
+ }
+ else
+ {
+ /* Append the remains. */
+ output.append(QLatin1Char('\n'));
+ output.append(indent);
+ output.append(input.mid(from).trimmed());
+ break;
+ }
+ }
+
+ return output;
+}
+
+/*!
+ Returns a list with the builtin options that the parser has
+ */
+QList<QApplicationArgument> QApplicationArgumentParserPrivate::builtinArguments()
+{
+ QList<QApplicationArgument> result;
+
+ result.append(QApplicationArgument(QLatin1String("help"),
+ QLatin1String("Displays this help.")));
+ result.append(QApplicationArgument(QLatin1String("version"),
+ QLatin1String("Displays version information.")));
+
+ result.append(QApplicationArgument(QLatin1String("-"),
+ QLatin1String("When appearing, any following options are not interpreted as switches.")));
+ return result;
+}
+
+/* TODO, I don't think we want this function in a public API. Add it first when there is a demand. */
+
+/*!
+ Creates a QApplicationArgumentParser that will parse the input in \a argc and \a argv.
+These arguments should be passed directly from the \c main() function, and the decoding
+of the input will be taken care of appropriately, depending on platform.
+
+ It is preferred to use the QStringList overload, in case the input is in the form of QStrings.
+ */
+QApplicationArgumentParser::QApplicationArgumentParser(int argc, char **argv) : d(new QApplicationArgumentParserPrivate(this, QApplicationArgumentParserPrivate::argumentsFromLocal(argc, argv)))
+{
+ Q_ASSERT_X(argv, Q_FUNC_INFO, "Argv cannot be null.");
+ Q_ASSERT_X(argc >= 1, Q_FUNC_INFO,
+ "argc must at least contain the application name. "
+ "Use the QStringList overload instead.");
+}
+
+/*!
+ \overload
+
+ Creates a QApplicationArgumentParser that will parse \a input. That is, instead of passing in \c argc
+ and \c argv, one can pass in a QStringList.
+
+ The caller guarantees that the first string in \a input is the name of the application.
+ */
+QApplicationArgumentParser::QApplicationArgumentParser(const QStringList &input) : d(new QApplicationArgumentParserPrivate(this, input))
+{
+ Q_ASSERT_X(input.count() >= 1, Q_FUNC_INFO,
+ "The input must at least contain the application name.");
+}
+
+/*!
+ This function is only of interest when subclassing.
+
+ Returns the strings that the user specified when starting the application. The first string
+ in the list is always the application name.
+ */
+QStringList QApplicationArgumentParser::input() const
+{
+ Q_ASSERT_X(d->input.count() >= 1, Q_FUNC_INFO, "Internal error, this should always hold true");
+ return d->input;
+}
+
+/*!
+ This function is only of interest when subclassing.
+
+ Sets the arguments that the user actually used on the command line to \a arguments.
+ The parse() function should call this, such that the result afterwards can be inspected
+ with for instance has() or count().
+
+\sa usedArguments()
+*/
+void QApplicationArgumentParser::setUsedArguments(const QList<QPair<QApplicationArgument, QVariant> > &arguments)
+{
+ d->usedArguments = arguments;
+}
+
+/*!
+ This function is only of interest when subclassing.
+
+ Returns the arguments that the user used on the command line.
+
+\sa setUsedArguments()
+*/
+QList<QPair<QApplicationArgument, QVariant> > QApplicationArgumentParser::usedArguments() const
+{
+ return d->usedArguments;
+}
+
+/*!
+ Destructs this QApplicationArgumentParser instance.
+ */
+QApplicationArgumentParser::~QApplicationArgumentParser()
+{
+ delete d;
+}
+
+/*!
+ Adds \a argument to this parser.
+
+ This function is provided for convenience. It is equivalent to creating a QList
+ containing \a argument, append the existing arguments, and then call setDeclaredArguments() with the list.
+
+ \sa setDeclaredArguments()
+ */
+void QApplicationArgumentParser::addArgument(const QApplicationArgument &argument)
+{
+ if(argument.isNameless())
+ d->declaredNamelessArguments.append(argument);
+ else
+ d->declaredArguments.insert(argument.name(), argument);
+}
+
+/*!
+ Makes the parser recognize all arguments in \a arguments.
+
+ Any arguments previously set, are discarded.
+
+ \sa addArgument(), declaredArguments()
+ */
+void QApplicationArgumentParser::setDeclaredArguments(const QList<QApplicationArgument> &arguments)
+{
+ // TODO If we have a QHash internally, why not use it in the public API too?
+ const int len = arguments.count();
+
+ for(int i = 0; i < len; ++i)
+ d->declaredArguments.insert(arguments.at(i).name(), arguments.at(i));
+}
+
+/*!
+ Returns the arguments that this parser recognizes.
+
+ \sa addArgument(), setDeclaredArguments()
+ */
+QList<QApplicationArgument> QApplicationArgumentParser::declaredArguments() const
+{
+ return d->declaredArguments.values();
+}
+
+bool QApplicationArgumentParserPrivate::parseNamelessArguments(const QString &in)
+{
+ /* It's a nameless options, such as simply "value". */
+ const QApplicationArgument nameless(nextNamelessArgument());
+
+ const QVariant val(q_ptr->convertToValue(nameless, in));
+ if(val.isValid())
+ {
+ usedArguments.append(qMakePair(nameless, val));
+ return true;
+ }
+ else
+ return false; // TODO error msg?
+}
+
+/*!
+ Parses input() together with declaredArguments() and returns \c false if the caller
+ should exit immediately, which is the case of which an error was encountered or
+ help or the version was requested.
+
+ In the case of \c true was returned, valid arguments were supplied, and they can
+ be requested with functions like value(), values(), count() and has().
+
+ parse() must only be called once per QApplicationArgumentParser instance. The
+ second time it's called, the effects and return value are undefined.
+
+ \sa convertToValue(), typeToName()
+ */
+bool QApplicationArgumentParser::parse()
+{
+ const QChar sep(QLatin1Char('-'));
+ const int inputCount = d->input.count();
+
+ /* We skip the first entry, which is the application name. */
+ int i = 1;
+
+ for(; i < inputCount; ++i)
+ {
+ const QString &in = d->input.at(i);
+
+ /* We have a single '-', signalling that the succeeding are not options. */
+ if(in == sep)
+ {
+ ++i;
+
+ for(; i < inputCount; ++i)
+ {
+ if(!d->parseNamelessArguments(d->input.at(i)))
+ return false;
+ /* Process nameless options. Have code for this elsewhere, factor it out. */
+ }
+
+ break;
+ }
+
+ if(in.startsWith(sep)) /* It is "-name". */
+ {
+ const QString name(in.mid(1));
+
+ if(name == QLatin1String("help"))
+ {
+ setExitCode(Success);
+ d->displayHelp();
+ return false;
+ }
+ else if(name == QLatin1String("version"))
+ {
+ setExitCode(Success);
+ d->displayVersion();
+ return false;
+ }
+
+ if(!d->declaredArguments.contains(name))
+ return d->error(QApplicationArgumentParserPrivate::tr("\"%1\" is an unknown argument.").arg(name));
+
+ const QApplicationArgument &arg = d->declaredArguments.value(name);
+ const int argCount = d->count(arg) + 1;
+ const int max = arg.maximumOccurrence();
+
+ if(argCount > max && max != -1)
+ {
+ /* Let's tailor the message for a common case. */
+ if(max == 1)
+ return d->error(QApplicationArgumentParserPrivate::tr("\"%1\" can only be used once.").arg(name));
+ else
+ return d->error(QApplicationArgumentParserPrivate::tr("\"%1\" can only be used %2 times.").arg(name, QString::number(max)));
+ }
+
+ if(QApplicationArgumentParserPrivate::isSwitch(arg))
+ {
+ d->usedArguments.append(qMakePair(arg, QVariant()));
+ continue;
+ }
+ else
+ {
+ ++i;
+
+ if(i == inputCount)
+ return d->error(QApplicationArgumentParserPrivate::tr("\"%1\" must be followed by a value.").arg(name));
+
+ /* Okidoki, got a value, always something. Let's
+ * see if it validates. */
+ const QString &value = d->input.at(i);
+
+ const QVariant val(convertToValue(arg, value));
+ if(val.isValid())
+ {
+ d->usedArguments.append(qMakePair(arg, val));
+ continue;
+ }
+ else
+ return false; // TODO error msg?
+ }
+ }
+ else
+ {
+ if(!d->parseNamelessArguments(in))
+ return false;
+ }
+ }
+
+ /* Check that all arguments that have been declared as mandatory, are actually
+ * specified. */
+ const QList<QApplicationArgument> declaredArguments(d->declaredArguments.values() + d->declaredNamelessArguments);
+ const int len = declaredArguments.count();
+ for(int i = 0; i < len; ++i)
+ {
+ const QApplicationArgument &at = declaredArguments.at(i);
+ const int min = at.minimumOccurrence();
+ const int max = at.maximumOccurrence(); // TODO What about infinite? -1
+ if(min == 0)
+ continue;
+ else
+ {
+ const int usedLen = d->usedArguments.count();
+ int useCount = 0;
+
+ for(int u = 0; u < usedLen; ++u)
+ {
+ const QPair<QApplicationArgument, QVariant> &used = d->usedArguments.at(u);
+ if(used.first == at)
+ ++useCount;
+ }
+
+ const QString originalName(at.name());
+ const QString effectiveName(originalName.isEmpty() ? QLatin1Char('<') + typeToName(at) + QLatin1Char('>') : originalName);
+
+ if(useCount < min)
+ {
+ /* For nameless options, we use the type as the name. Looks better. */
+ return d->error(QApplicationArgumentParserPrivate::tr("%1 must occur at least %2 times, therefore %3 times is insufficient.", "The number is for %2.", min)
+ .arg(effectiveName, QString::number(min), QString::number(useCount)));
+ }
+ else if(useCount > max)
+ return d->error(QApplicationArgumentParserPrivate::tr("%1 can occur at most %2 times", "", max).arg(effectiveName, QString::number(max)));
+ }
+ }
+
+ d->exitCode = Success;
+ return true;
+}
+
+/*!
+ This function is only of interest when subclassing.
+
+ parse() calls this function each time a value, that is \a input, on the command line needs to be
+ validated and subsequently converted to the type of \a argument. A descriptive error message will
+ be outputted if \a input cannot be converted to the required type.
+
+ The default implementation uses QVariant::canConvert() and QVariant::convert() for doing conversions.
+
+ QApplicationArgumentParser can be subclassed and this function subsequently overridden, to handle custom types.
+
+ If \a input isn't valid input for \a argument, this function returns a default constructed
+ QVariant.
+
+ \sa typeToName(), parse()
+ */
+QVariant QApplicationArgumentParser::convertToValue(const QApplicationArgument &argument,
+ const QString &input) const
+{
+ const int type = argument.type();
+
+ switch(type)
+ {
+ case QVariant::Bool:
+ {
+ if(input == QLatin1String("true") || input == QChar::fromLatin1('1'))
+ return QVariant(true);
+ else if(input == QLatin1String("false") || input == QChar::fromLatin1('0'))
+ return QVariant(false);
+ else
+ return QApplicationArgumentParserPrivate::conversionError(typeToName(argument), input);
+ }
+ case QVariant::RegExp:
+ {
+ const QRegExp exp(input);
+
+ if(exp.isValid())
+ return QVariant(exp);
+ else
+ return QApplicationArgumentParserPrivate::conversionError(typeToName(argument), input);
+ }
+ case QVariant::Url:
+ {
+ const QUrl result(QUrl::fromEncoded(input.toLatin1()));
+
+ if(result.isValid())
+ return QVariant(result);
+ else
+ return QApplicationArgumentParserPrivate::conversionError(typeToName(argument), input);
+ }
+ default:
+ {
+ QVariant result(input);
+
+ if(QApplicationArgumentParserPrivate::isBuiltinVariant(type) &&
+ result.convert(QVariant::Type(type)))
+ return result;
+ else
+ return QApplicationArgumentParserPrivate::conversionError(typeToName(argument), input);
+ }
+ }
+}
+
+/*!
+ This function is only of interest when subclassing.
+
+ convertToValue() calls this function when requiring a string for referring to \a type,
+ when generating user messages.
+
+ The implementation uses QVariant::typeToName() for most types, but special handles
+ some types, in order to let the message be better tailored for humans.
+
+ \sa convertToValue()
+ */
+QString QApplicationArgumentParser::typeToName(const QApplicationArgument &argument) const
+{
+ /* Personally I think nameForType() would be a better name but this is consistent
+ * with QVariant's function of the same name. */
+ const int type = argument.type();
+
+ switch(type)
+ {
+ case QVariant::RegExp:
+ return QApplicationArgumentParserPrivate::tr("regular expression");
+ case QVariant::Url:
+ return QLatin1String("URI");
+ case QVariant::String:
+ return QLatin1String("string");
+ default:
+ {
+ if(QApplicationArgumentParserPrivate::isBuiltinVariant(type))
+ return QString::fromLatin1(QVariant::typeToName(QVariant::Type(type)));
+ else
+ return QLatin1String(QVariant(type, static_cast<void *>(0)).typeName());
+ }
+ }
+}
+
+/*!
+ Returns the default value for \a argument. The default implementation returns
+ QApplicationArgument::defaultValue(), if \a argument has been added to this parser.
+
+ Overriding this function can be useful if creating the default value is resource
+ consuming, such as opening a file.
+ */
+QVariant QApplicationArgumentParser::defaultValue(const QApplicationArgument &argument) const
+{
+ return d->declaredArguments.value(argument.name()).defaultValue();
+}
+
+/*!
+ Returns the count of how many times \a argument was used on the command line.
+
+ \sa has()
+ */
+int QApplicationArgumentParser::count(const QApplicationArgument &argument) const
+{
+ Q_ASSERT_X(d->declaredArguments.contains(argument.name()) ||
+ d->declaredNamelessArguments.contains(argument), Q_FUNC_INFO,
+ "The argument isn't known to the parser. Has addArgument() been called?");
+ return d->count(argument);
+}
+
+/*!
+ Returns \c true if \a argument has been
+ specified one or more times on the command line, otherwise \a false.
+
+ \sa count()
+ */
+bool QApplicationArgumentParser::has(const QApplicationArgument &argument) const
+{
+ Q_ASSERT_X(d->declaredArguments.contains(argument.name()) ||
+ d->declaredNamelessArguments.contains(argument), Q_FUNC_INFO,
+ "The argument isn't known to the parser. Has addArgument() been called?");
+ return d->contains(argument);
+}
+
+/*!
+ // TODO docs
+
+ \sa values()
+ */
+QVariant QApplicationArgumentParser::value(const QApplicationArgument &argument) const
+{
+ Q_ASSERT_X(d->declaredArguments.contains(argument.name()) ||
+ d->declaredNamelessArguments.contains(argument), Q_FUNC_INFO,
+ "The argument isn't known to the parser. Has addArgument() been called?");
+
+ const int len = d->usedArguments.count();
+
+ for(int i = 0; i < len; ++i)
+ {
+ if(d->usedArguments.at(i).first == argument)
+ return d->usedArguments.at(i).second;
+ }
+
+ return defaultValue(argument);
+}
+
+/*!
+ // TODO docs
+ \sa value()
+ */
+QVariantList QApplicationArgumentParser::values(const QApplicationArgument &argument) const
+{
+ Q_ASSERT_X(d->declaredArguments.contains(argument.name()) ||
+ d->declaredNamelessArguments.contains(argument),
+ Q_FUNC_INFO,
+ "The argument isn't known to the parser. Has addArgument() been called?");
+
+ const int len = d->usedArguments.count();
+
+ QVariantList result;
+ for(int i = 0; i < len; ++i)
+ {
+ if(d->usedArguments.at(i).first == argument)
+ result.append(d->usedArguments.at(i).second);
+ }
+
+ // TODO how do we handle default values?
+ return result;
+}
+
+/*!
+ After parse() has been called, this function returns a code that can be used to
+ exit \c main() with. It returns zero upon success or if help was requested, and
+ otherwise a value signalling failure.
+ */
+QApplicationArgumentParser::ExitCode QApplicationArgumentParser::exitCode() const
+{
+ return d->exitCode;
+}
+
+/*!
+ This function is only of interest when subclassing.
+
+ Makes exitCode() return \a code.
+ */
+void QApplicationArgumentParser::setExitCode(ExitCode code)
+{
+ d->exitCode = code;
+}
+
+/*!
+ Sets the application description to \a description.
+
+ The application description is a sentence or two used for help and version
+ messages, that briefly describes the application.
+
+ The default is the empty string.
+ */
+void QApplicationArgumentParser::setApplicationDescription(const QString &description)
+{
+ d->applicationDescription = description;
+}
+
+/*!
+ Sets the application version to \a version.
+
+ This string, which is arbitrary but typically is "1.0" or so, is used when
+ generating a version statement.
+*/
+void QApplicationArgumentParser::setApplicationVersion(const QString &version)
+{
+ d->applicationVersion = version;
+}
+
+/*!
+ Writes out \a message to \c stderr.
+ */
+void QApplicationArgumentParser::message(const QString &message) const
+{
+ d->errorMessage(message);
+}
+
+QT_END_NAMESPACE
diff --git a/tools/xmlpatterns/qapplicationargumentparser_p.h b/tools/xmlpatterns/qapplicationargumentparser_p.h
new file mode 100644
index 0000000000..16bcfe0ca0
--- /dev/null
+++ b/tools/xmlpatterns/qapplicationargumentparser_p.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+** ** This file is part of the tools applications 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 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$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#ifndef QApplicationArgumentParser_H
+#define QApplicationArgumentParser_H
+
+#include <QtCore/QVariant> /* Needed, because we can't forward declare QVariantList. */
+
+QT_BEGIN_HEADER
+QT_BEGIN_NAMESPACE
+
+class QApplicationArgument;
+class QApplicationArgumentParserPrivate;
+class QStringList;
+template<typename A, typename B> struct QPair;
+template<typename T> class QList;
+template<typename Value> class QList;
+
+class QApplicationArgumentParser
+{
+public:
+ enum ExitCode
+ {
+ Success = 0,
+ ParseError = 1
+ };
+
+ QApplicationArgumentParser(int argc, char **argv);
+ QApplicationArgumentParser(const QStringList &input);
+ virtual ~QApplicationArgumentParser();
+ void addArgument(const QApplicationArgument &argument);
+ void setDeclaredArguments(const QList<QApplicationArgument> &arguments);
+ QList<QApplicationArgument> declaredArguments() const;
+
+ int count(const QApplicationArgument &argument) const;
+ bool has(const QApplicationArgument &argument) const;
+
+ virtual bool parse();
+ ExitCode exitCode() const;
+ QVariant value(const QApplicationArgument &argument) const;
+ QVariantList values(const QApplicationArgument &argument) const;
+ void setApplicationDescription(const QString &description);
+ void setApplicationVersion(const QString &version);
+ virtual void message(const QString &message) const;
+
+protected:
+ void setExitCode(ExitCode code);
+ void setUsedArguments(const QList<QPair<QApplicationArgument, QVariant> > &arguments);
+ QList<QPair<QApplicationArgument, QVariant> > usedArguments() const;
+ QStringList input() const;
+ virtual QVariant convertToValue(const QApplicationArgument &argument,
+ const QString &value) const;
+ virtual QString typeToName(const QApplicationArgument &argument) const;
+ virtual QVariant defaultValue(const QApplicationArgument &argument) const;
+
+private:
+ friend class QApplicationArgumentParserPrivate;
+ QApplicationArgumentParserPrivate *d;
+ Q_DISABLE_COPY(QApplicationArgumentParser)
+};
+
+QT_END_NAMESPACE
+QT_END_HEADER
+#endif
diff --git a/tools/xmlpatterns/qcoloringmessagehandler.cpp b/tools/xmlpatterns/qcoloringmessagehandler.cpp
new file mode 100644
index 0000000000..dd7e97fd4e
--- /dev/null
+++ b/tools/xmlpatterns/qcoloringmessagehandler.cpp
@@ -0,0 +1,193 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtCore 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 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 <QXmlStreamReader>
+
+#include "main.h"
+
+#include "qcoloringmessagehandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace QPatternist;
+
+ColoringMessageHandler::ColoringMessageHandler(QObject *parent) : QAbstractMessageHandler(parent)
+{
+ m_classToColor.insert(QLatin1String("XQuery-data"), Data);
+ m_classToColor.insert(QLatin1String("XQuery-expression"), Keyword);
+ m_classToColor.insert(QLatin1String("XQuery-function"), Keyword);
+ m_classToColor.insert(QLatin1String("XQuery-keyword"), Keyword);
+ m_classToColor.insert(QLatin1String("XQuery-type"), Keyword);
+ m_classToColor.insert(QLatin1String("XQuery-uri"), Data);
+ m_classToColor.insert(QLatin1String("XQuery-filepath"), Data);
+
+ /* If you're tuning the colors, take it easy laddie. Take into account:
+ *
+ * - Get over your own taste, there's others too on this planet
+ * - Make sure it works well on black & white
+ * - Make sure it works well on white & black
+ */
+ insertMapping(Location, CyanForeground);
+ insertMapping(ErrorCode, RedForeground);
+ insertMapping(Keyword, BlueForeground);
+ insertMapping(Data, BlueForeground);
+ insertMapping(RunningText, DefaultColor);
+}
+
+void ColoringMessageHandler::handleMessage(QtMsgType type,
+ const QString &description,
+ const QUrl &identifier,
+ const QSourceLocation &sourceLocation)
+{
+ const bool hasLine = sourceLocation.line() != -1;
+
+ switch(type)
+ {
+ case QtWarningMsg:
+ {
+ if(hasLine)
+ {
+ writeUncolored(QXmlPatternistCLI::tr("Warning in %1, at line %2, column %3: %4").arg(QString::fromLatin1(sourceLocation.uri().toEncoded()),
+ QString::number(sourceLocation.line()),
+ QString::number(sourceLocation.column()),
+ colorifyDescription(description)));
+ }
+ else
+ {
+ writeUncolored(QXmlPatternistCLI::tr("Warning in %1: %2").arg(QString::fromLatin1(sourceLocation.uri().toEncoded()),
+ colorifyDescription(description)));
+ }
+
+ break;
+ }
+ case QtFatalMsg:
+ {
+ Q_ASSERT(!sourceLocation.isNull());
+ const QString errorCode(identifier.fragment());
+ Q_ASSERT(!errorCode.isEmpty());
+ QUrl uri(identifier);
+ uri.setFragment(QString());
+
+ QString errorId;
+ /* If it's a standard error code, we don't want to output the
+ * whole URI. */
+ if(uri.toString() == QLatin1String("http://www.w3.org/2005/xqt-errors"))
+ errorId = errorCode;
+ else
+ errorId = QString::fromLatin1(identifier.toEncoded());
+
+ if(hasLine)
+ {
+ writeUncolored(QXmlPatternistCLI::tr("Error %1 in %2, at line %3, column %4: %5").arg(colorify(errorId, ErrorCode),
+ colorify(QString::fromLatin1(sourceLocation.uri().toEncoded()), Location),
+ colorify(QString::number(sourceLocation.line()), Location),
+ colorify(QString::number(sourceLocation.column()), Location),
+ colorifyDescription(description)));
+ }
+ else
+ {
+ writeUncolored(QXmlPatternistCLI::tr("Error %1 in %2: %3").arg(colorify(errorId, ErrorCode),
+ colorify(QString::fromLatin1(sourceLocation.uri().toEncoded()), Location),
+ colorifyDescription(description)));
+ }
+ break;
+ }
+ case QtCriticalMsg:
+ /* Fallthrough. */
+ case QtDebugMsg:
+ {
+ Q_ASSERT_X(false, Q_FUNC_INFO,
+ "message() is not supposed to receive QtCriticalMsg or QtDebugMsg.");
+ return;
+ }
+ }
+}
+
+QString ColoringMessageHandler::colorifyDescription(const QString &in) const
+{
+ QXmlStreamReader reader(in);
+ QString result;
+ result.reserve(in.size());
+ ColorType currentColor = RunningText;
+
+ while(!reader.atEnd())
+ {
+ reader.readNext();
+
+ switch(reader.tokenType())
+ {
+ case QXmlStreamReader::StartElement:
+ {
+ if(reader.name() == QLatin1String("span"))
+ {
+ Q_ASSERT(m_classToColor.contains(reader.attributes().value(QLatin1String("class")).toString()));
+ currentColor = m_classToColor.value(reader.attributes().value(QLatin1String("class")).toString());
+ }
+
+ continue;
+ }
+ case QXmlStreamReader::Characters:
+ {
+ result.append(colorify(reader.text().toString(), currentColor));
+ continue;
+ }
+ case QXmlStreamReader::EndElement:
+ {
+ currentColor = RunningText;
+ continue;
+ }
+ /* Fallthrough, */
+ case QXmlStreamReader::StartDocument:
+ /* Fallthrough, */
+ case QXmlStreamReader::EndDocument:
+ continue;
+ default:
+ Q_ASSERT_X(false, Q_FUNC_INFO,
+ "Unexpected node.");
+ }
+ }
+
+ Q_ASSERT_X(!reader.hasError(), Q_FUNC_INFO,
+ "The output from Patternist must be well-formed.");
+ return result;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/xmlpatterns/qcoloringmessagehandler_p.h b/tools/xmlpatterns/qcoloringmessagehandler_p.h
new file mode 100644
index 0000000000..69ca5b0c0e
--- /dev/null
+++ b/tools/xmlpatterns/qcoloringmessagehandler_p.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+ * ** * ** 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$
+ * **
+ * ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ * ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * **
+ * ****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#ifndef Patternist_ColoringMessageHandler_h
+#define Patternist_ColoringMessageHandler_h
+
+#include <QHash>
+
+#include "qcoloroutput_p.h"
+#include "qabstractmessagehandler.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+namespace QPatternist
+{
+ class ColoringMessageHandler : public QAbstractMessageHandler
+ , private ColorOutput
+ {
+ public:
+ ColoringMessageHandler(QObject *parent = 0);
+
+ protected:
+ virtual void handleMessage(QtMsgType type,
+ const QString &description,
+ const QUrl &identifier,
+ const QSourceLocation &sourceLocation);
+
+ private:
+ QString colorifyDescription(const QString &in) const;
+
+ enum ColorType
+ {
+ RunningText,
+ Location,
+ ErrorCode,
+ Keyword,
+ Data
+ };
+
+ QHash<QString, ColorType> m_classToColor;
+ };
+}
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/tools/xmlpatterns/qcoloroutput.cpp b/tools/xmlpatterns/qcoloroutput.cpp
new file mode 100644
index 0000000000..e401c5d93b
--- /dev/null
+++ b/tools/xmlpatterns/qcoloroutput.cpp
@@ -0,0 +1,350 @@
+/****************************************************************************
+ * ** * ** 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$
+ * **
+ * ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ * ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * **
+ * ****************************************************************************/
+
+#include <QFile>
+#include <QHash>
+#include <QTextCodec>
+
+#include "qcoloroutput_p.h"
+
+// TODO: rename insertMapping() to insertColorMapping()
+// TODO: Use a smart pointer for managing ColorOutputPrivate *d;
+// TODO: break out the C++ example into a snippet file
+
+/* This include must appear here, because if it appears at the beginning of the file for
+ * instance, it breaks build -- "qglobal.h:628: error: template with
+ * C linkage" -- on Mac OS X 10.4. */
+#ifndef Q_OS_WIN
+#include <unistd.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+using namespace QPatternist;
+
+namespace QPatternist
+{
+ class ColorOutputPrivate
+ {
+ public:
+ ColorOutputPrivate() : currentColorID(-1)
+
+ {
+ /* - QIODevice::Unbuffered because we want it to appear when the user actually calls, performance
+ * is considered of lower priority.
+ */
+ m_out.open(stderr, QIODevice::WriteOnly | QIODevice::Unbuffered);
+
+ coloringEnabled = isColoringPossible();
+ }
+
+ ColorOutput::ColorMapping colorMapping;
+ int currentColorID;
+ bool coloringEnabled;
+
+ static const char *const foregrounds[];
+ static const char *const backgrounds[];
+
+ inline void write(const QString &msg)
+ {
+ m_out.write(msg.toLocal8Bit());
+ }
+
+ static QString escapeCode(const QString &in)
+ {
+ QString result;
+ result.append(QChar(0x1B));
+ result.append(QLatin1Char('['));
+ result.append(in);
+ result.append(QLatin1Char('m'));
+ return result;
+ }
+
+ private:
+ QFile m_out;
+
+ /*!
+ Returns true if it's suitable to send colored output to \c stderr.
+ */
+ inline bool isColoringPossible() const
+ {
+# if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
+ /* Windows doesn't at all support ANSI escape codes, unless
+ * the user install a "device driver". See the Wikipedia links in the
+ * class documentation for details. */
+ return false;
+# else
+ /* We use QFile::handle() to get the file descriptor. It's a bit unsure
+ * whether it's 2 on all platforms and in all cases, so hopefully this layer
+ * of abstraction helps handle such cases. */
+ return isatty(m_out.handle());
+# endif
+ }
+ };
+}
+
+const char *const ColorOutputPrivate::foregrounds[] =
+{
+ "0;30",
+ "0;34",
+ "0;32",
+ "0;36",
+ "0;31",
+ "0;35",
+ "0;33",
+ "0;37",
+ "1;30",
+ "1;34",
+ "1;32",
+ "1;36",
+ "1;31",
+ "1;35",
+ "1;33",
+ "1;37"
+};
+
+const char *const ColorOutputPrivate::backgrounds[] =
+{
+ "0;40",
+ "0;44",
+ "0;42",
+ "0;46",
+ "0;41",
+ "0;45",
+ "0;43"
+};
+
+/*!
+ \since 4.4
+ \nonreentrant
+ \brief Outputs colored messages to \c stderr.
+ \internal
+
+ ColorOutput is a convenience class for outputting messages to \c stderr
+ using color escape codes, as mandated in ECMA-48. ColorOutput will only
+ color output when it is detected to be suitable. For instance, if \c stderr is
+ detected to be attached to a file instead of a TTY, no coloring will be done.
+
+ ColorOutput does its best attempt. but it is generally undefined what coloring
+ or effect the various coloring flags has. It depends strongly on what terminal
+ software that is being used.
+
+ When using `echo -e 'my escape sequence'`, \033 works as an initiator but not
+ when printing from a C++ program, despite having escaped the backslash.
+ That's why we below use characters with value 0x1B.
+
+ It can be convenient to subclass ColorOutput with a private scope, such that the
+ functions are directly available in the class using it.
+
+ \section1 Usage
+
+ To output messages, call write() or writeUncolored(). write() takes as second
+ argument an integer, which ColorOutput uses as a lookup key to find the color
+ it should color the text in. The mapping from keys to colors is done using
+ insertMapping(). Typically this is used by having enums for the various kinds
+ of messages, which subsequently are registered.
+
+ \code
+ enum MyMessage
+ {
+ Error,
+ Important
+ };
+
+ ColorOutput output;
+ output.insertMapping(Error, ColorOutput::RedForeground);
+ output.insertMapping(Import, ColorOutput::BlueForeground);
+
+ output.write("This is important", Important);
+ output.write("Jack, I'm only the selected official!", Error);
+ \endcode
+
+ \sa {http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html} {Bash Prompt HOWTO, 6.1. Colours}
+ {http://linuxgazette.net/issue51/livingston-blade.html} {Linux Gazette, Tweaking Eterm, Edward Livingston-Blade}
+ {http://www.ecma-international.org/publications/standards/Ecma-048.htm} {Standard ECMA-48, Control Functions for Coded Character Sets, ECMA International},
+ {http://en.wikipedia.org/wiki/ANSI_escape_code} {Wikipedia, ANSI escape code}
+ {http://linuxgazette.net/issue65/padala.html} {Linux Gazette, So You Like Color!, Pradeep Padala}
+ */
+
+/*!
+ \enum ColorOutput::ColorCode
+
+ \value DefaultColor ColorOutput performs no coloring. This typically means black on white
+ or white on black, depending on the settings of the user's terminal.
+ */
+
+/*!
+ Sets the color mapping to be \a cMapping.
+
+ Negative values are disallowed.
+
+ \sa colorMapping(), insertMapping()
+ */
+void ColorOutput::setColorMapping(const ColorMapping &cMapping)
+{
+ d->colorMapping = cMapping;
+}
+
+/*!
+ Returns the color mappings in use.
+
+ \sa setColorMapping(), insertMapping()
+ */
+ColorOutput::ColorMapping ColorOutput::colorMapping() const
+{
+ return d->colorMapping;
+}
+
+/*!
+ Constructs a ColorOutput instance, ready for use.
+ */
+ColorOutput::ColorOutput() : d(new ColorOutputPrivate())
+{
+}
+
+/*!
+ Destructs this ColorOutput instance.
+ */
+ColorOutput::~ColorOutput()
+{
+ delete d;
+}
+
+/*!
+ Sends \a message to \c stderr, using the color looked up in colorMapping() using \a colorID.
+
+ If \a color isn't available in colorMapping(), result and behavior is undefined.
+
+ If \a colorID is 0, which is the default value, the previously used coloring is used. ColorOutput
+ is initialized to not color at all.
+
+ If \a message is empty, effects are undefined.
+
+ \a message will be printed as is. For instance, no line endings will be inserted.
+ */
+void ColorOutput::write(const QString &message, int colorID)
+{
+ d->write(colorify(message, colorID));
+}
+
+/*!
+ Writes \a message to \c stderr as if for instance
+ QTextStream would have been used, and adds a line ending at the end.
+
+ This function can be practical to use such that one can use ColorOutput for all forms of writing.
+ */
+void ColorOutput::writeUncolored(const QString &message)
+{
+ d->write(message + QLatin1Char('\n'));
+}
+
+/*!
+ Treats \a message and \a colorID identically to write(), but instead of writing
+ \a message to \c stderr, it is prepared for being written to \c stderr, but is then
+ returned.
+
+ This is useful when the colored string is inserted into a translated string(dividing
+ the string into several small strings prevents proper translation).
+ */
+QString ColorOutput::colorify(const QString &message, int colorID) const
+{
+ Q_ASSERT_X(colorID == -1 || d->colorMapping.contains(colorID), Q_FUNC_INFO,
+ qPrintable(QString::fromLatin1("There is no color registered by id %1").arg(colorID)));
+ Q_ASSERT_X(!message.isEmpty(), Q_FUNC_INFO, "It makes no sense to attempt to print an empty string.");
+
+ if(colorID != -1)
+ d->currentColorID = colorID;
+
+ if(d->coloringEnabled && colorID != -1)
+ {
+ const int color(d->colorMapping.value(colorID));
+
+ /* If DefaultColor is set, we don't want to color it. */
+ if(color & DefaultColor)
+ return message;
+
+ const int foregroundCode = (int(color) & ForegroundMask) >> ForegroundShift;
+ const int backgroundCode = (int(color) & BackgroundMask) >> BackgroundShift;
+ QString finalMessage;
+ bool closureNeeded = false;
+
+ if(foregroundCode)
+ {
+ finalMessage.append(ColorOutputPrivate::escapeCode(QLatin1String(ColorOutputPrivate::foregrounds[foregroundCode - 1])));
+ closureNeeded = true;
+ }
+
+ if(backgroundCode)
+ {
+ finalMessage.append(ColorOutputPrivate::escapeCode(QLatin1String(ColorOutputPrivate::backgrounds[backgroundCode - 1])));
+ closureNeeded = true;
+ }
+
+ finalMessage.append(message);
+
+ if(closureNeeded)
+ {
+ finalMessage.append(QChar(0x1B));
+ finalMessage.append(QLatin1String("[0m"));
+ }
+
+ return finalMessage;
+ }
+ else
+ return message;
+}
+
+/*!
+ Adds a color mapping from \a colorID to \a colorCode, for this ColorOutput instance.
+
+ This is a convenience function for creating a ColorOutput::ColorMapping instance and
+ calling setColorMapping().
+
+ \sa colorMapping(), setColorMapping()
+ */
+void ColorOutput::insertMapping(int colorID, const ColorCode colorCode)
+{
+ d->colorMapping.insert(colorID, colorCode);
+}
+
+QT_END_NAMESPACE
diff --git a/tools/xmlpatterns/qcoloroutput_p.h b/tools/xmlpatterns/qcoloroutput_p.h
new file mode 100644
index 0000000000..d9daa14013
--- /dev/null
+++ b/tools/xmlpatterns/qcoloroutput_p.h
@@ -0,0 +1,134 @@
+/****************************************************************************
+ * ** * ** 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$
+ * **
+ * ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ * ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * **
+ * ****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#ifndef Patternist_ColorOutput_h
+#define Patternist_ColorOutput_h
+
+#include <QtCore/QtGlobal>
+#include <QtCore/QHash>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+namespace QPatternist
+{
+ class ColorOutputPrivate;
+
+ class ColorOutput
+ {
+ enum
+ {
+ ForegroundShift = 10,
+ BackgroundShift = 20,
+ SpecialShift = 20,
+ ForegroundMask = ((1 << ForegroundShift) - 1) << ForegroundShift,
+ BackgroundMask = ((1 << BackgroundShift) - 1) << BackgroundShift
+ };
+
+ public:
+ enum ColorCodeComponent
+ {
+ BlackForeground = 1 << ForegroundShift,
+ BlueForeground = 2 << ForegroundShift,
+ GreenForeground = 3 << ForegroundShift,
+ CyanForeground = 4 << ForegroundShift,
+ RedForeground = 5 << ForegroundShift,
+ PurpleForeground = 6 << ForegroundShift,
+ BrownForeground = 7 << ForegroundShift,
+ LightGrayForeground = 8 << ForegroundShift,
+ DarkGrayForeground = 9 << ForegroundShift,
+ LightBlueForeground = 10 << ForegroundShift,
+ LightGreenForeground = 11 << ForegroundShift,
+ LightCyanForeground = 12 << ForegroundShift,
+ LightRedForeground = 13 << ForegroundShift,
+ LightPurpleForeground = 14 << ForegroundShift,
+ YellowForeground = 15 << ForegroundShift,
+ WhiteForeground = 16 << ForegroundShift,
+
+ BlackBackground = 1 << BackgroundShift,
+ BlueBackground = 2 << BackgroundShift,
+ GreenBackground = 3 << BackgroundShift,
+ CyanBackground = 4 << BackgroundShift,
+ RedBackground = 5 << BackgroundShift,
+ PurpleBackground = 6 << BackgroundShift,
+ BrownBackground = 7 << BackgroundShift,
+ DefaultColor = 1 << SpecialShift
+ };
+
+ typedef QFlags<ColorCodeComponent> ColorCode;
+ typedef QHash<int, ColorCode> ColorMapping;
+
+ ColorOutput();
+ ~ColorOutput();
+
+ void setColorMapping(const ColorMapping &cMapping);
+ ColorMapping colorMapping() const;
+ void insertMapping(int colorID, const ColorCode colorCode);
+
+ void writeUncolored(const QString &message);
+ void write(const QString &message, int color = -1);
+ QString colorify(const QString &message, int color = -1) const;
+
+ private:
+ ColorOutputPrivate *d;
+ Q_DISABLE_COPY(ColorOutput)
+ };
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QPatternist::ColorOutput::ColorCode)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/tools/xmlpatterns/xmlpatterns.pro b/tools/xmlpatterns/xmlpatterns.pro
new file mode 100644
index 0000000000..9c1aac1d01
--- /dev/null
+++ b/tools/xmlpatterns/xmlpatterns.pro
@@ -0,0 +1,31 @@
+TEMPLATE = app
+TARGET = xmlpatterns
+DESTDIR = ../../bin
+QT -= gui
+QT += xmlpatterns
+
+target.path = $$[QT_INSTALL_BINS]
+INSTALLS += target
+
+# This ensures we get stderr and stdout on Windows.
+CONFIG += console
+
+# This ensures that this is a command-line program on OS X and not a GUI application.
+CONFIG -= app_bundle
+
+# Note that qcoloroutput.cpp and qcoloringmessagehandler.cpp are also used internally
+# in libQtXmlPatterns. See src/xmlpatterns/api/api.pri.
+SOURCES = main.cpp \
+ qapplicationargument.cpp \
+ qapplicationargumentparser.cpp \
+ qcoloringmessagehandler.cpp \
+ qcoloroutput.cpp
+
+
+HEADERS = main.h \
+ qapplicationargument.cpp \
+ qapplicationargumentparser.cpp \
+ qcoloringmessagehandler_p.h \
+ qcoloroutput_p.h
+
+include(../src/common.pri)