/******************************************************************************** Copyright (C) 2009 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_NAMESPACEXmlOutput::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;}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 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 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) << "\"";}QT_END_NAMESPACE