| // Copyright 2017 PDFium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com |
| |
| #include "core/fxcrt/xml/cxml_element.h" |
| |
| #include "core/fxcrt/xml/cxml_content.h" |
| #include "core/fxcrt/xml/cxml_parser.h" |
| |
| namespace { |
| |
| void SplitQualifiedName(const ByteStringView& bsFullName, |
| ByteStringView* bsSpace, |
| ByteStringView* bsName) { |
| if (bsFullName.IsEmpty()) |
| return; |
| |
| auto iStart = bsFullName.Find(':'); |
| if (iStart.has_value()) { |
| *bsSpace = bsFullName.Left(iStart.value()); |
| *bsName = bsFullName.Right(bsFullName.GetLength() - (iStart.value() + 1)); |
| } else { |
| *bsName = bsFullName; |
| } |
| } |
| |
| } // namespace |
| |
| // static |
| std::unique_ptr<CXML_Element> CXML_Element::Parse(const void* pBuffer, |
| size_t size) { |
| CXML_Parser parser; |
| if (!parser.Init(static_cast<const uint8_t*>(pBuffer), size)) |
| return nullptr; |
| return parser.ParseElement(nullptr, false); |
| } |
| |
| CXML_Element::CXML_Element(const CXML_Element* pParent, |
| const ByteStringView& qSpace, |
| const ByteStringView& tagname) |
| : m_pParent(pParent), m_QSpaceName(qSpace), m_TagName(tagname) {} |
| |
| CXML_Element::~CXML_Element() {} |
| |
| CXML_Element* CXML_Element::AsElement() { |
| return this; |
| } |
| |
| const CXML_Element* CXML_Element::AsElement() const { |
| return this; |
| } |
| |
| ByteString CXML_Element::GetTagName() const { |
| return m_TagName; |
| } |
| |
| ByteString CXML_Element::GetNamespaceURI(const ByteString& qName) const { |
| const CXML_Element* pElement = this; |
| do { |
| const WideString* pwsSpace; |
| if (qName.IsEmpty()) |
| pwsSpace = pElement->Lookup("", "xmlns"); |
| else |
| pwsSpace = pElement->Lookup("xmlns", qName); |
| if (pwsSpace) |
| return pwsSpace->UTF8Encode(); |
| |
| pElement = pElement->GetParent(); |
| } while (pElement); |
| return ByteString(); |
| } |
| |
| void CXML_Element::GetAttrByIndex(size_t index, |
| ByteString* space, |
| ByteString* name, |
| WideString* value) const { |
| if (index >= m_AttrMap.size()) |
| return; |
| |
| const CXML_AttrItem& item = m_AttrMap[index]; |
| *space = item.m_QSpaceName; |
| *name = item.m_AttrName; |
| *value = item.m_Value; |
| } |
| |
| WideString CXML_Element::GetAttrValue(const ByteStringView& name) const { |
| ByteStringView bsSpace; |
| ByteStringView bsName; |
| SplitQualifiedName(name, &bsSpace, &bsName); |
| |
| WideString attr; |
| const WideString* pValue = Lookup(ByteString(bsSpace), ByteString(bsName)); |
| if (pValue) |
| attr = *pValue; |
| return attr; |
| } |
| |
| int CXML_Element::GetAttrInteger(const ByteStringView& name) const { |
| ByteStringView bsSpace; |
| ByteStringView bsName; |
| SplitQualifiedName(name, &bsSpace, &bsName); |
| |
| const WideString* pwsValue = Lookup(ByteString(bsSpace), ByteString(bsName)); |
| return pwsValue ? pwsValue->GetInteger() : 0; |
| } |
| |
| size_t CXML_Element::CountElements(const ByteStringView& space, |
| const ByteStringView& tag) const { |
| size_t count = 0; |
| for (const auto& pChild : m_Children) { |
| const CXML_Element* pKid = pChild->AsElement(); |
| if (MatchesElement(pKid, space, tag)) |
| count++; |
| } |
| return count; |
| } |
| |
| CXML_Object* CXML_Element::GetChild(size_t index) const { |
| return index < m_Children.size() ? m_Children[index].get() : nullptr; |
| } |
| |
| CXML_Element* CXML_Element::GetElement(const ByteStringView& space, |
| const ByteStringView& tag, |
| size_t nth) const { |
| for (const auto& pChild : m_Children) { |
| CXML_Element* pKid = pChild->AsElement(); |
| if (MatchesElement(pKid, space, tag)) { |
| if (nth == 0) |
| return pKid; |
| --nth; |
| } |
| } |
| return nullptr; |
| } |
| |
| void CXML_Element::SetAttribute(const ByteString& space, |
| const ByteString& name, |
| const WideString& value) { |
| for (CXML_AttrItem& item : m_AttrMap) { |
| if (item.Matches(space, name)) { |
| item.m_Value = value; |
| return; |
| } |
| } |
| m_AttrMap.push_back({space, name, WideString(value)}); |
| } |
| |
| // static |
| bool CXML_Element::MatchesElement(const CXML_Element* pKid, |
| const ByteStringView& space, |
| const ByteStringView& tag) { |
| return pKid && pKid->m_TagName == tag && |
| (space.IsEmpty() || pKid->m_QSpaceName == space); |
| } |
| |
| const WideString* CXML_Element::Lookup(const ByteString& space, |
| const ByteString& name) const { |
| for (const CXML_AttrItem& item : m_AttrMap) { |
| if (item.Matches(space, name)) |
| return &item.m_Value; |
| } |
| return nullptr; |
| } |