JavaClassGenerator.cpp revision 838a68705bc79527e592265371cbe4d8e888d9d9
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "JavaClassGenerator.h"
18#include "NameMangler.h"
19#include "Resource.h"
20#include "ResourceTable.h"
21#include "ResourceValues.h"
22#include "StringPiece.h"
23
24#include <algorithm>
25#include <ostream>
26#include <set>
27#include <sstream>
28#include <tuple>
29
30namespace aapt {
31
32// The number of attributes to emit per line in a Styleable array.
33constexpr size_t kAttribsPerLine = 4;
34
35JavaClassGenerator::JavaClassGenerator(const std::shared_ptr<const ResourceTable>& table,
36                                       Options options) :
37        mTable(table), mOptions(options) {
38}
39
40static void generateHeader(std::ostream& out, const StringPiece16& package) {
41    out << "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n"
42           " *\n"
43           " * This class was automatically generated by the\n"
44           " * aapt tool from the resource data it found. It\n"
45           " * should not be modified by hand.\n"
46           " */\n\n";
47    out << "package " << package << ";"
48        << std::endl
49        << std::endl;
50}
51
52static const std::set<StringPiece16> sJavaIdentifiers = {
53    u"abstract", u"assert", u"boolean", u"break", u"byte",
54    u"case", u"catch", u"char", u"class", u"const", u"continue",
55    u"default", u"do", u"double", u"else", u"enum", u"extends",
56    u"final", u"finally", u"float", u"for", u"goto", u"if",
57    u"implements", u"import", u"instanceof", u"int", u"interface",
58    u"long", u"native", u"new", u"package", u"private", u"protected",
59    u"public", u"return", u"short", u"static", u"strictfp", u"super",
60    u"switch", u"synchronized", u"this", u"throw", u"throws",
61    u"transient", u"try", u"void", u"volatile", u"while", u"true",
62    u"false", u"null"
63};
64
65static bool isValidSymbol(const StringPiece16& symbol) {
66    return sJavaIdentifiers.find(symbol) == sJavaIdentifiers.end();
67}
68
69/*
70 * Java symbols can not contain . or -, but those are valid in a resource name.
71 * Replace those with '_'.
72 */
73static std::u16string transform(const StringPiece16& symbol) {
74    std::u16string output = symbol.toString();
75    for (char16_t& c : output) {
76        if (c == u'.' || c == u'-') {
77            c = u'_';
78        }
79    }
80    return output;
81}
82
83struct GenArgs : ValueVisitorArgs {
84    GenArgs(std::ostream* o, std::u16string* e) : out(o), entryName(e) {
85    }
86
87    std::ostream* out;
88    std::u16string* entryName;
89};
90
91void JavaClassGenerator::visit(const Styleable& styleable, ValueVisitorArgs& a) {
92    const StringPiece finalModifier = mOptions.useFinal ? " final" : "";
93    std::ostream* out = static_cast<GenArgs&>(a).out;
94    std::u16string* entryName = static_cast<GenArgs&>(a).entryName;
95
96    // This must be sorted by resource ID.
97    std::vector<std::pair<ResourceId, ResourceNameRef>> sortedAttributes;
98    sortedAttributes.reserve(styleable.entries.size());
99    for (const auto& attr : styleable.entries) {
100        assert(attr.id.isValid() && "no ID set for Styleable entry");
101        assert(attr.name.isValid() && "no name set for Styleable entry");
102        sortedAttributes.emplace_back(attr.id, attr.name);
103    }
104    std::sort(sortedAttributes.begin(), sortedAttributes.end());
105
106    // First we emit the array containing the IDs of each attribute.
107    *out << "        "
108         << "public static final int[] " << transform(*entryName) << " = {";
109
110    const size_t attrCount = sortedAttributes.size();
111    for (size_t i = 0; i < attrCount; i++) {
112        if (i % kAttribsPerLine == 0) {
113            *out << std::endl << "            ";
114        }
115
116        *out << sortedAttributes[i].first;
117        if (i != attrCount - 1) {
118            *out << ", ";
119        }
120    }
121    *out << std::endl << "        };" << std::endl;
122
123    // Now we emit the indices into the array.
124    for (size_t i = 0; i < attrCount; i++) {
125        *out << "        "
126             << "public static" << finalModifier
127             << " int " << transform(*entryName);
128
129        // We may reference IDs from other packages, so prefix the entry name with
130        // the package.
131        const ResourceNameRef& itemName = sortedAttributes[i].second;
132        if (itemName.package != mTable->getPackage()) {
133            *out << "_" << transform(itemName.package);
134        }
135        *out << "_" << transform(itemName.entry) << " = " << i << ";" << std::endl;
136    }
137}
138
139bool JavaClassGenerator::generateType(const std::u16string& package, size_t packageId,
140                                      const ResourceTableType& type, std::ostream& out) {
141    const StringPiece finalModifier = mOptions.useFinal ? " final" : "";
142
143    std::u16string unmangledPackage;
144    std::u16string unmangledName;
145    for (const auto& entry : type.entries) {
146        ResourceId id = { packageId, type.typeId, entry->entryId };
147        assert(id.isValid());
148
149        unmangledName = entry->name;
150        if (NameMangler::unmangle(&unmangledName, &unmangledPackage)) {
151            // The entry name was mangled, and we successfully unmangled it.
152            // Check that we want to emit this symbol.
153            if (package != unmangledPackage) {
154                // Skip the entry if it doesn't belong to the package we're writing.
155                continue;
156            }
157        } else {
158            if (package != mTable->getPackage()) {
159                // We are processing a mangled package name,
160                // but this is a non-mangled resource.
161                continue;
162            }
163        }
164
165        if (!isValidSymbol(unmangledName)) {
166            ResourceNameRef resourceName = { package, type.type, unmangledName };
167            std::stringstream err;
168            err << "invalid symbol name '" << resourceName << "'";
169            mError = err.str();
170            return false;
171        }
172
173        if (type.type == ResourceType::kStyleable) {
174            assert(!entry->values.empty());
175            entry->values.front().value->accept(*this, GenArgs{ &out, &unmangledName });
176        } else {
177            out << "        " << "public static" << finalModifier
178                << " int " << transform(unmangledName) << " = " << id << ";" << std::endl;
179        }
180    }
181    return true;
182}
183
184bool JavaClassGenerator::generate(const std::u16string& package, std::ostream& out) {
185    const size_t packageId = mTable->getPackageId();
186
187    generateHeader(out, package);
188
189    out << "public final class R {" << std::endl;
190
191    for (const auto& type : *mTable) {
192        out << "    public static final class " << type->type << " {" << std::endl;
193        if (!generateType(package, packageId, *type, out)) {
194            return false;
195        }
196        out << "    }" << std::endl;
197    }
198
199    out << "}" << std::endl;
200    return true;
201}
202
203} // namespace aapt
204