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 "talk/xmllite/xmlprinter.h"
     29 
     30 #include <sstream>
     31 #include <string>
     32 #include <vector>
     33 
     34 #include "talk/xmllite/xmlconstants.h"
     35 #include "talk/xmllite/xmlelement.h"
     36 #include "talk/xmllite/xmlnsstack.h"
     37 
     38 namespace buzz {
     39 
     40 class XmlPrinterImpl {
     41 public:
     42   XmlPrinterImpl(std::ostream* pout, XmlnsStack* ns_stack);
     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* ns_stack_;
     51 };
     52 
     53 void XmlPrinter::PrintXml(std::ostream* pout, const XmlElement* element) {
     54   XmlnsStack ns_stack;
     55   PrintXml(pout, element, &ns_stack);
     56 }
     57 
     58 void XmlPrinter::PrintXml(std::ostream* pout, const XmlElement* element,
     59                           XmlnsStack* ns_stack) {
     60   XmlPrinterImpl printer(pout, ns_stack);
     61   printer.PrintElement(element);
     62 }
     63 
     64 XmlPrinterImpl::XmlPrinterImpl(std::ostream* pout, XmlnsStack* ns_stack)
     65     : pout_(pout),
     66       ns_stack_(ns_stack) {
     67 }
     68 
     69 void XmlPrinterImpl::PrintElement(const XmlElement* element) {
     70   ns_stack_->PushFrame();
     71 
     72   // first go through attrs of pel to add xmlns definitions
     73   const XmlAttr* attr;
     74   for (attr = element->FirstAttr(); attr; attr = attr->NextAttr()) {
     75     if (attr->Name() == QN_XMLNS) {
     76       ns_stack_->AddXmlns(STR_EMPTY, attr->Value());
     77     } else if (attr->Name().Namespace() == NS_XMLNS) {
     78       ns_stack_->AddXmlns(attr->Name().LocalPart(),
     79                           attr->Value());
     80     }
     81   }
     82 
     83   // then go through qnames to make sure needed xmlns definitons are added
     84   std::vector<std::string> new_ns;
     85   std::pair<std::string, bool> prefix;
     86   prefix = ns_stack_->AddNewPrefix(element->Name().Namespace(), false);
     87   if (prefix.second) {
     88     new_ns.push_back(prefix.first);
     89     new_ns.push_back(element->Name().Namespace());
     90   }
     91 
     92   for (attr = element->FirstAttr(); attr; attr = attr->NextAttr()) {
     93     prefix = ns_stack_->AddNewPrefix(attr->Name().Namespace(), true);
     94     if (prefix.second) {
     95       new_ns.push_back(prefix.first);
     96       new_ns.push_back(attr->Name().Namespace());
     97     }
     98   }
     99 
    100   // print the element name
    101   *pout_ << '<' << ns_stack_->FormatQName(element->Name(), false);
    102 
    103   // and the attributes
    104   for (attr = element->FirstAttr(); attr; attr = attr->NextAttr()) {
    105     *pout_ << ' ' << ns_stack_->FormatQName(attr->Name(), true) << "=\"";
    106     PrintQuotedValue(attr->Value());
    107     *pout_ << '"';
    108   }
    109 
    110   // and the extra xmlns declarations
    111   std::vector<std::string>::iterator i(new_ns.begin());
    112   while (i < new_ns.end()) {
    113     if (*i == STR_EMPTY) {
    114       *pout_ << " xmlns=\"" << *(i + 1) << '"';
    115     } else {
    116       *pout_ << " xmlns:" << *i << "=\"" << *(i + 1) << '"';
    117     }
    118     i += 2;
    119   }
    120 
    121   // now the children
    122   const XmlChild* child = element->FirstChild();
    123 
    124   if (child == NULL)
    125     *pout_ << "/>";
    126   else {
    127     *pout_ << '>';
    128     while (child) {
    129       if (child->IsText()) {
    130         if (element->IsCDATA()) {
    131           PrintCDATAText(child->AsText()->Text());
    132         } else {
    133           PrintBodyText(child->AsText()->Text());
    134         }
    135       } else {
    136         PrintElement(child->AsElement());
    137       }
    138       child = child->NextChild();
    139     }
    140     *pout_ << "</" << ns_stack_->FormatQName(element->Name(), false) << '>';
    141   }
    142 
    143   ns_stack_->PopFrame();
    144 }
    145 
    146 void XmlPrinterImpl::PrintQuotedValue(const std::string& text) {
    147   size_t safe = 0;
    148   for (;;) {
    149     size_t unsafe = text.find_first_of("<>&\"", safe);
    150     if (unsafe == std::string::npos)
    151       unsafe = text.length();
    152     *pout_ << text.substr(safe, unsafe - safe);
    153     if (unsafe == text.length())
    154       return;
    155     switch (text[unsafe]) {
    156       case '<': *pout_ << "&lt;"; break;
    157       case '>': *pout_ << "&gt;"; break;
    158       case '&': *pout_ << "&amp;"; break;
    159       case '"': *pout_ << "&quot;"; break;
    160     }
    161     safe = unsafe + 1;
    162     if (safe == text.length())
    163       return;
    164   }
    165 }
    166 
    167 void XmlPrinterImpl::PrintBodyText(const std::string& text) {
    168   size_t safe = 0;
    169   for (;;) {
    170     size_t unsafe = text.find_first_of("<>&", safe);
    171     if (unsafe == std::string::npos)
    172       unsafe = text.length();
    173     *pout_ << text.substr(safe, unsafe - safe);
    174     if (unsafe == text.length())
    175       return;
    176     switch (text[unsafe]) {
    177       case '<': *pout_ << "&lt;"; break;
    178       case '>': *pout_ << "&gt;"; break;
    179       case '&': *pout_ << "&amp;"; break;
    180     }
    181     safe = unsafe + 1;
    182     if (safe == text.length())
    183       return;
    184   }
    185 }
    186 
    187 void XmlPrinterImpl::PrintCDATAText(const std::string& text) {
    188   *pout_ << "<![CDATA[" << text << "]]>";
    189 }
    190 
    191 }  // namespace buzz
    192