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