Home | History | Annotate | Download | only in xmllite
      1 /*
      2  * libjingle
      3  * Copyright 2004--2005, Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include <string>
     29 #include <iostream>
     30 #include <vector>
     31 #include <sstream>
     32 #include "talk/xmllite/xmlelement.h"
     33 #include "talk/xmllite/xmlprinter.h"
     34 #include "talk/xmllite/xmlnsstack.h"
     35 #include "talk/xmllite/xmlconstants.h"
     36 
     37 namespace buzz {
     38 
     39 class XmlPrinterImpl {
     40 public:
     41   XmlPrinterImpl(std::ostream * pout,
     42     const std::string * const xmlns, int xmlnsCount);
     43   void PrintElement(const XmlElement * element);
     44   void PrintQuotedValue(const std::string & text);
     45   void PrintBodyText(const std::string & text);
     46   void PrintCDATAText(const std::string & text);
     47 
     48 private:
     49   std::ostream *pout_;
     50   XmlnsStack xmlnsStack_;
     51 };
     52 
     53 void
     54 XmlPrinter::PrintXml(std::ostream * pout, const XmlElement * element) {
     55   PrintXml(pout, element, NULL, 0);
     56 }
     57 
     58 void
     59 XmlPrinter::PrintXml(std::ostream * pout, const XmlElement * element,
     60     const std::string * const xmlns, int xmlnsCount) {
     61   XmlPrinterImpl printer(pout, xmlns, xmlnsCount);
     62   printer.PrintElement(element);
     63 }
     64 
     65 XmlPrinterImpl::XmlPrinterImpl(std::ostream * pout,
     66     const std::string * const xmlns, int xmlnsCount) :
     67   pout_(pout),
     68   xmlnsStack_() {
     69   int i;
     70   for (i = 0; i < xmlnsCount; i += 2) {
     71     xmlnsStack_.AddXmlns(xmlns[i], xmlns[i + 1]);
     72   }
     73 }
     74 
     75 void
     76 XmlPrinterImpl::PrintElement(const XmlElement * element) {
     77   xmlnsStack_.PushFrame();
     78 
     79   // first go through attrs of pel to add xmlns definitions
     80   const XmlAttr * pattr;
     81   for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) {
     82     if (pattr->Name() == QN_XMLNS)
     83       xmlnsStack_.AddXmlns(STR_EMPTY, pattr->Value());
     84     else if (pattr->Name().Namespace() == NS_XMLNS)
     85       xmlnsStack_.AddXmlns(pattr->Name().LocalPart(),
     86         pattr->Value());
     87   }
     88 
     89   // then go through qnames to make sure needed xmlns definitons are added
     90   std::vector<std::string> newXmlns;
     91   std::pair<std::string, bool> prefix;
     92   prefix = xmlnsStack_.AddNewPrefix(element->Name().Namespace(), false);
     93   if (prefix.second) {
     94     newXmlns.push_back(prefix.first);
     95     newXmlns.push_back(element->Name().Namespace());
     96   }
     97 
     98   for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) {
     99     prefix = xmlnsStack_.AddNewPrefix(pattr->Name().Namespace(), true);
    100     if (prefix.second) {
    101       newXmlns.push_back(prefix.first);
    102       newXmlns.push_back(pattr->Name().Namespace());
    103     }
    104   }
    105 
    106   // print the element name
    107   *pout_ << '<' << xmlnsStack_.FormatQName(element->Name(), false);
    108 
    109   // and the attributes
    110   for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) {
    111     *pout_ << ' ' << xmlnsStack_.FormatQName(pattr->Name(), true) << "=\"";
    112     PrintQuotedValue(pattr->Value());
    113     *pout_ << '"';
    114   }
    115 
    116   // and the extra xmlns declarations
    117   std::vector<std::string>::iterator i(newXmlns.begin());
    118   while (i < newXmlns.end()) {
    119     if (*i == STR_EMPTY)
    120       *pout_ << " xmlns=\"" << *(i + 1) << '"';
    121     else
    122       *pout_ << " xmlns:" << *i << "=\"" << *(i + 1) << '"';
    123     i += 2;
    124   }
    125 
    126   // now the children
    127   const XmlChild * pchild = element->FirstChild();
    128 
    129   if (pchild == NULL)
    130     *pout_ << "/>";
    131   else {
    132     *pout_ << '>';
    133     while (pchild) {
    134       if (pchild->IsText()) {
    135         if (element->IsCDATA()) {
    136           PrintCDATAText(pchild->AsText()->Text());
    137         } else {
    138           PrintBodyText(pchild->AsText()->Text());
    139         }
    140       } else
    141         PrintElement(pchild->AsElement());
    142       pchild = pchild->NextChild();
    143     }
    144     *pout_ << "</" << xmlnsStack_.FormatQName(element->Name(), false) << '>';
    145   }
    146 
    147   xmlnsStack_.PopFrame();
    148 }
    149 
    150 void
    151 XmlPrinterImpl::PrintQuotedValue(const std::string & text) {
    152   size_t safe = 0;
    153   for (;;) {
    154     size_t unsafe = text.find_first_of("<>&\"", safe);
    155     if (unsafe == std::string::npos)
    156       unsafe = text.length();
    157     *pout_ << text.substr(safe, unsafe - safe);
    158     if (unsafe == text.length())
    159       return;
    160     switch (text[unsafe]) {
    161       case '<': *pout_ << "&lt;"; break;
    162       case '>': *pout_ << "&gt;"; break;
    163       case '&': *pout_ << "&amp;"; break;
    164       case '"': *pout_ << "&quot;"; break;
    165     }
    166     safe = unsafe + 1;
    167     if (safe == text.length())
    168       return;
    169   }
    170 }
    171 
    172 void
    173 XmlPrinterImpl::PrintBodyText(const std::string & text) {
    174   size_t safe = 0;
    175   for (;;) {
    176     size_t unsafe = text.find_first_of("<>&", safe);
    177     if (unsafe == std::string::npos)
    178       unsafe = text.length();
    179     *pout_ << text.substr(safe, unsafe - safe);
    180     if (unsafe == text.length())
    181       return;
    182     switch (text[unsafe]) {
    183       case '<': *pout_ << "&lt;"; break;
    184       case '>': *pout_ << "&gt;"; break;
    185       case '&': *pout_ << "&amp;"; break;
    186     }
    187     safe = unsafe + 1;
    188     if (safe == text.length())
    189       return;
    190   }
    191 }
    192 
    193 void
    194 XmlPrinterImpl::PrintCDATAText(const std::string & text) {
    195   *pout_ << "<![CDATA[" << text << "]]>";
    196 }
    197 
    198 }
    199