AAPT2: Forward @TestApi in resource comments to JavaDoc

Bug: 37894597
Test: make aapt2_tests
Change-Id: I357fb84941bfbb3892a8c46feb47f55b865b6649
This commit is contained in:
Adam Lesinski
2017-08-08 10:39:55 -07:00
parent b76b3056f5
commit 09f4d706d9
7 changed files with 186 additions and 190 deletions

View File

@@ -17,6 +17,7 @@
#include "java/AnnotationProcessor.h"
#include <algorithm>
#include <array>
#include "text/Unicode.h"
#include "text/Utf8Iterator.h"
@@ -41,30 +42,54 @@ StringPiece AnnotationProcessor::ExtractFirstSentence(const StringPiece& comment
return comment;
}
void AnnotationProcessor::AppendCommentLine(std::string& comment) {
struct AnnotationRule {
enum : uint32_t {
kDeprecated = 0x01,
kSystemApi = 0x02,
kTestApi = 0x04,
};
StringPiece doc_str;
uint32_t bit_mask;
StringPiece annotation;
};
static std::array<AnnotationRule, 2> sAnnotationRules = {{
{"@SystemApi", AnnotationRule::kSystemApi, "@android.annotation.SystemApi"},
{"@TestApi", AnnotationRule::kTestApi, "@android.annotation.TestApi"},
}};
void AnnotationProcessor::AppendCommentLine(std::string comment) {
static const std::string sDeprecated = "@deprecated";
static const std::string sSystemApi = "@SystemApi";
// Treat deprecated specially, since we don't remove it from the source comment.
if (comment.find(sDeprecated) != std::string::npos) {
annotation_bit_mask_ |= kDeprecated;
annotation_bit_mask_ |= AnnotationRule::kDeprecated;
}
std::string::size_type idx = comment.find(sSystemApi);
if (idx != std::string::npos) {
annotation_bit_mask_ |= kSystemApi;
comment.erase(comment.begin() + idx,
comment.begin() + idx + sSystemApi.size());
for (const AnnotationRule& rule : sAnnotationRules) {
std::string::size_type idx = comment.find(rule.doc_str.data());
if (idx != std::string::npos) {
annotation_bit_mask_ |= rule.bit_mask;
comment.erase(comment.begin() + idx, comment.begin() + idx + rule.doc_str.size());
}
}
if (util::TrimWhitespace(comment).empty()) {
// Check if after removal of annotations the line is empty.
const StringPiece trimmed = util::TrimWhitespace(comment);
if (trimmed.empty()) {
return;
}
// If there was trimming to do, copy the string.
if (trimmed.size() != comment.size()) {
comment = trimmed.to_string();
}
if (!has_comments_) {
has_comments_ = true;
comment_ << "/**";
}
comment_ << "\n * " << std::move(comment);
}
@@ -73,16 +98,18 @@ void AnnotationProcessor::AppendComment(const StringPiece& comment) {
for (StringPiece line : util::Tokenize(comment, '\n')) {
line = util::TrimWhitespace(line);
if (!line.empty()) {
std::string lineCopy = line.to_string();
AppendCommentLine(lineCopy);
AppendCommentLine(line.to_string());
}
}
}
void AnnotationProcessor::AppendNewLine() { comment_ << "\n *"; }
void AnnotationProcessor::AppendNewLine() {
if (has_comments_) {
comment_ << "\n *";
}
}
void AnnotationProcessor::WriteToStream(std::ostream* out,
const StringPiece& prefix) const {
void AnnotationProcessor::WriteToStream(const StringPiece& prefix, std::ostream* out) const {
if (has_comments_) {
std::string result = comment_.str();
for (StringPiece line : util::Tokenize(result, '\n')) {
@@ -92,12 +119,14 @@ void AnnotationProcessor::WriteToStream(std::ostream* out,
<< "\n";
}
if (annotation_bit_mask_ & kDeprecated) {
if (annotation_bit_mask_ & AnnotationRule::kDeprecated) {
*out << prefix << "@Deprecated\n";
}
if (annotation_bit_mask_ & kSystemApi) {
*out << prefix << "@android.annotation.SystemApi\n";
for (const AnnotationRule& rule : sAnnotationRules) {
if (annotation_bit_mask_ & rule.bit_mask) {
*out << prefix << rule.annotation << "\n";
}
}
}

View File

@@ -24,64 +24,53 @@
namespace aapt {
/**
* Builds a JavaDoc comment from a set of XML comments.
* This will also look for instances of @SystemApi and convert them to
* actual Java annotations.
*
* Example:
*
* Input XML:
*
* <!-- This is meant to be hidden because
* It is system api. Also it is @deprecated
* @SystemApi
* -->
*
* Output JavaDoc:
*
* /\*
* * This is meant to be hidden because
* * It is system api. Also it is @deprecated
* *\/
*
* Output Annotations:
*
* @Deprecated
* @android.annotation.SystemApi
*
*/
// Builds a JavaDoc comment from a set of XML comments.
// This will also look for instances of @SystemApi and convert them to
// actual Java annotations.
//
// Example:
//
// Input XML:
//
// <!-- This is meant to be hidden because
// It is system api. Also it is @deprecated
// @SystemApi
// -->
//
// Output JavaDoc:
//
// /**
// * This is meant to be hidden because
// * It is system api. Also it is @deprecated
// */
//
// Output Annotations:
//
// @Deprecated
// @android.annotation.SystemApi
class AnnotationProcessor {
public:
// Extracts the first sentence of a comment. The algorithm selects the substring starting from
// the beginning of the string, and ending at the first '.' character that is followed by a
// whitespace character. If these requirements are not met, the whole string is returned.
static android::StringPiece ExtractFirstSentence(const android::StringPiece& comment);
/**
* Adds more comments. Since resources can have various values with different
* configurations,
* we need to collect all the comments.
*/
// Adds more comments. Resources can have value definitions for various configurations, and
// each of the definitions may have comments that need to be processed.
void AppendComment(const android::StringPiece& comment);
void AppendNewLine();
/**
* Writes the comments and annotations to the stream, with the given prefix
* before each line.
*/
void WriteToStream(std::ostream* out, const android::StringPiece& prefix) const;
// Writes the comments and annotations to the stream, with the given prefix before each line.
void WriteToStream(const android::StringPiece& prefix, std::ostream* out) const;
private:
enum : uint32_t {
kDeprecated = 0x01,
kSystemApi = 0x02,
};
std::stringstream comment_;
std::stringstream mAnnotations;
bool has_comments_ = false;
uint32_t annotation_bit_mask_ = 0;
void AppendCommentLine(std::string& line);
void AppendCommentLine(std::string line);
};
} // namespace aapt

View File

@@ -34,7 +34,7 @@ TEST(AnnotationProcessorTest, EmitsDeprecated) {
processor.AppendComment(comment);
std::stringstream result;
processor.WriteToStream(&result, "");
processor.WriteToStream("", &result);
std::string annotations = result.str();
EXPECT_THAT(annotations, HasSubstr("@Deprecated"));
@@ -45,7 +45,7 @@ TEST(AnnotationProcessorTest, EmitsSystemApiAnnotationAndRemovesFromComment) {
processor.AppendComment("@SystemApi This is a system API");
std::stringstream result;
processor.WriteToStream(&result, "");
processor.WriteToStream("", &result);
std::string annotations = result.str();
EXPECT_THAT(annotations, HasSubstr("@android.annotation.SystemApi"));
@@ -53,6 +53,19 @@ TEST(AnnotationProcessorTest, EmitsSystemApiAnnotationAndRemovesFromComment) {
EXPECT_THAT(annotations, HasSubstr("This is a system API"));
}
TEST(AnnotationProcessorTest, EmitsTestApiAnnotationAndRemovesFromComment) {
AnnotationProcessor processor;
processor.AppendComment("@TestApi This is a test API");
std::stringstream result;
processor.WriteToStream("", &result);
std::string annotations = result.str();
EXPECT_THAT(annotations, HasSubstr("@android.annotation.TestApi"));
EXPECT_THAT(annotations, Not(HasSubstr("@TestApi")));
EXPECT_THAT(annotations, HasSubstr("This is a test API"));
}
TEST(AnnotationProcessor, ExtractsFirstSentence) {
EXPECT_THAT(AnnotationProcessor::ExtractFirstSentence("This is the only sentence"),
Eq("This is the only sentence"));

View File

@@ -18,12 +18,12 @@
#include "androidfw/StringPiece.h"
using android::StringPiece;
using ::android::StringPiece;
namespace aapt {
void ClassMember::WriteToStream(const StringPiece& prefix, bool final, std::ostream* out) const {
processor_.WriteToStream(out, prefix);
processor_.WriteToStream(prefix, out);
}
void MethodDefinition::AppendStatement(const StringPiece& statement) {
@@ -81,9 +81,8 @@ constexpr static const char* sWarningHeader =
" * should not be modified by hand.\n"
" */\n\n";
bool ClassDefinition::WriteJavaFile(const ClassDefinition* def,
const StringPiece& package, bool final,
std::ostream* out) {
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);

View File

@@ -22,7 +22,9 @@
#include "test/Test.h"
#include "util/Util.h"
using android::StringPiece;
using ::android::StringPiece;
using ::testing::HasSubstr;
using ::testing::Not;
namespace aapt {
@@ -52,17 +54,15 @@ TEST(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) {
.AddSimple("android:id/hey-man", ResourceId(0x01020000))
.AddValue("android:attr/cool.attr", ResourceId(0x01010000),
test::AttributeBuilder(false).Build())
.AddValue(
"android:styleable/hey.dude", ResourceId(0x01030000),
test::StyleableBuilder()
.AddItem("android:attr/cool.attr", ResourceId(0x01010000))
.Build())
.AddValue("android:styleable/hey.dude", ResourceId(0x01030000),
test::StyleableBuilder()
.AddItem("android:attr/cool.attr", ResourceId(0x01010000))
.Build())
.Build();
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
.AddSymbolSource(
util::make_unique<ResourceTableSymbolSource>(table.get()))
.AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
@@ -72,14 +72,9 @@ TEST(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) {
std::string output = out.str();
EXPECT_NE(std::string::npos,
output.find("public static final int hey_man=0x01020000;"));
EXPECT_NE(std::string::npos,
output.find("public static final int[] hey_dude={"));
EXPECT_NE(std::string::npos,
output.find("public static final int hey_dude_cool_attr=0;"));
EXPECT_THAT(output, HasSubstr("public static final int hey_man=0x01020000;"));
EXPECT_THAT(output, HasSubstr("public static final int[] hey_dude={"));
EXPECT_THAT(output, HasSubstr("public static final int hey_dude_cool_attr=0;"));
}
TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) {
@@ -92,8 +87,7 @@ TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) {
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
.AddSymbolSource(
util::make_unique<ResourceTableSymbolSource>(table.get()))
.AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
@@ -101,11 +95,10 @@ TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) {
ASSERT_TRUE(generator.Generate("android", "com.android.internal", &out));
std::string output = out.str();
EXPECT_NE(std::string::npos, output.find("package com.android.internal;"));
EXPECT_NE(std::string::npos,
output.find("public static final int one=0x01020000;"));
EXPECT_EQ(std::string::npos, output.find("two"));
EXPECT_EQ(std::string::npos, output.find("com_foo$two"));
EXPECT_THAT(output, HasSubstr("package com.android.internal;"));
EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
EXPECT_THAT(output, Not(HasSubstr("two")));
EXPECT_THAT(output, Not(HasSubstr("com_foo$two")));
}
TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
@@ -118,8 +111,7 @@ TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
.AddSymbolSource(
util::make_unique<ResourceTableSymbolSource>(table.get()))
.AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
@@ -127,9 +119,8 @@ TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) {
ASSERT_TRUE(generator.Generate("android", &out));
std::string output = out.str();
EXPECT_NE(std::string::npos, output.find("public static final class attr"));
EXPECT_EQ(std::string::npos,
output.find("public static final class ^attr-private"));
EXPECT_THAT(output, HasSubstr("public static final class attr"));
EXPECT_THAT(output, Not(HasSubstr("public static final class ^attr-private")));
}
TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
@@ -140,16 +131,13 @@ TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
.AddSimple("android:id/one", ResourceId(0x01020000))
.AddSimple("android:id/two", ResourceId(0x01020001))
.AddSimple("android:id/three", ResourceId(0x01020002))
.SetSymbolState("android:id/one", ResourceId(0x01020000),
SymbolState::kPublic)
.SetSymbolState("android:id/two", ResourceId(0x01020001),
SymbolState::kPrivate)
.SetSymbolState("android:id/one", ResourceId(0x01020000), SymbolState::kPublic)
.SetSymbolState("android:id/two", ResourceId(0x01020001), SymbolState::kPrivate)
.Build();
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
.AddSymbolSource(
util::make_unique<ResourceTableSymbolSource>(table.get()))
.AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
@@ -160,10 +148,9 @@ TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
std::stringstream out;
ASSERT_TRUE(generator.Generate("android", &out));
std::string output = out.str();
EXPECT_NE(std::string::npos,
output.find("public static final int one=0x01020000;"));
EXPECT_EQ(std::string::npos, output.find("two"));
EXPECT_EQ(std::string::npos, output.find("three"));
EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
EXPECT_THAT(output, Not(HasSubstr("two")));
EXPECT_THAT(output, Not(HasSubstr("three")));
}
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
@@ -172,11 +159,9 @@ TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
std::stringstream out;
ASSERT_TRUE(generator.Generate("android", &out));
std::string output = out.str();
EXPECT_NE(std::string::npos,
output.find("public static final int one=0x01020000;"));
EXPECT_NE(std::string::npos,
output.find("public static final int two=0x01020001;"));
EXPECT_EQ(std::string::npos, output.find("three"));
EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
EXPECT_THAT(output, HasSubstr("public static final int two=0x01020001;"));
EXPECT_THAT(output, Not(HasSubstr("three")));
}
options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
@@ -185,12 +170,9 @@ TEST(JavaClassGeneratorTest, OnlyWritePublicResources) {
std::stringstream out;
ASSERT_TRUE(generator.Generate("android", &out));
std::string output = out.str();
EXPECT_NE(std::string::npos,
output.find("public static final int one=0x01020000;"));
EXPECT_NE(std::string::npos,
output.find("public static final int two=0x01020001;"));
EXPECT_NE(std::string::npos,
output.find("public static final int three=0x01020002;"));
EXPECT_THAT(output, HasSubstr("public static final int one=0x01020000;"));
EXPECT_THAT(output, HasSubstr("public static final int two=0x01020001;"));
EXPECT_THAT(output, HasSubstr("public static final int three=0x01020002;"));
}
}
@@ -246,8 +228,7 @@ TEST(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) {
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
.AddSymbolSource(
util::make_unique<ResourceTableSymbolSource>(table.get()))
.AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
@@ -256,8 +237,8 @@ TEST(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) {
EXPECT_TRUE(generator.Generate("android", &out));
std::string output = out.str();
EXPECT_NE(std::string::npos, output.find("int foo_bar="));
EXPECT_NE(std::string::npos, output.find("int foo_com_lib_bar="));
EXPECT_THAT(output, HasSubstr("int foo_bar="));
EXPECT_THAT(output, HasSubstr("int foo_com_lib_bar="));
}
TEST(JavaClassGeneratorTest, CommentsForSimpleResourcesArePresent) {
@@ -271,24 +252,22 @@ TEST(JavaClassGeneratorTest, CommentsForSimpleResourcesArePresent) {
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
.AddSymbolSource(
util::make_unique<ResourceTableSymbolSource>(table.get()))
.AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGenerator generator(context.get(), table.get(), {});
std::stringstream out;
ASSERT_TRUE(generator.Generate("android", &out));
std::string actual = out.str();
std::string output = out.str();
const char* expectedText =
const char* expected_text =
R"EOF(/**
* This is a comment
* @deprecated
*/
@Deprecated
public static final int foo=0x01010000;)EOF";
EXPECT_NE(std::string::npos, actual.find(expectedText));
EXPECT_THAT(output, HasSubstr(expected_text));
}
TEST(JavaClassGeneratorTest, CommentsForEnumAndFlagAttributesArePresent) {}
@@ -298,8 +277,7 @@ TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent)
attr.SetComment(StringPiece("This is an attribute"));
Styleable styleable;
styleable.entries.push_back(
Reference(test::ParseNameOrDie("android:attr/one")));
styleable.entries.push_back(Reference(test::ParseNameOrDie("android:attr/one")));
styleable.SetComment(StringPiece("This is a styleable"));
std::unique_ptr<ResourceTable> table =
@@ -312,8 +290,7 @@ TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent)
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
.AddSymbolSource(
util::make_unique<ResourceTableSymbolSource>(table.get()))
.AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGeneratorOptions options;
@@ -321,12 +298,12 @@ TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent)
JavaClassGenerator generator(context.get(), table.get(), options);
std::stringstream out;
ASSERT_TRUE(generator.Generate("android", &out));
std::string actual = out.str();
std::string output = out.str();
EXPECT_NE(std::string::npos, actual.find("attr name android:one"));
EXPECT_NE(std::string::npos, actual.find("attr description"));
EXPECT_NE(std::string::npos, actual.find(attr.GetComment().data()));
EXPECT_NE(std::string::npos, actual.find(styleable.GetComment().data()));
EXPECT_THAT(output, HasSubstr("attr name android:one"));
EXPECT_THAT(output, HasSubstr("attr description"));
EXPECT_THAT(output, HasSubstr(attr.GetComment()));
EXPECT_THAT(output, HasSubstr(styleable.GetComment()));
}
TEST(JavaClassGeneratorTest, CommentsForRemovedAttributesAreNotPresentInClass) {
@@ -341,8 +318,7 @@ TEST(JavaClassGeneratorTest, CommentsForRemovedAttributesAreNotPresentInClass) {
std::unique_ptr<IAaptContext> context =
test::ContextBuilder()
.AddSymbolSource(
util::make_unique<ResourceTableSymbolSource>(table.get()))
.AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.Build();
JavaClassGeneratorOptions options;
@@ -350,17 +326,17 @@ TEST(JavaClassGeneratorTest, CommentsForRemovedAttributesAreNotPresentInClass) {
JavaClassGenerator generator(context.get(), table.get(), options);
std::stringstream out;
ASSERT_TRUE(generator.Generate("android", &out));
std::string actual = out.str();
std::string output = out.str();
EXPECT_EQ(std::string::npos, actual.find("@attr name android:one"));
EXPECT_EQ(std::string::npos, actual.find("@attr description"));
EXPECT_THAT(output, Not(HasSubstr("@attr name android:one")));
EXPECT_THAT(output, Not(HasSubstr("@attr description")));
// We should find @removed only in the attribute javadoc and not anywhere else
// (i.e. the class
// javadoc).
const size_t pos = actual.find("removed");
EXPECT_NE(std::string::npos, pos);
EXPECT_EQ(std::string::npos, actual.find("removed", pos + 1));
// (i.e. the class javadoc).
const std::string kRemoved("removed");
ASSERT_THAT(output, HasSubstr(kRemoved));
std::string after_first_match = output.substr(output.find(kRemoved) + kRemoved.size());
EXPECT_THAT(after_first_match, Not(HasSubstr(kRemoved)));
}
TEST(JavaClassGeneratorTest, GenerateOnResourcesLoadedCallbackForSharedLibrary) {
@@ -381,19 +357,17 @@ TEST(JavaClassGeneratorTest, GenerateOnResourcesLoadedCallbackForSharedLibrary)
JavaClassGeneratorOptions options;
options.use_final = false;
options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{
{"com.foo", "com.boo"},
};
options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{{"com.foo", "com.boo"}};
JavaClassGenerator generator(context.get(), table.get(), options);
std::stringstream out;
ASSERT_TRUE(generator.Generate("android", &out));
std::string actual = out.str();
std::string output = out.str();
EXPECT_NE(std::string::npos, actual.find("void onResourcesLoaded"));
EXPECT_NE(std::string::npos, actual.find("com.foo.R.onResourcesLoaded"));
EXPECT_NE(std::string::npos, actual.find("com.boo.R.onResourcesLoaded"));
EXPECT_THAT(output, HasSubstr("void onResourcesLoaded"));
EXPECT_THAT(output, HasSubstr("com.foo.R.onResourcesLoaded"));
EXPECT_THAT(output, HasSubstr("com.boo.R.onResourcesLoaded"));
}
} // namespace aapt

View File

@@ -21,24 +21,21 @@
#include "Source.h"
#include "java/AnnotationProcessor.h"
#include "java/ClassDefinition.h"
#include "text/Unicode.h"
#include "util/Maybe.h"
#include "xml/XmlDom.h"
using android::StringPiece;
using ::android::StringPiece;
using ::aapt::text::IsJavaIdentifier;
namespace aapt {
static Maybe<StringPiece> ExtractJavaIdentifier(IDiagnostics* diag,
const Source& source,
const StringPiece& value) {
const StringPiece sep = ".";
auto iter = std::find_end(value.begin(), value.end(), sep.begin(), sep.end());
StringPiece result;
if (iter != value.end()) {
result.assign(iter + sep.size(), value.end() - (iter + sep.size()));
} else {
result = value;
static Maybe<StringPiece> ExtractJavaIdentifier(IDiagnostics* diag, const Source& source,
const std::string& value) {
StringPiece result = value;
size_t pos = value.rfind('.');
if (pos != std::string::npos) {
result = result.substr(pos + 1);
}
if (result.empty()) {
@@ -46,33 +43,23 @@ static Maybe<StringPiece> ExtractJavaIdentifier(IDiagnostics* diag,
return {};
}
iter = util::FindNonAlphaNumericAndNotInSet(result, "_");
if (iter != result.end()) {
diag->Error(DiagMessage(source) << "invalid character '"
<< StringPiece(iter, 1) << "' in '"
<< result << "'");
if (!IsJavaIdentifier(result)) {
diag->Error(DiagMessage(source) << "invalid Java identifier '" << result << "'");
return {};
}
if (*result.begin() >= '0' && *result.begin() <= '9') {
diag->Error(DiagMessage(source) << "symbol can not start with a digit");
return {};
}
return result;
}
static bool WriteSymbol(const Source& source, IDiagnostics* diag,
xml::Element* el, ClassDefinition* class_def) {
static bool WriteSymbol(const Source& source, IDiagnostics* diag, xml::Element* el,
ClassDefinition* class_def) {
xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name");
if (!attr) {
diag->Error(DiagMessage(source) << "<" << el->name
<< "> must define 'android:name'");
diag->Error(DiagMessage(source) << "<" << el->name << "> must define 'android:name'");
return false;
}
Maybe<StringPiece> result = ExtractJavaIdentifier(
diag, source.WithLine(el->line_number), attr->value);
Maybe<StringPiece> result =
ExtractJavaIdentifier(diag, source.WithLine(el->line_number), attr->value);
if (!result) {
return false;
}
@@ -85,8 +72,7 @@ static bool WriteSymbol(const Source& source, IDiagnostics* diag,
return true;
}
std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag,
xml::XmlResource* res) {
std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag, xml::XmlResource* res) {
xml::Element* el = xml::FindRootElement(res->root.get());
if (!el) {
diag->Error(DiagMessage(res->file.source) << "no root tag defined");
@@ -94,8 +80,7 @@ std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag,
}
if (el->name != "manifest" && !el->namespace_uri.empty()) {
diag->Error(DiagMessage(res->file.source)
<< "no <manifest> root tag defined");
diag->Error(DiagMessage(res->file.source) << "no <manifest> root tag defined");
return {};
}
@@ -109,11 +94,9 @@ std::unique_ptr<ClassDefinition> GenerateManifestClass(IDiagnostics* diag,
for (xml::Element* child_el : children) {
if (child_el->namespace_uri.empty()) {
if (child_el->name == "permission") {
error |= !WriteSymbol(res->file.source, diag, child_el,
permission_class.get());
error |= !WriteSymbol(res->file.source, diag, child_el, permission_class.get());
} else if (child_el->name == "permission-group") {
error |= !WriteSymbol(res->file.source, diag, child_el,
permission_group_class.get());
error |= !WriteSymbol(res->file.source, diag, child_el, permission_group_class.get());
}
}
}

View File

@@ -84,6 +84,8 @@ TEST(ManifestClassGeneratorTest, CommentsAndAnnotationsArePresent) {
@hide
@SystemApi -->
<permission android:name="android.permission.SECRET" />
<!-- @TestApi This is a test only permission. -->
<permission android:name="android.permission.TEST_ONLY" />
</manifest>)");
std::string actual;
@@ -110,6 +112,13 @@ TEST(ManifestClassGeneratorTest, CommentsAndAnnotationsArePresent) {
@android.annotation.SystemApi
public static final String SECRET="android.permission.SECRET";)";
EXPECT_THAT(actual, HasSubstr(expected_secret));
const char* expected_test = R"( /**
* This is a test only permission.
*/
@android.annotation.TestApi
public static final String TEST_ONLY="android.permission.TEST_ONLY";)";
EXPECT_THAT(actual, HasSubstr(expected_test));
}
static ::testing::AssertionResult GetManifestClassText(IAaptContext* context, xml::XmlResource* res,