Home | History | Annotate | Download | only in xml
      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 "Resource.h"
     21 #include "process/IResourceTableConsumer.h"
     22 #include "util/Maybe.h"
     23 #include "util/StringPiece.h"
     24 #include "xml/XmlUtil.h"
     25 
     26 #include <algorithm>
     27 #include <expat.h>
     28 #include <istream>
     29 #include <ostream>
     30 #include <queue>
     31 #include <stack>
     32 #include <string>
     33 #include <vector>
     34 
     35 namespace aapt {
     36 namespace xml {
     37 
     38 class XmlPullParser : public IPackageDeclStack {
     39 public:
     40     enum class Event {
     41         kBadDocument,
     42         kStartDocument,
     43         kEndDocument,
     44 
     45         kStartNamespace,
     46         kEndNamespace,
     47         kStartElement,
     48         kEndElement,
     49         kText,
     50         kComment,
     51     };
     52 
     53     /**
     54      * Skips to the next direct descendant node of the given startDepth,
     55      * skipping namespace nodes.
     56      *
     57      * When nextChildNode returns true, you can expect Comments, Text, and StartElement events.
     58      */
     59     static bool nextChildNode(XmlPullParser* parser, size_t startDepth);
     60     static bool skipCurrentElement(XmlPullParser* parser);
     61     static bool isGoodEvent(Event event);
     62 
     63     XmlPullParser(std::istream& in);
     64     ~XmlPullParser();
     65 
     66     /**
     67      * Returns the current event that is being processed.
     68      */
     69     Event getEvent() const;
     70 
     71     const std::string& getLastError() const;
     72 
     73     /**
     74      * Note, unlike XmlPullParser, the first call to next() will return
     75      * StartElement of the first element.
     76      */
     77     Event next();
     78 
     79     //
     80     // These are available for all nodes.
     81     //
     82 
     83     const std::u16string& getComment() const;
     84     size_t getLineNumber() const;
     85     size_t getDepth() const;
     86 
     87     /**
     88      * Returns the character data for a Text event.
     89      */
     90     const std::u16string& getText() const;
     91 
     92     //
     93     // Namespace prefix and URI are available for StartNamespace and EndNamespace.
     94     //
     95 
     96     const std::u16string& getNamespacePrefix() const;
     97     const std::u16string& getNamespaceUri() const;
     98 
     99     //
    100     // These are available for StartElement and EndElement.
    101     //
    102 
    103     const std::u16string& getElementNamespace() const;
    104     const std::u16string& getElementName() const;
    105 
    106     /*
    107      * Uses the current stack of namespaces to resolve the package. Eg:
    108      * xmlns:app = "http://schemas.android.com/apk/res/com.android.app"
    109      * ...
    110      * android:text="@app:string/message"
    111      *
    112      * In this case, 'app' will be converted to 'com.android.app'.
    113      *
    114      * If xmlns:app="http://schemas.android.com/apk/res-auto", then
    115      * 'package' will be set to 'defaultPackage'.
    116      */
    117     Maybe<ExtractedPackage> transformPackageAlias(
    118             const StringPiece16& alias, const StringPiece16& localPackage) const override;
    119 
    120     //
    121     // Remaining methods are for retrieving information about attributes
    122     // associated with a StartElement.
    123     //
    124     // Attributes must be in sorted order (according to the less than operator
    125     // of struct Attribute).
    126     //
    127 
    128     struct Attribute {
    129         std::u16string namespaceUri;
    130         std::u16string name;
    131         std::u16string value;
    132 
    133         int compare(const Attribute& rhs) const;
    134         bool operator<(const Attribute& rhs) const;
    135         bool operator==(const Attribute& rhs) const;
    136         bool operator!=(const Attribute& rhs) const;
    137     };
    138 
    139     using const_iterator = std::vector<Attribute>::const_iterator;
    140 
    141     const_iterator beginAttributes() const;
    142     const_iterator endAttributes() const;
    143     size_t getAttributeCount() const;
    144     const_iterator findAttribute(StringPiece16 namespaceUri, StringPiece16 name) const;
    145 
    146 private:
    147     static void XMLCALL startNamespaceHandler(void* userData, const char* prefix, const char* uri);
    148     static void XMLCALL startElementHandler(void* userData, const char* name, const char** attrs);
    149     static void XMLCALL characterDataHandler(void* userData, const char* s, int len);
    150     static void XMLCALL endElementHandler(void* userData, const char* name);
    151     static void XMLCALL endNamespaceHandler(void* userData, const char* prefix);
    152     static void XMLCALL commentDataHandler(void* userData, const char* comment);
    153 
    154     struct EventData {
    155         Event event;
    156         size_t lineNumber;
    157         size_t depth;
    158         std::u16string data1;
    159         std::u16string data2;
    160         std::vector<Attribute> attributes;
    161     };
    162 
    163     std::istream& mIn;
    164     XML_Parser mParser;
    165     char mBuffer[16384];
    166     std::queue<EventData> mEventQueue;
    167     std::string mLastError;
    168     const std::u16string mEmpty;
    169     size_t mDepth;
    170     std::stack<std::u16string> mNamespaceUris;
    171 
    172     struct PackageDecl {
    173         std::u16string prefix;
    174         ExtractedPackage package;
    175     };
    176     std::vector<PackageDecl> mPackageAliases;
    177 };
    178 
    179 /**
    180  * Finds the attribute in the current element within the global namespace.
    181  */
    182 Maybe<StringPiece16> findAttribute(const XmlPullParser* parser, const StringPiece16& name);
    183 
    184 /**
    185  * Finds the attribute in the current element within the global namespace. The attribute's value
    186  * must not be the empty string.
    187  */
    188 Maybe<StringPiece16> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece16& name);
    189 
    190 //
    191 // Implementation
    192 //
    193 
    194 inline ::std::ostream& operator<<(::std::ostream& out, XmlPullParser::Event event) {
    195     switch (event) {
    196         case XmlPullParser::Event::kBadDocument: return out << "BadDocument";
    197         case XmlPullParser::Event::kStartDocument: return out << "StartDocument";
    198         case XmlPullParser::Event::kEndDocument: return out << "EndDocument";
    199         case XmlPullParser::Event::kStartNamespace: return out << "StartNamespace";
    200         case XmlPullParser::Event::kEndNamespace: return out << "EndNamespace";
    201         case XmlPullParser::Event::kStartElement: return out << "StartElement";
    202         case XmlPullParser::Event::kEndElement: return out << "EndElement";
    203         case XmlPullParser::Event::kText: return out << "Text";
    204         case XmlPullParser::Event::kComment: return out << "Comment";
    205     }
    206     return out;
    207 }
    208 
    209 inline bool XmlPullParser::nextChildNode(XmlPullParser* parser, size_t startDepth) {
    210     Event event;
    211 
    212     // First get back to the start depth.
    213     while (isGoodEvent(event = parser->next()) && parser->getDepth() > startDepth + 1) {}
    214 
    215     // Now look for the first good node.
    216     while ((event != Event::kEndElement || parser->getDepth() > startDepth) && isGoodEvent(event)) {
    217         switch (event) {
    218         case Event::kText:
    219         case Event::kComment:
    220         case Event::kStartElement:
    221             return true;
    222         default:
    223             break;
    224         }
    225         event = parser->next();
    226     }
    227     return false;
    228 }
    229 
    230 inline bool XmlPullParser::skipCurrentElement(XmlPullParser* parser) {
    231     int depth = 1;
    232     while (depth > 0) {
    233         switch (parser->next()) {
    234             case Event::kEndDocument:
    235                 return true;
    236             case Event::kBadDocument:
    237                 return false;
    238             case Event::kStartElement:
    239                 depth++;
    240                 break;
    241             case Event::kEndElement:
    242                 depth--;
    243                 break;
    244             default:
    245                 break;
    246         }
    247     }
    248     return true;
    249 }
    250 
    251 inline bool XmlPullParser::isGoodEvent(XmlPullParser::Event event) {
    252     return event != Event::kBadDocument && event != Event::kEndDocument;
    253 }
    254 
    255 inline int XmlPullParser::Attribute::compare(const Attribute& rhs) const {
    256     int cmp = namespaceUri.compare(rhs.namespaceUri);
    257     if (cmp != 0) return cmp;
    258     return name.compare(rhs.name);
    259 }
    260 
    261 inline bool XmlPullParser::Attribute::operator<(const Attribute& rhs) const {
    262     return compare(rhs) < 0;
    263 }
    264 
    265 inline bool XmlPullParser::Attribute::operator==(const Attribute& rhs) const {
    266     return compare(rhs) == 0;
    267 }
    268 
    269 inline bool XmlPullParser::Attribute::operator!=(const Attribute& rhs) const {
    270     return compare(rhs) != 0;
    271 }
    272 
    273 inline XmlPullParser::const_iterator XmlPullParser::findAttribute(StringPiece16 namespaceUri,
    274                                                                   StringPiece16 name) const {
    275     const auto endIter = endAttributes();
    276     const auto iter = std::lower_bound(beginAttributes(), endIter,
    277             std::pair<StringPiece16, StringPiece16>(namespaceUri, name),
    278             [](const Attribute& attr, const std::pair<StringPiece16, StringPiece16>& rhs) -> bool {
    279                 int cmp = attr.namespaceUri.compare(0, attr.namespaceUri.size(),
    280                         rhs.first.data(), rhs.first.size());
    281                 if (cmp < 0) return true;
    282                 if (cmp > 0) return false;
    283                 cmp = attr.name.compare(0, attr.name.size(), rhs.second.data(), rhs.second.size());
    284                 if (cmp < 0) return true;
    285                 return false;
    286             }
    287     );
    288 
    289     if (iter != endIter && namespaceUri == iter->namespaceUri && name == iter->name) {
    290         return iter;
    291     }
    292     return endIter;
    293 }
    294 
    295 } // namespace xml
    296 } // namespace aapt
    297 
    298 #endif // AAPT_XML_PULL_PARSER_H
    299