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   //
127   // Remaining methods are for retrieving information about attributes
128   // associated with a StartElement.
129   //
130   // Attributes must be in sorted order (according to the less than operator
131   // of struct Attribute).
132   //
133 
134   struct Attribute {
135     std::string namespace_uri;
136     std::string name;
137     std::string value;
138 
139     int compare(const Attribute& rhs) const;
140     bool operator<(const Attribute& rhs) const;
141     bool operator==(const Attribute& rhs) const;
142     bool operator!=(const Attribute& rhs) const;
143   };
144 
145   using const_iterator = std::vector<Attribute>::const_iterator;
146 
147   const_iterator begin_attributes() const;
148   const_iterator end_attributes() const;
149   size_t attribute_count() const;
150   const_iterator FindAttribute(android::StringPiece namespace_uri, android::StringPiece name) const;
151 
152  private:
153   DISALLOW_COPY_AND_ASSIGN(XmlPullParser);
154 
155   static void XMLCALL StartNamespaceHandler(void* user_data, const char* prefix,
156                                             const char* uri);
157   static void XMLCALL StartElementHandler(void* user_data, const char* name,
158                                           const char** attrs);
159   static void XMLCALL CharacterDataHandler(void* user_data, const char* s,
160                                            int len);
161   static void XMLCALL EndElementHandler(void* user_data, const char* name);
162   static void XMLCALL EndNamespaceHandler(void* user_data, const char* prefix);
163   static void XMLCALL CommentDataHandler(void* user_data, const char* comment);
164   static void XMLCALL StartCdataSectionHandler(void* user_data);
165   static void XMLCALL EndCdataSectionHandler(void* user_data);
166 
167   struct EventData {
168     Event event;
169     size_t line_number;
170     size_t depth;
171     std::string data1;
172     std::string data2;
173     std::vector<Attribute> attributes;
174   };
175 
176   io::InputStream* in_;
177   XML_Parser parser_;
178   std::queue<EventData> event_queue_;
179   std::string error_;
180   const std::string empty_;
181   size_t depth_;
182   std::stack<std::string> namespace_uris_;
183 
184   struct PackageDecl {
185     std::string prefix;
186     ExtractedPackage package;
187   };
188   std::vector<PackageDecl> package_aliases_;
189 };
190 
191 /**
192  * Finds the attribute in the current element within the global namespace.
193  */
194 Maybe<android::StringPiece> FindAttribute(const XmlPullParser* parser,
195                                           const 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 Maybe<android::StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
203                                                   const 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