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/xmlelement.h"
29
30#include <ostream>
31#include <sstream>
32#include <string>
33#include <vector>
34
35#include "talk/xmllite/qname.h"
36#include "talk/xmllite/xmlbuilder.h"
37#include "talk/xmllite/xmlconstants.h"
38#include "talk/xmllite/xmlparser.h"
39#include "talk/xmllite/xmlprinter.h"
40#include "webrtc/base/common.h"
41
42namespace buzz {
43
44XmlChild::~XmlChild() {
45}
46
47bool XmlText::IsTextImpl() const {
48  return true;
49}
50
51XmlElement* XmlText::AsElementImpl() const {
52  return NULL;
53}
54
55XmlText* XmlText::AsTextImpl() const {
56  return const_cast<XmlText *>(this);
57}
58
59void XmlText::SetText(const std::string& text) {
60  text_ = text;
61}
62
63void XmlText::AddParsedText(const char* buf, int len) {
64  text_.append(buf, len);
65}
66
67void XmlText::AddText(const std::string& text) {
68  text_ += text;
69}
70
71XmlText::~XmlText() {
72}
73
74XmlElement::XmlElement(const QName& name) :
75    name_(name),
76    first_attr_(NULL),
77    last_attr_(NULL),
78    first_child_(NULL),
79    last_child_(NULL),
80    cdata_(false) {
81}
82
83XmlElement::XmlElement(const XmlElement& elt) :
84    XmlChild(),
85    name_(elt.name_),
86    first_attr_(NULL),
87    last_attr_(NULL),
88    first_child_(NULL),
89    last_child_(NULL),
90    cdata_(false) {
91
92  // copy attributes
93  XmlAttr* attr;
94  XmlAttr ** plast_attr = &first_attr_;
95  XmlAttr* newAttr = NULL;
96  for (attr = elt.first_attr_; attr; attr = attr->NextAttr()) {
97    newAttr = new XmlAttr(*attr);
98    *plast_attr = newAttr;
99    plast_attr = &(newAttr->next_attr_);
100  }
101  last_attr_ = newAttr;
102
103  // copy children
104  XmlChild* pChild;
105  XmlChild ** ppLast = &first_child_;
106  XmlChild* newChild = NULL;
107
108  for (pChild = elt.first_child_; pChild; pChild = pChild->NextChild()) {
109    if (pChild->IsText()) {
110      newChild = new XmlText(*(pChild->AsText()));
111    } else {
112      newChild = new XmlElement(*(pChild->AsElement()));
113    }
114    *ppLast = newChild;
115    ppLast = &(newChild->next_child_);
116  }
117  last_child_ = newChild;
118
119  cdata_ = elt.cdata_;
120}
121
122XmlElement::XmlElement(const QName& name, bool useDefaultNs) :
123  name_(name),
124  first_attr_(useDefaultNs ? new XmlAttr(QN_XMLNS, name.Namespace()) : NULL),
125  last_attr_(first_attr_),
126  first_child_(NULL),
127  last_child_(NULL),
128  cdata_(false) {
129}
130
131bool XmlElement::IsTextImpl() const {
132  return false;
133}
134
135XmlElement* XmlElement::AsElementImpl() const {
136  return const_cast<XmlElement *>(this);
137}
138
139XmlText* XmlElement::AsTextImpl() const {
140  return NULL;
141}
142
143const std::string XmlElement::BodyText() const {
144  if (first_child_ && first_child_->IsText() && last_child_ == first_child_) {
145    return first_child_->AsText()->Text();
146  }
147
148  return std::string();
149}
150
151void XmlElement::SetBodyText(const std::string& text) {
152  if (text.empty()) {
153    ClearChildren();
154  } else if (first_child_ == NULL) {
155    AddText(text);
156  } else if (first_child_->IsText() && last_child_ == first_child_) {
157    first_child_->AsText()->SetText(text);
158  } else {
159    ClearChildren();
160    AddText(text);
161  }
162}
163
164const QName XmlElement::FirstElementName() const {
165  const XmlElement* element = FirstElement();
166  if (element == NULL)
167    return QName();
168  return element->Name();
169}
170
171XmlAttr* XmlElement::FirstAttr() {
172  return first_attr_;
173}
174
175const std::string XmlElement::Attr(const StaticQName& name) const {
176  XmlAttr* attr;
177  for (attr = first_attr_; attr; attr = attr->next_attr_) {
178    if (attr->name_ == name)
179      return attr->value_;
180  }
181  return std::string();
182}
183
184const std::string XmlElement::Attr(const QName& name) const {
185  XmlAttr* attr;
186  for (attr = first_attr_; attr; attr = attr->next_attr_) {
187    if (attr->name_ == name)
188      return attr->value_;
189  }
190  return std::string();
191}
192
193bool XmlElement::HasAttr(const StaticQName& name) const {
194  XmlAttr* attr;
195  for (attr = first_attr_; attr; attr = attr->next_attr_) {
196    if (attr->name_ == name)
197      return true;
198  }
199  return false;
200}
201
202bool XmlElement::HasAttr(const QName& name) const {
203  XmlAttr* attr;
204  for (attr = first_attr_; attr; attr = attr->next_attr_) {
205    if (attr->name_ == name)
206      return true;
207  }
208  return false;
209}
210
211void XmlElement::SetAttr(const QName& name, const std::string& value) {
212  XmlAttr* attr;
213  for (attr = first_attr_; attr; attr = attr->next_attr_) {
214    if (attr->name_ == name)
215      break;
216  }
217  if (!attr) {
218    attr = new XmlAttr(name, value);
219    if (last_attr_)
220      last_attr_->next_attr_ = attr;
221    else
222      first_attr_ = attr;
223    last_attr_ = attr;
224    return;
225  }
226  attr->value_ = value;
227}
228
229void XmlElement::ClearAttr(const QName& name) {
230  XmlAttr* attr;
231  XmlAttr* last_attr = NULL;
232  for (attr = first_attr_; attr; attr = attr->next_attr_) {
233    if (attr->name_ == name)
234      break;
235    last_attr = attr;
236  }
237  if (!attr)
238    return;
239  if (!last_attr)
240    first_attr_ = attr->next_attr_;
241  else
242    last_attr->next_attr_ = attr->next_attr_;
243  if (last_attr_ == attr)
244    last_attr_ = last_attr;
245  delete attr;
246}
247
248XmlChild* XmlElement::FirstChild() {
249  return first_child_;
250}
251
252XmlElement* XmlElement::FirstElement() {
253  XmlChild* pChild;
254  for (pChild = first_child_; pChild; pChild = pChild->next_child_) {
255    if (!pChild->IsText())
256      return pChild->AsElement();
257  }
258  return NULL;
259}
260
261XmlElement* XmlElement::NextElement() {
262  XmlChild* pChild;
263  for (pChild = next_child_; pChild; pChild = pChild->next_child_) {
264    if (!pChild->IsText())
265      return pChild->AsElement();
266  }
267  return NULL;
268}
269
270XmlElement* XmlElement::FirstWithNamespace(const std::string& ns) {
271  XmlChild* pChild;
272  for (pChild = first_child_; pChild; pChild = pChild->next_child_) {
273    if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns)
274      return pChild->AsElement();
275  }
276  return NULL;
277}
278
279XmlElement *
280XmlElement::NextWithNamespace(const std::string& ns) {
281  XmlChild* pChild;
282  for (pChild = next_child_; pChild; pChild = pChild->next_child_) {
283    if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns)
284      return pChild->AsElement();
285  }
286  return NULL;
287}
288
289XmlElement *
290XmlElement::FirstNamed(const QName& name) {
291  XmlChild* pChild;
292  for (pChild = first_child_; pChild; pChild = pChild->next_child_) {
293    if (!pChild->IsText() && pChild->AsElement()->Name() == name)
294      return pChild->AsElement();
295  }
296  return NULL;
297}
298
299XmlElement *
300XmlElement::FirstNamed(const StaticQName& name) {
301  XmlChild* pChild;
302  for (pChild = first_child_; pChild; pChild = pChild->next_child_) {
303    if (!pChild->IsText() && pChild->AsElement()->Name() == name)
304      return pChild->AsElement();
305  }
306  return NULL;
307}
308
309XmlElement *
310XmlElement::NextNamed(const QName& name) {
311  XmlChild* pChild;
312  for (pChild = next_child_; pChild; pChild = pChild->next_child_) {
313    if (!pChild->IsText() && pChild->AsElement()->Name() == name)
314      return pChild->AsElement();
315  }
316  return NULL;
317}
318
319XmlElement *
320XmlElement::NextNamed(const StaticQName& name) {
321  XmlChild* pChild;
322  for (pChild = next_child_; pChild; pChild = pChild->next_child_) {
323    if (!pChild->IsText() && pChild->AsElement()->Name() == name)
324      return pChild->AsElement();
325  }
326  return NULL;
327}
328
329XmlElement* XmlElement::FindOrAddNamedChild(const QName& name) {
330  XmlElement* child = FirstNamed(name);
331  if (!child) {
332    child = new XmlElement(name);
333    AddElement(child);
334  }
335
336  return child;
337}
338
339const std::string XmlElement::TextNamed(const QName& name) const {
340  XmlChild* pChild;
341  for (pChild = first_child_; pChild; pChild = pChild->next_child_) {
342    if (!pChild->IsText() && pChild->AsElement()->Name() == name)
343      return pChild->AsElement()->BodyText();
344  }
345  return std::string();
346}
347
348void XmlElement::InsertChildAfter(XmlChild* predecessor, XmlChild* next) {
349  if (predecessor == NULL) {
350    next->next_child_ = first_child_;
351    first_child_ = next;
352  }
353  else {
354    next->next_child_ = predecessor->next_child_;
355    predecessor->next_child_ = next;
356  }
357}
358
359void XmlElement::RemoveChildAfter(XmlChild* predecessor) {
360  XmlChild* next;
361
362  if (predecessor == NULL) {
363    next = first_child_;
364    first_child_ = next->next_child_;
365  }
366  else {
367    next = predecessor->next_child_;
368    predecessor->next_child_ = next->next_child_;
369  }
370
371  if (last_child_ == next)
372    last_child_ = predecessor;
373
374  delete next;
375}
376
377void XmlElement::AddAttr(const QName& name, const std::string& value) {
378  ASSERT(!HasAttr(name));
379
380  XmlAttr ** pprev = last_attr_ ? &(last_attr_->next_attr_) : &first_attr_;
381  last_attr_ = (*pprev = new XmlAttr(name, value));
382}
383
384void XmlElement::AddAttr(const QName& name, const std::string& value,
385                         int depth) {
386  XmlElement* element = this;
387  while (depth--) {
388    element = element->last_child_->AsElement();
389  }
390  element->AddAttr(name, value);
391}
392
393void XmlElement::AddParsedText(const char* cstr, int len) {
394  if (len == 0)
395    return;
396
397  if (last_child_ && last_child_->IsText()) {
398    last_child_->AsText()->AddParsedText(cstr, len);
399    return;
400  }
401  XmlChild ** pprev = last_child_ ? &(last_child_->next_child_) : &first_child_;
402  last_child_ = *pprev = new XmlText(cstr, len);
403}
404
405void XmlElement::AddCDATAText(const char* buf, int len) {
406  cdata_ = true;
407  AddParsedText(buf, len);
408}
409
410void XmlElement::AddText(const std::string& text) {
411  if (text == STR_EMPTY)
412    return;
413
414  if (last_child_ && last_child_->IsText()) {
415    last_child_->AsText()->AddText(text);
416    return;
417  }
418  XmlChild ** pprev = last_child_ ? &(last_child_->next_child_) : &first_child_;
419  last_child_ = *pprev = new XmlText(text);
420}
421
422void XmlElement::AddText(const std::string& text, int depth) {
423  // note: the first syntax is ambigious for msvc 6
424  // XmlElement* pel(this);
425  XmlElement* element = this;
426  while (depth--) {
427    element = element->last_child_->AsElement();
428  }
429  element->AddText(text);
430}
431
432void XmlElement::AddElement(XmlElement *child) {
433  if (child == NULL)
434    return;
435
436  XmlChild ** pprev = last_child_ ? &(last_child_->next_child_) : &first_child_;
437  *pprev = child;
438  last_child_ = child;
439  child->next_child_ = NULL;
440}
441
442void XmlElement::AddElement(XmlElement *child, int depth) {
443  XmlElement* element = this;
444  while (depth--) {
445    element = element->last_child_->AsElement();
446  }
447  element->AddElement(child);
448}
449
450void XmlElement::ClearNamedChildren(const QName& name) {
451  XmlChild* prev_child = NULL;
452  XmlChild* next_child;
453  XmlChild* child;
454  for (child = FirstChild(); child; child = next_child) {
455    next_child = child->NextChild();
456    if (!child->IsText() && child->AsElement()->Name() == name)
457    {
458      RemoveChildAfter(prev_child);
459      continue;
460    }
461    prev_child = child;
462  }
463}
464
465void XmlElement::ClearAttributes() {
466  XmlAttr* attr;
467  for (attr = first_attr_; attr; ) {
468    XmlAttr* to_delete = attr;
469    attr = attr->next_attr_;
470    delete to_delete;
471  }
472  first_attr_ = last_attr_ = NULL;
473}
474
475void XmlElement::ClearChildren() {
476  XmlChild* pchild;
477  for (pchild = first_child_; pchild; ) {
478    XmlChild* to_delete = pchild;
479    pchild = pchild->next_child_;
480    delete to_delete;
481  }
482  first_child_ = last_child_ = NULL;
483}
484
485std::string XmlElement::Str() const {
486  std::stringstream ss;
487  XmlPrinter::PrintXml(&ss, this);
488  return ss.str();
489}
490
491XmlElement* XmlElement::ForStr(const std::string& str) {
492  XmlBuilder builder;
493  XmlParser::ParseXml(&builder, str);
494  return builder.CreateElement();
495}
496
497XmlElement::~XmlElement() {
498  XmlAttr* attr;
499  for (attr = first_attr_; attr; ) {
500    XmlAttr* to_delete = attr;
501    attr = attr->next_attr_;
502    delete to_delete;
503  }
504
505  XmlChild* pchild;
506  for (pchild = first_child_; pchild; ) {
507    XmlChild* to_delete = pchild;
508    pchild = pchild->next_child_;
509    delete to_delete;
510  }
511}
512
513}  // namespace buzz
514