/****************************************************************************+ −
**+ −
** 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 qmake application 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 "xmloutput.h"+ −
+ −
QT_BEGIN_NAMESPACE+ −
+ −
XmlOutput::XmlOutput(QTextStream &file, ConverstionType type)+ −
: xmlFile(file), indent("\t"), currentLevel(0), currentState(Bare), format(NewLine),+ −
conversion(type)+ −
{+ −
tagStack.clear();+ −
}+ −
+ −
XmlOutput::~XmlOutput()+ −
{+ −
closeAll();+ −
}+ −
+ −
// Settings ------------------------------------------------------------------+ −
void XmlOutput::setIndentString(const QString &indentString)+ −
{+ −
indent = indentString;+ −
}+ −
+ −
QString XmlOutput::indentString()+ −
{+ −
return indent;+ −
}+ −
+ −
void XmlOutput::setIndentLevel(int level)+ −
{+ −
currentLevel = level;+ −
}+ −
+ −
int XmlOutput::indentLevel()+ −
{+ −
return currentLevel;+ −
}+ −
+ −
void XmlOutput::setState(XMLState state)+ −
{+ −
currentState = state;+ −
}+ −
+ −
void XmlOutput::setFormat(XMLFormat newFormat)+ −
{+ −
format = newFormat;+ −
}+ −
+ −
XmlOutput::XMLState XmlOutput::state()+ −
{+ −
return currentState;+ −
}+ −
+ −
void XmlOutput::updateIndent()+ −
{+ −
currentIndent.clear();+ −
for (int i = 0; i < currentLevel; ++i)+ −
currentIndent.append(indent);+ −
}+ −
+ −
void XmlOutput::increaseIndent()+ −
{+ −
++currentLevel;+ −
updateIndent();+ −
}+ −
+ −
void XmlOutput::decreaseIndent()+ −
{+ −
if (currentLevel)+ −
--currentLevel;+ −
updateIndent();+ −
if (!currentLevel)+ −
currentState = Bare;+ −
}+ −
+ −
QString XmlOutput::doConversion(const QString &text)+ −
{+ −
if (!text.count())+ −
return QString();+ −
else if (conversion == NoConversion)+ −
return text;+ −
+ −
QString output;+ −
if (conversion == XMLConversion) {+ −
+ −
// this is a way to escape characters that shouldn't be converted+ −
for (int i=0; i<text.count(); ++i) {+ −
if (text.at(i) == QLatin1Char('&')) {+ −
if ( (i + 7) < text.count() &&+ −
text.at(i + 1) == QLatin1Char('#') &&+ −
text.at(i + 2) == QLatin1Char('x') &&+ −
text.at(i + 7) == QLatin1Char(';') ) {+ −
output += text.at(i);+ −
} else {+ −
output += QLatin1String("&");+ −
}+ −
} else {+ −
QChar c = text.at(i);+ −
if (c.unicode() < 0x20) {+ −
output += QString("&#x%1;").arg(c.unicode(), 2, 16, QLatin1Char('0'));+ −
} else {+ −
output += text.at(i);+ −
}+ −
}+ −
}+ −
} else {+ −
output = text;+ −
}+ −
+ −
if (conversion == XMLConversion) {+ −
output.replace('\"', """);+ −
output.replace('\'', "'");+ −
} else if (conversion == EscapeConversion) {+ −
output.replace('\"', "\\\"");+ −
output.replace('\'', "\\\'");+ −
}+ −
return output;+ −
}+ −
+ −
// Stream functions ----------------------------------------------------------+ −
XmlOutput& XmlOutput::operator<<(const QString& o)+ −
{+ −
return operator<<(data(o));+ −
}+ −
+ −
XmlOutput& XmlOutput::operator<<(const xml_output& o)+ −
{+ −
switch(o.xo_type) {+ −
case tNothing:+ −
break;+ −
case tRaw:+ −
addRaw(o.xo_text);+ −
break;+ −
case tDeclaration:+ −
addDeclaration(o.xo_text, o.xo_value);+ −
break;+ −
case tTag:+ −
newTagOpen(o.xo_text);+ −
break;+ −
case tTagValue:+ −
addRaw(QString("\n%1<%2>").arg(currentIndent).arg(o.xo_text));+ −
addRaw(QString("%1").arg(o.xo_value));+ −
addRaw(QString("</%1>").arg(o.xo_text));+ −
break;+ −
case tValueTag:+ −
addRaw(QString("%1").arg(doConversion(o.xo_text)));+ −
setFormat(NoNewLine);+ −
closeTag();+ −
setFormat(NewLine);+ −
break;+ −
case tImport:+ −
addRaw(QString("\n%1<Import %2=\"%3\" />").arg(currentIndent).arg(o.xo_text).arg(o.xo_value));+ −
break;+ −
case tCloseTag:+ −
if (o.xo_value.count())+ −
closeAll();+ −
else if (o.xo_text.count())+ −
closeTo(o.xo_text);+ −
else+ −
closeTag();+ −
break;+ −
case tAttribute:+ −
addAttribute(o.xo_text, o.xo_value);+ −
break;+ −
case tAttributeTag:+ −
addAttributeTag(o.xo_text, o.xo_value);+ −
break;+ −
case tData:+ −
{+ −
// Special case to be able to close tag in normal+ −
// way ("</tag>", not "/>") without using addRaw()..+ −
if (!o.xo_text.count()) {+ −
closeOpen();+ −
break;+ −
}+ −
QString output = doConversion(o.xo_text);+ −
output.replace('\n', "\n" + currentIndent);+ −
addRaw(QString("\n%1%2").arg(currentIndent).arg(output));+ −
}+ −
break;+ −
case tComment:+ −
{+ −
QString output("<!--%1-->");+ −
addRaw(output.arg(o.xo_text));+ −
}+ −
break;+ −
case tCDATA:+ −
{+ −
QString output("<![CDATA[\n%1\n]]>");+ −
addRaw(output.arg(o.xo_text));+ −
}+ −
break;+ −
}+ −
return *this;+ −
}+ −
+ −
+ −
// Output functions ----------------------------------------------------------+ −
void XmlOutput::newTag(const QString &tag)+ −
{+ −
Q_ASSERT_X(tag.count(), "XmlOutput", "Cannot open an empty tag");+ −
newTagOpen(tag);+ −
closeOpen();+ −
}+ −
+ −
void XmlOutput::newTagOpen(const QString &tag)+ −
{+ −
Q_ASSERT_X(tag.count(), "XmlOutput", "Cannot open an empty tag");+ −
closeOpen();+ −
+ −
if (format == NewLine)+ −
xmlFile << endl << currentIndent;+ −
xmlFile << '<' << doConversion(tag);+ −
currentState = Attribute;+ −
tagStack.append(tag);+ −
increaseIndent(); // ---> indent+ −
}+ −
+ −
void XmlOutput::closeOpen()+ −
{+ −
switch(currentState) {+ −
case Bare:+ −
case Tag:+ −
return;+ −
case Attribute:+ −
break;+ −
}+ −
xmlFile << '>';+ −
currentState = Tag;+ −
}+ −
+ −
void XmlOutput::closeTag()+ −
{+ −
switch(currentState) {+ −
case Bare:+ −
if (tagStack.count())+ −
//warn_msg(WarnLogic, "<Root>: Cannot close tag in Bare state, %d tags on stack", tagStack.count());+ −
qDebug("<Root>: Cannot close tag in Bare state, %d tags on stack", tagStack.count());+ −
else+ −
//warn_msg(WarnLogic, "<Root>: Cannot close tag, no tags on stack");+ −
qDebug("<Root>: Cannot close tag, no tags on stack");+ −
return;+ −
case Tag:+ −
decreaseIndent(); // <--- Pre-decrease indent+ −
if (format == NewLine)+ −
xmlFile << endl << currentIndent;+ −
xmlFile << "</" << doConversion(tagStack.last()) << '>';+ −
tagStack.pop_back();+ −
break;+ −
case Attribute:+ −
xmlFile << " />";+ −
tagStack.pop_back();+ −
currentState = Tag;+ −
decreaseIndent(); // <--- Post-decrease indent+ −
break;+ −
}+ −
}+ −
+ −
void XmlOutput::closeTo(const QString &tag)+ −
{+ −
bool cont = true;+ −
if (!tagStack.contains(tag) && !tag.isNull()) {+ −
//warn_msg(WarnLogic, "<%s>: Cannot close to tag <%s>, not on stack", tagStack.last().latin1(), tag.latin1());+ −
qDebug("<%s>: Cannot close to tag <%s>, not on stack", tagStack.last().toLatin1().constData(), tag.toLatin1().constData());+ −
return;+ −
}+ −
int left = tagStack.count();+ −
while (left-- && cont) {+ −
cont = tagStack.last().compare(tag) != 0;+ −
closeTag();+ −
}+ −
}+ −
+ −
void XmlOutput::closeAll()+ −
{+ −
if (!tagStack.count())+ −
return;+ −
closeTo(QString());+ −
}+ −
+ −
void XmlOutput::addDeclaration(const QString &version, const QString &encoding)+ −
{+ −
switch(currentState) {+ −
case Bare:+ −
break;+ −
case Tag:+ −
case Attribute:+ −
//warn_msg(WarnLogic, "<%s>: Cannot add declaration when not in bare state", tagStack.last().toLatin1().constData());+ −
qDebug("<%s>: Cannot add declaration when not in bare state", tagStack.last().toLatin1().constData());+ −
return;+ −
}+ −
QString outData = QString("<?xml version=\"%1\" encoding=\"%2\"?>")+ −
.arg(doConversion(version))+ −
.arg(doConversion(encoding));+ −
addRaw(outData);+ −
}+ −
+ −
void XmlOutput::addRaw(const QString &rawText)+ −
{+ −
closeOpen();+ −
xmlFile << rawText;+ −
}+ −
+ −
void XmlOutput::addAttribute(const QString &attribute, const QString &value)+ −
{+ −
switch(currentState) {+ −
case Bare:+ −
case Tag:+ −
//warn_msg(WarnLogic, "<%s>: Cannot add attribute since tags not open", tagStack.last().toLatin1().constData());+ −
qDebug("<%s>: Cannot add attribute (%s) since tag's not open",+ −
(tagStack.count() ? tagStack.last().toLatin1().constData() : "Root"),+ −
attribute.toLatin1().constData());+ −
return;+ −
case Attribute:+ −
break;+ −
}+ −
if (format == NewLine)+ −
xmlFile << endl;+ −
xmlFile << currentIndent << doConversion(attribute) << "=\"" << doConversion(value) << "\"";+ −
}+ −
+ −
void XmlOutput::addAttributeTag(const QString &attribute, const QString &value)+ −
{+ −
switch(currentState) {+ −
case Bare:+ −
case Tag:+ −
//warn_msg(WarnLogic, "<%s>: Cannot add attribute since tags not open", tagStack.last().toLatin1().constData());+ −
qDebug("<%s>: Cannot add attribute (%s) since tag's not open",+ −
(tagStack.count() ? tagStack.last().toLatin1().constData() : "Root"),+ −
attribute.toLatin1().constData());+ −
return;+ −
case Attribute:+ −
break;+ −
}+ −
xmlFile << " " << doConversion(attribute) << "=\"" << doConversion(value) << "\"";+ −
}+ −
+ −
QT_END_NAMESPACE+ −