| /* |
| * 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), |
| pFirstAttr_(NULL), |
| pLastAttr_(NULL), |
| pFirstChild_(NULL), |
| pLastChild_(NULL), |
| cdata_(false) { |
| } |
| |
| XmlElement::XmlElement(const XmlElement & elt) : |
| XmlChild(), |
| name_(elt.name_), |
| pFirstAttr_(NULL), |
| pLastAttr_(NULL), |
| pFirstChild_(NULL), |
| pLastChild_(NULL), |
| cdata_(false) { |
| |
| // copy attributes |
| XmlAttr * pAttr; |
| XmlAttr ** ppLastAttr = &pFirstAttr_; |
| XmlAttr * newAttr = NULL; |
| for (pAttr = elt.pFirstAttr_; pAttr; pAttr = pAttr->NextAttr()) { |
| newAttr = new XmlAttr(*pAttr); |
| *ppLastAttr = newAttr; |
| ppLastAttr = &(newAttr->pNextAttr_); |
| } |
| pLastAttr_ = newAttr; |
| |
| // copy children |
| XmlChild * pChild; |
| XmlChild ** ppLast = &pFirstChild_; |
| XmlChild * newChild = NULL; |
| |
| for (pChild = elt.pFirstChild_; pChild; pChild = pChild->NextChild()) { |
| if (pChild->IsText()) { |
| newChild = new XmlText(*(pChild->AsText())); |
| } else { |
| newChild = new XmlElement(*(pChild->AsElement())); |
| } |
| *ppLast = newChild; |
| ppLast = &(newChild->pNextChild_); |
| } |
| pLastChild_ = newChild; |
| |
| cdata_ = elt.cdata_; |
| } |
| |
| XmlElement::XmlElement(const QName & name, bool useDefaultNs) : |
| name_(name), |
| pFirstAttr_(useDefaultNs ? new XmlAttr(QN_XMLNS, name.Namespace()) : NULL), |
| pLastAttr_(pFirstAttr_), |
| pFirstChild_(NULL), |
| pLastChild_(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 (pFirstChild_ && pFirstChild_->IsText() && pLastChild_ == pFirstChild_) { |
| return pFirstChild_->AsText()->Text(); |
| } |
| |
| return EmptyStringRef(); |
| } |
| |
| void |
| XmlElement::SetBodyText(const std::string & text) { |
| if (text == STR_EMPTY) { |
| ClearChildren(); |
| } else if (pFirstChild_ == NULL) { |
| AddText(text); |
| } else if (pFirstChild_->IsText() && pLastChild_ == pFirstChild_) { |
| pFirstChild_->AsText()->SetText(text); |
| } else { |
| ClearChildren(); |
| AddText(text); |
| } |
| } |
| |
| const QName & |
| XmlElement::FirstElementName() const { |
| const XmlElement * element = FirstElement(); |
| if (element == NULL) |
| return EmptyQNameRef(); |
| return element->Name(); |
| } |
| |
| XmlAttr * |
| XmlElement::FirstAttr() { |
| return pFirstAttr_; |
| } |
| |
| const std::string & |
| XmlElement::Attr(const StaticQName & name) const { |
| XmlAttr * pattr; |
| for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { |
| if (pattr->name_ == name) |
| return pattr->value_; |
| } |
| return EmptyStringRef(); |
| } |
| |
| const std::string & |
| XmlElement::Attr(const QName & name) const { |
| XmlAttr * pattr; |
| for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { |
| if (pattr->name_ == name) |
| return pattr->value_; |
| } |
| return EmptyStringRef(); |
| } |
| |
| bool |
| XmlElement::HasAttr(const StaticQName & name) const { |
| XmlAttr * pattr; |
| for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { |
| if (pattr->name_ == name) |
| return true; |
| } |
| return false; |
| } |
| |
| bool |
| XmlElement::HasAttr(const QName & name) const { |
| XmlAttr * pattr; |
| for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { |
| if (pattr->name_ == name) |
| return true; |
| } |
| return false; |
| } |
| |
| void |
| XmlElement::SetAttr(const QName & name, const std::string & value) { |
| XmlAttr * pattr; |
| for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { |
| if (pattr->name_ == name) |
| break; |
| } |
| if (!pattr) { |
| pattr = new XmlAttr(name, value); |
| if (pLastAttr_) |
| pLastAttr_->pNextAttr_ = pattr; |
| else |
| pFirstAttr_ = pattr; |
| pLastAttr_ = pattr; |
| return; |
| } |
| pattr->value_ = value; |
| } |
| |
| void |
| XmlElement::ClearAttr(const QName & name) { |
| XmlAttr * pattr; |
| XmlAttr *pLastAttr = NULL; |
| for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) { |
| if (pattr->name_ == name) |
| break; |
| pLastAttr = pattr; |
| } |
| if (!pattr) |
| return; |
| if (!pLastAttr) |
| pFirstAttr_ = pattr->pNextAttr_; |
| else |
| pLastAttr->pNextAttr_ = pattr->pNextAttr_; |
| if (pLastAttr_ == pattr) |
| pLastAttr_ = pLastAttr; |
| delete pattr; |
| } |
| |
| XmlChild * |
| XmlElement::FirstChild() { |
| return pFirstChild_; |
| } |
| |
| XmlElement * |
| XmlElement::FirstElement() { |
| XmlChild * pChild; |
| for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { |
| if (!pChild->IsText()) |
| return pChild->AsElement(); |
| } |
| return NULL; |
| } |
| |
| XmlElement * |
| XmlElement::NextElement() { |
| XmlChild * pChild; |
| for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) { |
| if (!pChild->IsText()) |
| return pChild->AsElement(); |
| } |
| return NULL; |
| } |
| |
| XmlElement * |
| XmlElement::FirstWithNamespace(const std::string & ns) { |
| XmlChild * pChild; |
| for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { |
| if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns) |
| return pChild->AsElement(); |
| } |
| return NULL; |
| } |
| |
| XmlElement * |
| XmlElement::NextWithNamespace(const std::string & ns) { |
| XmlChild * pChild; |
| for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) { |
| if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns) |
| return pChild->AsElement(); |
| } |
| return NULL; |
| } |
| |
| XmlElement * |
| XmlElement::FirstNamed(const QName & name) { |
| XmlChild * pChild; |
| for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { |
| if (!pChild->IsText() && pChild->AsElement()->Name() == name) |
| return pChild->AsElement(); |
| } |
| return NULL; |
| } |
| |
| XmlElement * |
| XmlElement::FirstNamed(const StaticQName & name) { |
| XmlChild * pChild; |
| for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) { |
| if (!pChild->IsText() && pChild->AsElement()->Name() == name) |
| return pChild->AsElement(); |
| } |
| return NULL; |
| } |
| |
| XmlElement * |
| XmlElement::NextNamed(const QName & name) { |
| XmlChild * pChild; |
| for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) { |
| if (!pChild->IsText() && pChild->AsElement()->Name() == name) |
| return pChild->AsElement(); |
| } |
| return NULL; |
| } |
| |
| XmlElement * |
| XmlElement::NextNamed(const StaticQName & name) { |
| XmlChild * pChild; |
| for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) { |
| 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 = pFirstChild_; pChild; pChild = pChild->pNextChild_) { |
| if (!pChild->IsText() && pChild->AsElement()->Name() == name) |
| return pChild->AsElement()->BodyText(); |
| } |
| return EmptyStringRef(); |
| } |
| |
| void |
| XmlElement::InsertChildAfter(XmlChild * pPredecessor, XmlChild * pNext) { |
| if (pPredecessor == NULL) { |
| pNext->pNextChild_ = pFirstChild_; |
| pFirstChild_ = pNext; |
| } |
| else { |
| pNext->pNextChild_ = pPredecessor->pNextChild_; |
| pPredecessor->pNextChild_ = pNext; |
| } |
| } |
| |
| void |
| XmlElement::RemoveChildAfter(XmlChild * pPredecessor) { |
| XmlChild * pNext; |
| |
| if (pPredecessor == NULL) { |
| pNext = pFirstChild_; |
| pFirstChild_ = pNext->pNextChild_; |
| } |
| else { |
| pNext = pPredecessor->pNextChild_; |
| pPredecessor->pNextChild_ = pNext->pNextChild_; |
| } |
| |
| if (pLastChild_ == pNext) |
| pLastChild_ = pPredecessor; |
| |
| delete pNext; |
| } |
| |
| void |
| XmlElement::AddAttr(const QName & name, const std::string & value) { |
| ASSERT(!HasAttr(name)); |
| |
| XmlAttr ** pprev = pLastAttr_ ? &(pLastAttr_->pNextAttr_) : &pFirstAttr_; |
| pLastAttr_ = (*pprev = new XmlAttr(name, value)); |
| } |
| |
| void |
| XmlElement::AddAttr(const QName & name, const std::string & value, |
| int depth) { |
| XmlElement * element = this; |
| while (depth--) { |
| element = element->pLastChild_->AsElement(); |
| } |
| element->AddAttr(name, value); |
| } |
| |
| void |
| XmlElement::AddParsedText(const char * cstr, int len) { |
| if (len == 0) |
| return; |
| |
| if (pLastChild_ && pLastChild_->IsText()) { |
| pLastChild_->AsText()->AddParsedText(cstr, len); |
| return; |
| } |
| XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_; |
| pLastChild_ = *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 (pLastChild_ && pLastChild_->IsText()) { |
| pLastChild_->AsText()->AddText(text); |
| return; |
| } |
| XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_; |
| pLastChild_ = *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->pLastChild_->AsElement(); |
| } |
| element->AddText(text); |
| } |
| |
| void |
| XmlElement::AddElement(XmlElement *pelChild) { |
| if (pelChild == NULL) |
| return; |
| |
| XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_; |
| pLastChild_ = *pprev = pelChild; |
| pelChild->pNextChild_ = NULL; |
| } |
| |
| void |
| XmlElement::AddElement(XmlElement *pelChild, int depth) { |
| XmlElement * element = this; |
| while (depth--) { |
| element = element->pLastChild_->AsElement(); |
| } |
| element->AddElement(pelChild); |
| } |
| |
| 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 * pattr; |
| for (pattr = pFirstAttr_; pattr; ) { |
| XmlAttr * pToDelete = pattr; |
| pattr = pattr->pNextAttr_; |
| delete pToDelete; |
| } |
| pFirstAttr_ = pLastAttr_ = NULL; |
| } |
| |
| void |
| XmlElement::ClearChildren() { |
| XmlChild * pchild; |
| for (pchild = pFirstChild_; pchild; ) { |
| XmlChild * pToDelete = pchild; |
| pchild = pchild->pNextChild_; |
| delete pToDelete; |
| } |
| pFirstChild_ = pLastChild_ = 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 * pattr; |
| for (pattr = pFirstAttr_; pattr; ) { |
| XmlAttr * pToDelete = pattr; |
| pattr = pattr->pNextAttr_; |
| delete pToDelete; |
| } |
| |
| XmlChild * pchild; |
| for (pchild = pFirstChild_; pchild; ) { |
| XmlChild * pToDelete = pchild; |
| pchild = pchild->pNextChild_; |
| delete pToDelete; |
| } |
| } |
| |
| } |