2013-07-10 02:45:36 +02:00
|
|
|
/*
|
|
|
|
* libjingle
|
|
|
|
* Copyright 2004--2005, Google Inc.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
*
|
|
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
|
|
* and/or other materials provided with the distribution.
|
|
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
|
|
* derived from this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
|
|
|
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "talk/xmllite/xmlelement.h"
|
|
|
|
|
|
|
|
#include <ostream>
|
|
|
|
#include <sstream>
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
2014-07-29 19:36:52 +02:00
|
|
|
#include "webrtc/base/common.h"
|
2013-07-10 02:45:36 +02:00
|
|
|
#include "talk/xmllite/qname.h"
|
|
|
|
#include "talk/xmllite/xmlparser.h"
|
|
|
|
#include "talk/xmllite/xmlbuilder.h"
|
|
|
|
#include "talk/xmllite/xmlprinter.h"
|
|
|
|
#include "talk/xmllite/xmlconstants.h"
|
|
|
|
|
|
|
|
namespace buzz {
|
|
|
|
|
|
|
|
XmlChild::~XmlChild() {
|
|
|
|
}
|
|
|
|
|
|
|
|
bool XmlText::IsTextImpl() const {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlElement* XmlText::AsElementImpl() const {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlText* XmlText::AsTextImpl() const {
|
|
|
|
return const_cast<XmlText *>(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void XmlText::SetText(const std::string& text) {
|
|
|
|
text_ = text;
|
|
|
|
}
|
|
|
|
|
|
|
|
void XmlText::AddParsedText(const char* buf, int len) {
|
|
|
|
text_.append(buf, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
void XmlText::AddText(const std::string& text) {
|
|
|
|
text_ += text;
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlText::~XmlText() {
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlElement::XmlElement(const QName& name) :
|
|
|
|
name_(name),
|
|
|
|
first_attr_(NULL),
|
|
|
|
last_attr_(NULL),
|
|
|
|
first_child_(NULL),
|
|
|
|
last_child_(NULL),
|
|
|
|
cdata_(false) {
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlElement::XmlElement(const XmlElement& elt) :
|
|
|
|
XmlChild(),
|
|
|
|
name_(elt.name_),
|
|
|
|
first_attr_(NULL),
|
|
|
|
last_attr_(NULL),
|
|
|
|
first_child_(NULL),
|
|
|
|
last_child_(NULL),
|
|
|
|
cdata_(false) {
|
|
|
|
|
|
|
|
// copy attributes
|
|
|
|
XmlAttr* attr;
|
|
|
|
XmlAttr ** plast_attr = &first_attr_;
|
|
|
|
XmlAttr* newAttr = NULL;
|
|
|
|
for (attr = elt.first_attr_; attr; attr = attr->NextAttr()) {
|
|
|
|
newAttr = new XmlAttr(*attr);
|
|
|
|
*plast_attr = newAttr;
|
|
|
|
plast_attr = &(newAttr->next_attr_);
|
|
|
|
}
|
|
|
|
last_attr_ = newAttr;
|
|
|
|
|
|
|
|
// copy children
|
|
|
|
XmlChild* pChild;
|
|
|
|
XmlChild ** ppLast = &first_child_;
|
|
|
|
XmlChild* newChild = NULL;
|
|
|
|
|
|
|
|
for (pChild = elt.first_child_; pChild; pChild = pChild->NextChild()) {
|
|
|
|
if (pChild->IsText()) {
|
|
|
|
newChild = new XmlText(*(pChild->AsText()));
|
|
|
|
} else {
|
|
|
|
newChild = new XmlElement(*(pChild->AsElement()));
|
|
|
|
}
|
|
|
|
*ppLast = newChild;
|
|
|
|
ppLast = &(newChild->next_child_);
|
|
|
|
}
|
|
|
|
last_child_ = newChild;
|
|
|
|
|
|
|
|
cdata_ = elt.cdata_;
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlElement::XmlElement(const QName& name, bool useDefaultNs) :
|
|
|
|
name_(name),
|
|
|
|
first_attr_(useDefaultNs ? new XmlAttr(QN_XMLNS, name.Namespace()) : NULL),
|
|
|
|
last_attr_(first_attr_),
|
|
|
|
first_child_(NULL),
|
|
|
|
last_child_(NULL),
|
|
|
|
cdata_(false) {
|
|
|
|
}
|
|
|
|
|
|
|
|
bool XmlElement::IsTextImpl() const {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlElement* XmlElement::AsElementImpl() const {
|
|
|
|
return const_cast<XmlElement *>(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlText* XmlElement::AsTextImpl() const {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::string XmlElement::BodyText() const {
|
|
|
|
if (first_child_ && first_child_->IsText() && last_child_ == first_child_) {
|
|
|
|
return first_child_->AsText()->Text();
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::string();
|
|
|
|
}
|
|
|
|
|
|
|
|
void XmlElement::SetBodyText(const std::string& text) {
|
|
|
|
if (text.empty()) {
|
|
|
|
ClearChildren();
|
|
|
|
} else if (first_child_ == NULL) {
|
|
|
|
AddText(text);
|
|
|
|
} else if (first_child_->IsText() && last_child_ == first_child_) {
|
|
|
|
first_child_->AsText()->SetText(text);
|
|
|
|
} else {
|
|
|
|
ClearChildren();
|
|
|
|
AddText(text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const QName XmlElement::FirstElementName() const {
|
|
|
|
const XmlElement* element = FirstElement();
|
|
|
|
if (element == NULL)
|
|
|
|
return QName();
|
|
|
|
return element->Name();
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlAttr* XmlElement::FirstAttr() {
|
|
|
|
return first_attr_;
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::string XmlElement::Attr(const StaticQName& name) const {
|
|
|
|
XmlAttr* attr;
|
|
|
|
for (attr = first_attr_; attr; attr = attr->next_attr_) {
|
|
|
|
if (attr->name_ == name)
|
|
|
|
return attr->value_;
|
|
|
|
}
|
|
|
|
return std::string();
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::string XmlElement::Attr(const QName& name) const {
|
|
|
|
XmlAttr* attr;
|
|
|
|
for (attr = first_attr_; attr; attr = attr->next_attr_) {
|
|
|
|
if (attr->name_ == name)
|
|
|
|
return attr->value_;
|
|
|
|
}
|
|
|
|
return std::string();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool XmlElement::HasAttr(const StaticQName& name) const {
|
|
|
|
XmlAttr* attr;
|
|
|
|
for (attr = first_attr_; attr; attr = attr->next_attr_) {
|
|
|
|
if (attr->name_ == name)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool XmlElement::HasAttr(const QName& name) const {
|
|
|
|
XmlAttr* attr;
|
|
|
|
for (attr = first_attr_; attr; attr = attr->next_attr_) {
|
|
|
|
if (attr->name_ == name)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void XmlElement::SetAttr(const QName& name, const std::string& value) {
|
|
|
|
XmlAttr* attr;
|
|
|
|
for (attr = first_attr_; attr; attr = attr->next_attr_) {
|
|
|
|
if (attr->name_ == name)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!attr) {
|
|
|
|
attr = new XmlAttr(name, value);
|
|
|
|
if (last_attr_)
|
|
|
|
last_attr_->next_attr_ = attr;
|
|
|
|
else
|
|
|
|
first_attr_ = attr;
|
|
|
|
last_attr_ = attr;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
attr->value_ = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
void XmlElement::ClearAttr(const QName& name) {
|
|
|
|
XmlAttr* attr;
|
|
|
|
XmlAttr* last_attr = NULL;
|
|
|
|
for (attr = first_attr_; attr; attr = attr->next_attr_) {
|
|
|
|
if (attr->name_ == name)
|
|
|
|
break;
|
|
|
|
last_attr = attr;
|
|
|
|
}
|
|
|
|
if (!attr)
|
|
|
|
return;
|
|
|
|
if (!last_attr)
|
|
|
|
first_attr_ = attr->next_attr_;
|
|
|
|
else
|
|
|
|
last_attr->next_attr_ = attr->next_attr_;
|
|
|
|
if (last_attr_ == attr)
|
|
|
|
last_attr_ = last_attr;
|
|
|
|
delete attr;
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlChild* XmlElement::FirstChild() {
|
|
|
|
return first_child_;
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlElement* XmlElement::FirstElement() {
|
|
|
|
XmlChild* pChild;
|
|
|
|
for (pChild = first_child_; pChild; pChild = pChild->next_child_) {
|
|
|
|
if (!pChild->IsText())
|
|
|
|
return pChild->AsElement();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlElement* XmlElement::NextElement() {
|
|
|
|
XmlChild* pChild;
|
|
|
|
for (pChild = next_child_; pChild; pChild = pChild->next_child_) {
|
|
|
|
if (!pChild->IsText())
|
|
|
|
return pChild->AsElement();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlElement* XmlElement::FirstWithNamespace(const std::string& ns) {
|
|
|
|
XmlChild* pChild;
|
|
|
|
for (pChild = first_child_; pChild; pChild = pChild->next_child_) {
|
|
|
|
if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns)
|
|
|
|
return pChild->AsElement();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlElement *
|
|
|
|
XmlElement::NextWithNamespace(const std::string& ns) {
|
|
|
|
XmlChild* pChild;
|
|
|
|
for (pChild = next_child_; pChild; pChild = pChild->next_child_) {
|
|
|
|
if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns)
|
|
|
|
return pChild->AsElement();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlElement *
|
|
|
|
XmlElement::FirstNamed(const QName& name) {
|
|
|
|
XmlChild* pChild;
|
|
|
|
for (pChild = first_child_; pChild; pChild = pChild->next_child_) {
|
|
|
|
if (!pChild->IsText() && pChild->AsElement()->Name() == name)
|
|
|
|
return pChild->AsElement();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlElement *
|
|
|
|
XmlElement::FirstNamed(const StaticQName& name) {
|
|
|
|
XmlChild* pChild;
|
|
|
|
for (pChild = first_child_; pChild; pChild = pChild->next_child_) {
|
|
|
|
if (!pChild->IsText() && pChild->AsElement()->Name() == name)
|
|
|
|
return pChild->AsElement();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlElement *
|
|
|
|
XmlElement::NextNamed(const QName& name) {
|
|
|
|
XmlChild* pChild;
|
|
|
|
for (pChild = next_child_; pChild; pChild = pChild->next_child_) {
|
|
|
|
if (!pChild->IsText() && pChild->AsElement()->Name() == name)
|
|
|
|
return pChild->AsElement();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlElement *
|
|
|
|
XmlElement::NextNamed(const StaticQName& name) {
|
|
|
|
XmlChild* pChild;
|
|
|
|
for (pChild = next_child_; pChild; pChild = pChild->next_child_) {
|
|
|
|
if (!pChild->IsText() && pChild->AsElement()->Name() == name)
|
|
|
|
return pChild->AsElement();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlElement* XmlElement::FindOrAddNamedChild(const QName& name) {
|
|
|
|
XmlElement* child = FirstNamed(name);
|
|
|
|
if (!child) {
|
|
|
|
child = new XmlElement(name);
|
|
|
|
AddElement(child);
|
|
|
|
}
|
|
|
|
|
|
|
|
return child;
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::string XmlElement::TextNamed(const QName& name) const {
|
|
|
|
XmlChild* pChild;
|
|
|
|
for (pChild = first_child_; pChild; pChild = pChild->next_child_) {
|
|
|
|
if (!pChild->IsText() && pChild->AsElement()->Name() == name)
|
|
|
|
return pChild->AsElement()->BodyText();
|
|
|
|
}
|
|
|
|
return std::string();
|
|
|
|
}
|
|
|
|
|
|
|
|
void XmlElement::InsertChildAfter(XmlChild* predecessor, XmlChild* next) {
|
|
|
|
if (predecessor == NULL) {
|
|
|
|
next->next_child_ = first_child_;
|
|
|
|
first_child_ = next;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
next->next_child_ = predecessor->next_child_;
|
|
|
|
predecessor->next_child_ = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void XmlElement::RemoveChildAfter(XmlChild* predecessor) {
|
|
|
|
XmlChild* next;
|
|
|
|
|
|
|
|
if (predecessor == NULL) {
|
|
|
|
next = first_child_;
|
|
|
|
first_child_ = next->next_child_;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
next = predecessor->next_child_;
|
|
|
|
predecessor->next_child_ = next->next_child_;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (last_child_ == next)
|
|
|
|
last_child_ = predecessor;
|
|
|
|
|
|
|
|
delete next;
|
|
|
|
}
|
|
|
|
|
|
|
|
void XmlElement::AddAttr(const QName& name, const std::string& value) {
|
|
|
|
ASSERT(!HasAttr(name));
|
|
|
|
|
|
|
|
XmlAttr ** pprev = last_attr_ ? &(last_attr_->next_attr_) : &first_attr_;
|
|
|
|
last_attr_ = (*pprev = new XmlAttr(name, value));
|
|
|
|
}
|
|
|
|
|
|
|
|
void XmlElement::AddAttr(const QName& name, const std::string& value,
|
|
|
|
int depth) {
|
|
|
|
XmlElement* element = this;
|
|
|
|
while (depth--) {
|
|
|
|
element = element->last_child_->AsElement();
|
|
|
|
}
|
|
|
|
element->AddAttr(name, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void XmlElement::AddParsedText(const char* cstr, int len) {
|
|
|
|
if (len == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (last_child_ && last_child_->IsText()) {
|
|
|
|
last_child_->AsText()->AddParsedText(cstr, len);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
XmlChild ** pprev = last_child_ ? &(last_child_->next_child_) : &first_child_;
|
|
|
|
last_child_ = *pprev = new XmlText(cstr, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
void XmlElement::AddCDATAText(const char* buf, int len) {
|
|
|
|
cdata_ = true;
|
|
|
|
AddParsedText(buf, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
void XmlElement::AddText(const std::string& text) {
|
|
|
|
if (text == STR_EMPTY)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (last_child_ && last_child_->IsText()) {
|
|
|
|
last_child_->AsText()->AddText(text);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
XmlChild ** pprev = last_child_ ? &(last_child_->next_child_) : &first_child_;
|
|
|
|
last_child_ = *pprev = new XmlText(text);
|
|
|
|
}
|
|
|
|
|
|
|
|
void XmlElement::AddText(const std::string& text, int depth) {
|
|
|
|
// note: the first syntax is ambigious for msvc 6
|
|
|
|
// XmlElement* pel(this);
|
|
|
|
XmlElement* element = this;
|
|
|
|
while (depth--) {
|
|
|
|
element = element->last_child_->AsElement();
|
|
|
|
}
|
|
|
|
element->AddText(text);
|
|
|
|
}
|
|
|
|
|
|
|
|
void XmlElement::AddElement(XmlElement *child) {
|
|
|
|
if (child == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
XmlChild ** pprev = last_child_ ? &(last_child_->next_child_) : &first_child_;
|
|
|
|
*pprev = child;
|
|
|
|
last_child_ = child;
|
|
|
|
child->next_child_ = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void XmlElement::AddElement(XmlElement *child, int depth) {
|
|
|
|
XmlElement* element = this;
|
|
|
|
while (depth--) {
|
|
|
|
element = element->last_child_->AsElement();
|
|
|
|
}
|
|
|
|
element->AddElement(child);
|
|
|
|
}
|
|
|
|
|
|
|
|
void XmlElement::ClearNamedChildren(const QName& name) {
|
|
|
|
XmlChild* prev_child = NULL;
|
|
|
|
XmlChild* next_child;
|
|
|
|
XmlChild* child;
|
|
|
|
for (child = FirstChild(); child; child = next_child) {
|
|
|
|
next_child = child->NextChild();
|
|
|
|
if (!child->IsText() && child->AsElement()->Name() == name)
|
|
|
|
{
|
|
|
|
RemoveChildAfter(prev_child);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
prev_child = child;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void XmlElement::ClearAttributes() {
|
|
|
|
XmlAttr* attr;
|
|
|
|
for (attr = first_attr_; attr; ) {
|
|
|
|
XmlAttr* to_delete = attr;
|
|
|
|
attr = attr->next_attr_;
|
|
|
|
delete to_delete;
|
|
|
|
}
|
|
|
|
first_attr_ = last_attr_ = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void XmlElement::ClearChildren() {
|
|
|
|
XmlChild* pchild;
|
|
|
|
for (pchild = first_child_; pchild; ) {
|
|
|
|
XmlChild* to_delete = pchild;
|
|
|
|
pchild = pchild->next_child_;
|
|
|
|
delete to_delete;
|
|
|
|
}
|
|
|
|
first_child_ = last_child_ = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string XmlElement::Str() const {
|
|
|
|
std::stringstream ss;
|
|
|
|
XmlPrinter::PrintXml(&ss, this);
|
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlElement* XmlElement::ForStr(const std::string& str) {
|
|
|
|
XmlBuilder builder;
|
|
|
|
XmlParser::ParseXml(&builder, str);
|
|
|
|
return builder.CreateElement();
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlElement::~XmlElement() {
|
|
|
|
XmlAttr* attr;
|
|
|
|
for (attr = first_attr_; attr; ) {
|
|
|
|
XmlAttr* to_delete = attr;
|
|
|
|
attr = attr->next_attr_;
|
|
|
|
delete to_delete;
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlChild* pchild;
|
|
|
|
for (pchild = first_child_; pchild; ) {
|
|
|
|
XmlChild* to_delete = pchild;
|
|
|
|
pchild = pchild->next_child_;
|
|
|
|
delete to_delete;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace buzz
|