qmake/generators/xmloutput.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the qmake application of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "xmloutput.h"
       
    43 
       
    44 QT_BEGIN_NAMESPACE
       
    45 
       
    46 XmlOutput::XmlOutput(QTextStream &file, ConverstionType type)
       
    47     : xmlFile(file), indent("\t"), currentLevel(0), currentState(Bare), format(NewLine),
       
    48       conversion(type)
       
    49 {
       
    50     tagStack.clear();
       
    51 }
       
    52 
       
    53 XmlOutput::~XmlOutput()
       
    54 {
       
    55     closeAll();
       
    56 }
       
    57 
       
    58 // Settings ------------------------------------------------------------------
       
    59 void XmlOutput::setIndentString(const QString &indentString)
       
    60 {
       
    61     indent = indentString;
       
    62 }
       
    63 
       
    64 QString XmlOutput::indentString()
       
    65 {
       
    66     return indent;
       
    67 }
       
    68 
       
    69 void XmlOutput::setIndentLevel(int level)
       
    70 {
       
    71     currentLevel = level;
       
    72 }
       
    73 
       
    74 int XmlOutput::indentLevel()
       
    75 {
       
    76     return currentLevel;
       
    77 }
       
    78 
       
    79 void XmlOutput::setState(XMLState state)
       
    80 {
       
    81     currentState = state;
       
    82 }
       
    83 
       
    84 XmlOutput::XMLState XmlOutput::state()
       
    85 {
       
    86     return currentState;
       
    87 }
       
    88 
       
    89 void XmlOutput::updateIndent()
       
    90 {
       
    91     currentIndent.clear();
       
    92     for (int i = 0; i < currentLevel; ++i)
       
    93         currentIndent.append(indent);
       
    94 }
       
    95 
       
    96 void XmlOutput::increaseIndent()
       
    97 {
       
    98     ++currentLevel;
       
    99     updateIndent();
       
   100 }
       
   101 
       
   102 void XmlOutput::decreaseIndent()
       
   103 {
       
   104     if (currentLevel)
       
   105         --currentLevel;
       
   106     updateIndent();
       
   107     if (!currentLevel)
       
   108         currentState = Bare;
       
   109 }
       
   110 
       
   111 QString XmlOutput::doConversion(const QString &text)
       
   112 {
       
   113     if (!text.count())
       
   114         return QString();
       
   115     else if (conversion == NoConversion)
       
   116         return text;
       
   117 
       
   118     QString output;
       
   119     if (conversion == XMLConversion) {
       
   120 
       
   121         // this is a way to escape characters that shouldn't be converted
       
   122         for (int i=0; i<text.count(); ++i) {
       
   123             if (text.at(i) == QLatin1Char('&')) {
       
   124                 if ( (i + 7) < text.count() &&
       
   125                     text.at(i + 1) == QLatin1Char('#') &&
       
   126                     text.at(i + 2) == QLatin1Char('x') &&
       
   127                     text.at(i + 7) == QLatin1Char(';') ) {
       
   128                     output += text.at(i);
       
   129                 } else {
       
   130                     output += QLatin1String("&amp;");
       
   131                 }
       
   132             } else {
       
   133                 QChar c = text.at(i);
       
   134                 if (c.unicode() < 0x20) {
       
   135                     output += QString("&#x%1;").arg(c.unicode(), 2, 16, QLatin1Char('0'));
       
   136                 } else {
       
   137                     output += text.at(i);
       
   138                 }
       
   139             }
       
   140         }
       
   141     } else {
       
   142         output = text;
       
   143     }
       
   144 
       
   145     if (conversion == XMLConversion) {
       
   146         output.replace('\"', "&quot;");
       
   147         output.replace('\'', "&apos;");
       
   148     } else if (conversion == EscapeConversion) {
       
   149         output.replace('\"', "\\\"");
       
   150         output.replace('\'', "\\\'");
       
   151     }
       
   152     return output;
       
   153 }
       
   154 
       
   155 // Stream functions ----------------------------------------------------------
       
   156 XmlOutput& XmlOutput::operator<<(const QString& o)
       
   157 {
       
   158     return operator<<(data(o));
       
   159 }
       
   160 
       
   161 XmlOutput& XmlOutput::operator<<(const xml_output& o)
       
   162 {
       
   163     switch(o.xo_type) {
       
   164     case tNothing:
       
   165         break;
       
   166     case tRaw:
       
   167         addRaw(o.xo_text);
       
   168         break;
       
   169     case tDeclaration:
       
   170         addDeclaration(o.xo_text, o.xo_value);
       
   171         break;
       
   172     case tTag:
       
   173         newTagOpen(o.xo_text);
       
   174         break;
       
   175     case tCloseTag:
       
   176         if (o.xo_value.count())
       
   177             closeAll();
       
   178         else if (o.xo_text.count())
       
   179             closeTo(o.xo_text);
       
   180         else
       
   181             closeTag();
       
   182         break;
       
   183     case tAttribute:
       
   184         addAttribute(o.xo_text, o.xo_value);
       
   185         break;
       
   186     case tData:
       
   187         {
       
   188             // Special case to be able to close tag in normal
       
   189             // way ("</tag>", not "/>") without using addRaw()..
       
   190             if (!o.xo_text.count()) {
       
   191                 closeOpen();
       
   192                 break;
       
   193             }
       
   194             QString output = doConversion(o.xo_text);
       
   195             output.replace('\n', "\n" + currentIndent);
       
   196             addRaw(QString("\n%1%2").arg(currentIndent).arg(output));
       
   197         }
       
   198         break;
       
   199     case tComment:
       
   200         {
       
   201             QString output("<!--%1-->");
       
   202             addRaw(output.arg(o.xo_text));
       
   203         }
       
   204         break;
       
   205     case tCDATA:
       
   206         {
       
   207             QString output("<![CDATA[\n%1\n]]>");
       
   208             addRaw(output.arg(o.xo_text));
       
   209         }
       
   210         break;
       
   211     }
       
   212     return *this;
       
   213 }
       
   214 
       
   215 
       
   216 // Output functions ----------------------------------------------------------
       
   217 void XmlOutput::newTag(const QString &tag)
       
   218 {
       
   219     Q_ASSERT_X(tag.count(), "XmlOutput", "Cannot open an empty tag");
       
   220     newTagOpen(tag);
       
   221     closeOpen();
       
   222 }
       
   223 
       
   224 void XmlOutput::newTagOpen(const QString &tag)
       
   225 {
       
   226     Q_ASSERT_X(tag.count(), "XmlOutput", "Cannot open an empty tag");
       
   227     closeOpen();
       
   228 
       
   229     if (format == NewLine)
       
   230         xmlFile << endl << currentIndent;
       
   231     xmlFile << '<' << doConversion(tag);
       
   232     currentState = Attribute;
       
   233     tagStack.append(tag);
       
   234     increaseIndent(); // ---> indent
       
   235 }
       
   236 
       
   237 void XmlOutput::closeOpen()
       
   238 {
       
   239     switch(currentState) {
       
   240         case Bare:
       
   241         case Tag:
       
   242             return;
       
   243         case Attribute:
       
   244             break;
       
   245     }
       
   246     xmlFile << '>';
       
   247     currentState = Tag;
       
   248 }
       
   249 
       
   250 void XmlOutput::closeTag()
       
   251 {
       
   252     switch(currentState) {
       
   253         case Bare:
       
   254             if (tagStack.count())
       
   255                 //warn_msg(WarnLogic, "<Root>: Cannot close tag in Bare state, %d tags on stack", tagStack.count());
       
   256                 qDebug("<Root>: Cannot close tag in Bare state, %d tags on stack", tagStack.count());
       
   257             else
       
   258                 //warn_msg(WarnLogic, "<Root>: Cannot close tag, no tags on stack");
       
   259                 qDebug("<Root>: Cannot close tag, no tags on stack");
       
   260             return;
       
   261         case Tag:
       
   262             decreaseIndent(); // <--- Pre-decrease indent
       
   263             if (format == NewLine)
       
   264                 xmlFile << endl << currentIndent;
       
   265             xmlFile << "</" << doConversion(tagStack.last()) << '>';
       
   266             tagStack.pop_back();
       
   267             break;
       
   268         case Attribute:
       
   269             xmlFile << "/>";
       
   270             tagStack.pop_back();
       
   271             currentState = Tag;
       
   272             decreaseIndent(); // <--- Post-decrease indent
       
   273             break;
       
   274     }
       
   275 }
       
   276 
       
   277 void XmlOutput::closeTo(const QString &tag)
       
   278 {
       
   279     bool cont = true;
       
   280     if (!tagStack.contains(tag) && !tag.isNull()) {
       
   281         //warn_msg(WarnLogic, "<%s>: Cannot close to tag <%s>, not on stack", tagStack.last().latin1(), tag.latin1());
       
   282         qDebug("<%s>: Cannot close to tag <%s>, not on stack", tagStack.last().toLatin1().constData(), tag.toLatin1().constData());
       
   283         return;
       
   284     }
       
   285     int left = tagStack.count();
       
   286     while (left-- && cont) {
       
   287         cont = tagStack.last().compare(tag) != 0;
       
   288         closeTag();
       
   289     }
       
   290 }
       
   291 
       
   292 void XmlOutput::closeAll()
       
   293 {
       
   294     if (!tagStack.count())
       
   295         return;
       
   296     closeTo(QString());
       
   297 }
       
   298 
       
   299 void XmlOutput::addDeclaration(const QString &version, const QString &encoding)
       
   300 {
       
   301     switch(currentState) {
       
   302         case Bare:
       
   303             break;
       
   304         case Tag:
       
   305         case Attribute:
       
   306             //warn_msg(WarnLogic, "<%s>: Cannot add declaration when not in bare state", tagStack.last().toLatin1().constData());
       
   307             qDebug("<%s>: Cannot add declaration when not in bare state", tagStack.last().toLatin1().constData());
       
   308             return;
       
   309     }
       
   310     QString outData = QString("<?xml version=\"%1\" encoding = \"%2\"?>")
       
   311                               .arg(doConversion(version))
       
   312                               .arg(doConversion(encoding));
       
   313     addRaw(outData);
       
   314 }
       
   315 
       
   316 void XmlOutput::addRaw(const QString &rawText)
       
   317 {
       
   318     closeOpen();
       
   319     xmlFile << rawText;
       
   320 }
       
   321 
       
   322 void XmlOutput::addAttribute(const QString &attribute, const QString &value)
       
   323 {
       
   324      switch(currentState) {
       
   325         case Bare:
       
   326         case Tag:
       
   327             //warn_msg(WarnLogic, "<%s>: Cannot add attribute since tags not open", tagStack.last().toLatin1().constData());
       
   328             qDebug("<%s>: Cannot add attribute (%s) since tag's not open",
       
   329                    (tagStack.count() ? tagStack.last().toLatin1().constData() : "Root"),
       
   330                    attribute.toLatin1().constData());
       
   331             return;
       
   332         case Attribute:
       
   333             break;
       
   334     }
       
   335     if (format == NewLine)
       
   336         xmlFile << endl;
       
   337     xmlFile << currentIndent << doConversion(attribute) << "=\"" << doConversion(value) << "\"";
       
   338 }
       
   339 
       
   340 QT_END_NAMESPACE