1 /*
2  * Copyright (C) 2015 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 AAPT_XML_PULL_PARSER_H
18 #define AAPT_XML_PULL_PARSER_H
19 
20 #include <expat.h>
21 
22 #include <algorithm>
23 #include <istream>
24 #include <ostream>
25 #include <queue>
26 #include <stack>
27 #include <string>
28 #include <vector>
29 
30 #include "Resource.h"
31 #include "android-base/macros.h"
32 #include "androidfw/Streams.h"
33 #include "androidfw/StringPiece.h"
34 #include "process/IResourceTableConsumer.h"
35 #include "xml/XmlUtil.h"
36 
37 namespace aapt {
38 namespace xml {
39 
40 class XmlPullParser : public IPackageDeclStack {
41  public:
42   enum class Event {
43     kBadDocument,
44     kStartDocument,
45     kEndDocument,
46 
47     kStartNamespace,
48     kEndNamespace,
49     kStartElement,
50     kEndElement,
51     kText,
52     kComment,
53     kCdataStart,
54     kCdataEnd,
55   };
56 
57   /**
58    * Skips to the next direct descendant node of the given start_depth,
59    * skipping namespace nodes.
60    *
61    * When NextChildNode() returns true, you can expect Comments, Text, and
62    * StartElement events.
63    */
64   static bool NextChildNode(XmlPullParser* parser, size_t start_depth);
65   static bool SkipCurrentElement(XmlPullParser* parser);
66   static bool IsGoodEvent(Event event);
67 
68   explicit XmlPullParser(android::InputStream* in);
69   ~XmlPullParser();
70 
71   /**
72    * Returns the current event that is being processed.
73    */
74   Event event() const;
75 
76   const std::string& error() const;
77 
78   /**
79    * Note, unlike XmlPullParser, the first call to next() will return
80    * StartElement of the first element.
81    */
82   Event Next();
83 
84   //
85   // These are available for all nodes.
86   //
87 
88   const std::string& comment() const;
89   size_t line_number() const;
90   size_t depth() const;
91 
92   /**
93    * Returns the character data for a Text event.
94    */
95   const std::string& text() const;
96 
97   //
98   // Namespace prefix and URI are available for StartNamespace and EndNamespace.
99   //
100 
101   const std::string& namespace_prefix() const;
102   const std::string& namespace_uri() const;
103 
104   //
105   // These are available for StartElement and EndElement.
106   //
107 
108   const std::string& element_namespace() const;
109   const std::string& element_name() const;
110 
111   /*
112    * Uses the current stack of namespaces to resolve the package. Eg:
113    * xmlns:app = "http://schemas.android.com/apk/res/com.android.app"
114    * ...
115    * android:text="@app:string/message"
116    *
117    * In this case, 'app' will be converted to 'com.android.app'.
118    *
119    * If xmlns:app="http://schemas.android.com/apk/res-auto", then
120    * 'package' will be set to 'defaultPackage'.
121    */
122   std::optional<ExtractedPackage> TransformPackageAlias(android::StringPiece alias) const override;
123 
124   struct PackageDecl {
125     std::string prefix;
126     ExtractedPackage package;
127   };
128 
129   const std::vector<PackageDecl>& package_decls() const;
130 
131   //
132   // Remaining methods are for retrieving information about attributes
133   // associated with a StartElement.
134   //
135   // Attributes must be in sorted order (according to the less than operator
136   // of struct Attribute).
137   //
138 
139   struct Attribute {
140     std::string namespace_uri;
141     std::string name;
142     std::string value;
143 
144     int compare(const Attribute& rhs) const;
145     bool operator<(const Attribute& rhs) const;
146     bool operator==(const Attribute& rhs) const;
147     bool operator!=(const Attribute& rhs) const;
148   };
149 
150   using const_iterator = std::vector<Attribute>::const_iterator;
151 
152   const_iterator begin_attributes() const;
153   const_iterator end_attributes() const;
154   size_t attribute_count() const;
155   const_iterator FindAttribute(android::StringPiece namespace_uri, android::StringPiece name) const;
156 
157  private:
158   DISALLOW_COPY_AND_ASSIGN(XmlPullParser);
159 
160   static void XMLCALL StartNamespaceHandler(void* user_data, const char* prefix,
161                                             const char* uri);
162   static void XMLCALL StartElementHandler(void* user_data, const char* name,
163                                           const char** attrs);
164   static void XMLCALL CharacterDataHandler(void* user_data, const char* s,
165                                            int len);
166   static void XMLCALL EndElementHandler(void* user_data, const char* name);
167   static void XMLCALL EndNamespaceHandler(void* user_data, const char* prefix);
168   static void XMLCALL CommentDataHandler(void* user_data, const char* comment);
169   static void XMLCALL StartCdataSectionHandler(void* user_data);
170   static void XMLCALL EndCdataSectionHandler(void* user_data);
171 
172   struct EventData {
173     Event event;
174     size_t line_number;
175     size_t depth;
176     std::string data1;
177     std::string data2;
178     std::vector<Attribute> attributes;
179   };
180 
181   android::InputStream* in_;
182   XML_Parser parser_;
183   std::queue<EventData> event_queue_;
184   std::string error_;
185   const std::string empty_;
186   size_t depth_;
187   std::stack<std::string> namespace_uris_;
188   std::vector<PackageDecl> package_aliases_;
189 };
190 
191 /**
192  * Finds the attribute in the current element within the global namespace.
193  */
194 std::optional<android::StringPiece> FindAttribute(const XmlPullParser* parser,
195                                                   android::StringPiece name);
196 
197 /**
198  * Finds the attribute in the current element within the global namespace. The
199  * attribute's value
200  * must not be the empty string.
201  */
202 std::optional<android::StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
203                                                           android::StringPiece name);
204 
205 //
206 // Implementation
207 //
208 
209 inline ::std::ostream& operator<<(::std::ostream& out,
210                                   XmlPullParser::Event event) {
211   switch (event) {
212     case XmlPullParser::Event::kBadDocument:
213       return out << "BadDocument";
214     case XmlPullParser::Event::kStartDocument:
215       return out << "StartDocument";
216     case XmlPullParser::Event::kEndDocument:
217       return out << "EndDocument";
218     case XmlPullParser::Event::kStartNamespace:
219       return out << "StartNamespace";
220     case XmlPullParser::Event::kEndNamespace:
221       return out << "EndNamespace";
222     case XmlPullParser::Event::kStartElement:
223       return out << "StartElement";
224     case XmlPullParser::Event::kEndElement:
225       return out << "EndElement";
226     case XmlPullParser::Event::kText:
227       return out << "Text";
228     case XmlPullParser::Event::kComment:
229       return out << "Comment";
230     case XmlPullParser::Event::kCdataStart:
231       return out << "CdataStart";
232     case XmlPullParser::Event::kCdataEnd:
233       return out << "CdataEnd";
234   }
235   return out;
236 }
237 
NextChildNode(XmlPullParser * parser,size_t start_depth)238 inline bool XmlPullParser::NextChildNode(XmlPullParser* parser, size_t start_depth) {
239   Event event;
240 
241   // First get back to the start depth.
242   while (IsGoodEvent(event = parser->Next()) && parser->depth() > start_depth + 1) {
243   }
244 
245   // Now look for the first good node.
246   while ((event != Event::kEndElement || parser->depth() > start_depth) && IsGoodEvent(event)) {
247     switch (event) {
248       case Event::kText:
249       case Event::kComment:
250       case Event::kStartElement:
251       case Event::kCdataStart:
252       case Event::kCdataEnd:
253         return true;
254       default:
255         break;
256     }
257     event = parser->Next();
258   }
259   return false;
260 }
261 
SkipCurrentElement(XmlPullParser * parser)262 inline bool XmlPullParser::SkipCurrentElement(XmlPullParser* parser) {
263   int depth = 1;
264   while (depth > 0) {
265     switch (parser->Next()) {
266       case Event::kEndDocument:
267         return true;
268       case Event::kBadDocument:
269         return false;
270       case Event::kStartElement:
271         depth++;
272         break;
273       case Event::kEndElement:
274         depth--;
275         break;
276       default:
277         break;
278     }
279   }
280   return true;
281 }
282 
IsGoodEvent(XmlPullParser::Event event)283 inline bool XmlPullParser::IsGoodEvent(XmlPullParser::Event event) {
284   return event != Event::kBadDocument && event != Event::kEndDocument;
285 }
286 
compare(const Attribute & rhs)287 inline int XmlPullParser::Attribute::compare(const Attribute& rhs) const {
288   int cmp = namespace_uri.compare(rhs.namespace_uri);
289   if (cmp != 0) return cmp;
290   return name.compare(rhs.name);
291 }
292 
293 inline bool XmlPullParser::Attribute::operator<(const Attribute& rhs) const {
294   return compare(rhs) < 0;
295 }
296 
297 inline bool XmlPullParser::Attribute::operator==(const Attribute& rhs) const {
298   return compare(rhs) == 0;
299 }
300 
301 inline bool XmlPullParser::Attribute::operator!=(const Attribute& rhs) const {
302   return compare(rhs) != 0;
303 }
304 
FindAttribute(android::StringPiece namespace_uri,android::StringPiece name)305 inline XmlPullParser::const_iterator XmlPullParser::FindAttribute(
306     android::StringPiece namespace_uri, android::StringPiece name) const {
307   const auto end_iter = end_attributes();
308   const auto iter = std::lower_bound(
309       begin_attributes(), end_iter,
310       std::pair<android::StringPiece, android::StringPiece>(namespace_uri, name),
311       [](const Attribute& attr,
312          const std::pair<android::StringPiece, android::StringPiece>& rhs) -> bool {
313         int cmp = attr.namespace_uri.compare(
314             0, attr.namespace_uri.size(), rhs.first.data(), rhs.first.size());
315         if (cmp < 0) return true;
316         if (cmp > 0) return false;
317         cmp = attr.name.compare(0, attr.name.size(), rhs.second.data(),
318                                 rhs.second.size());
319         if (cmp < 0) return true;
320         return false;
321       });
322 
323   if (iter != end_iter && namespace_uri == iter->namespace_uri &&
324       name == iter->name) {
325     return iter;
326   }
327   return end_iter;
328 }
329 
330 }  // namespace xml
331 }  // namespace aapt
332 
333 #endif  // AAPT_XML_PULL_PARSER_H
334