/****************************************************************************+ −
**+ −
** Copyright (C) 2010 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 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. */+ −
_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 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;+ −
}+ −
+ −