AAPT2: Fail compiling when private symbols are referenced

Also moved some XML specific stuff into its own directory,
and refactored ReferenceLinker a bit.

Change-Id: I912247a82023c1bbf72dc191fbdaf62858cbec0c
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
new file mode 100644
index 0000000..d27b62fd
--- /dev/null
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "XmlDom.h"
+#include "XmlPullParser.h"
+#include "util/Util.h"
+
+#include <cassert>
+#include <expat.h>
+#include <memory>
+#include <stack>
+#include <string>
+#include <tuple>
+
+namespace aapt {
+namespace xml {
+
+constexpr char kXmlNamespaceSep = 1;
+
+struct Stack {
+    std::unique_ptr<xml::Node> root;
+    std::stack<xml::Node*> nodeStack;
+    std::u16string pendingComment;
+};
+
+/**
+ * Extracts the namespace and name of an expanded element or attribute name.
+ */
+static void splitName(const char* name, std::u16string* outNs, std::u16string* outName) {
+    const char* p = name;
+    while (*p != 0 && *p != kXmlNamespaceSep) {
+        p++;
+    }
+
+    if (*p == 0) {
+        outNs->clear();
+        *outName = util::utf8ToUtf16(name);
+    } else {
+        *outNs = util::utf8ToUtf16(StringPiece(name, (p - name)));
+        *outName = util::utf8ToUtf16(p + 1);
+    }
+}
+
+static void addToStack(Stack* stack, XML_Parser parser, std::unique_ptr<Node> node) {
+    node->lineNumber = XML_GetCurrentLineNumber(parser);
+    node->columnNumber = XML_GetCurrentColumnNumber(parser);
+
+    Node* thisNode = node.get();
+    if (!stack->nodeStack.empty()) {
+        stack->nodeStack.top()->addChild(std::move(node));
+    } else {
+        stack->root = std::move(node);
+    }
+
+    if (!nodeCast<Text>(thisNode)) {
+        stack->nodeStack.push(thisNode);
+    }
+}
+
+static void XMLCALL startNamespaceHandler(void* userData, const char* prefix, const char* uri) {
+    XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
+    Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+
+    std::unique_ptr<Namespace> ns = util::make_unique<Namespace>();
+    if (prefix) {
+        ns->namespacePrefix = util::utf8ToUtf16(prefix);
+    }
+
+    if (uri) {
+        ns->namespaceUri = util::utf8ToUtf16(uri);
+    }
+
+    addToStack(stack, parser, std::move(ns));
+}
+
+static void XMLCALL endNamespaceHandler(void* userData, const char* prefix) {
+    XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
+    Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+
+    assert(!stack->nodeStack.empty());
+    stack->nodeStack.pop();
+}
+
+static bool lessAttribute(const Attribute& lhs, const Attribute& rhs) {
+    return std::tie(lhs.namespaceUri, lhs.name, lhs.value) <
+            std::tie(rhs.namespaceUri, rhs.name, rhs.value);
+}
+
+static void XMLCALL startElementHandler(void* userData, const char* name, const char** attrs) {
+    XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
+    Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+
+    std::unique_ptr<Element> el = util::make_unique<Element>();
+    splitName(name, &el->namespaceUri, &el->name);
+
+    while (*attrs) {
+        Attribute attribute;
+        splitName(*attrs++, &attribute.namespaceUri, &attribute.name);
+        attribute.value = util::utf8ToUtf16(*attrs++);
+
+        // Insert in sorted order.
+        auto iter = std::lower_bound(el->attributes.begin(), el->attributes.end(), attribute,
+                                     lessAttribute);
+        el->attributes.insert(iter, std::move(attribute));
+    }
+
+    el->comment = std::move(stack->pendingComment);
+    addToStack(stack, parser, std::move(el));
+}
+
+static void XMLCALL endElementHandler(void* userData, const char* name) {
+    XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
+    Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+
+    assert(!stack->nodeStack.empty());
+    //stack->nodeStack.top()->comment = std::move(stack->pendingComment);
+    stack->nodeStack.pop();
+}
+
+static void XMLCALL characterDataHandler(void* userData, const char* s, int len) {
+    XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
+    Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+
+    if (!s || len <= 0) {
+        return;
+    }
+
+    // See if we can just append the text to a previous text node.
+    if (!stack->nodeStack.empty()) {
+        Node* currentParent = stack->nodeStack.top();
+        if (!currentParent->children.empty()) {
+            Node* lastChild = currentParent->children.back().get();
+            if (Text* text = nodeCast<Text>(lastChild)) {
+                text->text += util::utf8ToUtf16(StringPiece(s, len));
+                return;
+            }
+        }
+    }
+
+    std::unique_ptr<Text> text = util::make_unique<Text>();
+    text->text = util::utf8ToUtf16(StringPiece(s, len));
+    addToStack(stack, parser, std::move(text));
+}
+
+static void XMLCALL commentDataHandler(void* userData, const char* comment) {
+    XML_Parser parser = reinterpret_cast<XML_Parser>(userData);
+    Stack* stack = reinterpret_cast<Stack*>(XML_GetUserData(parser));
+
+    if (!stack->pendingComment.empty()) {
+        stack->pendingComment += '\n';
+    }
+    stack->pendingComment += util::utf8ToUtf16(comment);
+}
+
+std::unique_ptr<XmlResource> inflate(std::istream* in, IDiagnostics* diag, const Source& source) {
+    Stack stack;
+
+    XML_Parser parser = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
+    XML_SetUserData(parser, &stack);
+    XML_UseParserAsHandlerArg(parser);
+    XML_SetElementHandler(parser, startElementHandler, endElementHandler);
+    XML_SetNamespaceDeclHandler(parser, startNamespaceHandler, endNamespaceHandler);
+    XML_SetCharacterDataHandler(parser, characterDataHandler);
+    XML_SetCommentHandler(parser, commentDataHandler);
+
+    char buffer[1024];
+    while (!in->eof()) {
+        in->read(buffer, sizeof(buffer) / sizeof(buffer[0]));
+        if (in->bad() && !in->eof()) {
+            stack.root = {};
+            diag->error(DiagMessage(source) << strerror(errno));
+            break;
+        }
+
+        if (XML_Parse(parser, buffer, in->gcount(), in->eof()) == XML_STATUS_ERROR) {
+            stack.root = {};
+            diag->error(DiagMessage(source.withLine(XML_GetCurrentLineNumber(parser)))
+                        << XML_ErrorString(XML_GetErrorCode(parser)));
+            break;
+        }
+    }
+
+    XML_ParserFree(parser);
+    if (stack.root) {
+        return util::make_unique<XmlResource>(ResourceFile{ {}, {}, source }, std::move(stack.root));
+    }
+    return {};
+}
+
+static void copyAttributes(Element* el, android::ResXMLParser* parser) {
+    const size_t attrCount = parser->getAttributeCount();
+    if (attrCount > 0) {
+        el->attributes.reserve(attrCount);
+        for (size_t i = 0; i < attrCount; i++) {
+            Attribute attr;
+            size_t len;
+            const char16_t* str16 = parser->getAttributeNamespace(i, &len);
+            if (str16) {
+                attr.namespaceUri.assign(str16, len);
+            }
+
+            str16 = parser->getAttributeName(i, &len);
+            if (str16) {
+                attr.name.assign(str16, len);
+            }
+
+            str16 = parser->getAttributeStringValue(i, &len);
+            if (str16) {
+                attr.value.assign(str16, len);
+            }
+            el->attributes.push_back(std::move(attr));
+        }
+    }
+}
+
+std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen, IDiagnostics* diag,
+                                     const Source& source) {
+    std::unique_ptr<Node> root;
+    std::stack<Node*> nodeStack;
+
+    android::ResXMLTree tree;
+    if (tree.setTo(data, dataLen) != android::NO_ERROR) {
+        return {};
+    }
+
+    android::ResXMLParser::event_code_t code;
+    while ((code = tree.next()) != android::ResXMLParser::BAD_DOCUMENT &&
+            code != android::ResXMLParser::END_DOCUMENT) {
+        std::unique_ptr<Node> newNode;
+        switch (code) {
+            case android::ResXMLParser::START_NAMESPACE: {
+                std::unique_ptr<Namespace> node = util::make_unique<Namespace>();
+                size_t len;
+                const char16_t* str16 = tree.getNamespacePrefix(&len);
+                if (str16) {
+                    node->namespacePrefix.assign(str16, len);
+                }
+
+                str16 = tree.getNamespaceUri(&len);
+                if (str16) {
+                    node->namespaceUri.assign(str16, len);
+                }
+                newNode = std::move(node);
+                break;
+            }
+
+            case android::ResXMLParser::START_TAG: {
+                std::unique_ptr<Element> node = util::make_unique<Element>();
+                size_t len;
+                const char16_t* str16 = tree.getElementNamespace(&len);
+                if (str16) {
+                    node->namespaceUri.assign(str16, len);
+                }
+
+                str16 = tree.getElementName(&len);
+                if (str16) {
+                    node->name.assign(str16, len);
+                }
+
+                copyAttributes(node.get(), &tree);
+
+                newNode = std::move(node);
+                break;
+            }
+
+            case android::ResXMLParser::TEXT: {
+                std::unique_ptr<Text> node = util::make_unique<Text>();
+                size_t len;
+                const char16_t* str16 = tree.getText(&len);
+                if (str16) {
+                    node->text.assign(str16, len);
+                }
+                newNode = std::move(node);
+                break;
+            }
+
+            case android::ResXMLParser::END_NAMESPACE:
+            case android::ResXMLParser::END_TAG:
+                assert(!nodeStack.empty());
+                nodeStack.pop();
+                break;
+
+            default:
+                assert(false);
+                break;
+        }
+
+        if (newNode) {
+            newNode->lineNumber = tree.getLineNumber();
+
+            Node* thisNode = newNode.get();
+            if (!root) {
+                assert(nodeStack.empty());
+                root = std::move(newNode);
+            } else {
+                assert(!nodeStack.empty());
+                nodeStack.top()->addChild(std::move(newNode));
+            }
+
+            if (!nodeCast<Text>(thisNode)) {
+                nodeStack.push(thisNode);
+            }
+        }
+    }
+    return util::make_unique<XmlResource>(ResourceFile{}, std::move(root));
+}
+
+Element* findRootElement(XmlResource* doc) {
+    return findRootElement(doc->root.get());
+}
+
+Element* findRootElement(Node* node) {
+    if (!node) {
+        return nullptr;
+    }
+
+    Element* el = nullptr;
+    while ((el = nodeCast<Element>(node)) == nullptr) {
+        if (node->children.empty()) {
+            return nullptr;
+        }
+        // We are looking for the first element, and namespaces can only have one child.
+        node = node->children.front().get();
+    }
+    return el;
+}
+
+void Node::addChild(std::unique_ptr<Node> child) {
+    child->parent = this;
+    children.push_back(std::move(child));
+}
+
+Attribute* Element::findAttribute(const StringPiece16& ns, const StringPiece16& name) {
+    for (auto& attr : attributes) {
+        if (ns == attr.namespaceUri && name == attr.name) {
+            return &attr;
+        }
+    }
+    return nullptr;
+}
+
+Element* Element::findChild(const StringPiece16& ns, const StringPiece16& name) {
+    return findChildWithAttribute(ns, name, {}, {}, {});
+}
+
+Element* Element::findChildWithAttribute(const StringPiece16& ns, const StringPiece16& name,
+                                         const StringPiece16& attrNs, const StringPiece16& attrName,
+                                         const StringPiece16& attrValue) {
+    for (auto& childNode : children) {
+        Node* child = childNode.get();
+        while (nodeCast<Namespace>(child)) {
+            if (child->children.empty()) {
+                break;
+            }
+            child = child->children[0].get();
+        }
+
+        if (Element* el = nodeCast<Element>(child)) {
+            if (ns == el->namespaceUri && name == el->name) {
+                if (attrNs.empty() && attrName.empty()) {
+                    return el;
+                }
+
+                Attribute* attr = el->findAttribute(attrNs, attrName);
+                if (attr && attrValue == attr->value) {
+                    return el;
+                }
+            }
+        }
+    }
+    return nullptr;
+}
+
+std::vector<Element*> Element::getChildElements() {
+    std::vector<Element*> elements;
+    for (auto& childNode : children) {
+        Node* child = childNode.get();
+        while (nodeCast<Namespace>(child)) {
+            if (child->children.empty()) {
+                break;
+            }
+            child = child->children[0].get();
+        }
+
+        if (Element* el = nodeCast<Element>(child)) {
+            elements.push_back(el);
+        }
+    }
+    return elements;
+}
+
+void PackageAwareVisitor::visit(Namespace* ns) {
+   bool added = false;
+   if (Maybe<ExtractedPackage> maybePackage = extractPackageFromNamespace(ns->namespaceUri)) {
+       ExtractedPackage& package = maybePackage.value();
+       mPackageDecls.push_back(PackageDecl{ ns->namespacePrefix, std::move(package) });
+       added = true;
+   }
+
+   Visitor::visit(ns);
+
+   if (added) {
+       mPackageDecls.pop_back();
+   }
+}
+
+Maybe<ExtractedPackage> PackageAwareVisitor::transformPackageAlias(
+       const StringPiece16& alias, const StringPiece16& localPackage) const {
+   if (alias.empty()) {
+       return ExtractedPackage{ localPackage.toString(), false /* private */ };
+   }
+
+   const auto rend = mPackageDecls.rend();
+   for (auto iter = mPackageDecls.rbegin(); iter != rend; ++iter) {
+       if (alias == iter->prefix) {
+           if (iter->package.package.empty()) {
+               return ExtractedPackage{ localPackage.toString(),
+                                              iter->package.privateNamespace };
+           }
+           return iter->package;
+       }
+   }
+   return {};
+}
+
+} // namespace xml
+} // namespace aapt
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
new file mode 100644
index 0000000..033b0a4d
--- /dev/null
+++ b/tools/aapt2/xml/XmlDom.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_XML_DOM_H
+#define AAPT_XML_DOM_H
+
+#include "Diagnostics.h"
+#include "Resource.h"
+#include "ResourceValues.h"
+#include "util/StringPiece.h"
+#include "util/Util.h"
+#include "xml/XmlUtil.h"
+
+#include <istream>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace aapt {
+namespace xml {
+
+struct RawVisitor;
+
+/**
+ * Base class for all XML nodes.
+ */
+struct Node {
+    Node* parent = nullptr;
+    size_t lineNumber = 0;
+    size_t columnNumber = 0;
+    std::u16string comment;
+    std::vector<std::unique_ptr<Node>> children;
+
+    virtual ~Node() = default;
+
+    void addChild(std::unique_ptr<Node> child);
+    virtual void accept(RawVisitor* visitor) = 0;
+};
+
+/**
+ * Base class that implements the visitor methods for a
+ * subclass of Node.
+ */
+template <typename Derived>
+struct BaseNode : public Node {
+    virtual void accept(RawVisitor* visitor) override;
+};
+
+/**
+ * A Namespace XML node. Can only have one child.
+ */
+struct Namespace : public BaseNode<Namespace> {
+    std::u16string namespacePrefix;
+    std::u16string namespaceUri;
+};
+
+struct AaptAttribute {
+    ResourceId id;
+    aapt::Attribute attribute;
+};
+
+/**
+ * An XML attribute.
+ */
+struct Attribute {
+    std::u16string namespaceUri;
+    std::u16string name;
+    std::u16string value;
+
+    Maybe<AaptAttribute> compiledAttribute;
+    std::unique_ptr<Item> compiledValue;
+};
+
+/**
+ * An Element XML node.
+ */
+struct Element : public BaseNode<Element> {
+    std::u16string namespaceUri;
+    std::u16string name;
+    std::vector<Attribute> attributes;
+
+    Attribute* findAttribute(const StringPiece16& ns, const StringPiece16& name);
+    xml::Element* findChild(const StringPiece16& ns, const StringPiece16& name);
+    xml::Element* findChildWithAttribute(const StringPiece16& ns, const StringPiece16& name,
+                                         const StringPiece16& attrNs,
+                                         const StringPiece16& attrName,
+                                         const StringPiece16& attrValue);
+    std::vector<xml::Element*> getChildElements();
+};
+
+/**
+ * A Text (CDATA) XML node. Can not have any children.
+ */
+struct Text : public BaseNode<Text> {
+    std::u16string text;
+};
+
+/**
+ * An XML resource with a source, name, and XML tree.
+ */
+struct XmlResource {
+    ResourceFile file;
+    std::unique_ptr<xml::Node> root;
+};
+
+/**
+ * Inflates an XML DOM from a text stream, logging errors to the logger.
+ * Returns the root node on success, or nullptr on failure.
+ */
+std::unique_ptr<XmlResource> inflate(std::istream* in, IDiagnostics* diag, const Source& source);
+
+/**
+ * Inflates an XML DOM from a binary ResXMLTree, logging errors to the logger.
+ * Returns the root node on success, or nullptr on failure.
+ */
+std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen, IDiagnostics* diag,
+                                     const Source& source);
+
+Element* findRootElement(XmlResource* doc);
+Element* findRootElement(Node* node);
+
+/**
+ * A visitor interface for the different XML Node subtypes. This will not traverse into
+ * children. Use Visitor for that.
+ */
+struct RawVisitor {
+    virtual ~RawVisitor() = default;
+
+    virtual void visit(Namespace* node) {}
+    virtual void visit(Element* node) {}
+    virtual void visit(Text* text) {}
+};
+
+/**
+ * Visitor whose default implementation visits the children nodes of any node.
+ */
+struct Visitor : public RawVisitor {
+    using RawVisitor::visit;
+
+    void visit(Namespace* node) override {
+        visitChildren(node);
+    }
+
+    void visit(Element* node) override {
+        visitChildren(node);
+    }
+
+    void visit(Text* text) override {
+        visitChildren(text);
+    }
+
+    void visitChildren(Node* node) {
+        for (auto& child : node->children) {
+            child->accept(this);
+        }
+    }
+};
+
+/**
+ * An XML DOM visitor that will record the package name for a namespace prefix.
+ */
+class PackageAwareVisitor : public Visitor, public IPackageDeclStack {
+private:
+    struct PackageDecl {
+        std::u16string prefix;
+        ExtractedPackage package;
+    };
+
+    std::vector<PackageDecl> mPackageDecls;
+
+public:
+    using Visitor::visit;
+
+    void visit(Namespace* ns) override;
+    Maybe<ExtractedPackage> transformPackageAlias(
+            const StringPiece16& alias, const StringPiece16& localPackage) const override;
+};
+
+// Implementations
+
+template <typename Derived>
+void BaseNode<Derived>::accept(RawVisitor* visitor) {
+    visitor->visit(static_cast<Derived*>(this));
+}
+
+template <typename T>
+struct NodeCastImpl : public RawVisitor {
+    using RawVisitor::visit;
+
+    T* value = nullptr;
+
+    void visit(T* v) override {
+        value = v;
+    }
+};
+
+template <typename T>
+T* nodeCast(Node* node) {
+    NodeCastImpl<T> visitor;
+    node->accept(&visitor);
+    return visitor.value;
+}
+
+} // namespace xml
+} // namespace aapt
+
+#endif // AAPT_XML_DOM_H
diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp
new file mode 100644
index 0000000..431ee2c
--- /dev/null
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "xml/XmlDom.h"
+
+#include <gtest/gtest.h>
+#include <sstream>
+#include <string>
+
+namespace aapt {
+
+constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+
+TEST(XmlDomTest, Inflate) {
+    std::stringstream in(kXmlPreamble);
+    in << R"EOF(
+        <Layout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+            <TextView android:id="@+id/id"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content" />
+        </Layout>
+    )EOF";
+
+    const Source source = { "test.xml" };
+    StdErrDiagnostics diag;
+    std::unique_ptr<xml::XmlResource> doc = xml::inflate(&in, &diag, source);
+    ASSERT_NE(doc, nullptr);
+
+    xml::Namespace* ns = xml::nodeCast<xml::Namespace>(doc->root.get());
+    ASSERT_NE(ns, nullptr);
+    EXPECT_EQ(ns->namespaceUri, u"http://schemas.android.com/apk/res/android");
+    EXPECT_EQ(ns->namespacePrefix, u"android");
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
new file mode 100644
index 0000000..323ec05
--- /dev/null
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "util/Maybe.h"
+#include "util/Util.h"
+#include "xml/XmlPullParser.h"
+#include "xml/XmlUtil.h"
+
+#include <iostream>
+#include <string>
+
+namespace aapt {
+namespace xml {
+
+constexpr char kXmlNamespaceSep = 1;
+
+XmlPullParser::XmlPullParser(std::istream& in) : mIn(in), mEmpty(), mDepth(0) {
+    mParser = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
+    XML_SetUserData(mParser, this);
+    XML_SetElementHandler(mParser, startElementHandler, endElementHandler);
+    XML_SetNamespaceDeclHandler(mParser, startNamespaceHandler, endNamespaceHandler);
+    XML_SetCharacterDataHandler(mParser, characterDataHandler);
+    XML_SetCommentHandler(mParser, commentDataHandler);
+    mEventQueue.push(EventData{ Event::kStartDocument, 0, mDepth++ });
+}
+
+XmlPullParser::~XmlPullParser() {
+    XML_ParserFree(mParser);
+}
+
+XmlPullParser::Event XmlPullParser::next() {
+    const Event currentEvent = getEvent();
+    if (currentEvent == Event::kBadDocument || currentEvent == Event::kEndDocument) {
+        return currentEvent;
+    }
+
+    mEventQueue.pop();
+    while (mEventQueue.empty()) {
+        mIn.read(mBuffer, sizeof(mBuffer) / sizeof(*mBuffer));
+
+        const bool done = mIn.eof();
+        if (mIn.bad() && !done) {
+            mLastError = strerror(errno);
+            mEventQueue.push(EventData{ Event::kBadDocument });
+            continue;
+        }
+
+        if (XML_Parse(mParser, mBuffer, mIn.gcount(), done) == XML_STATUS_ERROR) {
+            mLastError = XML_ErrorString(XML_GetErrorCode(mParser));
+            mEventQueue.push(EventData{ Event::kBadDocument });
+            continue;
+        }
+
+        if (done) {
+            mEventQueue.push(EventData{ Event::kEndDocument, 0, 0 });
+        }
+    }
+
+    Event event = getEvent();
+
+    // Record namespace prefixes and package names so that we can do our own
+    // handling of references that use namespace aliases.
+    if (event == Event::kStartNamespace || event == Event::kEndNamespace) {
+        Maybe<ExtractedPackage> result = extractPackageFromNamespace(getNamespaceUri());
+        if (event == Event::kStartNamespace) {
+            if (result) {
+                mPackageAliases.emplace_back(
+                        PackageDecl{ getNamespacePrefix(), std::move(result.value()) });
+            }
+        } else {
+            if (result) {
+                mPackageAliases.pop_back();
+            }
+        }
+    }
+
+    return event;
+}
+
+XmlPullParser::Event XmlPullParser::getEvent() const {
+    return mEventQueue.front().event;
+}
+
+const std::string& XmlPullParser::getLastError() const {
+    return mLastError;
+}
+
+const std::u16string& XmlPullParser::getComment() const {
+    return mEventQueue.front().data1;
+}
+
+size_t XmlPullParser::getLineNumber() const {
+    return mEventQueue.front().lineNumber;
+}
+
+size_t XmlPullParser::getDepth() const {
+    return mEventQueue.front().depth;
+}
+
+const std::u16string& XmlPullParser::getText() const {
+    if (getEvent() != Event::kText) {
+        return mEmpty;
+    }
+    return mEventQueue.front().data1;
+}
+
+const std::u16string& XmlPullParser::getNamespacePrefix() const {
+    const Event currentEvent = getEvent();
+    if (currentEvent != Event::kStartNamespace && currentEvent != Event::kEndNamespace) {
+        return mEmpty;
+    }
+    return mEventQueue.front().data1;
+}
+
+const std::u16string& XmlPullParser::getNamespaceUri() const {
+    const Event currentEvent = getEvent();
+    if (currentEvent != Event::kStartNamespace && currentEvent != Event::kEndNamespace) {
+        return mEmpty;
+    }
+    return mEventQueue.front().data2;
+}
+
+Maybe<ExtractedPackage> XmlPullParser::transformPackageAlias(
+        const StringPiece16& alias, const StringPiece16& localPackage) const {
+    if (alias.empty()) {
+        return ExtractedPackage{ localPackage.toString(), false /* private */ };
+    }
+
+    const auto endIter = mPackageAliases.rend();
+    for (auto iter = mPackageAliases.rbegin(); iter != endIter; ++iter) {
+        if (alias == iter->prefix) {
+            if (iter->package.package.empty()) {
+                return ExtractedPackage{ localPackage.toString(),
+                                         iter->package.privateNamespace };
+            }
+            return iter->package;
+        }
+    }
+    return {};
+}
+
+const std::u16string& XmlPullParser::getElementNamespace() const {
+    const Event currentEvent = getEvent();
+    if (currentEvent != Event::kStartElement && currentEvent != Event::kEndElement) {
+        return mEmpty;
+    }
+    return mEventQueue.front().data1;
+}
+
+const std::u16string& XmlPullParser::getElementName() const {
+    const Event currentEvent = getEvent();
+    if (currentEvent != Event::kStartElement && currentEvent != Event::kEndElement) {
+        return mEmpty;
+    }
+    return mEventQueue.front().data2;
+}
+
+XmlPullParser::const_iterator XmlPullParser::beginAttributes() const {
+    return mEventQueue.front().attributes.begin();
+}
+
+XmlPullParser::const_iterator XmlPullParser::endAttributes() const {
+    return mEventQueue.front().attributes.end();
+}
+
+size_t XmlPullParser::getAttributeCount() const {
+    if (getEvent() != Event::kStartElement) {
+        return 0;
+    }
+    return mEventQueue.front().attributes.size();
+}
+
+/**
+ * Extracts the namespace and name of an expanded element or attribute name.
+ */
+static void splitName(const char* name, std::u16string& outNs, std::u16string& outName) {
+    const char* p = name;
+    while (*p != 0 && *p != kXmlNamespaceSep) {
+        p++;
+    }
+
+    if (*p == 0) {
+        outNs = std::u16string();
+        outName = util::utf8ToUtf16(name);
+    } else {
+        outNs = util::utf8ToUtf16(StringPiece(name, (p - name)));
+        outName = util::utf8ToUtf16(p + 1);
+    }
+}
+
+void XMLCALL XmlPullParser::startNamespaceHandler(void* userData, const char* prefix,
+        const char* uri) {
+    XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
+    std::u16string namespaceUri = uri != nullptr ? util::utf8ToUtf16(uri) : std::u16string();
+    parser->mNamespaceUris.push(namespaceUri);
+    parser->mEventQueue.push(EventData{
+            Event::kStartNamespace,
+            XML_GetCurrentLineNumber(parser->mParser),
+            parser->mDepth++,
+            prefix != nullptr ? util::utf8ToUtf16(prefix) : std::u16string(),
+            namespaceUri
+    });
+}
+
+void XMLCALL XmlPullParser::startElementHandler(void* userData, const char* name,
+        const char** attrs) {
+    XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
+
+    EventData data = {
+            Event::kStartElement, XML_GetCurrentLineNumber(parser->mParser), parser->mDepth++
+    };
+    splitName(name, data.data1, data.data2);
+
+    while (*attrs) {
+        Attribute attribute;
+        splitName(*attrs++, attribute.namespaceUri, attribute.name);
+        attribute.value = util::utf8ToUtf16(*attrs++);
+
+        // Insert in sorted order.
+        auto iter = std::lower_bound(data.attributes.begin(), data.attributes.end(), attribute);
+        data.attributes.insert(iter, std::move(attribute));
+    }
+
+    // Move the structure into the queue (no copy).
+    parser->mEventQueue.push(std::move(data));
+}
+
+void XMLCALL XmlPullParser::characterDataHandler(void* userData, const char* s, int len) {
+    XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
+
+    parser->mEventQueue.push(EventData{
+            Event::kText,
+            XML_GetCurrentLineNumber(parser->mParser),
+            parser->mDepth,
+            util::utf8ToUtf16(StringPiece(s, len))
+    });
+}
+
+void XMLCALL XmlPullParser::endElementHandler(void* userData, const char* name) {
+    XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
+
+    EventData data = {
+            Event::kEndElement, XML_GetCurrentLineNumber(parser->mParser), --(parser->mDepth)
+    };
+    splitName(name, data.data1, data.data2);
+
+    // Move the data into the queue (no copy).
+    parser->mEventQueue.push(std::move(data));
+}
+
+void XMLCALL XmlPullParser::endNamespaceHandler(void* userData, const char* prefix) {
+    XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
+
+    parser->mEventQueue.push(EventData{
+            Event::kEndNamespace,
+            XML_GetCurrentLineNumber(parser->mParser),
+            --(parser->mDepth),
+            prefix != nullptr ? util::utf8ToUtf16(prefix) : std::u16string(),
+            parser->mNamespaceUris.top()
+    });
+    parser->mNamespaceUris.pop();
+}
+
+void XMLCALL XmlPullParser::commentDataHandler(void* userData, const char* comment) {
+    XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData);
+
+    parser->mEventQueue.push(EventData{
+            Event::kComment,
+            XML_GetCurrentLineNumber(parser->mParser),
+            parser->mDepth,
+            util::utf8ToUtf16(comment)
+    });
+}
+
+Maybe<StringPiece16> findAttribute(const XmlPullParser* parser, const StringPiece16& name) {
+    auto iter = parser->findAttribute(u"", name);
+    if (iter != parser->endAttributes()) {
+        return StringPiece16(util::trimWhitespace(iter->value));
+    }
+    return {};
+}
+
+Maybe<StringPiece16> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece16& name) {
+    auto iter = parser->findAttribute(u"", name);
+    if (iter != parser->endAttributes()) {
+        StringPiece16 trimmed = util::trimWhitespace(iter->value);
+        if (!trimmed.empty()) {
+            return trimmed;
+        }
+    }
+    return {};
+}
+
+} // namespace xml
+} // namespace aapt
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
new file mode 100644
index 0000000..7e7070e
--- /dev/null
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_XML_PULL_PARSER_H
+#define AAPT_XML_PULL_PARSER_H
+
+#include "Resource.h"
+#include "process/IResourceTableConsumer.h"
+#include "util/Maybe.h"
+#include "util/StringPiece.h"
+#include "xml/XmlUtil.h"
+
+#include <algorithm>
+#include <expat.h>
+#include <istream>
+#include <ostream>
+#include <queue>
+#include <stack>
+#include <string>
+#include <vector>
+
+namespace aapt {
+namespace xml {
+
+class XmlPullParser : public IPackageDeclStack {
+public:
+    enum class Event {
+        kBadDocument,
+        kStartDocument,
+        kEndDocument,
+
+        kStartNamespace,
+        kEndNamespace,
+        kStartElement,
+        kEndElement,
+        kText,
+        kComment,
+    };
+
+    /**
+     * 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);
+
+    XmlPullParser(std::istream& in);
+    ~XmlPullParser();
+
+    /**
+     * Returns the current event that is being processed.
+     */
+    Event getEvent() const;
+
+    const std::string& getLastError() const;
+
+    /**
+     * Note, unlike XmlPullParser, the first call to next() will return
+     * StartElement of the first element.
+     */
+    Event next();
+
+    //
+    // These are available for all nodes.
+    //
+
+    const std::u16string& getComment() const;
+    size_t getLineNumber() const;
+    size_t getDepth() const;
+
+    /**
+     * Returns the character data for a Text event.
+     */
+    const std::u16string& getText() const;
+
+    //
+    // Namespace prefix and URI are available for StartNamespace and EndNamespace.
+    //
+
+    const std::u16string& getNamespacePrefix() const;
+    const std::u16string& getNamespaceUri() const;
+
+    //
+    // These are available for StartElement and EndElement.
+    //
+
+    const std::u16string& getElementNamespace() const;
+    const std::u16string& getElementName() const;
+
+    /*
+     * Uses the current stack of namespaces to resolve the package. Eg:
+     * xmlns:app = "http://schemas.android.com/apk/res/com.android.app"
+     * ...
+     * android:text="@app:string/message"
+     *
+     * In this case, 'app' will be converted to 'com.android.app'.
+     *
+     * If xmlns:app="http://schemas.android.com/apk/res-auto", then
+     * 'package' will be set to 'defaultPackage'.
+     */
+    Maybe<ExtractedPackage> transformPackageAlias(
+            const StringPiece16& alias, const StringPiece16& localPackage) const override;
+
+    //
+    // Remaining methods are for retrieving information about attributes
+    // associated with a StartElement.
+    //
+    // Attributes must be in sorted order (according to the less than operator
+    // of struct Attribute).
+    //
+
+    struct Attribute {
+        std::u16string namespaceUri;
+        std::u16string name;
+        std::u16string value;
+
+        int compare(const Attribute& rhs) const;
+        bool operator<(const Attribute& rhs) const;
+        bool operator==(const Attribute& rhs) const;
+        bool operator!=(const Attribute& rhs) const;
+    };
+
+    using const_iterator = std::vector<Attribute>::const_iterator;
+
+    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::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;
+
+    struct PackageDecl {
+        std::u16string prefix;
+        ExtractedPackage package;
+    };
+    std::vector<PackageDecl> mPackageAliases;
+};
+
+/**
+ * Finds the attribute in the current element within the global namespace.
+ */
+Maybe<StringPiece16> findAttribute(const XmlPullParser* parser, const StringPiece16& name);
+
+/**
+ * Finds the attribute in the current element within the global namespace. The attribute's value
+ * must not be the empty string.
+ */
+Maybe<StringPiece16> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece16& name);
+
+//
+// Implementation
+//
+
+inline ::std::ostream& operator<<(::std::ostream& out, XmlPullParser::Event event) {
+    switch (event) {
+        case XmlPullParser::Event::kBadDocument: return out << "BadDocument";
+        case XmlPullParser::Event::kStartDocument: return out << "StartDocument";
+        case XmlPullParser::Event::kEndDocument: return out << "EndDocument";
+        case XmlPullParser::Event::kStartNamespace: return out << "StartNamespace";
+        case XmlPullParser::Event::kEndNamespace: return out << "EndNamespace";
+        case XmlPullParser::Event::kStartElement: return out << "StartElement";
+        case XmlPullParser::Event::kEndElement: return out << "EndElement";
+        case XmlPullParser::Event::kText: return out << "Text";
+        case XmlPullParser::Event::kComment: return out << "Comment";
+    }
+    return out;
+}
+
+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 false;
+            case Event::kStartElement:
+                depth++;
+                break;
+            case Event::kEndElement:
+                depth--;
+                break;
+            default:
+                break;
+        }
+    }
+    return true;
+}
+
+inline bool XmlPullParser::isGoodEvent(XmlPullParser::Event event) {
+    return event != Event::kBadDocument && event != Event::kEndDocument;
+}
+
+inline int XmlPullParser::Attribute::compare(const Attribute& rhs) const {
+    int cmp = namespaceUri.compare(rhs.namespaceUri);
+    if (cmp != 0) return cmp;
+    return name.compare(rhs.name);
+}
+
+inline bool XmlPullParser::Attribute::operator<(const Attribute& rhs) const {
+    return compare(rhs) < 0;
+}
+
+inline bool XmlPullParser::Attribute::operator==(const Attribute& rhs) const {
+    return compare(rhs) == 0;
+}
+
+inline bool XmlPullParser::Attribute::operator!=(const Attribute& rhs) const {
+    return compare(rhs) != 0;
+}
+
+inline XmlPullParser::const_iterator XmlPullParser::findAttribute(StringPiece16 namespaceUri,
+                                                                  StringPiece16 name) const {
+    const auto endIter = endAttributes();
+    const auto iter = std::lower_bound(beginAttributes(), endIter,
+            std::pair<StringPiece16, StringPiece16>(namespaceUri, name),
+            [](const Attribute& attr, const std::pair<StringPiece16, StringPiece16>& rhs) -> bool {
+                int cmp = attr.namespaceUri.compare(0, attr.namespaceUri.size(),
+                        rhs.first.data(), rhs.first.size());
+                if (cmp < 0) return true;
+                if (cmp > 0) return false;
+                cmp = attr.name.compare(0, attr.name.size(), rhs.second.data(), rhs.second.size());
+                if (cmp < 0) return true;
+                return false;
+            }
+    );
+
+    if (iter != endIter && namespaceUri == iter->namespaceUri && name == iter->name) {
+        return iter;
+    }
+    return endIter;
+}
+
+} // namespace xml
+} // namespace aapt
+
+#endif // AAPT_XML_PULL_PARSER_H
diff --git a/tools/aapt2/xml/XmlPullParser_test.cpp b/tools/aapt2/xml/XmlPullParser_test.cpp
new file mode 100644
index 0000000..8fa2c6d
--- /dev/null
+++ b/tools/aapt2/xml/XmlPullParser_test.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "util/StringPiece.h"
+#include "xml/XmlPullParser.h"
+
+#include <gtest/gtest.h>
+#include <sstream>
+
+namespace aapt {
+
+TEST(XmlPullParserTest, NextChildNodeTraversesCorrectly) {
+    std::stringstream str;
+    str << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+            "<a><b><c xmlns:a=\"http://schema.org\"><d/></c><e/></b></a>";
+    xml::XmlPullParser parser(str);
+
+    const size_t depthOuter = parser.getDepth();
+    ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthOuter));
+
+    EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent());
+    EXPECT_EQ(StringPiece16(u"a"), StringPiece16(parser.getElementName()));
+
+    const size_t depthA = parser.getDepth();
+    ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthA));
+    EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent());
+    EXPECT_EQ(StringPiece16(u"b"), StringPiece16(parser.getElementName()));
+
+    const size_t depthB = parser.getDepth();
+    ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthB));
+    EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent());
+    EXPECT_EQ(StringPiece16(u"c"), StringPiece16(parser.getElementName()));
+
+    ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthB));
+    EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent());
+    EXPECT_EQ(StringPiece16(u"e"), StringPiece16(parser.getElementName()));
+
+    ASSERT_FALSE(xml::XmlPullParser::nextChildNode(&parser, depthOuter));
+    EXPECT_EQ(xml::XmlPullParser::Event::kEndDocument, parser.getEvent());
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/xml/XmlUtil.cpp b/tools/aapt2/xml/XmlUtil.cpp
new file mode 100644
index 0000000..ab9f544
--- /dev/null
+++ b/tools/aapt2/xml/XmlUtil.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "util/Maybe.h"
+#include "util/Util.h"
+#include "xml/XmlUtil.h"
+
+#include <string>
+
+namespace aapt {
+namespace xml {
+
+Maybe<ExtractedPackage> extractPackageFromNamespace(const std::u16string& namespaceUri) {
+    if (util::stringStartsWith<char16_t>(namespaceUri, kSchemaPublicPrefix)) {
+        StringPiece16 schemaPrefix = kSchemaPublicPrefix;
+        StringPiece16 package = namespaceUri;
+        package = package.substr(schemaPrefix.size(), package.size() - schemaPrefix.size());
+        if (package.empty()) {
+            return {};
+        }
+        return ExtractedPackage{ package.toString(), false /* isPrivate */ };
+
+    } else if (util::stringStartsWith<char16_t>(namespaceUri, kSchemaPrivatePrefix)) {
+        StringPiece16 schemaPrefix = kSchemaPrivatePrefix;
+        StringPiece16 package = namespaceUri;
+        package = package.substr(schemaPrefix.size(), package.size() - schemaPrefix.size());
+        if (package.empty()) {
+            return {};
+        }
+        return ExtractedPackage{ package.toString(), true /* isPrivate */ };
+
+    } else if (namespaceUri == kSchemaAuto) {
+        return ExtractedPackage{ std::u16string(), true /* isPrivate */ };
+    }
+    return {};
+}
+
+void transformReferenceFromNamespace(IPackageDeclStack* declStack,
+                                     const StringPiece16& localPackage, Reference* inRef) {
+    if (inRef->name) {
+        if (Maybe<ExtractedPackage> transformedPackage =
+                   declStack->transformPackageAlias(inRef->name.value().package, localPackage)) {
+            ExtractedPackage& extractedPackage = transformedPackage.value();
+            inRef->name.value().package = std::move(extractedPackage.package);
+
+            // If the reference was already private (with a * prefix) and the namespace is public,
+            // we keep the reference private.
+            inRef->privateReference |= extractedPackage.privateNamespace;
+        }
+    }
+}
+
+} // namespace xml
+} // namespace aapt
diff --git a/tools/aapt2/xml/XmlUtil.h b/tools/aapt2/xml/XmlUtil.h
new file mode 100644
index 0000000..98e5520
--- /dev/null
+++ b/tools/aapt2/xml/XmlUtil.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_XML_XMLUTIL_H
+#define AAPT_XML_XMLUTIL_H
+
+#include "ResourceValues.h"
+#include "util/Maybe.h"
+
+#include <string>
+
+namespace aapt {
+namespace xml {
+
+constexpr const char16_t* kSchemaAuto = u"http://schemas.android.com/apk/res-auto";
+constexpr const char16_t* kSchemaPublicPrefix = u"http://schemas.android.com/apk/res/";
+constexpr const char16_t* kSchemaPrivatePrefix = u"http://schemas.android.com/apk/prv/res/";
+constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
+
+/**
+ * Result of extracting a package name from a namespace URI declaration.
+ */
+struct ExtractedPackage {
+    /**
+     * The name of the package. This can be the empty string, which means that the package
+     * should be assumed to be the package being compiled.
+     */
+    std::u16string package;
+
+    /**
+     * True if the package's private namespace was declared. This means that private resources
+     * are made visible.
+     */
+    bool privateNamespace;
+};
+
+/**
+ * Returns an ExtractedPackage struct if the namespace URI is of the form:
+ * http://schemas.android.com/apk/res/<package> or
+ * http://schemas.android.com/apk/prv/res/<package>
+ *
+ * Special case: if namespaceUri is http://schemas.android.com/apk/res-auto,
+ * returns an empty package name.
+ */
+Maybe<ExtractedPackage> extractPackageFromNamespace(const std::u16string& namespaceUri);
+
+/**
+ * Interface representing a stack of XML namespace declarations. When looking up the package
+ * for a namespace prefix, the stack is checked from top to bottom.
+ */
+struct IPackageDeclStack {
+    virtual ~IPackageDeclStack() = default;
+
+    /**
+     * Returns an ExtractedPackage struct if the alias given corresponds with a package declaration.
+     */
+    virtual Maybe<ExtractedPackage> transformPackageAlias(
+            const StringPiece16& alias, const StringPiece16& localPackage) const = 0;
+};
+
+/**
+ * Helper function for transforming the original Reference inRef to a fully qualified reference
+ * via the IPackageDeclStack. This will also mark the Reference as private if the namespace of
+ * the package declaration was private.
+ */
+void transformReferenceFromNamespace(IPackageDeclStack* declStack,
+                                     const StringPiece16& localPackage, Reference* inRef);
+
+} // namespace xml
+} // namespace aapt
+
+#endif /* AAPT_XML_XMLUTIL_H */
diff --git a/tools/aapt2/xml/XmlUtil_test.cpp b/tools/aapt2/xml/XmlUtil_test.cpp
new file mode 100644
index 0000000..7796b3e
--- /dev/null
+++ b/tools/aapt2/xml/XmlUtil_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "test/Common.h"
+#include "xml/XmlUtil.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(XmlUtilTest, ExtractPackageFromNamespace) {
+    AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"com.android"));
+    AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"http://schemas.android.com/apk"));
+    AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res"));
+    AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res/"));
+    AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(
+            u"http://schemas.android.com/apk/prv/res/"));
+
+    Maybe<xml::ExtractedPackage> p =
+            xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res/a");
+    AAPT_ASSERT_TRUE(p);
+    EXPECT_EQ(std::u16string(u"a"), p.value().package);
+    EXPECT_EQ(false, p.value().privateNamespace);
+
+    p = xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/prv/res/android");
+    AAPT_ASSERT_TRUE(p);
+    EXPECT_EQ(std::u16string(u"android"), p.value().package);
+    EXPECT_EQ(true, p.value().privateNamespace);
+
+    p = xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/prv/res/com.test");
+    AAPT_ASSERT_TRUE(p);
+    EXPECT_EQ(std::u16string(u"com.test"), p.value().package);
+    EXPECT_EQ(true, p.value().privateNamespace);
+
+    p = xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res-auto");
+    AAPT_ASSERT_TRUE(p);
+    EXPECT_EQ(std::u16string(), p.value().package);
+    EXPECT_EQ(true, p.value().privateNamespace);
+}
+
+} // namespace aapt