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 37namespace buzz { 38 39class XmlPrinterImpl { 40public: 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 48private: 49 std::ostream *pout_; 50 XmlnsStack xmlnsStack_; 51}; 52 53void 54XmlPrinter::PrintXml(std::ostream * pout, const XmlElement * element) { 55 PrintXml(pout, element, NULL, 0); 56} 57 58void 59XmlPrinter::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 65XmlPrinterImpl::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 75void 76XmlPrinterImpl::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 150void 151XmlPrinterImpl::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_ << "<"; break; 162 case '>': *pout_ << ">"; break; 163 case '&': *pout_ << "&"; break; 164 case '"': *pout_ << """; break; 165 } 166 safe = unsafe + 1; 167 if (safe == text.length()) 168 return; 169 } 170} 171 172void 173XmlPrinterImpl::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_ << "<"; break; 184 case '>': *pout_ << ">"; break; 185 case '&': *pout_ << "&"; break; 186 } 187 safe = unsafe + 1; 188 if (safe == text.length()) 189 return; 190 } 191} 192 193void 194XmlPrinterImpl::PrintCDATAText(const std::string & text) { 195 *pout_ << "<![CDATA[" << text << "]]>"; 196} 197 198} 199