1/*
2 *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/libjingle/xmllite/xmlprinter.h"
12
13#include <sstream>
14#include <string>
15#include <vector>
16
17#include "webrtc/libjingle/xmllite/xmlconstants.h"
18#include "webrtc/libjingle/xmllite/xmlelement.h"
19#include "webrtc/libjingle/xmllite/xmlnsstack.h"
20
21namespace buzz {
22
23class XmlPrinterImpl {
24public:
25  XmlPrinterImpl(std::ostream* pout, XmlnsStack* ns_stack);
26  void PrintElement(const XmlElement* element);
27  void PrintQuotedValue(const std::string& text);
28  void PrintBodyText(const std::string& text);
29  void PrintCDATAText(const std::string& text);
30
31private:
32  std::ostream *pout_;
33  XmlnsStack* ns_stack_;
34};
35
36void XmlPrinter::PrintXml(std::ostream* pout, const XmlElement* element) {
37  XmlnsStack ns_stack;
38  PrintXml(pout, element, &ns_stack);
39}
40
41void XmlPrinter::PrintXml(std::ostream* pout, const XmlElement* element,
42                          XmlnsStack* ns_stack) {
43  XmlPrinterImpl printer(pout, ns_stack);
44  printer.PrintElement(element);
45}
46
47XmlPrinterImpl::XmlPrinterImpl(std::ostream* pout, XmlnsStack* ns_stack)
48    : pout_(pout),
49      ns_stack_(ns_stack) {
50}
51
52void XmlPrinterImpl::PrintElement(const XmlElement* element) {
53  ns_stack_->PushFrame();
54
55  // first go through attrs of pel to add xmlns definitions
56  const XmlAttr* attr;
57  for (attr = element->FirstAttr(); attr; attr = attr->NextAttr()) {
58    if (attr->Name() == QN_XMLNS) {
59      ns_stack_->AddXmlns(STR_EMPTY, attr->Value());
60    } else if (attr->Name().Namespace() == NS_XMLNS) {
61      ns_stack_->AddXmlns(attr->Name().LocalPart(),
62                          attr->Value());
63    }
64  }
65
66  // then go through qnames to make sure needed xmlns definitons are added
67  std::vector<std::string> new_ns;
68  std::pair<std::string, bool> prefix;
69  prefix = ns_stack_->AddNewPrefix(element->Name().Namespace(), false);
70  if (prefix.second) {
71    new_ns.push_back(prefix.first);
72    new_ns.push_back(element->Name().Namespace());
73  }
74
75  for (attr = element->FirstAttr(); attr; attr = attr->NextAttr()) {
76    prefix = ns_stack_->AddNewPrefix(attr->Name().Namespace(), true);
77    if (prefix.second) {
78      new_ns.push_back(prefix.first);
79      new_ns.push_back(attr->Name().Namespace());
80    }
81  }
82
83  // print the element name
84  *pout_ << '<' << ns_stack_->FormatQName(element->Name(), false);
85
86  // and the attributes
87  for (attr = element->FirstAttr(); attr; attr = attr->NextAttr()) {
88    *pout_ << ' ' << ns_stack_->FormatQName(attr->Name(), true) << "=\"";
89    PrintQuotedValue(attr->Value());
90    *pout_ << '"';
91  }
92
93  // and the extra xmlns declarations
94  std::vector<std::string>::iterator i(new_ns.begin());
95  while (i < new_ns.end()) {
96    if (*i == STR_EMPTY) {
97      *pout_ << " xmlns=\"" << *(i + 1) << '"';
98    } else {
99      *pout_ << " xmlns:" << *i << "=\"" << *(i + 1) << '"';
100    }
101    i += 2;
102  }
103
104  // now the children
105  const XmlChild* child = element->FirstChild();
106
107  if (child == NULL)
108    *pout_ << "/>";
109  else {
110    *pout_ << '>';
111    while (child) {
112      if (child->IsText()) {
113        if (element->IsCDATA()) {
114          PrintCDATAText(child->AsText()->Text());
115        } else {
116          PrintBodyText(child->AsText()->Text());
117        }
118      } else {
119        PrintElement(child->AsElement());
120      }
121      child = child->NextChild();
122    }
123    *pout_ << "</" << ns_stack_->FormatQName(element->Name(), false) << '>';
124  }
125
126  ns_stack_->PopFrame();
127}
128
129void XmlPrinterImpl::PrintQuotedValue(const std::string& text) {
130  size_t safe = 0;
131  for (;;) {
132    size_t unsafe = text.find_first_of("<>&\"", safe);
133    if (unsafe == std::string::npos)
134      unsafe = text.length();
135    *pout_ << text.substr(safe, unsafe - safe);
136    if (unsafe == text.length())
137      return;
138    switch (text[unsafe]) {
139      case '<': *pout_ << "&lt;"; break;
140      case '>': *pout_ << "&gt;"; break;
141      case '&': *pout_ << "&amp;"; break;
142      case '"': *pout_ << "&quot;"; break;
143    }
144    safe = unsafe + 1;
145    if (safe == text.length())
146      return;
147  }
148}
149
150void XmlPrinterImpl::PrintBodyText(const std::string& text) {
151  size_t safe = 0;
152  for (;;) {
153    size_t unsafe = text.find_first_of("<>&", safe);
154    if (unsafe == std::string::npos)
155      unsafe = text.length();
156    *pout_ << text.substr(safe, unsafe - safe);
157    if (unsafe == text.length())
158      return;
159    switch (text[unsafe]) {
160      case '<': *pout_ << "&lt;"; break;
161      case '>': *pout_ << "&gt;"; break;
162      case '&': *pout_ << "&amp;"; break;
163    }
164    safe = unsafe + 1;
165    if (safe == text.length())
166      return;
167  }
168}
169
170void XmlPrinterImpl::PrintCDATAText(const std::string& text) {
171  *pout_ << "<![CDATA[" << text << "]]>";
172}
173
174}  // namespace buzz
175