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
PrintXml(std::ostream * pout,const XmlElement * element)53 void XmlPrinter::PrintXml(std::ostream* pout, const XmlElement* element) {
54 XmlnsStack ns_stack;
55 PrintXml(pout, element, &ns_stack);
56 }
57
PrintXml(std::ostream * pout,const XmlElement * element,XmlnsStack * ns_stack)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
XmlPrinterImpl(std::ostream * pout,XmlnsStack * ns_stack)64 XmlPrinterImpl::XmlPrinterImpl(std::ostream* pout, XmlnsStack* ns_stack)
65 : pout_(pout),
66 ns_stack_(ns_stack) {
67 }
68
PrintElement(const XmlElement * element)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
PrintQuotedValue(const std::string & text)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_ << "<"; break;
157 case '>': *pout_ << ">"; break;
158 case '&': *pout_ << "&"; break;
159 case '"': *pout_ << """; break;
160 }
161 safe = unsafe + 1;
162 if (safe == text.length())
163 return;
164 }
165 }
166
PrintBodyText(const std::string & text)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_ << "<"; break;
178 case '>': *pout_ << ">"; break;
179 case '&': *pout_ << "&"; break;
180 }
181 safe = unsafe + 1;
182 if (safe == text.length())
183 return;
184 }
185 }
186
PrintCDATAText(const std::string & text)187 void XmlPrinterImpl::PrintCDATAText(const std::string& text) {
188 *pout_ << "<![CDATA[" << text << "]]>";
189 }
190
191 } // namespace buzz
192