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