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 #ifndef IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
18 #define IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
19 
20 #include <memory>
21 #include <string>
22 
23 #include "ResourceUtils.h"
24 #include "Result.h"
25 #include "android-base/macros.h"
26 #include "androidfw/ResourceTypes.h"
27 #include "utils/String16.h"
28 
29 namespace android::idmap2 {
30 
31 struct XmlParser {
32   using Event = ResXMLParser::event_code_t;
33   class iterator;
34 
35   class Node {
36    public:
37     Event event() const;
38     std::string name() const;
39 
40     Result<Res_value> GetAttributeValue(const std::string& name) const;
41     Result<Res_value> GetAttributeValue(ResourceId attr, const std::string& label) const;
42 
43     Result<std::string> GetAttributeStringValue(const std::string& name) const;
44     Result<std::string> GetAttributeStringValue(ResourceId attr, const std::string& label) const;
45 
46     bool operator==(const Node& rhs) const;
47     bool operator!=(const Node& rhs) const;
48 
49    private:
50     explicit Node(const ResXMLTree& tree);
51     Node(const ResXMLTree& tree, const ResXMLParser::ResXMLPosition& pos);
52 
53     // Retrieves/Sets the position of the position of the xml parser in the xml tree.
54     ResXMLParser::ResXMLPosition get_position() const;
55     void set_position(const ResXMLParser::ResXMLPosition& pos);
56 
57     // If `inner_child` is true, seek advances the parser to the first inner child of the current
58     // node. Otherwise, seek advances the parser to the following node. Returns false if there is
59     // no node to seek to.
60     bool Seek(bool inner_child);
61 
62     ResXMLParser parser_;
63     friend iterator;
64   };
65 
66   class iterator {
67    public:
iteratorXmlParser68     iterator(const iterator& other) : iterator(other.tree_, other.iter_) {
69     }
70 
71     inline iterator& operator=(const iterator& rhs) {
72       iter_.set_position(rhs.iter_.get_position());
73       return *this;
74     }
75 
76     inline bool operator==(const iterator& rhs) const {
77       return iter_ == rhs.iter_;
78     }
79 
80     inline bool operator!=(const iterator& rhs) const {
81       return !(*this == rhs);
82     }
83 
84     inline iterator operator++() {
85       // Seek to the following xml node.
86       iter_.Seek(false /* inner_child */);
87       return *this;
88     }
89 
beginXmlParser90     iterator begin() const {
91       iterator child_it(*this);
92       // Seek to the first inner child of the current node.
93       child_it.iter_.Seek(true /* inner_child */);
94       return child_it;
95     }
96 
endXmlParser97     iterator end() const {
98       iterator child_it = begin();
99       while (child_it.iter_.Seek(false /* inner_child */)) {
100         // Continue iterating until the end tag is found.
101       }
102 
103       return child_it;
104     }
105 
106     inline const Node operator*() {
107       return Node(tree_, iter_.get_position());
108     }
109 
110     inline const Node* operator->() {
111       return &iter_;
112     }
113 
114    private:
iteratorXmlParser115     explicit iterator(const ResXMLTree& tree) : tree_(tree), iter_(Node(tree)) {
116     }
iteratorXmlParser117     iterator(const ResXMLTree& tree, const Node& node)
118         : tree_(tree), iter_(Node(tree, node.get_position())) {
119     }
120 
121     const ResXMLTree& tree_;
122     Node iter_;
123     friend XmlParser;
124   };
125 
126   // Creates a new xml parser beginning at the first tag.
127   static Result<XmlParser> Create(const void* data, size_t size, bool copy_data = false);
128 
tree_iteratorXmlParser129   inline iterator tree_iterator() const {
130     return iterator(*tree_);
131   }
132 
get_stringsXmlParser133   inline const ResStringPool& get_strings() const {
134     return tree_->getStrings();
135   }
136 
137  private:
138   explicit XmlParser(std::unique_ptr<ResXMLTree> tree);
139   mutable std::unique_ptr<ResXMLTree> tree_;
140 };
141 
142 }  // namespace android::idmap2
143 
144 #endif  // IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_
145