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