| /* |
| * 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> |
| |
| #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<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 |