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