Debug.cpp \
Flags.cpp \
java/AnnotationProcessor.cpp \
+ java/ClassDefinition.cpp \
java/JavaClassGenerator.cpp \
java/ManifestClassGenerator.cpp \
java/ProguardRules.cpp \
mComment << "\n *";
}
-void AnnotationProcessor::writeToStream(std::ostream* out, const StringPiece& prefix) {
+void AnnotationProcessor::writeToStream(std::ostream* out, const StringPiece& prefix) const {
if (mHasComments) {
std::string result = mComment.str();
for (StringPiece line : util::tokenize<char>(result, '\n')) {
/**
* Writes the comments and annotations to the stream, with the given prefix before each line.
*/
- void writeToStream(std::ostream* out, const StringPiece& prefix);
+ void writeToStream(std::ostream* out, const StringPiece& prefix) const;
private:
enum : uint32_t {
--- /dev/null
+/*
+ * Copyright (C) 2016 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/ClassDefinition.h"
+#include "util/StringPiece.h"
+
+#include <ostream>
+
+namespace aapt {
+
+bool ClassDefinition::empty() const {
+ for (const std::unique_ptr<ClassMember>& member : mMembers) {
+ if (!member->empty()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void ClassDefinition::writeToStream(const StringPiece& prefix, bool final,
+ std::ostream* out) const {
+ if (mMembers.empty() && !mCreateIfEmpty) {
+ return;
+ }
+
+ ClassMember::writeToStream(prefix, final, out);
+
+ *out << prefix << "public ";
+ if (mQualifier == ClassQualifier::Static) {
+ *out << "static ";
+ }
+ *out << "final class " << mName << " {\n";
+
+ std::string newPrefix = prefix.toString();
+ newPrefix.append(kIndent);
+
+ for (const std::unique_ptr<ClassMember>& member : mMembers) {
+ member->writeToStream(newPrefix, final, out);
+ *out << "\n";
+ }
+
+ *out << prefix << "}";
+}
+
+constexpr static const char* sWarningHeader =
+ "/* 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";
+
+bool ClassDefinition::writeJavaFile(const ClassDefinition* def,
+ const StringPiece& package,
+ bool final,
+ std::ostream* out) {
+ *out << sWarningHeader << "package " << package << ";\n\n";
+ def->writeToStream("", final, out);
+ return bool(*out);
+}
+
+} // namespace aapt
--- /dev/null
+/*
+ * 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_CLASSDEFINITION_H
+#define AAPT_JAVA_CLASSDEFINITION_H
+
+#include "Resource.h"
+#include "java/AnnotationProcessor.h"
+#include "util/StringPiece.h"
+#include "util/Util.h"
+
+#include <android-base/macros.h>
+#include <sstream>
+#include <string>
+
+namespace aapt {
+
+// The number of attributes to emit per line in a Styleable array.
+constexpr static size_t kAttribsPerLine = 4;
+constexpr static const char* kIndent = " ";
+
+class ClassMember {
+public:
+ virtual ~ClassMember() = default;
+
+ AnnotationProcessor* getCommentBuilder() {
+ return &mProcessor;
+ }
+
+ virtual bool empty() const = 0;
+
+ virtual void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const {
+ mProcessor.writeToStream(out, prefix);
+ }
+
+private:
+ AnnotationProcessor mProcessor;
+};
+
+template <typename T>
+class PrimitiveMember : public ClassMember {
+public:
+ PrimitiveMember(const StringPiece& name, const T& val) :
+ mName(name.toString()), mVal(val) {
+ }
+
+ bool empty() const override {
+ return false;
+ }
+
+ void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override {
+ ClassMember::writeToStream(prefix, final, out);
+
+ *out << prefix << "public static " << (final ? "final " : "")
+ << "int " << mName << "=" << mVal << ";";
+ }
+
+private:
+ std::string mName;
+ T mVal;
+
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
+};
+
+/**
+ * Specialization for strings so they get the right type and are quoted with "".
+ */
+template <>
+class PrimitiveMember<std::string> : public ClassMember {
+public:
+ PrimitiveMember(const StringPiece& name, const std::string& val) :
+ mName(name.toString()), mVal(val) {
+ }
+
+ bool empty() const override {
+ return false;
+ }
+
+ void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override {
+ ClassMember::writeToStream(prefix, final, out);
+
+ *out << prefix << "public static " << (final ? "final " : "")
+ << "String " << mName << "=\"" << mVal << "\";";
+ }
+
+private:
+ std::string mName;
+ std::string mVal;
+
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
+};
+
+using IntMember = PrimitiveMember<uint32_t>;
+using ResourceMember = PrimitiveMember<ResourceId>;
+using StringMember = PrimitiveMember<std::string>;
+
+template <typename T>
+class PrimitiveArrayMember : public ClassMember {
+public:
+ PrimitiveArrayMember(const StringPiece& name) :
+ mName(name.toString()) {
+ }
+
+ void addElement(const T& val) {
+ mElements.push_back(val);
+ }
+
+ bool empty() const override {
+ return false;
+ }
+
+ void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override {
+ ClassMember::writeToStream(prefix, final, out);
+
+ *out << "public static final int[] " << mName << "={";
+
+ const auto begin = mElements.begin();
+ const auto end = mElements.end();
+ for (auto current = begin; current != end; ++current) {
+ if (std::distance(begin, current) % kAttribsPerLine == 0) {
+ *out << "\n" << prefix << kIndent << kIndent;
+ }
+
+ *out << *current;
+ if (std::distance(current, end) > 1) {
+ *out << ", ";
+ }
+ }
+ *out << "\n" << prefix << kIndent <<"};";
+ }
+
+private:
+ std::string mName;
+ std::vector<T> mElements;
+
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveArrayMember);
+};
+
+using ResourceArrayMember = PrimitiveArrayMember<ResourceId>;
+
+enum class ClassQualifier {
+ None,
+ Static
+};
+
+class ClassDefinition : public ClassMember {
+public:
+ static bool writeJavaFile(const ClassDefinition* def,
+ const StringPiece& package,
+ bool final,
+ std::ostream* out);
+
+ ClassDefinition(const StringPiece& name, ClassQualifier qualifier, bool createIfEmpty) :
+ mName(name.toString()), mQualifier(qualifier), mCreateIfEmpty(createIfEmpty) {
+ }
+
+ void addMember(std::unique_ptr<ClassMember> member) {
+ mMembers.push_back(std::move(member));
+ }
+
+ bool empty() const override;
+ void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override;
+
+private:
+ std::string mName;
+ ClassQualifier mQualifier;
+ bool mCreateIfEmpty;
+ std::vector<std::unique_ptr<ClassMember>> mMembers;
+
+ DISALLOW_COPY_AND_ASSIGN(ClassDefinition);
+};
+
+} // namespace aapt
+
+#endif /* AAPT_JAVA_CLASSDEFINITION_H */
+++ /dev/null
-/*
- * 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_CLASSDEFINITION_H
-#define AAPT_JAVA_CLASSDEFINITION_H
-
-#include "Resource.h"
-#include "java/AnnotationProcessor.h"
-#include "util/StringPiece.h"
-#include "util/Util.h"
-
-#include <sstream>
-#include <string>
-
-namespace aapt {
-
-struct ClassDefinitionWriterOptions {
- bool useFinalQualifier = false;
- bool forceCreationIfEmpty = false;
-};
-
-/**
- * Writes a class for use in R.java or Manifest.java.
- */
-class ClassDefinitionWriter {
-public:
- ClassDefinitionWriter(const StringPiece& name, const ClassDefinitionWriterOptions& options) :
- mName(name.toString()), mOptions(options), mStarted(false) {
- }
-
- ClassDefinitionWriter(const StringPiece16& name, const ClassDefinitionWriterOptions& options) :
- mName(util::utf16ToUtf8(name)), mOptions(options), mStarted(false) {
- }
-
- void addIntMember(const StringPiece& name, AnnotationProcessor* processor,
- const uint32_t val) {
- ensureClassDeclaration();
- if (processor) {
- processor->writeToStream(&mOut, kIndent);
- }
- mOut << kIndent << "public static " << (mOptions.useFinalQualifier ? "final " : "")
- << "int " << name << "=" << val << ";\n";
- }
-
- void addStringMember(const StringPiece16& name, AnnotationProcessor* processor,
- const StringPiece16& val) {
- ensureClassDeclaration();
- if (processor) {
- processor->writeToStream(&mOut, kIndent);
- }
- mOut << kIndent << "public static " << (mOptions.useFinalQualifier ? "final " : "")
- << "String " << name << "=\"" << val << "\";\n";
- }
-
- void addResourceMember(const StringPiece& name, AnnotationProcessor* processor,
- const ResourceId id) {
- ensureClassDeclaration();
- if (processor) {
- processor->writeToStream(&mOut, kIndent);
- }
- mOut << kIndent << "public static " << (mOptions.useFinalQualifier ? "final " : "")
- << "int " << name << "=" << id <<";\n";
- }
-
- template <typename Iterator, typename FieldAccessorFunc>
- void addArrayMember(const StringPiece& name, AnnotationProcessor* processor,
- const Iterator begin, const Iterator end, FieldAccessorFunc f) {
- ensureClassDeclaration();
- if (processor) {
- processor->writeToStream(&mOut, kIndent);
- }
- mOut << kIndent << "public static final int[] " << name << "={";
-
- for (Iterator current = begin; current != end; ++current) {
- if (std::distance(begin, current) % kAttribsPerLine == 0) {
- mOut << "\n" << kIndent << kIndent;
- }
-
- mOut << f(*current);
- if (std::distance(current, end) > 1) {
- mOut << ", ";
- }
- }
- mOut << "\n" << kIndent <<"};\n";
- }
-
- void writeToStream(std::ostream* out, const StringPiece& prefix,
- AnnotationProcessor* processor=nullptr) {
- if (mOptions.forceCreationIfEmpty) {
- ensureClassDeclaration();
- }
-
- if (!mStarted) {
- return;
- }
-
- if (processor) {
- processor->writeToStream(out, prefix);
- }
-
- std::string result = mOut.str();
- for (StringPiece line : util::tokenize<char>(result, '\n')) {
- *out << prefix << line << "\n";
- }
- *out << prefix << "}\n";
- }
-
-private:
- constexpr static const char* kIndent = " ";
-
- // The number of attributes to emit per line in a Styleable array.
- constexpr static size_t kAttribsPerLine = 4;
-
- void ensureClassDeclaration() {
- if (!mStarted) {
- mStarted = true;
- mOut << "public static final class " << mName << " {\n";
- }
- }
-
- std::stringstream mOut;
- std::string mName;
- ClassDefinitionWriterOptions mOptions;
- bool mStarted;
-};
-
-} // namespace aapt
-
-#endif /* AAPT_JAVA_CLASSDEFINITION_H */
#include "ValueVisitor.h"
#include "java/AnnotationProcessor.h"
-#include "java/ClassDefinitionWriter.h"
+#include "java/ClassDefinition.h"
#include "java/JavaClassGenerator.h"
#include "process/SymbolTable.h"
#include "util/StringPiece.h"
mContext(context), 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",
if (typeMask & android::ResTable_map::TYPE_REFERENCE) {
processor->appendComment(
"<p>May be a reference to another resource, in the form\n"
- "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a theme\n"
- "attribute in the form\n"
- "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
+ "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a theme\n"
+ "attribute in the form\n"
+ "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
}
if (typeMask & android::ResTable_map::TYPE_STRING) {
processor->appendComment(
"<p>May be a string value, using '\\\\;' to escape characters such as\n"
- "'\\\\n' or '\\\\uxxxx' for a unicode character;");
+ "'\\\\n' or '\\\\uxxxx' for a unicode character;");
}
if (typeMask & android::ResTable_map::TYPE_INTEGER) {
if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
processor->appendComment(
"<p>May be a boolean value, such as \"<code>true</code>\" or\n"
- "\"<code>false</code>\".");
+ "\"<code>false</code>\".");
}
if (typeMask & android::ResTable_map::TYPE_COLOR) {
processor->appendComment(
"<p>May be a color value, in the form of \"<code>#<i>rgb</i></code>\",\n"
- "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code\", or \n"
- "\"<code>#<i>aarrggbb</i></code>\".");
+ "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code\", or \n"
+ "\"<code>#<i>aarrggbb</i></code>\".");
}
if (typeMask & android::ResTable_map::TYPE_FLOAT) {
if (typeMask & android::ResTable_map::TYPE_DIMENSION) {
processor->appendComment(
"<p>May be a dimension value, which is a floating point number appended with a\n"
- "unit such as \"<code>14.5sp</code>\".\n"
- "Available units are: px (pixels), dp (density-independent pixels),\n"
- "sp (scaled pixels based on preferred font size), in (inches), and\n"
- "mm (millimeters).");
+ "unit such as \"<code>14.5sp</code>\".\n"
+ "Available units are: px (pixels), dp (density-independent pixels),\n"
+ "sp (scaled pixels based on preferred font size), in (inches), and\n"
+ "mm (millimeters).");
}
if (typeMask & android::ResTable_map::TYPE_FRACTION) {
processor->appendComment(
"<p>May be a fractional value, which is a floating point number appended with\n"
- "either % or %p, such as \"<code>14.5%</code>\".\n"
- "The % suffix always means a percentage of the base size;\n"
- "the optional %p suffix provides a size relative to some parent container.");
+ "either % or %p, such as \"<code>14.5%</code>\".\n"
+ "The % suffix always means a percentage of the base size;\n"
+ "the optional %p suffix provides a size relative to some parent container.");
}
if (typeMask & (android::ResTable_map::TYPE_FLAGS | android::ResTable_map::TYPE_ENUM)) {
if (typeMask & android::ResTable_map::TYPE_FLAGS) {
processor->appendComment(
"<p>Must be one or more (separated by '|') of the following "
- "constant values.</p>");
+ "constant values.</p>");
} else {
processor->appendComment("<p>Must be one of the following constant values.</p>");
}
processor->appendComment("<table>\n<colgroup align=\"left\" />\n"
- "<colgroup align=\"left\" />\n"
- "<colgroup align=\"left\" />\n"
- "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
+ "<colgroup align=\"left\" />\n"
+ "<colgroup align=\"left\" />\n"
+ "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
for (const Attribute::Symbol& symbol : attr->symbols) {
std::stringstream line;
line << "<tr><td>" << symbol.symbol.name.value().entry << "</td>"
}
}
-void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outClassDef,
- AnnotationProcessor* processor,
- const StringPiece16& packageNameToGenerate,
- const std::u16string& entryName,
- const Styleable* styleable) {
+void JavaClassGenerator::addMembersToStyleableClass(const StringPiece16& packageNameToGenerate,
+ const std::u16string& entryName,
+ const Styleable* styleable,
+ ClassDefinition* outStyleableClassDef) {
const std::string className = transform(entryName);
+ std::unique_ptr<ResourceArrayMember> styleableArrayDef =
+ util::make_unique<ResourceArrayMember>(className);
+
// This must be sorted by resource ID.
std::vector<StyleableAttr> sortedAttributes;
sortedAttributes.reserve(styleable->entries.size());
assert((!mOptions.useFinal || attr.id) && "no ID set for Styleable entry");
assert(attr.name && "no name set for Styleable entry");
+ // We will need the unmangled, transformed name in the comments and the field,
+ // so create it once and cache it in this StyleableAttr data structure.
StyleableAttr styleableAttr = {};
styleableAttr.attrRef = &attr;
styleableAttr.fieldName = transformNestedAttr(attr.name.value(), className,
mangledReference.name = mangledName;
}
+ // Look up the symbol so that we can write out in the comments what are possible
+ // legal values for this attribute.
const SymbolTable::Symbol* symbol = mContext->getExternalSymbols()->findByReference(
mangledReference);
if (symbol) {
}
sortedAttributes.push_back(std::move(styleableAttr));
}
+
+ // Sort the attributes by ID.
std::sort(sortedAttributes.begin(), sortedAttributes.end(), lessStyleableAttr);
const size_t attrCount = sortedAttributes.size();
-
if (attrCount > 0) {
// Build the comment string for the Styleable. It includes details about the
// child attributes.
} else {
styleableComment << "Attributes that can be used with a " << className << ".\n";
}
+
styleableComment <<
"<p>Includes the following attributes:</p>\n"
"<table>\n"
"<colgroup align=\"left\" />\n"
"<tr><th>Attribute</th><th>Description</th></tr>\n";
- for (const auto& entry : sortedAttributes) {
+ for (const StyleableAttr& entry : sortedAttributes) {
const ResourceName& attrName = entry.attrRef->name.value();
styleableComment << "<tr><td>";
styleableComment << "<code>{@link #"
styleableComment << "</td></tr>\n";
}
styleableComment << "</table>\n";
- for (const auto& entry : sortedAttributes) {
+
+ for (const StyleableAttr& entry : sortedAttributes) {
styleableComment << "@see #" << entry.fieldName << "\n";
}
- processor->appendComment(styleableComment.str());
+
+ styleableArrayDef->getCommentBuilder()->appendComment(styleableComment.str());
}
- auto accessorFunc = [](const StyleableAttr& a) -> ResourceId {
- return a.attrRef->id ? a.attrRef->id.value() : ResourceId(0);
- };
+ // Add the ResourceIds to the array member.
+ for (const StyleableAttr& styleableAttr : sortedAttributes) {
+ styleableArrayDef->addElement(
+ styleableAttr.attrRef->id ? styleableAttr.attrRef->id.value() : ResourceId(0));
+ }
- // First we emit the array containing the IDs of each attribute.
- outClassDef->addArrayMember(className, processor,
- sortedAttributes.begin(),
- sortedAttributes.end(),
- accessorFunc);
+ // Add the Styleable array to the Styleable class.
+ outStyleableClassDef->addMember(std::move(styleableArrayDef));
// Now we emit the indices into the array.
for (size_t i = 0; i < attrCount; i++) {
packageName = mContext->getCompilationPackage();
}
- AnnotationProcessor attrProcessor;
+ std::unique_ptr<IntMember> indexMember = util::make_unique<IntMember>(
+ sortedAttributes[i].fieldName, i);
+
+ AnnotationProcessor* attrProcessor = indexMember->getCommentBuilder();
StringPiece16 comment = styleableAttr.attrRef->getComment();
if (styleableAttr.attribute && comment.empty()) {
}
if (!comment.empty()) {
- attrProcessor.appendComment("<p>\n@attr description");
- attrProcessor.appendComment(comment);
+ attrProcessor->appendComment("<p>\n@attr description");
+ attrProcessor->appendComment(comment);
} else {
std::stringstream defaultComment;
defaultComment
<< "{@link " << packageName << ".R.attr#" << transform(attrName.entry) << "}\n"
<< "attribute's value can be found in the "
<< "{@link #" << className << "} array.";
- attrProcessor.appendComment(defaultComment.str());
+ attrProcessor->appendComment(defaultComment.str());
}
- attrProcessor.appendNewLine();
+ attrProcessor->appendNewLine();
if (styleableAttr.attribute) {
- addAttributeFormatDoc(&attrProcessor, styleableAttr.attribute.get());
- attrProcessor.appendNewLine();
+ addAttributeFormatDoc(attrProcessor, styleableAttr.attribute.get());
+ attrProcessor->appendNewLine();
}
std::stringstream doclavaName;
doclavaName << "@attr name " << packageName << ":" << attrName.entry;;
- attrProcessor.appendComment(doclavaName.str());
- outClassDef->addIntMember(sortedAttributes[i].fieldName, &attrProcessor, i);
+ attrProcessor->appendComment(doclavaName.str());
+
+ outStyleableClassDef->addMember(std::move(indexMember));
}
}
-bool JavaClassGenerator::writeEntriesForClass(ClassDefinitionWriter* outClassDef,
- const StringPiece16& packageNameToGenerate,
- const ResourceTablePackage* package,
- const ResourceTableType* type) {
+bool JavaClassGenerator::addMembersToTypeClass(const StringPiece16& packageNameToGenerate,
+ const ResourceTablePackage* package,
+ const ResourceTableType* type,
+ ClassDefinition* outTypeClassDef) {
+
for (const auto& entry : type->entries) {
if (skipSymbol(entry->symbolStatus.state)) {
continue;
return false;
}
- // Build the comments and annotations for this entry.
-
- AnnotationProcessor processor;
- if (entry->symbolStatus.state != SymbolState::kUndefined) {
- processor.appendComment(entry->symbolStatus.comment);
- }
-
- for (const auto& configValue : entry->values) {
- processor.appendComment(configValue->value->getComment());
- }
-
- // If this is an Attribute, append the format Javadoc.
- if (!entry->values.empty()) {
- if (Attribute* attr = valueCast<Attribute>(entry->values.front()->value.get())) {
- // We list out the available values for the given attribute.
- addAttributeFormatDoc(&processor, attr);
- }
- }
-
if (type->type == ResourceType::kStyleable) {
assert(!entry->values.empty());
+
const Styleable* styleable = static_cast<const Styleable*>(
entry->values.front()->value.get());
- writeStyleableEntryForClass(outClassDef, &processor, packageNameToGenerate,
- unmangledName, styleable);
+
+ // Comments are handled within this method.
+ addMembersToStyleableClass(packageNameToGenerate, unmangledName, styleable,
+ outTypeClassDef);
} else {
- outClassDef->addResourceMember(transform(unmangledName), &processor, id);
+ std::unique_ptr<ResourceMember> resourceMember =
+ util::make_unique<ResourceMember>(transform(unmangledName), id);
+
+ // Build the comments and annotations for this entry.
+ AnnotationProcessor* processor = resourceMember->getCommentBuilder();
+
+ // Add the comments from any <public> tags.
+ if (entry->symbolStatus.state != SymbolState::kUndefined) {
+ processor->appendComment(entry->symbolStatus.comment);
+ }
+
+ // Add the comments from all configurations of this entry.
+ for (const auto& configValue : entry->values) {
+ processor->appendComment(configValue->value->getComment());
+ }
+
+ // If this is an Attribute, append the format Javadoc.
+ if (!entry->values.empty()) {
+ if (Attribute* attr = valueCast<Attribute>(entry->values.front()->value.get())) {
+ // We list out the available values for the given attribute.
+ addAttributeFormatDoc(processor, attr);
+ }
+ }
+
+ outTypeClassDef->addMember(std::move(resourceMember));
}
}
return true;
bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate,
const StringPiece16& outPackageName, std::ostream* out) {
- generateHeader(outPackageName, out);
- *out << "public final class R {\n";
+ ClassDefinition rClass("R", ClassQualifier::None, true);
for (const auto& package : mTable->packages) {
for (const auto& type : package->types) {
continue;
}
- ClassDefinitionWriterOptions classOptions;
- classOptions.useFinalQualifier = mOptions.useFinal;
- classOptions.forceCreationIfEmpty =
+ const bool forceCreationIfEmpty =
(mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
- ClassDefinitionWriter classDef(toString(type->type), classOptions);
- bool result = writeEntriesForClass(&classDef, packageNameToGenerate,
- package.get(), type.get());
+
+ std::unique_ptr<ClassDefinition> classDef = util::make_unique<ClassDefinition>(
+ util::utf16ToUtf8(toString(type->type)), ClassQualifier::Static,
+ forceCreationIfEmpty);
+
+ bool result = addMembersToTypeClass(packageNameToGenerate, package.get(), type.get(),
+ classDef.get());
if (!result) {
return false;
}
// Also include private attributes in this same class.
ResourceTableType* privType = package->findType(ResourceType::kAttrPrivate);
if (privType) {
- result = writeEntriesForClass(&classDef, packageNameToGenerate,
- package.get(), privType);
+ result = addMembersToTypeClass(packageNameToGenerate, package.get(), privType,
+ classDef.get());
if (!result) {
return false;
}
}
}
- AnnotationProcessor processor;
if (type->type == ResourceType::kStyleable &&
mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic) {
// When generating a public R class, we don't want Styleable to be part of the API.
// It is only emitted for documentation purposes.
- processor.appendComment("@doconly");
+ AnnotationProcessor* processor = classDef->getCommentBuilder();
+ processor->appendComment("@doconly");
}
- classDef.writeToStream(out, " ", &processor);
+
+ rClass.addMember(std::move(classDef));
}
}
- *out << "}\n";
+ if (!ClassDefinition::writeJavaFile(&rClass, util::utf16ToUtf8(outPackageName),
+ mOptions.useFinal, out)) {
+ return false;
+ }
+
out->flush();
return true;
}
namespace aapt {
class AnnotationProcessor;
-class ClassDefinitionWriter;
+class ClassDefinition;
struct JavaClassGeneratorOptions {
/*
const std::string& getError() const;
private:
- bool writeEntriesForClass(ClassDefinitionWriter* outClassDef,
- const StringPiece16& packageNameToGenerate,
- const ResourceTablePackage* package,
- const ResourceTableType* type);
-
- void writeStyleableEntryForClass(ClassDefinitionWriter* outClassDef,
- AnnotationProcessor* processor,
- const StringPiece16& packageNameToGenerate,
- const std::u16string& entryName,
- const Styleable* styleable);
+ bool addMembersToTypeClass(const StringPiece16& packageNameToGenerate,
+ const ResourceTablePackage* package,
+ const ResourceTableType* type,
+ ClassDefinition* outTypeClassDef);
+
+ void addMembersToStyleableClass(const StringPiece16& packageNameToGenerate,
+ const std::u16string& entryName,
+ const Styleable* styleable,
+ ClassDefinition* outStyleableClassDef);
bool skipSymbol(SymbolState state);
#include "Source.h"
#include "java/AnnotationProcessor.h"
-#include "java/ClassDefinitionWriter.h"
+#include "java/ClassDefinition.h"
#include "java/ManifestClassGenerator.h"
#include "util/Maybe.h"
#include "xml/XmlDom.h"
return result;
}
-static bool writeSymbol(IDiagnostics* diag, ClassDefinitionWriter* outClassDef, const Source& source,
- xml::Element* el) {
+static bool writeSymbol(const Source& source, IDiagnostics* diag, xml::Element* el,
+ ClassDefinition* classDef) {
xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name");
if (!attr) {
diag->error(DiagMessage(source) << "<" << el->name << "> must define 'android:name'");
return false;
}
- AnnotationProcessor processor;
- processor.appendComment(el->comment);
- outClassDef->addStringMember(result.value(), &processor, attr->value);
+ std::unique_ptr<StringMember> stringMember = util::make_unique<StringMember>(
+ util::utf16ToUtf8(result.value()), util::utf16ToUtf8(attr->value));
+ stringMember->getCommentBuilder()->appendComment(el->comment);
+
+ classDef->addMember(std::move(stringMember));
return true;
}
-bool ManifestClassGenerator::generate(IDiagnostics* diag, const StringPiece16& package,
- xml::XmlResource* res, std::ostream* out) {
+std::unique_ptr<ClassDefinition> generateManifestClass(IDiagnostics* diag, xml::XmlResource* res) {
xml::Element* el = xml::findRootElement(res->root.get());
if (!el) {
- return false;
+ diag->error(DiagMessage(res->file.source) << "no root tag defined");
+ return {};
}
if (el->name != u"manifest" && !el->namespaceUri.empty()) {
diag->error(DiagMessage(res->file.source) << "no <manifest> root tag defined");
- return false;
+ return {};
}
- *out << "package " << package << ";\n\n"
- << "public final class Manifest {\n";
+ std::unique_ptr<ClassDefinition> permissionClass =
+ util::make_unique<ClassDefinition>("permission", ClassQualifier::Static, false);
+ std::unique_ptr<ClassDefinition> permissionGroupClass =
+ util::make_unique<ClassDefinition>("permission_group", ClassQualifier::Static, false);
bool error = false;
- std::vector<xml::Element*> children = el->getChildElements();
- ClassDefinitionWriterOptions classOptions;
- classOptions.useFinalQualifier = true;
- classOptions.forceCreationIfEmpty = false;
-
- // First write out permissions.
- ClassDefinitionWriter classDef("permission", classOptions);
+ std::vector<xml::Element*> children = el->getChildElements();
for (xml::Element* childEl : children) {
- if (childEl->namespaceUri.empty() && childEl->name == u"permission") {
- error |= !writeSymbol(diag, &classDef, res->file.source, childEl);
+ if (childEl->namespaceUri.empty()) {
+ if (childEl->name == u"permission") {
+ error |= !writeSymbol(res->file.source, diag, childEl, permissionClass.get());
+ } else if (childEl->name == u"permission-group") {
+ error |= !writeSymbol(res->file.source, diag, childEl, permissionGroupClass.get());
+ }
}
}
- classDef.writeToStream(out, " ");
- // Next write out permission groups.
- classDef = ClassDefinitionWriter("permission_group", classOptions);
- for (xml::Element* childEl : children) {
- if (childEl->namespaceUri.empty() && childEl->name == u"permission-group") {
- error |= !writeSymbol(diag, &classDef, res->file.source, childEl);
- }
+ if (error) {
+ return {};
}
- classDef.writeToStream(out, " ");
- *out << "}\n";
- return !error;
+ std::unique_ptr<ClassDefinition> manifestClass =
+ util::make_unique<ClassDefinition>("Manifest", ClassQualifier::None, false);
+ manifestClass->addMember(std::move(permissionClass));
+ manifestClass->addMember(std::move(permissionGroupClass));
+ return manifestClass;
}
} // namespace aapt
#define AAPT_JAVA_MANIFESTCLASSGENERATOR_H
#include "Diagnostics.h"
+#include "java/ClassDefinition.h"
#include "util/StringPiece.h"
#include "xml/XmlDom.h"
namespace aapt {
-struct ManifestClassGenerator {
- bool generate(IDiagnostics* diag, const StringPiece16& package, xml::XmlResource* res,
- std::ostream* out);
-};
+std::unique_ptr<ClassDefinition> generateManifestClass(IDiagnostics* diag, xml::XmlResource* res);
} // namespace aapt
namespace aapt {
+static ::testing::AssertionResult getManifestClassText(IAaptContext* context, xml::XmlResource* res,
+ std::string* outStr) {
+ std::unique_ptr<ClassDefinition> manifestClass = generateManifestClass(
+ context->getDiagnostics(), res);
+ if (!manifestClass) {
+ return ::testing::AssertionFailure() << "manifestClass == nullptr";
+ }
+
+ std::stringstream out;
+ if (!manifestClass->writeJavaFile(manifestClass.get(), "android", true, &out)) {
+ return ::testing::AssertionFailure() << "failed to write java file";
+ }
+
+ *outStr = out.str();
+ return ::testing::AssertionSuccess();
+}
+
TEST(ManifestClassGeneratorTest, NameIsProperlyGeneratedFromSymbol) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
std::unique_ptr<xml::XmlResource> manifest = test::buildXmlDom(R"EOF(
<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();
+ std::string actual;
+ ASSERT_TRUE(getManifestClassText(context.get(), manifest.get(), &actual));
const size_t permissionClassPos = actual.find("public static final class permission {");
const size_t permissionGroupClassPos =
<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();
+ std::string actual;
+ ASSERT_TRUE(getManifestClassText(context.get(), manifest.get(), &actual));
EXPECT_NE(std::string::npos, actual.find(
R"EOF( /**
return true;
}
+ std::unique_ptr<ClassDefinition> manifestClass = generateManifestClass(
+ mContext->getDiagnostics(), manifestXml);
+
+ if (!manifestClass) {
+ // Something bad happened, but we already logged it, so exit.
+ return false;
+ }
+
+ if (manifestClass->empty()) {
+ // Empty Manifest class, no need to generate it.
+ return true;
+ }
+
+ const std::string packageUtf8 = util::utf16ToUtf8(mContext->getCompilationPackage());
+
std::string outPath = mOptions.generateJavaClassPath.value();
- file::appendPath(&outPath,
- file::packageToPath(util::utf16ToUtf8(mContext->getCompilationPackage())));
+ file::appendPath(&outPath, file::packageToPath(packageUtf8));
+
if (!file::mkdirs(outPath)) {
mContext->getDiagnostics()->error(
DiagMessage() << "failed to create directory '" << outPath << "'");
return false;
}
- ManifestClassGenerator generator;
- if (!generator.generate(mContext->getDiagnostics(), mContext->getCompilationPackage(),
- manifestXml, &fout)) {
- return false;
- }
-
- if (!fout) {
+ if (!ClassDefinition::writeJavaFile(manifestClass.get(), packageUtf8, true, &fout)) {
mContext->getDiagnostics()->error(
DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
return false;