AAPT2: Separate out the various steps
An early refactor. Some ideas became clearer as
development continued. Now the various phases are much
clearer and more easily reusable.
Also added a ton of tests!
Change-Id: Ic8f0a70c8222370352e63533b329c40457c0903e
diff --git a/tools/aapt2/XmlPullParser.h b/tools/aapt2/XmlPullParser.h
index accfd30a..f7d7a03 100644
--- a/tools/aapt2/XmlPullParser.h
+++ b/tools/aapt2/XmlPullParser.h
@@ -17,16 +17,24 @@
#ifndef AAPT_XML_PULL_PARSER_H
#define AAPT_XML_PULL_PARSER_H
+#include "util/Maybe.h"
+#include "Resource.h"
+#include "util/StringPiece.h"
+
+#include "process/IResourceTableConsumer.h"
+
#include <algorithm>
+#include <expat.h>
+#include <istream>
#include <ostream>
+#include <queue>
+#include <stack>
#include <string>
#include <vector>
-#include "StringPiece.h"
-
namespace aapt {
-class XmlPullParser {
+class XmlPullParser : public IPackageDeclStack {
public:
enum class Event {
kBadDocument,
@@ -41,43 +49,51 @@
kComment,
};
- static void skipCurrentElement(XmlPullParser* parser);
+ /**
+ * Skips to the next direct descendant node of the given startDepth,
+ * skipping namespace nodes.
+ *
+ * When nextChildNode returns true, you can expect Comments, Text, and StartElement events.
+ */
+ static bool nextChildNode(XmlPullParser* parser, size_t startDepth);
+ static bool skipCurrentElement(XmlPullParser* parser);
static bool isGoodEvent(Event event);
- virtual ~XmlPullParser() {}
+ XmlPullParser(std::istream& in);
+ virtual ~XmlPullParser();
/**
* Returns the current event that is being processed.
*/
- virtual Event getEvent() const = 0;
+ Event getEvent() const;
- virtual const std::string& getLastError() const = 0;
+ const std::string& getLastError() const;
/**
* Note, unlike XmlPullParser, the first call to next() will return
* StartElement of the first element.
*/
- virtual Event next() = 0;
+ Event next();
//
// These are available for all nodes.
//
- virtual const std::u16string& getComment() const = 0;
- virtual size_t getLineNumber() const = 0;
- virtual size_t getDepth() const = 0;
+ const std::u16string& getComment() const;
+ size_t getLineNumber() const;
+ size_t getDepth() const;
/**
* Returns the character data for a Text event.
*/
- virtual const std::u16string& getText() const = 0;
+ const std::u16string& getText() const;
//
// Namespace prefix and URI are available for StartNamespace and EndNamespace.
//
- virtual const std::u16string& getNamespacePrefix() const = 0;
- virtual const std::u16string& getNamespaceUri() const = 0;
+ const std::u16string& getNamespacePrefix() const;
+ const std::u16string& getNamespaceUri() const;
/*
* Uses the current stack of namespaces to resolve the package. Eg:
@@ -90,15 +106,17 @@
* If xmlns:app="http://schemas.android.com/apk/res-auto", then
* 'package' will be set to 'defaultPackage'.
*/
- virtual bool applyPackageAlias(std::u16string* package,
- const std::u16string& defaultPackage) const = 0;
+ //
//
// These are available for StartElement and EndElement.
//
- virtual const std::u16string& getElementNamespace() const = 0;
- virtual const std::u16string& getElementName() const = 0;
+ const std::u16string& getElementNamespace() const;
+ const std::u16string& getElementName() const;
+
+ Maybe<ResourceName> transformPackage(const ResourceName& name,
+ const StringPiece16& localPackage) const override;
//
// Remaining methods are for retrieving information about attributes
@@ -121,10 +139,38 @@
using const_iterator = std::vector<Attribute>::const_iterator;
- virtual const_iterator beginAttributes() const = 0;
- virtual const_iterator endAttributes() const = 0;
- virtual size_t getAttributeCount() const = 0;
+ const_iterator beginAttributes() const;
+ const_iterator endAttributes() const;
+ size_t getAttributeCount() const;
const_iterator findAttribute(StringPiece16 namespaceUri, StringPiece16 name) const;
+
+private:
+ static void XMLCALL startNamespaceHandler(void* userData, const char* prefix, const char* uri);
+ static void XMLCALL startElementHandler(void* userData, const char* name, const char** attrs);
+ static void XMLCALL characterDataHandler(void* userData, const char* s, int len);
+ static void XMLCALL endElementHandler(void* userData, const char* name);
+ static void XMLCALL endNamespaceHandler(void* userData, const char* prefix);
+ static void XMLCALL commentDataHandler(void* userData, const char* comment);
+
+ struct EventData {
+ Event event;
+ size_t lineNumber;
+ size_t depth;
+ std::u16string data1;
+ std::u16string data2;
+ std::u16string comment;
+ std::vector<Attribute> attributes;
+ };
+
+ std::istream& mIn;
+ XML_Parser mParser;
+ char mBuffer[16384];
+ std::queue<EventData> mEventQueue;
+ std::string mLastError;
+ const std::u16string mEmpty;
+ size_t mDepth;
+ std::stack<std::u16string> mNamespaceUris;
+ std::vector<std::pair<std::u16string, std::u16string>> mPackageAliases;
};
//
@@ -146,13 +192,35 @@
return out;
}
-inline void XmlPullParser::skipCurrentElement(XmlPullParser* parser) {
+inline bool XmlPullParser::nextChildNode(XmlPullParser* parser, size_t startDepth) {
+ Event event;
+
+ // First get back to the start depth.
+ while (isGoodEvent(event = parser->next()) && parser->getDepth() > startDepth + 1) {}
+
+ // Now look for the first good node.
+ while ((event != Event::kEndElement || parser->getDepth() > startDepth) && isGoodEvent(event)) {
+ switch (event) {
+ case Event::kText:
+ case Event::kComment:
+ case Event::kStartElement:
+ return true;
+ default:
+ break;
+ }
+ event = parser->next();
+ }
+ return false;
+}
+
+inline bool XmlPullParser::skipCurrentElement(XmlPullParser* parser) {
int depth = 1;
while (depth > 0) {
switch (parser->next()) {
case Event::kEndDocument:
+ return true;
case Event::kBadDocument:
- return;
+ return false;
case Event::kStartElement:
depth++;
break;
@@ -163,6 +231,7 @@
break;
}
}
+ return true;
}
inline bool XmlPullParser::isGoodEvent(XmlPullParser::Event event) {