1 // Copyright 2017 PDFium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com 6 7 #include "core/fxcrt/xml/cxml_element.h" 8 9 #include "core/fxcrt/xml/cxml_content.h" 10 #include "core/fxcrt/xml/cxml_parser.h" 11 12 namespace { 13 14 void SplitQualifiedName(const ByteStringView& bsFullName, 15 ByteStringView* bsSpace, 16 ByteStringView* bsName) { 17 if (bsFullName.IsEmpty()) 18 return; 19 20 auto iStart = bsFullName.Find(':'); 21 if (iStart.has_value()) { 22 *bsSpace = bsFullName.Left(iStart.value()); 23 *bsName = bsFullName.Right(bsFullName.GetLength() - (iStart.value() + 1)); 24 } else { 25 *bsName = bsFullName; 26 } 27 } 28 29 } // namespace 30 31 // static 32 std::unique_ptr<CXML_Element> CXML_Element::Parse(const void* pBuffer, 33 size_t size) { 34 CXML_Parser parser; 35 if (!parser.Init(static_cast<const uint8_t*>(pBuffer), size)) 36 return nullptr; 37 return parser.ParseElement(nullptr, false); 38 } 39 40 CXML_Element::CXML_Element(const CXML_Element* pParent, 41 const ByteStringView& qSpace, 42 const ByteStringView& tagname) 43 : m_pParent(pParent), m_QSpaceName(qSpace), m_TagName(tagname) {} 44 45 CXML_Element::~CXML_Element() {} 46 47 CXML_Element* CXML_Element::AsElement() { 48 return this; 49 } 50 51 const CXML_Element* CXML_Element::AsElement() const { 52 return this; 53 } 54 55 ByteString CXML_Element::GetTagName() const { 56 return m_TagName; 57 } 58 59 ByteString CXML_Element::GetNamespaceURI(const ByteString& qName) const { 60 const CXML_Element* pElement = this; 61 do { 62 const WideString* pwsSpace; 63 if (qName.IsEmpty()) 64 pwsSpace = pElement->Lookup("", "xmlns"); 65 else 66 pwsSpace = pElement->Lookup("xmlns", qName); 67 if (pwsSpace) 68 return pwsSpace->UTF8Encode(); 69 70 pElement = pElement->GetParent(); 71 } while (pElement); 72 return ByteString(); 73 } 74 75 void CXML_Element::GetAttrByIndex(size_t index, 76 ByteString* space, 77 ByteString* name, 78 WideString* value) const { 79 if (index >= m_AttrMap.size()) 80 return; 81 82 const CXML_AttrItem& item = m_AttrMap[index]; 83 *space = item.m_QSpaceName; 84 *name = item.m_AttrName; 85 *value = item.m_Value; 86 } 87 88 WideString CXML_Element::GetAttrValue(const ByteStringView& name) const { 89 ByteStringView bsSpace; 90 ByteStringView bsName; 91 SplitQualifiedName(name, &bsSpace, &bsName); 92 93 WideString attr; 94 const WideString* pValue = Lookup(ByteString(bsSpace), ByteString(bsName)); 95 if (pValue) 96 attr = *pValue; 97 return attr; 98 } 99 100 int CXML_Element::GetAttrInteger(const ByteStringView& name) const { 101 ByteStringView bsSpace; 102 ByteStringView bsName; 103 SplitQualifiedName(name, &bsSpace, &bsName); 104 105 const WideString* pwsValue = Lookup(ByteString(bsSpace), ByteString(bsName)); 106 return pwsValue ? pwsValue->GetInteger() : 0; 107 } 108 109 size_t CXML_Element::CountElements(const ByteStringView& space, 110 const ByteStringView& tag) const { 111 size_t count = 0; 112 for (const auto& pChild : m_Children) { 113 const CXML_Element* pKid = pChild->AsElement(); 114 if (MatchesElement(pKid, space, tag)) 115 count++; 116 } 117 return count; 118 } 119 120 CXML_Object* CXML_Element::GetChild(size_t index) const { 121 return index < m_Children.size() ? m_Children[index].get() : nullptr; 122 } 123 124 CXML_Element* CXML_Element::GetElement(const ByteStringView& space, 125 const ByteStringView& tag, 126 size_t nth) const { 127 for (const auto& pChild : m_Children) { 128 CXML_Element* pKid = pChild->AsElement(); 129 if (MatchesElement(pKid, space, tag)) { 130 if (nth == 0) 131 return pKid; 132 --nth; 133 } 134 } 135 return nullptr; 136 } 137 138 void CXML_Element::SetAttribute(const ByteString& space, 139 const ByteString& name, 140 const WideString& value) { 141 for (CXML_AttrItem& item : m_AttrMap) { 142 if (item.Matches(space, name)) { 143 item.m_Value = value; 144 return; 145 } 146 } 147 m_AttrMap.push_back({space, name, WideString(value)}); 148 } 149 150 // static 151 bool CXML_Element::MatchesElement(const CXML_Element* pKid, 152 const ByteStringView& space, 153 const ByteStringView& tag) { 154 return pKid && pKid->m_TagName == tag && 155 (space.IsEmpty() || pKid->m_QSpaceName == space); 156 } 157 158 const WideString* CXML_Element::Lookup(const ByteString& space, 159 const ByteString& name) const { 160 for (const CXML_AttrItem& item : m_AttrMap) { 161 if (item.Matches(space, name)) 162 return &item.m_Value; 163 } 164 return nullptr; 165 } 166