xref: /aosp_15_r20/frameworks/base/cmds/idmap2/libidmap2/XmlParser.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker  * Copyright (C) 2019 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker  *
4*d57664e9SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker  *
8*d57664e9SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker  *
10*d57664e9SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker  * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker  */
16*d57664e9SAndroid Build Coastguard Worker 
17*d57664e9SAndroid Build Coastguard Worker #include "idmap2/XmlParser.h"
18*d57664e9SAndroid Build Coastguard Worker 
19*d57664e9SAndroid Build Coastguard Worker #include <memory>
20*d57664e9SAndroid Build Coastguard Worker #include <string>
21*d57664e9SAndroid Build Coastguard Worker #include <utility>
22*d57664e9SAndroid Build Coastguard Worker 
23*d57664e9SAndroid Build Coastguard Worker namespace android::idmap2 {
24*d57664e9SAndroid Build Coastguard Worker 
25*d57664e9SAndroid Build Coastguard Worker template <typename T>
get_tree_position(const T & tree)26*d57664e9SAndroid Build Coastguard Worker ResXMLParser::ResXMLPosition get_tree_position(const T& tree) {
27*d57664e9SAndroid Build Coastguard Worker   ResXMLParser::ResXMLPosition pos{};
28*d57664e9SAndroid Build Coastguard Worker   tree.getPosition(&pos);
29*d57664e9SAndroid Build Coastguard Worker   return pos;
30*d57664e9SAndroid Build Coastguard Worker }
31*d57664e9SAndroid Build Coastguard Worker 
Node(const ResXMLTree & tree)32*d57664e9SAndroid Build Coastguard Worker XmlParser::Node::Node(const ResXMLTree& tree) : Node(tree, get_tree_position(tree)) {
33*d57664e9SAndroid Build Coastguard Worker }
Node(const ResXMLTree & tree,const ResXMLParser::ResXMLPosition & pos)34*d57664e9SAndroid Build Coastguard Worker XmlParser::Node::Node(const ResXMLTree& tree, const ResXMLParser::ResXMLPosition& pos)
35*d57664e9SAndroid Build Coastguard Worker     : parser_(tree) {
36*d57664e9SAndroid Build Coastguard Worker   set_position(pos);
37*d57664e9SAndroid Build Coastguard Worker }
38*d57664e9SAndroid Build Coastguard Worker 
operator ==(const XmlParser::Node & rhs) const39*d57664e9SAndroid Build Coastguard Worker bool XmlParser::Node::operator==(const XmlParser::Node& rhs) const {
40*d57664e9SAndroid Build Coastguard Worker   ResXMLParser::ResXMLPosition pos = get_position();
41*d57664e9SAndroid Build Coastguard Worker   ResXMLParser::ResXMLPosition rhs_pos = rhs.get_position();
42*d57664e9SAndroid Build Coastguard Worker   return pos.curExt == rhs_pos.curExt && pos.curNode == rhs_pos.curNode &&
43*d57664e9SAndroid Build Coastguard Worker          pos.eventCode == rhs_pos.eventCode;
44*d57664e9SAndroid Build Coastguard Worker }
45*d57664e9SAndroid Build Coastguard Worker 
operator !=(const XmlParser::Node & rhs) const46*d57664e9SAndroid Build Coastguard Worker bool XmlParser::Node::operator!=(const XmlParser::Node& rhs) const {
47*d57664e9SAndroid Build Coastguard Worker   return !(*this == rhs);
48*d57664e9SAndroid Build Coastguard Worker }
49*d57664e9SAndroid Build Coastguard Worker 
get_position() const50*d57664e9SAndroid Build Coastguard Worker ResXMLParser::ResXMLPosition XmlParser::Node::get_position() const {
51*d57664e9SAndroid Build Coastguard Worker   return get_tree_position(parser_);
52*d57664e9SAndroid Build Coastguard Worker }
53*d57664e9SAndroid Build Coastguard Worker 
set_position(const ResXMLParser::ResXMLPosition & pos)54*d57664e9SAndroid Build Coastguard Worker void XmlParser::Node::set_position(const ResXMLParser::ResXMLPosition& pos) {
55*d57664e9SAndroid Build Coastguard Worker   parser_.setPosition(pos);
56*d57664e9SAndroid Build Coastguard Worker }
57*d57664e9SAndroid Build Coastguard Worker 
Seek(bool inner_child)58*d57664e9SAndroid Build Coastguard Worker bool XmlParser::Node::Seek(bool inner_child) {
59*d57664e9SAndroid Build Coastguard Worker   if (parser_.getEventType() == XmlParser::Event::END_TAG) {
60*d57664e9SAndroid Build Coastguard Worker     return false;
61*d57664e9SAndroid Build Coastguard Worker   }
62*d57664e9SAndroid Build Coastguard Worker 
63*d57664e9SAndroid Build Coastguard Worker   ssize_t depth = 0;
64*d57664e9SAndroid Build Coastguard Worker   XmlParser::Event code;
65*d57664e9SAndroid Build Coastguard Worker   while ((code = parser_.next()) != XmlParser::Event::BAD_DOCUMENT &&
66*d57664e9SAndroid Build Coastguard Worker          code != XmlParser::Event::END_DOCUMENT) {
67*d57664e9SAndroid Build Coastguard Worker     if (code == XmlParser::Event::START_TAG) {
68*d57664e9SAndroid Build Coastguard Worker       if (++depth == (inner_child ? 1 : 0)) {
69*d57664e9SAndroid Build Coastguard Worker         return true;
70*d57664e9SAndroid Build Coastguard Worker       }
71*d57664e9SAndroid Build Coastguard Worker     } else if (code == XmlParser::Event::END_TAG) {
72*d57664e9SAndroid Build Coastguard Worker       if (--depth == (inner_child ? -1 : -2)) {
73*d57664e9SAndroid Build Coastguard Worker         return false;
74*d57664e9SAndroid Build Coastguard Worker       }
75*d57664e9SAndroid Build Coastguard Worker     }
76*d57664e9SAndroid Build Coastguard Worker   }
77*d57664e9SAndroid Build Coastguard Worker 
78*d57664e9SAndroid Build Coastguard Worker   return false;
79*d57664e9SAndroid Build Coastguard Worker }
80*d57664e9SAndroid Build Coastguard Worker 
event() const81*d57664e9SAndroid Build Coastguard Worker XmlParser::Event XmlParser::Node::event() const {
82*d57664e9SAndroid Build Coastguard Worker   return parser_.getEventType();
83*d57664e9SAndroid Build Coastguard Worker }
84*d57664e9SAndroid Build Coastguard Worker 
name() const85*d57664e9SAndroid Build Coastguard Worker std::string XmlParser::Node::name() const {
86*d57664e9SAndroid Build Coastguard Worker   size_t len;
87*d57664e9SAndroid Build Coastguard Worker   const String16 key16(parser_.getElementName(&len));
88*d57664e9SAndroid Build Coastguard Worker   return String8(key16).c_str();
89*d57664e9SAndroid Build Coastguard Worker }
90*d57664e9SAndroid Build Coastguard Worker 
91*d57664e9SAndroid Build Coastguard Worker template <typename Func>
FindAttribute(const ResXMLParser & parser,const std::string & label,Func && predicate)92*d57664e9SAndroid Build Coastguard Worker Result<Res_value> FindAttribute(const ResXMLParser& parser, const std::string& label,
93*d57664e9SAndroid Build Coastguard Worker                                 Func&& predicate) {
94*d57664e9SAndroid Build Coastguard Worker   for (size_t i = 0; i < parser.getAttributeCount(); i++) {
95*d57664e9SAndroid Build Coastguard Worker     if (!predicate(i)) {
96*d57664e9SAndroid Build Coastguard Worker       continue;
97*d57664e9SAndroid Build Coastguard Worker     }
98*d57664e9SAndroid Build Coastguard Worker     Res_value res_value{};
99*d57664e9SAndroid Build Coastguard Worker     if (parser.getAttributeValue(i, &res_value) == BAD_TYPE) {
100*d57664e9SAndroid Build Coastguard Worker       return Error(R"(Bad value for attribute "%s")", label.c_str());
101*d57664e9SAndroid Build Coastguard Worker     }
102*d57664e9SAndroid Build Coastguard Worker     return res_value;
103*d57664e9SAndroid Build Coastguard Worker   }
104*d57664e9SAndroid Build Coastguard Worker   return Error(R"(Failed to find attribute "%s")", label.c_str());
105*d57664e9SAndroid Build Coastguard Worker }
106*d57664e9SAndroid Build Coastguard Worker 
GetStringValue(const ResXMLParser & parser,const Res_value & value,const std::string & label)107*d57664e9SAndroid Build Coastguard Worker Result<std::string> GetStringValue(const ResXMLParser& parser, const Res_value& value,
108*d57664e9SAndroid Build Coastguard Worker                                    const std::string& label) {
109*d57664e9SAndroid Build Coastguard Worker   switch (value.dataType) {
110*d57664e9SAndroid Build Coastguard Worker     case Res_value::TYPE_STRING: {
111*d57664e9SAndroid Build Coastguard Worker       if (auto str = parser.getStrings().string8ObjectAt(value.data); str.ok()) {
112*d57664e9SAndroid Build Coastguard Worker         return std::string(str->c_str());
113*d57664e9SAndroid Build Coastguard Worker       }
114*d57664e9SAndroid Build Coastguard Worker       break;
115*d57664e9SAndroid Build Coastguard Worker     }
116*d57664e9SAndroid Build Coastguard Worker     case Res_value::TYPE_INT_DEC:
117*d57664e9SAndroid Build Coastguard Worker     case Res_value::TYPE_INT_HEX:
118*d57664e9SAndroid Build Coastguard Worker     case Res_value::TYPE_INT_BOOLEAN: {
119*d57664e9SAndroid Build Coastguard Worker       return std::to_string(value.data);
120*d57664e9SAndroid Build Coastguard Worker     }
121*d57664e9SAndroid Build Coastguard Worker   }
122*d57664e9SAndroid Build Coastguard Worker   return Error(R"(Failed to convert attribute "%s" value to a string)", label.c_str());
123*d57664e9SAndroid Build Coastguard Worker }
124*d57664e9SAndroid Build Coastguard Worker 
GetAttributeValue(ResourceId attr,const std::string & label) const125*d57664e9SAndroid Build Coastguard Worker Result<Res_value> XmlParser::Node::GetAttributeValue(ResourceId attr,
126*d57664e9SAndroid Build Coastguard Worker                                                      const std::string& label) const {
127*d57664e9SAndroid Build Coastguard Worker   return FindAttribute(parser_, label, [&](size_t index) -> bool {
128*d57664e9SAndroid Build Coastguard Worker     return parser_.getAttributeNameResID(index) == attr;
129*d57664e9SAndroid Build Coastguard Worker   });
130*d57664e9SAndroid Build Coastguard Worker }
131*d57664e9SAndroid Build Coastguard Worker 
GetAttributeValue(const std::string & name) const132*d57664e9SAndroid Build Coastguard Worker Result<Res_value> XmlParser::Node::GetAttributeValue(const std::string& name) const {
133*d57664e9SAndroid Build Coastguard Worker   String16 name16;
134*d57664e9SAndroid Build Coastguard Worker   return FindAttribute(parser_, name, [&](size_t index) -> bool {
135*d57664e9SAndroid Build Coastguard Worker     if (name16.empty()) {
136*d57664e9SAndroid Build Coastguard Worker         name16 = String16(name.c_str(), name.size());
137*d57664e9SAndroid Build Coastguard Worker     }
138*d57664e9SAndroid Build Coastguard Worker     size_t key_len;
139*d57664e9SAndroid Build Coastguard Worker     const auto key16 = parser_.getAttributeName(index, &key_len);
140*d57664e9SAndroid Build Coastguard Worker     return key16 && name16.size() == key_len && name16 == key16;
141*d57664e9SAndroid Build Coastguard Worker   });
142*d57664e9SAndroid Build Coastguard Worker }
143*d57664e9SAndroid Build Coastguard Worker 
GetAttributeStringValue(ResourceId attr,const std::string & label) const144*d57664e9SAndroid Build Coastguard Worker Result<std::string> XmlParser::Node::GetAttributeStringValue(ResourceId attr,
145*d57664e9SAndroid Build Coastguard Worker                                                              const std::string& label) const {
146*d57664e9SAndroid Build Coastguard Worker   auto value = GetAttributeValue(attr, label);
147*d57664e9SAndroid Build Coastguard Worker   return value ? GetStringValue(parser_, *value, label) : value.GetError();
148*d57664e9SAndroid Build Coastguard Worker }
149*d57664e9SAndroid Build Coastguard Worker 
GetAttributeStringValue(const std::string & name) const150*d57664e9SAndroid Build Coastguard Worker Result<std::string> XmlParser::Node::GetAttributeStringValue(const std::string& name) const {
151*d57664e9SAndroid Build Coastguard Worker   auto value = GetAttributeValue(name);
152*d57664e9SAndroid Build Coastguard Worker   return value ? GetStringValue(parser_, *value, name) : value.GetError();
153*d57664e9SAndroid Build Coastguard Worker }
154*d57664e9SAndroid Build Coastguard Worker 
XmlParser(std::unique_ptr<ResXMLTree> tree)155*d57664e9SAndroid Build Coastguard Worker XmlParser::XmlParser(std::unique_ptr<ResXMLTree> tree) : tree_(std::move(tree)) {
156*d57664e9SAndroid Build Coastguard Worker }
157*d57664e9SAndroid Build Coastguard Worker 
Create(const void * data,size_t size,bool copy_data)158*d57664e9SAndroid Build Coastguard Worker Result<XmlParser> XmlParser::Create(const void* data, size_t size, bool copy_data) {
159*d57664e9SAndroid Build Coastguard Worker   auto tree = std::make_unique<ResXMLTree>();
160*d57664e9SAndroid Build Coastguard Worker   if (tree->setTo(data, size, copy_data) != NO_ERROR) {
161*d57664e9SAndroid Build Coastguard Worker     return Error("Malformed xml block");
162*d57664e9SAndroid Build Coastguard Worker   }
163*d57664e9SAndroid Build Coastguard Worker 
164*d57664e9SAndroid Build Coastguard Worker   // Find the beginning of the first tag.
165*d57664e9SAndroid Build Coastguard Worker   XmlParser::Event event;
166*d57664e9SAndroid Build Coastguard Worker   while ((event = tree->next()) != XmlParser::Event::BAD_DOCUMENT &&
167*d57664e9SAndroid Build Coastguard Worker          event != XmlParser::Event::END_DOCUMENT && event != XmlParser::Event::START_TAG) {
168*d57664e9SAndroid Build Coastguard Worker   }
169*d57664e9SAndroid Build Coastguard Worker 
170*d57664e9SAndroid Build Coastguard Worker   if (event == XmlParser::Event::END_DOCUMENT) {
171*d57664e9SAndroid Build Coastguard Worker     return Error("Root tag was not be found");
172*d57664e9SAndroid Build Coastguard Worker   }
173*d57664e9SAndroid Build Coastguard Worker 
174*d57664e9SAndroid Build Coastguard Worker   if (event == XmlParser::Event::BAD_DOCUMENT) {
175*d57664e9SAndroid Build Coastguard Worker     return Error("Bad xml document");
176*d57664e9SAndroid Build Coastguard Worker   }
177*d57664e9SAndroid Build Coastguard Worker 
178*d57664e9SAndroid Build Coastguard Worker   return XmlParser{std::move(tree)};
179*d57664e9SAndroid Build Coastguard Worker }
180*d57664e9SAndroid Build Coastguard Worker 
181*d57664e9SAndroid Build Coastguard Worker }  // namespace android::idmap2
182