/* * 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 #include #include #include #include "talk/base/common.h" #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(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(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