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