Merge "AAPT2: Change how we generate Java classes" into nyc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
30906ad2d6
@@ -54,6 +54,7 @@ sources := \
|
||||
Debug.cpp \
|
||||
Flags.cpp \
|
||||
java/AnnotationProcessor.cpp \
|
||||
java/ClassDefinition.cpp \
|
||||
java/JavaClassGenerator.cpp \
|
||||
java/ManifestClassGenerator.cpp \
|
||||
java/ProguardRules.cpp \
|
||||
|
||||
@@ -64,7 +64,7 @@ void AnnotationProcessor::appendNewLine() {
|
||||
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')) {
|
||||
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
75
tools/aapt2/java/ClassDefinition.cpp
Normal file
75
tools/aapt2/java/ClassDefinition.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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
|
||||
188
tools/aapt2/java/ClassDefinition.h
Normal file
188
tools/aapt2/java/ClassDefinition.h
Normal file
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* 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 */
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* 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 */
|
||||
@@ -21,7 +21,7 @@
|
||||
#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"
|
||||
@@ -39,16 +39,6 @@ JavaClassGenerator::JavaClassGenerator(IAaptContext* context, ResourceTable* tab
|
||||
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",
|
||||
@@ -110,15 +100,15 @@ static void addAttributeFormatDoc(AnnotationProcessor* processor, Attribute* att
|
||||
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) {
|
||||
@@ -128,14 +118,14 @@ static void addAttributeFormatDoc(AnnotationProcessor* processor, Attribute* att
|
||||
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) {
|
||||
@@ -146,33 +136,33 @@ static void addAttributeFormatDoc(AnnotationProcessor* processor, Attribute* att
|
||||
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>"
|
||||
@@ -214,13 +204,15 @@ static bool lessStyleableAttr(const StyleableAttr& lhs, const StyleableAttr& rhs
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
@@ -230,6 +222,8 @@ void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outC
|
||||
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,
|
||||
@@ -247,6 +241,8 @@ void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outC
|
||||
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) {
|
||||
@@ -254,10 +250,11 @@ void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outC
|
||||
}
|
||||
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.
|
||||
@@ -267,6 +264,7 @@ void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outC
|
||||
} else {
|
||||
styleableComment << "Attributes that can be used with a " << className << ".\n";
|
||||
}
|
||||
|
||||
styleableComment <<
|
||||
"<p>Includes the following attributes:</p>\n"
|
||||
"<table>\n"
|
||||
@@ -274,7 +272,7 @@ void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outC
|
||||
"<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 #"
|
||||
@@ -292,21 +290,22 @@ void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outC
|
||||
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++) {
|
||||
@@ -318,7 +317,10 @@ void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outC
|
||||
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()) {
|
||||
@@ -326,8 +328,8 @@ void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outC
|
||||
}
|
||||
|
||||
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
|
||||
@@ -335,27 +337,29 @@ void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outC
|
||||
<< "{@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;
|
||||
@@ -389,33 +393,41 @@ bool JavaClassGenerator::writeEntriesForClass(ClassDefinitionWriter* outClassDef
|
||||
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;
|
||||
@@ -427,9 +439,8 @@ bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate, st
|
||||
|
||||
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) {
|
||||
@@ -437,13 +448,15 @@ bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate,
|
||||
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;
|
||||
}
|
||||
@@ -452,26 +465,31 @@ bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate,
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
namespace aapt {
|
||||
|
||||
class AnnotationProcessor;
|
||||
class ClassDefinitionWriter;
|
||||
class ClassDefinition;
|
||||
|
||||
struct JavaClassGeneratorOptions {
|
||||
/*
|
||||
@@ -70,16 +70,15 @@ public:
|
||||
const std::string& getError() const;
|
||||
|
||||
private:
|
||||
bool writeEntriesForClass(ClassDefinitionWriter* outClassDef,
|
||||
const StringPiece16& packageNameToGenerate,
|
||||
const ResourceTablePackage* package,
|
||||
const ResourceTableType* type);
|
||||
bool addMembersToTypeClass(const StringPiece16& packageNameToGenerate,
|
||||
const ResourceTablePackage* package,
|
||||
const ResourceTableType* type,
|
||||
ClassDefinition* outTypeClassDef);
|
||||
|
||||
void writeStyleableEntryForClass(ClassDefinitionWriter* outClassDef,
|
||||
AnnotationProcessor* processor,
|
||||
const StringPiece16& packageNameToGenerate,
|
||||
const std::u16string& entryName,
|
||||
const Styleable* styleable);
|
||||
void addMembersToStyleableClass(const StringPiece16& packageNameToGenerate,
|
||||
const std::u16string& entryName,
|
||||
const Styleable* styleable,
|
||||
ClassDefinition* outStyleableClassDef);
|
||||
|
||||
bool skipSymbol(SymbolState state);
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#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"
|
||||
@@ -58,8 +58,8 @@ static Maybe<StringPiece16> extractJavaIdentifier(IDiagnostics* diag, const Sour
|
||||
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'");
|
||||
@@ -72,54 +72,53 @@ static bool writeSymbol(IDiagnostics* diag, ClassDefinitionWriter* outClassDef,
|
||||
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);
|
||||
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
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define AAPT_JAVA_MANIFESTCLASSGENERATOR_H
|
||||
|
||||
#include "Diagnostics.h"
|
||||
#include "java/ClassDefinition.h"
|
||||
#include "util/StringPiece.h"
|
||||
#include "xml/XmlDom.h"
|
||||
|
||||
@@ -25,10 +26,7 @@
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -22,6 +22,23 @@
|
||||
|
||||
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(
|
||||
@@ -32,11 +49,8 @@ TEST(ManifestClassGeneratorTest, NameIsProperlyGeneratedFromSymbol) {
|
||||
<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 =
|
||||
@@ -87,11 +101,8 @@ TEST(ManifestClassGeneratorTest, CommentsAndAnnotationsArePresent) {
|
||||
<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( /**
|
||||
|
||||
@@ -762,9 +762,24 @@ public:
|
||||
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 << "'");
|
||||
@@ -780,13 +795,7 @@ public:
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user