blob: 779a346f9289073a6f7a509632f8d0bd0ed2d68f [file] [log] [blame]
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001/*
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 "Resource.h"
19#include "ResourceTable.h"
20#include "ResourceValues.h"
21#include "StringPiece.h"
22
Adam Lesinskica2fc352015-04-03 12:08:26 -070023#include <algorithm>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080024#include <ostream>
25#include <set>
26#include <sstream>
27#include <tuple>
28
29namespace aapt {
30
31// The number of attributes to emit per line in a Styleable array.
32constexpr size_t kAttribsPerLine = 4;
33
34JavaClassGenerator::JavaClassGenerator(std::shared_ptr<const ResourceTable> table,
35 Options options) :
36 mTable(table), mOptions(options) {
37}
38
39static void generateHeader(std::ostream& out, const StringPiece16& package) {
40 out << "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n"
41 " *\n"
42 " * This class was automatically generated by the\n"
43 " * aapt tool from the resource data it found. It\n"
44 " * should not be modified by hand.\n"
45 " */\n\n";
46 out << "package " << package << ";"
47 << std::endl
48 << std::endl;
49}
50
51static const std::set<StringPiece16> sJavaIdentifiers = {
52 u"abstract", u"assert", u"boolean", u"break", u"byte",
53 u"case", u"catch", u"char", u"class", u"const", u"continue",
54 u"default", u"do", u"double", u"else", u"enum", u"extends",
55 u"final", u"finally", u"float", u"for", u"goto", u"if",
56 u"implements", u"import", u"instanceof", u"int", u"interface",
57 u"long", u"native", u"new", u"package", u"private", u"protected",
58 u"public", u"return", u"short", u"static", u"strictfp", u"super",
59 u"switch", u"synchronized", u"this", u"throw", u"throws",
60 u"transient", u"try", u"void", u"volatile", u"while", u"true",
61 u"false", u"null"
62};
63
64static bool isValidSymbol(const StringPiece16& symbol) {
65 return sJavaIdentifiers.find(symbol) == sJavaIdentifiers.end();
66}
67
68/*
69 * Java symbols can not contain . or -, but those are valid in a resource name.
70 * Replace those with '_'.
71 */
72static std::u16string transform(const StringPiece16& symbol) {
73 std::u16string output = symbol.toString();
74 for (char16_t& c : output) {
75 if (c == u'.' || c == u'-') {
76 c = u'_';
77 }
78 }
79 return output;
80}
81
82bool JavaClassGenerator::generateType(std::ostream& out, const ResourceTableType& type,
83 size_t packageId) {
84 const StringPiece finalModifier = mOptions.useFinal ? " final" : "";
85
86 for (const auto& entry : type.entries) {
87 ResourceId id = { packageId, type.typeId, entry->entryId };
88 assert(id.isValid());
89
90 if (!isValidSymbol(entry->name)) {
Adam Lesinskica2fc352015-04-03 12:08:26 -070091 std::stringstream err;
92 err << "invalid symbol name '"
93 << StringPiece16(entry->name)
94 << "'";
95 mError = err.str();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080096 return false;
97 }
98
99 out << " "
100 << "public static" << finalModifier
101 << " int " << transform(entry->name) << " = " << id << ";" << std::endl;
102 }
103 return true;
104}
105
106struct GenArgs : ValueVisitorArgs {
107 GenArgs(std::ostream& o, const ResourceEntry& e) : out(o), entry(e) {
108 }
109
110 std::ostream& out;
111 const ResourceEntry& entry;
112};
113
114void JavaClassGenerator::visit(const Styleable& styleable, ValueVisitorArgs& a) {
115 const StringPiece finalModifier = mOptions.useFinal ? " final" : "";
116 std::ostream& out = static_cast<GenArgs&>(a).out;
117 const ResourceEntry& entry = static_cast<GenArgs&>(a).entry;
118
119 // This must be sorted by resource ID.
120 std::vector<std::pair<ResourceId, StringPiece16>> sortedAttributes;
121 sortedAttributes.reserve(styleable.entries.size());
122 for (const auto& attr : styleable.entries) {
123 assert(attr.id.isValid() && "no ID set for Styleable entry");
124 assert(attr.name.isValid() && "no name set for Styleable entry");
125 sortedAttributes.emplace_back(attr.id, attr.name.entry);
126 }
127 std::sort(sortedAttributes.begin(), sortedAttributes.end());
128
129 // First we emit the array containing the IDs of each attribute.
130 out << " "
131 << "public static final int[] " << transform(entry.name) << " = {";
132
133 const size_t attrCount = sortedAttributes.size();
134 for (size_t i = 0; i < attrCount; i++) {
135 if (i % kAttribsPerLine == 0) {
136 out << std::endl << " ";
137 }
138
139 out << sortedAttributes[i].first;
140 if (i != attrCount - 1) {
141 out << ", ";
142 }
143 }
144 out << std::endl << " };" << std::endl;
145
146 // Now we emit the indices into the array.
147 for (size_t i = 0; i < attrCount; i++) {
148 out << " "
149 << "public static" << finalModifier
150 << " int " << transform(entry.name) << "_" << transform(sortedAttributes[i].second)
151 << " = " << i << ";" << std::endl;
152 }
153}
154
155bool JavaClassGenerator::generate(std::ostream& out) {
156 const size_t packageId = mTable->getPackageId();
157
158 generateHeader(out, mTable->getPackage());
159
160 out << "public final class R {" << std::endl;
161
162 for (const auto& type : *mTable) {
163 out << " public static final class " << type->type << " {" << std::endl;
164 bool result;
165 if (type->type == ResourceType::kStyleable) {
166 for (const auto& entry : type->entries) {
167 assert(!entry->values.empty());
168 if (!isValidSymbol(entry->name)) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700169 std::stringstream err;
170 err << "invalid symbol name '"
171 << StringPiece16(entry->name)
172 << "'";
173 mError = err.str();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800174 return false;
175 }
176 entry->values.front().value->accept(*this, GenArgs{ out, *entry });
177 }
178 } else {
179 result = generateType(out, *type, packageId);
180 }
181
182 if (!result) {
183 return false;
184 }
185 out << " }" << std::endl;
186 }
187
188 out << "}" << std::endl;
189 return true;
190}
191
192} // namespace aapt