AAPT2: Support generating Manifest.java

This includes comments from AndroidManifest.xml.

Change-Id: I412d9ecb12bad20a49a683d6b3bea4a0be1235ae
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
new file mode 100644
index 0000000..16440bc
--- /dev/null
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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 "java/AnnotationProcessor.h"
+#include "util/Util.h"
+
+#include <algorithm>
+
+namespace aapt {
+
+void AnnotationProcessor::appendCommentLine(const StringPiece16& line) {
+    static const std::string sDeprecated = "@deprecated";
+    static const std::string sSystemApi = "@SystemApi";
+
+    if (line.empty()) {
+        return;
+    }
+
+    std::string comment = util::utf16ToUtf8(line);
+
+    if (comment.find(sDeprecated) != std::string::npos && !mDeprecated) {
+        mDeprecated = true;
+        if (!mAnnotations.empty()) {
+            mAnnotations += "\n";
+        }
+        mAnnotations += mPrefix;
+        mAnnotations += "@Deprecated";
+    }
+
+    if (comment.find(sSystemApi) != std::string::npos && !mSystemApi) {
+        mSystemApi = true;
+        if (!mAnnotations.empty()) {
+            mAnnotations += "\n";
+        }
+        mAnnotations += mPrefix;
+        mAnnotations += "@android.annotations.SystemApi";
+    }
+
+    if (mComment.empty()) {
+        mComment += mPrefix;
+        mComment += "/**";
+    }
+
+    mComment += "\n";
+    mComment += mPrefix;
+    mComment += " * ";
+    mComment += std::move(comment);
+}
+
+void AnnotationProcessor::appendComment(const StringPiece16& comment) {
+    // We need to process line by line to clean-up whitespace and append prefixes.
+    for (StringPiece16 line : util::tokenize(comment, u'\n')) {
+        appendCommentLine(util::trimWhitespace(line));
+    }
+}
+
+std::string AnnotationProcessor::buildComment() {
+    mComment += "\n";
+    mComment += mPrefix;
+    mComment += " */";
+    return std::move(mComment);
+}
+
+std::string AnnotationProcessor::buildAnnotations() {
+    return std::move(mAnnotations);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
new file mode 100644
index 0000000..b472109
--- /dev/null
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -0,0 +1,93 @@
+/*
+ * 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_JAVA_ANNOTATIONPROCESSOR_H
+#define AAPT_JAVA_ANNOTATIONPROCESSOR_H
+
+#include "util/StringPiece.h"
+
+#include <string>
+
+namespace aapt {
+
+/**
+ * Builds a JavaDoc comment from a set of XML comments.
+ * This will also look for instances of @SystemApi and convert them to
+ * actual Java annotations.
+ *
+ * Example:
+ *
+ * Input XML:
+ *
+ * <!-- This is meant to be hidden because
+ *      It is system api. Also it is @deprecated
+ *      @SystemApi
+ *      -->
+ *
+ * Output JavaDoc:
+ *
+ *  /\*
+ *   * This is meant to be hidden because
+ *   * It is system api. Also it is @deprecated
+ *   * @SystemApi
+ *   *\/
+ *
+ * Output Annotations:
+ *
+ * @Deprecated
+ * @android.annotation.SystemApi
+ *
+ */
+class AnnotationProcessor {
+public:
+    /**
+     * Creates an AnnotationProcessor with a given prefix for each line generated.
+     * This is usually a set of spaces for indentation.
+     */
+    AnnotationProcessor(const StringPiece& prefix) : mPrefix(prefix.toString()) {
+    }
+
+    /**
+     * Adds more comments. Since resources can have various values with different configurations,
+     * we need to collect all the comments.
+     */
+    void appendComment(const StringPiece16& comment);
+
+    /**
+     * Finishes the comment and moves it to the caller. Subsequent calls to buildComment() have
+     * undefined results.
+     */
+    std::string buildComment();
+
+    /**
+     * Finishes the annotation and moves it to the caller. Subsequent calls to buildAnnotations()
+     * have undefined results.
+     */
+    std::string buildAnnotations();
+
+private:
+    std::string mPrefix;
+    std::string mComment;
+    std::string mAnnotations;
+    bool mDeprecated = false;
+    bool mSystemApi = false;
+
+    void appendCommentLine(const StringPiece16& line);
+};
+
+} // namespace aapt
+
+#endif /* AAPT_JAVA_ANNOTATIONPROCESSOR_H */
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
new file mode 100644
index 0000000..0175489
--- /dev/null
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -0,0 +1,229 @@
+/*
+ * 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 "NameMangler.h"
+#include "Resource.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "java/AnnotationProcessor.h"
+#include "java/JavaClassGenerator.h"
+#include "util/StringPiece.h"
+
+#include <algorithm>
+#include <ostream>
+#include <set>
+#include <sstream>
+#include <tuple>
+
+namespace aapt {
+
+// The number of attributes to emit per line in a Styleable array.
+constexpr size_t kAttribsPerLine = 4;
+
+JavaClassGenerator::JavaClassGenerator(ResourceTable* table, JavaClassGeneratorOptions options) :
+        mTable(table), mOptions(options) {
+}
+
+static void generateHeader(const StringPiece16& packageNameToGenerate, std::ostream* out) {
+    *out << "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n"
+            " *\n"
+            " * This class was automatically generated by the\n"
+            " * aapt tool from the resource data it found. It\n"
+            " * should not be modified by hand.\n"
+            " */\n\n"
+            "package " << packageNameToGenerate << ";\n\n";
+}
+
+static const std::set<StringPiece16> sJavaIdentifiers = {
+    u"abstract", u"assert", u"boolean", u"break", u"byte",
+    u"case", u"catch", u"char", u"class", u"const", u"continue",
+    u"default", u"do", u"double", u"else", u"enum", u"extends",
+    u"final", u"finally", u"float", u"for", u"goto", u"if",
+    u"implements", u"import", u"instanceof", u"int", u"interface",
+    u"long", u"native", u"new", u"package", u"private", u"protected",
+    u"public", u"return", u"short", u"static", u"strictfp", u"super",
+    u"switch", u"synchronized", u"this", u"throw", u"throws",
+    u"transient", u"try", u"void", u"volatile", u"while", u"true",
+    u"false", u"null"
+};
+
+static bool isValidSymbol(const StringPiece16& symbol) {
+    return sJavaIdentifiers.find(symbol) == sJavaIdentifiers.end();
+}
+
+/*
+ * Java symbols can not contain . or -, but those are valid in a resource name.
+ * Replace those with '_'.
+ */
+static std::u16string transform(const StringPiece16& symbol) {
+    std::u16string output = symbol.toString();
+    for (char16_t& c : output) {
+        if (c == u'.' || c == u'-') {
+            c = u'_';
+        }
+    }
+    return output;
+}
+
+bool JavaClassGenerator::skipSymbol(SymbolState state) {
+    switch (mOptions.types) {
+    case JavaClassGeneratorOptions::SymbolTypes::kAll:
+        return false;
+    case JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate:
+        return state == SymbolState::kUndefined;
+    case JavaClassGeneratorOptions::SymbolTypes::kPublic:
+        return state != SymbolState::kPublic;
+    }
+    return true;
+}
+
+void JavaClassGenerator::generateStyleable(const StringPiece16& packageNameToGenerate,
+                                           const std::u16string& entryName,
+                                           const Styleable* styleable,
+                                           std::ostream* out) {
+    const StringPiece finalModifier = mOptions.useFinal ? " final" : "";
+
+    // This must be sorted by resource ID.
+    std::vector<std::pair<ResourceId, ResourceNameRef>> sortedAttributes;
+    sortedAttributes.reserve(styleable->entries.size());
+    for (const auto& attr : styleable->entries) {
+        // If we are not encoding final attributes, the styleable entry may have no ID
+        // if we are building a static library.
+        assert((!mOptions.useFinal || attr.id) && "no ID set for Styleable entry");
+        assert(attr.name && "no name set for Styleable entry");
+        sortedAttributes.emplace_back(attr.id ? attr.id.value() : ResourceId(0), attr.name.value());
+    }
+    std::sort(sortedAttributes.begin(), sortedAttributes.end());
+
+    // First we emit the array containing the IDs of each attribute.
+    *out << "        "
+         << "public static final int[] " << transform(entryName) << " = {";
+
+    const size_t attrCount = sortedAttributes.size();
+    for (size_t i = 0; i < attrCount; i++) {
+        if (i % kAttribsPerLine == 0) {
+            *out << "\n            ";
+        }
+
+        *out << sortedAttributes[i].first;
+        if (i != attrCount - 1) {
+            *out << ", ";
+        }
+    }
+    *out << "\n        };\n";
+
+    // Now we emit the indices into the array.
+    for (size_t i = 0; i < attrCount; i++) {
+        *out << "        "
+             << "public static" << finalModifier
+             << " int " << transform(entryName);
+
+        // We may reference IDs from other packages, so prefix the entry name with
+        // the package.
+        const ResourceNameRef& itemName = sortedAttributes[i].second;
+        if (!itemName.package.empty() && packageNameToGenerate != itemName.package) {
+            *out << "_" << transform(itemName.package);
+        }
+        *out << "_" << transform(itemName.entry) << " = " << i << ";\n";
+    }
+}
+
+bool JavaClassGenerator::generateType(const StringPiece16& packageNameToGenerate,
+                                      const ResourceTablePackage* package,
+                                      const ResourceTableType* type,
+                                      std::ostream* out) {
+    const StringPiece finalModifier = mOptions.useFinal ? " final" : "";
+
+    std::u16string unmangledPackage;
+    std::u16string unmangledName;
+    for (const auto& entry : type->entries) {
+        if (skipSymbol(entry->symbolStatus.state)) {
+            continue;
+        }
+
+        ResourceId id(package->id.value(), type->id.value(), entry->id.value());
+        assert(id.isValid());
+
+        unmangledName = entry->name;
+        if (NameMangler::unmangle(&unmangledName, &unmangledPackage)) {
+            // The entry name was mangled, and we successfully unmangled it.
+            // Check that we want to emit this symbol.
+            if (package->name != unmangledPackage) {
+                // Skip the entry if it doesn't belong to the package we're writing.
+                continue;
+            }
+        } else {
+            if (packageNameToGenerate != package->name) {
+                // We are processing a mangled package name,
+                // but this is a non-mangled resource.
+                continue;
+            }
+        }
+
+        if (!isValidSymbol(unmangledName)) {
+            ResourceNameRef resourceName(packageNameToGenerate, type->type, unmangledName);
+            std::stringstream err;
+            err << "invalid symbol name '" << resourceName << "'";
+            mError = err.str();
+            return false;
+        }
+
+        if (type->type == ResourceType::kStyleable) {
+            assert(!entry->values.empty());
+            generateStyleable(packageNameToGenerate, unmangledName, static_cast<const Styleable*>(
+                    entry->values.front().value.get()), out);
+        } else {
+            *out << "        " << "public static" << finalModifier
+                 << " int " << transform(unmangledName) << " = " << id << ";\n";
+        }
+    }
+    return true;
+}
+
+bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate, std::ostream* out) {
+    return generate(packageNameToGenerate, packageNameToGenerate, out);
+}
+
+bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate,
+                                  const StringPiece16& outPackageName, std::ostream* out) {
+    generateHeader(outPackageName, out);
+
+    *out << "public final class R {\n";
+
+    for (const auto& package : mTable->packages) {
+        for (const auto& type : package->types) {
+            StringPiece16 typeStr;
+            if (type->type == ResourceType::kAttrPrivate) {
+                typeStr = toString(ResourceType::kAttr);
+            } else {
+                typeStr = toString(type->type);
+            }
+            *out << "    public static final class " << typeStr << " {\n";
+            if (!generateType(packageNameToGenerate, package.get(), type.get(), out)) {
+                return false;
+            }
+            *out << "    }\n";
+        }
+    }
+
+    *out << "}\n";
+    out->flush();
+    return true;
+}
+
+
+
+} // namespace aapt
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
new file mode 100644
index 0000000..e53a765
--- /dev/null
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -0,0 +1,95 @@
+/*
+ * 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_JAVA_CLASS_GENERATOR_H
+#define AAPT_JAVA_CLASS_GENERATOR_H
+
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+
+#include "util/StringPiece.h"
+
+#include <ostream>
+#include <string>
+
+namespace aapt {
+
+struct JavaClassGeneratorOptions {
+    /*
+     * Specifies whether to use the 'final' modifier
+     * on resource entries. Default is true.
+     */
+    bool useFinal = true;
+
+    enum class SymbolTypes {
+        kAll,
+        kPublicPrivate,
+        kPublic,
+    };
+
+    /*
+     *
+     */
+    SymbolTypes types = SymbolTypes::kAll;
+};
+
+/*
+ * Generates the R.java file for a resource table.
+ */
+class JavaClassGenerator {
+public:
+    JavaClassGenerator(ResourceTable* table, JavaClassGeneratorOptions options);
+
+    /*
+     * Writes the R.java file to `out`. Only symbols belonging to `package` are written.
+     * All symbols technically belong to a single package, but linked libraries will
+     * have their names mangled, denoting that they came from a different package.
+     * We need to generate these symbols in a separate file.
+     * Returns true on success.
+     */
+    bool generate(const StringPiece16& packageNameToGenerate, std::ostream* out);
+
+    bool generate(const StringPiece16& packageNameToGenerate,
+                  const StringPiece16& outputPackageName,
+                  std::ostream* out);
+
+    const std::string& getError() const;
+
+private:
+    bool generateType(const StringPiece16& packageNameToGenerate,
+                      const ResourceTablePackage* package,
+                      const ResourceTableType* type,
+                      std::ostream* out);
+
+    void generateStyleable(const StringPiece16& packageNameToGenerate,
+                           const std::u16string& entryName,
+                           const Styleable* styleable,
+                           std::ostream* out);
+
+    bool skipSymbol(SymbolState state);
+
+    ResourceTable* mTable;
+    JavaClassGeneratorOptions mOptions;
+    std::string mError;
+};
+
+inline const std::string& JavaClassGenerator::getError() const {
+    return mError;
+}
+
+} // namespace aapt
+
+#endif // AAPT_JAVA_CLASS_GENERATOR_H
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
new file mode 100644
index 0000000..3625f9c
--- /dev/null
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -0,0 +1,201 @@
+/*
+ * 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 "java/JavaClassGenerator.h"
+#include "util/Util.h"
+
+#include "test/Builders.h"
+
+#include <gtest/gtest.h>
+#include <sstream>
+#include <string>
+
+namespace aapt {
+
+TEST(JavaClassGeneratorTest, FailWhenEntryIsJavaKeyword) {
+    std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+            .setPackageId(u"android", 0x01)
+            .addSimple(u"@android:id/class", ResourceId(0x01020000))
+            .build();
+
+    JavaClassGenerator generator(table.get(), {});
+
+    std::stringstream out;
+    EXPECT_FALSE(generator.generate(u"android", &out));
+}
+
+TEST(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) {
+    std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+            .setPackageId(u"android", 0x01)
+            .addSimple(u"@android:id/hey-man", ResourceId(0x01020000))
+            .addSimple(u"@android:attr/cool.attr", ResourceId(0x01010000))
+            .addValue(u"@android:styleable/hey.dude", ResourceId(0x01030000),
+                      test::StyleableBuilder()
+                              .addItem(u"@android:attr/cool.attr", ResourceId(0x01010000))
+                              .build())
+            .build();
+
+    JavaClassGenerator generator(table.get(), {});
+
+    std::stringstream out;
+    EXPECT_TRUE(generator.generate(u"android", &out));
+
+    std::string output = out.str();
+
+    EXPECT_NE(std::string::npos,
+              output.find("public static final int hey_man = 0x01020000;"));
+
+    EXPECT_NE(std::string::npos,
+              output.find("public static final int[] hey_dude = {"));
+
+    EXPECT_NE(std::string::npos,
+              output.find("public static final int hey_dude_cool_attr = 0;"));
+}
+
+TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) {
+    std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+            .setPackageId(u"android", 0x01)
+            .addSimple(u"@android:id/one", ResourceId(0x01020000))
+            .addSimple(u"@android:id/com.foo$two", ResourceId(0x01020001))
+            .build();
+
+    JavaClassGenerator generator(table.get(), {});
+    std::stringstream out;
+    ASSERT_TRUE(generator.generate(u"android", u"com.android.internal", &out));
+
+    std::string output = out.str();
+    EXPECT_NE(std::string::npos, output.find("package com.android.internal;"));
+    EXPECT_NE(std::string::npos, output.find("public static final int one = 0x01020000;"));
+    EXPECT_EQ(std::string::npos, output.find("two"));
+    EXPECT_EQ(std::string::npos, output.find("com_foo$two"));
+}
+
+TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
+    std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+            .setPackageId(u"android", 0x01)
+            .addSimple(u"@android:^attr-private/one", ResourceId(0x01010000))
+            .build();
+
+    JavaClassGenerator generator(table.get(), {});
+    std::stringstream out;
+    ASSERT_TRUE(generator.generate(u"android", &out));
+
+    std::string output = out.str();
+    EXPECT_NE(std::string::npos, output.find("public static final class attr"));
+    EXPECT_EQ(std::string::npos, output.find("public static final class ^attr-private"));
+}
+
+TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
+    StdErrDiagnostics diag;
+    std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+            .setPackageId(u"android", 0x01)
+            .addSimple(u"@android:id/one", ResourceId(0x01020000))
+            .addSimple(u"@android:id/two", ResourceId(0x01020001))
+            .addSimple(u"@android:id/three", ResourceId(0x01020002))
+            .setSymbolState(u"@android:id/one", ResourceId(0x01020000), SymbolState::kPublic)
+            .setSymbolState(u"@android:id/two", ResourceId(0x01020001), SymbolState::kPrivate)
+            .build();
+
+    JavaClassGeneratorOptions options;
+    options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
+    {
+        JavaClassGenerator generator(table.get(), options);
+        std::stringstream out;
+        ASSERT_TRUE(generator.generate(u"android", &out));
+        std::string output = out.str();
+        EXPECT_NE(std::string::npos, output.find("public static final int one = 0x01020000;"));
+        EXPECT_EQ(std::string::npos, output.find("two"));
+        EXPECT_EQ(std::string::npos, output.find("three"));
+    }
+
+    options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
+    {
+        JavaClassGenerator generator(table.get(), options);
+        std::stringstream out;
+        ASSERT_TRUE(generator.generate(u"android", &out));
+        std::string output = out.str();
+        EXPECT_NE(std::string::npos, output.find("public static final int one = 0x01020000;"));
+        EXPECT_NE(std::string::npos, output.find("public static final int two = 0x01020001;"));
+        EXPECT_EQ(std::string::npos, output.find("three"));
+    }
+
+    options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+    {
+        JavaClassGenerator generator(table.get(), options);
+        std::stringstream out;
+        ASSERT_TRUE(generator.generate(u"android", &out));
+        std::string output = out.str();
+        EXPECT_NE(std::string::npos, output.find("public static final int one = 0x01020000;"));
+        EXPECT_NE(std::string::npos, output.find("public static final int two = 0x01020001;"));
+        EXPECT_NE(std::string::npos, output.find("public static final int three = 0x01020002;"));
+    }
+}
+
+/*
+ * TODO(adamlesinski): Re-enable this once we get merging working again.
+ * TEST(JavaClassGeneratorTest, EmitPackageMangledSymbols) {
+    ASSERT_TRUE(addResource(ResourceName{ {}, ResourceType::kId, u"foo" },
+                            ResourceId{ 0x01, 0x02, 0x0000 }));
+    ResourceTable table;
+    table.setPackage(u"com.lib");
+    ASSERT_TRUE(table.addResource(ResourceName{ {}, ResourceType::kId, u"test" }, {},
+                                  Source{ "lib.xml", 33 }, util::make_unique<Id>()));
+    ASSERT_TRUE(mTable->merge(std::move(table)));
+
+    Linker linker(mTable,
+                  std::make_shared<MockResolver>(mTable, std::map<ResourceName, ResourceId>()),
+                  {});
+    ASSERT_TRUE(linker.linkAndValidate());
+
+    JavaClassGenerator generator(mTable, {});
+
+    std::stringstream out;
+    EXPECT_TRUE(generator.generate(mTable->getPackage(), out));
+    std::string output = out.str();
+    EXPECT_NE(std::string::npos, output.find("int foo ="));
+    EXPECT_EQ(std::string::npos, output.find("int test ="));
+
+    out.str("");
+    EXPECT_TRUE(generator.generate(u"com.lib", out));
+    output = out.str();
+    EXPECT_NE(std::string::npos, output.find("int test ="));
+    EXPECT_EQ(std::string::npos, output.find("int foo ="));
+}*/
+
+TEST(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) {
+    std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+                .setPackageId(u"android", 0x01)
+                .setPackageId(u"com.lib", 0x02)
+                .addSimple(u"@android:attr/bar", ResourceId(0x01010000))
+                .addSimple(u"@com.lib:attr/bar", ResourceId(0x02010000))
+                .addValue(u"@android:styleable/foo", ResourceId(0x01030000),
+                          test::StyleableBuilder()
+                                  .addItem(u"@android:attr/bar", ResourceId(0x01010000))
+                                  .addItem(u"@com.lib:attr/bar", ResourceId(0x02010000))
+                                  .build())
+                .build();
+
+    JavaClassGenerator generator(table.get(), {});
+
+    std::stringstream out;
+    EXPECT_TRUE(generator.generate(u"android", &out));
+
+    std::string output = out.str();
+    EXPECT_NE(std::string::npos, output.find("int foo_bar ="));
+    EXPECT_NE(std::string::npos, output.find("int foo_com_lib_bar ="));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
new file mode 100644
index 0000000..c12da64
--- /dev/null
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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 "Source.h"
+#include "XmlDom.h"
+
+#include "java/AnnotationProcessor.h"
+#include "java/ManifestClassGenerator.h"
+#include "util/Maybe.h"
+
+#include <algorithm>
+
+namespace aapt {
+
+constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
+
+static Maybe<StringPiece16> extractJavaIdentifier(IDiagnostics* diag, const Source& source,
+                                                  const StringPiece16& value) {
+    const StringPiece16 sep = u".";
+    auto iter = std::find_end(value.begin(), value.end(), sep.begin(), sep.end());
+
+    StringPiece16 result;
+    if (iter != value.end()) {
+        result.assign(iter + sep.size(), value.end() - (iter + sep.size()));
+    } else {
+        result = value;
+    }
+
+    if (result.empty()) {
+        diag->error(DiagMessage(source) << "empty symbol");
+        return {};
+    }
+
+    iter = util::findNonAlphaNumericAndNotInSet(result, u"_");
+    if (iter != result.end()) {
+        diag->error(DiagMessage(source)
+                    << "invalid character '" << StringPiece16(iter, 1)
+                    << "' in '" << result << "'");
+        return {};
+    }
+
+    if (*result.begin() >= u'0' && *result.begin() <= u'9') {
+        diag->error(DiagMessage(source) << "symbol can not start with a digit");
+        return {};
+    }
+
+    return result;
+}
+
+static bool writeSymbol(IDiagnostics* diag, const Source& source, xml::Element* el,
+                        std::ostream* out) {
+    xml::Attribute* attr = el->findAttribute(kSchemaAndroid, u"name");
+    if (!attr) {
+        diag->error(DiagMessage(source) << "<" << el->name << "> must define 'android:name'");
+        return false;
+    }
+
+    Maybe<StringPiece16> result = extractJavaIdentifier(diag, source.withLine(el->lineNumber),
+                                                        attr->value);
+    if (!result) {
+        return false;
+    }
+
+    *out << "\n";
+
+    if (!util::trimWhitespace(el->comment).empty()) {
+        AnnotationProcessor processor("    ");
+        processor.appendComment(el->comment);
+        *out << processor.buildComment() << "\n";
+        std::string annotations = processor.buildAnnotations();
+        if (!annotations.empty()) {
+            *out << annotations << "\n";
+        }
+    }
+    *out << "    public static final String " << result.value() << "=\"" << attr->value << "\";\n";
+    return true;
+}
+
+bool ManifestClassGenerator::generate(IDiagnostics* diag, const StringPiece16& package,
+                                      XmlResource* res, std::ostream* out) {
+    xml::Element* el = xml::findRootElement(res->root.get());
+    if (!el) {
+        return false;
+    }
+
+    if (el->name != u"manifest" && !el->namespaceUri.empty()) {
+        diag->error(DiagMessage(res->file.source) << "no <manifest> root tag defined");
+        return false;
+    }
+
+    *out << "package " << package << ";\n\n"
+         << "public class Manifest {\n";
+
+    bool error = false;
+    std::vector<xml::Element*> children = el->getChildElements();
+
+
+    // First write out permissions.
+    *out << "  public static class permission {\n";
+    for (xml::Element* childEl : children) {
+        if (childEl->namespaceUri.empty() && childEl->name == u"permission") {
+            error |= !writeSymbol(diag, res->file.source, childEl, out);
+        }
+    }
+    *out << "  }\n";
+
+    // Next write out permission groups.
+    *out << "  public static class permission_group {\n";
+    for (xml::Element* childEl : children) {
+        if (childEl->namespaceUri.empty() && childEl->name == u"permission-group") {
+            error |= !writeSymbol(diag, res->file.source, childEl, out);
+        }
+    }
+    *out << "  }\n";
+
+    *out << "}\n";
+    return !error;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/java/ManifestClassGenerator.h b/tools/aapt2/java/ManifestClassGenerator.h
new file mode 100644
index 0000000..0f0998f
--- /dev/null
+++ b/tools/aapt2/java/ManifestClassGenerator.h
@@ -0,0 +1,35 @@
+/*
+ * 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_JAVA_MANIFESTCLASSGENERATOR_H
+#define AAPT_JAVA_MANIFESTCLASSGENERATOR_H
+
+#include "Diagnostics.h"
+#include "process/IResourceTableConsumer.h"
+#include "util/StringPiece.h"
+
+#include <iostream>
+
+namespace aapt {
+
+struct ManifestClassGenerator {
+    bool generate(IDiagnostics* diag, const StringPiece16& package, XmlResource* res,
+                  std::ostream* out);
+};
+
+} // namespace aapt
+
+#endif /* AAPT_JAVA_MANIFESTCLASSGENERATOR_H */
diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp
new file mode 100644
index 0000000..1b5bc05
--- /dev/null
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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 "java/ManifestClassGenerator.h"
+
+#include "test/Builders.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(ManifestClassGeneratorTest, NameIsProperlyGeneratedFromSymbol) {
+    std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+    std::unique_ptr<XmlResource> manifest = test::buildXmlDom(R"EOF(
+        <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+          <permission android:name="android.permission.ACCESS_INTERNET" />
+          <permission android:name="android.DO_DANGEROUS_THINGS" />
+          <permission android:name="com.test.sample.permission.HUH" />
+          <permission-group android:name="foo.bar.PERMISSION" />
+        </manifest>)EOF");
+
+    std::stringstream out;
+    ManifestClassGenerator generator;
+    ASSERT_TRUE(generator.generate(context->getDiagnostics(), u"android", manifest.get(), &out));
+
+    std::string actual = out.str();
+
+    const size_t permissionClassPos = actual.find("public static class permission {");
+    const size_t permissionGroupClassPos = actual.find("public static class permission_group {");
+    ASSERT_NE(std::string::npos, permissionClassPos);
+    ASSERT_NE(std::string::npos, permissionGroupClassPos);
+
+    //
+    // Make sure these permissions are in the permission class.
+    //
+
+    size_t pos = actual.find("public static final String ACCESS_INTERNET="
+                             "\"android.permission.ACCESS_INTERNET\";");
+    EXPECT_GT(pos, permissionClassPos);
+    EXPECT_LT(pos, permissionGroupClassPos);
+
+    pos = actual.find("public static final String DO_DANGEROUS_THINGS="
+                      "\"android.DO_DANGEROUS_THINGS\";");
+    EXPECT_GT(pos, permissionClassPos);
+    EXPECT_LT(pos, permissionGroupClassPos);
+
+    pos = actual.find("public static final String HUH=\"com.test.sample.permission.HUH\";");
+    EXPECT_GT(pos, permissionClassPos);
+    EXPECT_LT(pos, permissionGroupClassPos);
+
+    //
+    // Make sure these permissions are in the permission_group class
+    //
+
+    pos = actual.find("public static final String PERMISSION="
+                      "\"foo.bar.PERMISSION\";");
+    EXPECT_GT(pos, permissionGroupClassPos);
+    EXPECT_LT(pos, std::string::npos);
+}
+
+TEST(ManifestClassGeneratorTest, CommentsAndAnnotationsArePresent) {
+    std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+    std::unique_ptr<XmlResource> manifest = test::buildXmlDom(R"EOF(
+        <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+          <!-- Required to access the internet.
+               Added in API 1. -->
+          <permission android:name="android.permission.ACCESS_INTERNET" />
+          <!-- @deprecated This permission is for playing outside. -->
+          <permission android:name="android.permission.PLAY_OUTSIDE" />
+          <!-- This is a private permission for system only!
+               @hide
+               @SystemApi -->
+          <permission android:name="android.permission.SECRET" />
+        </manifest>)EOF");
+
+    std::stringstream out;
+    ManifestClassGenerator generator;
+    ASSERT_TRUE(generator.generate(context->getDiagnostics(), u"android", manifest.get(), &out));
+
+    std::string actual = out.str();
+
+    EXPECT_NE(std::string::npos, actual.find(
+R"EOF(    /**
+     * Required to access the internet.
+     * Added in API 1.
+     */
+    public static final String ACCESS_INTERNET="android.permission.ACCESS_INTERNET";)EOF"));
+
+    EXPECT_NE(std::string::npos, actual.find(
+R"EOF(    /**
+     * @deprecated This permission is for playing outside.
+     */
+    @Deprecated
+    public static final String PLAY_OUTSIDE="android.permission.PLAY_OUTSIDE";)EOF"));
+
+    EXPECT_NE(std::string::npos, actual.find(
+R"EOF(    /**
+     * This is a private permission for system only!
+     * @hide
+     * @SystemApi
+     */
+    @android.annotations.SystemApi
+    public static final String SECRET="android.permission.SECRET";)EOF"));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
new file mode 100644
index 0000000..7683f27
--- /dev/null
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -0,0 +1,247 @@
+/*
+ * 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 "java/ProguardRules.h"
+#include "util/Util.h"
+
+#include <memory>
+#include <string>
+
+namespace aapt {
+namespace proguard {
+
+constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
+
+class BaseVisitor : public xml::Visitor {
+public:
+    BaseVisitor(const Source& source, KeepSet* keepSet) : mSource(source), mKeepSet(keepSet) {
+    }
+
+    virtual void visit(xml::Text*) override {};
+
+    virtual void visit(xml::Namespace* node) override {
+        for (const auto& child : node->children) {
+            child->accept(this);
+        }
+    }
+
+    virtual void visit(xml::Element* node) override {
+        if (!node->namespaceUri.empty()) {
+            Maybe<std::u16string> maybePackage = util::extractPackageFromNamespace(
+                    node->namespaceUri);
+            if (maybePackage) {
+                // This is a custom view, let's figure out the class name from this.
+                std::u16string package = maybePackage.value() + u"." + node->name;
+                if (util::isJavaClassName(package)) {
+                    addClass(node->lineNumber, package);
+                }
+            }
+        } else if (util::isJavaClassName(node->name)) {
+            addClass(node->lineNumber, node->name);
+        }
+
+        for (const auto& child: node->children) {
+            child->accept(this);
+        }
+    }
+
+protected:
+    void addClass(size_t lineNumber, const std::u16string& className) {
+        mKeepSet->addClass(Source(mSource.path, lineNumber), className);
+    }
+
+    void addMethod(size_t lineNumber, const std::u16string& methodName) {
+        mKeepSet->addMethod(Source(mSource.path, lineNumber), methodName);
+    }
+
+private:
+    Source mSource;
+    KeepSet* mKeepSet;
+};
+
+struct LayoutVisitor : public BaseVisitor {
+    LayoutVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
+    }
+
+    virtual void visit(xml::Element* node) override {
+        bool checkClass = false;
+        bool checkName = false;
+        if (node->namespaceUri.empty()) {
+            checkClass = node->name == u"view" || node->name == u"fragment";
+        } else if (node->namespaceUri == kSchemaAndroid) {
+            checkName = node->name == u"fragment";
+        }
+
+        for (const auto& attr : node->attributes) {
+            if (checkClass && attr.namespaceUri.empty() && attr.name == u"class" &&
+                    util::isJavaClassName(attr.value)) {
+                addClass(node->lineNumber, attr.value);
+            } else if (checkName && attr.namespaceUri == kSchemaAndroid && attr.name == u"name" &&
+                    util::isJavaClassName(attr.value)) {
+                addClass(node->lineNumber, attr.value);
+            } else if (attr.namespaceUri == kSchemaAndroid && attr.name == u"onClick") {
+                addMethod(node->lineNumber, attr.value);
+            }
+        }
+
+        BaseVisitor::visit(node);
+    }
+};
+
+struct XmlResourceVisitor : public BaseVisitor {
+    XmlResourceVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
+    }
+
+    virtual void visit(xml::Element* node) override {
+        bool checkFragment = false;
+        if (node->namespaceUri.empty()) {
+            checkFragment = node->name == u"PreferenceScreen" || node->name == u"header";
+        }
+
+        if (checkFragment) {
+            xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"fragment");
+            if (attr && util::isJavaClassName(attr->value)) {
+                addClass(node->lineNumber, attr->value);
+            }
+        }
+
+        BaseVisitor::visit(node);
+    }
+};
+
+struct TransitionVisitor : public BaseVisitor {
+    TransitionVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
+    }
+
+    virtual void visit(xml::Element* node) override {
+        bool checkClass = node->namespaceUri.empty() &&
+                (node->name == u"transition" || node->name == u"pathMotion");
+        if (checkClass) {
+            xml::Attribute* attr = node->findAttribute({}, u"class");
+            if (attr && util::isJavaClassName(attr->value)) {
+                addClass(node->lineNumber, attr->value);
+            }
+        }
+
+        BaseVisitor::visit(node);
+    }
+};
+
+struct ManifestVisitor : public BaseVisitor {
+    ManifestVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) {
+    }
+
+    virtual void visit(xml::Element* node) override {
+        if (node->namespaceUri.empty()) {
+            bool getName = false;
+            if (node->name == u"manifest") {
+                xml::Attribute* attr = node->findAttribute({}, u"package");
+                if (attr) {
+                    mPackage = attr->value;
+                }
+            } else if (node->name == u"application") {
+                getName = true;
+                xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"backupAgent");
+                if (attr) {
+                    Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
+                                                                                    attr->value);
+                    if (result) {
+                        addClass(node->lineNumber, result.value());
+                    }
+                }
+            } else if (node->name == u"activity" || node->name == u"service" ||
+                    node->name == u"receiver" || node->name == u"provider" ||
+                    node->name == u"instrumentation") {
+                getName = true;
+            }
+
+            if (getName) {
+                xml::Attribute* attr = node->findAttribute(kSchemaAndroid, u"name");
+                if (attr) {
+                    Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage,
+                                                                                    attr->value);
+                    if (result) {
+                        addClass(node->lineNumber, result.value());
+                    }
+                }
+            }
+        }
+        BaseVisitor::visit(node);
+    }
+
+    std::u16string mPackage;
+};
+
+bool collectProguardRulesForManifest(const Source& source, XmlResource* res, KeepSet* keepSet) {
+    ManifestVisitor visitor(source, keepSet);
+    if (res->root) {
+        res->root->accept(&visitor);
+        return true;
+    }
+    return false;
+}
+
+bool collectProguardRules(const Source& source, XmlResource* res, KeepSet* keepSet) {
+    if (!res->root) {
+        return false;
+    }
+
+    switch (res->file.name.type) {
+        case ResourceType::kLayout: {
+            LayoutVisitor visitor(source, keepSet);
+            res->root->accept(&visitor);
+            break;
+        }
+
+        case ResourceType::kXml: {
+            XmlResourceVisitor visitor(source, keepSet);
+            res->root->accept(&visitor);
+            break;
+        }
+
+        case ResourceType::kTransition: {
+            TransitionVisitor visitor(source, keepSet);
+            res->root->accept(&visitor);
+            break;
+        }
+
+        default:
+            break;
+    }
+    return true;
+}
+
+bool writeKeepSet(std::ostream* out, const KeepSet& keepSet) {
+    for (const auto& entry : keepSet.mKeepSet) {
+        for (const Source& source : entry.second) {
+            *out << "// Referenced at " << source << "\n";
+        }
+        *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
+    }
+
+    for (const auto& entry : keepSet.mKeepMethodSet) {
+        for (const Source& source : entry.second) {
+            *out << "// Referenced at " << source << "\n";
+        }
+        *out << "-keepclassmembers class * { *** " << entry.first << "(...); }\n" << std::endl;
+    }
+    return true;
+}
+
+} // namespace proguard
+} // namespace aapt
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
new file mode 100644
index 0000000..be61eb9
--- /dev/null
+++ b/tools/aapt2/java/ProguardRules.h
@@ -0,0 +1,58 @@
+/*
+ * 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_PROGUARD_RULES_H
+#define AAPT_PROGUARD_RULES_H
+
+#include "Resource.h"
+#include "Source.h"
+
+#include "process/IResourceTableConsumer.h"
+
+#include <map>
+#include <ostream>
+#include <set>
+#include <string>
+
+namespace aapt {
+namespace proguard {
+
+class KeepSet {
+public:
+    inline void addClass(const Source& source, const std::u16string& className) {
+        mKeepSet[className].insert(source);
+    }
+
+    inline void addMethod(const Source& source, const std::u16string& methodName) {
+        mKeepMethodSet[methodName].insert(source);
+    }
+
+private:
+    friend bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
+
+    std::map<std::u16string, std::set<Source>> mKeepSet;
+    std::map<std::u16string, std::set<Source>> mKeepMethodSet;
+};
+
+bool collectProguardRulesForManifest(const Source& source, XmlResource* res, KeepSet* keepSet);
+bool collectProguardRules(const Source& source, XmlResource* res, KeepSet* keepSet);
+
+bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
+
+} // namespace proguard
+} // namespace aapt
+
+#endif // AAPT_PROGUARD_RULES_H