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