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