diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/aapt2/ResourceParser.cpp | 30 | ||||
| -rw-r--r-- | tools/aapt2/ResourceUtils.cpp | 43 | ||||
| -rw-r--r-- | tools/aapt2/ResourceUtils.h | 10 | ||||
| -rw-r--r-- | tools/aapt2/ResourceUtils_test.cpp | 20 | ||||
| -rw-r--r-- | tools/aapt2/XmlDom.h | 7 | ||||
| -rw-r--r-- | tools/aapt2/link/Link.cpp | 31 | ||||
| -rw-r--r-- | tools/aapt2/link/ReferenceLinkerVisitor.h | 4 | ||||
| -rw-r--r-- | tools/aapt2/link/XmlReferenceLinker.cpp | 23 | ||||
| -rw-r--r-- | tools/aapt2/process/SymbolTable.cpp | 48 | ||||
| -rw-r--r-- | tools/aapt2/unflatten/BinaryResourceParser.cpp | 5 | ||||
| -rw-r--r-- | tools/aapt2/util/Util.h | 35 | ||||
| -rw-r--r-- | tools/aapt2/util/Util_test.cpp | 9 | ||||
| -rw-r--r-- | tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java | 23 |
13 files changed, 219 insertions, 69 deletions
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index f2a1878d0dc5..d292f623a33e 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -747,7 +747,13 @@ bool ResourceParser::parseAttrImpl(XmlPullParser* parser, ParsedResource* outRes } } - std::vector<Attribute::Symbol> items; + struct SymbolComparator { + bool operator()(const Attribute::Symbol& a, const Attribute::Symbol& b) { + return a.symbol.name.value() < b.symbol.name.value(); + } + }; + + std::set<Attribute::Symbol, SymbolComparator> items; std::u16string comment; bool error = false; @@ -785,15 +791,27 @@ bool ResourceParser::parseAttrImpl(XmlPullParser* parser, ParsedResource* outRes } if (Maybe<Attribute::Symbol> s = parseEnumOrFlagItem(parser, elementName)) { + Attribute::Symbol& symbol = s.value(); ParsedResource childResource; - childResource.name = s.value().symbol.name.value(); + childResource.name = symbol.symbol.name.value(); childResource.source = itemSource; childResource.value = util::make_unique<Id>(); outResource->childResources.push_back(std::move(childResource)); - s.value().symbol.setComment(std::move(comment)); - s.value().symbol.setSource(itemSource); - items.push_back(std::move(s.value())); + symbol.symbol.setComment(std::move(comment)); + symbol.symbol.setSource(itemSource); + + auto insertResult = items.insert(std::move(symbol)); + if (!insertResult.second) { + const Attribute::Symbol& existingSymbol = *insertResult.first; + mDiag->error(DiagMessage(itemSource) + << "duplicate symbol '" << existingSymbol.symbol.name.value().entry + << "'"); + + mDiag->note(DiagMessage(existingSymbol.symbol.getSource()) + << "first defined here"); + error = true; + } } else { error = true; } @@ -810,7 +828,7 @@ bool ResourceParser::parseAttrImpl(XmlPullParser* parser, ParsedResource* outRes } std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(weak); - attr->symbols.swap(items); + attr->symbols = std::vector<Attribute::Symbol>(items.begin(), items.end()); attr->typeMask = typeMask ? typeMask : uint32_t(android::ResTable_map::TYPE_ANY); outResource->value = std::move(attr); return true; diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index d3c3c1044ae7..b1a4c7dee73f 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -23,22 +23,28 @@ namespace aapt { namespace ResourceUtils { -void extractResourceName(const StringPiece16& str, StringPiece16* outPackage, +bool extractResourceName(const StringPiece16& str, StringPiece16* outPackage, StringPiece16* outType, StringPiece16* outEntry) { + bool hasPackageSeparator = false; + bool hasTypeSeparator = false; const char16_t* start = str.data(); const char16_t* end = start + str.size(); const char16_t* current = start; while (current != end) { if (outType->size() == 0 && *current == u'/') { + hasTypeSeparator = true; outType->assign(start, current - start); start = current + 1; } else if (outPackage->size() == 0 && *current == u':') { + hasPackageSeparator = true; outPackage->assign(start, current - start); start = current + 1; } current++; } outEntry->assign(start, end - start); + + return !(hasPackageSeparator && outPackage->empty()) && !(hasTypeSeparator && outType->empty()); } bool tryParseReference(const StringPiece16& str, ResourceNameRef* outRef, bool* outCreate, @@ -62,26 +68,34 @@ bool tryParseReference(const StringPiece16& str, ResourceNameRef* outRef, bool* StringPiece16 package; StringPiece16 type; StringPiece16 entry; - extractResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset), &package, &type, - &entry); + if (!extractResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset), + &package, &type, &entry)) { + return false; + } const ResourceType* parsedType = parseResourceType(type); if (!parsedType) { return false; } + if (entry.empty()) { + return false; + } + if (create && *parsedType != ResourceType::kId) { return false; } - if (outRef != nullptr) { + if (outRef) { outRef->package = package; outRef->type = *parsedType; outRef->entry = entry; } + if (outCreate) { *outCreate = create; } + if (outPrivate) { *outPrivate = priv; } @@ -104,20 +118,33 @@ bool tryParseAttributeReference(const StringPiece16& str, ResourceNameRef* outRe StringPiece16 package; StringPiece16 type; StringPiece16 entry; - extractResourceName(trimmedStr.substr(1, trimmedStr.size() - 1), &package, &type, &entry); + if (!extractResourceName(trimmedStr.substr(1, trimmedStr.size() - 1), + &package, &type, &entry)) { + return false; + } if (!type.empty() && type != u"attr") { return false; } - outRef->package = package; - outRef->type = ResourceType::kAttr; - outRef->entry = entry; + if (entry.empty()) { + return false; + } + + if (outRef) { + outRef->package = package; + outRef->type = ResourceType::kAttr; + outRef->entry = entry; + } return true; } return false; } +bool isAttributeReference(const StringPiece16& str) { + return tryParseAttributeReference(str, nullptr); +} + /* * Style parent's are a bit different. We accept the following formats: * diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h index 851edc89fa90..34daa66b1546 100644 --- a/tools/aapt2/ResourceUtils.h +++ b/tools/aapt2/ResourceUtils.h @@ -34,8 +34,9 @@ namespace ResourceUtils { * * where the package can be empty. Validation must be performed on each * individual extracted piece to verify that the pieces are valid. + * Returns false if there was no package but a ':' was present. */ -void extractResourceName(const StringPiece16& str, StringPiece16* outPackage, +bool extractResourceName(const StringPiece16& str, StringPiece16* outPackage, StringPiece16* outType, StringPiece16* outEntry); /* @@ -54,12 +55,17 @@ bool tryParseReference(const StringPiece16& str, ResourceNameRef* outReference, bool isReference(const StringPiece16& str); /* - * Returns true if the string was parsed as an attribute reference (?[package:]type/name), + * Returns true if the string was parsed as an attribute reference (?[package:][type/]name), * with `outReference` set to the parsed reference. */ bool tryParseAttributeReference(const StringPiece16& str, ResourceNameRef* outReference); /** + * Returns true if the string is in the form of an attribute reference(?[package:][type/]name). + */ +bool isAttributeReference(const StringPiece16& str); + +/** * Returns true if the value is a boolean, putting the result in `outValue`. */ bool tryParseBool(const StringPiece16& str, bool* outValue); diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp index 7de8f4130316..3d2a6e18e2bc 100644 --- a/tools/aapt2/ResourceUtils_test.cpp +++ b/tools/aapt2/ResourceUtils_test.cpp @@ -90,6 +90,26 @@ TEST(ResourceUtilsTest, FailToParseAutoCreateNonIdReference) { &privateRef)); } +TEST(ResourceUtilsTest, ParseAttributeReferences) { + EXPECT_TRUE(ResourceUtils::isAttributeReference(u"?android")); + EXPECT_TRUE(ResourceUtils::isAttributeReference(u"?android:foo")); + EXPECT_TRUE(ResourceUtils::isAttributeReference(u"?attr/foo")); + EXPECT_TRUE(ResourceUtils::isAttributeReference(u"?android:attr/foo")); +} + +TEST(ResourceUtilsTest, FailParseIncompleteReference) { + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?style/foo")); + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?android:style/foo")); + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?android:")); + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?android:attr/")); + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?:attr/")); + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?:attr/foo")); + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?:/")); + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?:/foo")); + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?attr/")); + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?/foo")); +} + TEST(ResourceUtilsTest, ParseStyleParentReference) { const ResourceName kAndroidStyleFooName = { u"android", ResourceType::kStyle, u"foo" }; const ResourceName kStyleFooName = { {}, ResourceType::kStyle, u"foo" }; diff --git a/tools/aapt2/XmlDom.h b/tools/aapt2/XmlDom.h index 9a46bcb9fc5c..721bf5bd6320 100644 --- a/tools/aapt2/XmlDom.h +++ b/tools/aapt2/XmlDom.h @@ -215,10 +215,13 @@ public: for (auto iter = mPackageDecls.rbegin(); iter != rend; ++iter) { if (name.package == iter->prefix) { if (iter->package.empty()) { - return ResourceName{ localPackage.toString(), name.type, name.entry }; - } else { + if (localPackage != name.package) { + return ResourceName{ localPackage.toString(), name.type, name.entry }; + } + } else if (iter->package != name.package) { return ResourceName{ iter->package, name.type, name.entry }; } + break; } } return {}; diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp index 93f2dc6f04cd..97be774acaf1 100644 --- a/tools/aapt2/link/Link.cpp +++ b/tools/aapt2/link/Link.cpp @@ -50,7 +50,7 @@ struct LinkOptions { std::vector<std::string> includePaths; std::vector<std::string> overlayFiles; Maybe<std::string> generateJavaClassPath; - std::vector<std::string> extraJavaPackages; + std::set<std::string> extraJavaPackages; Maybe<std::string> generateProguardRulesPath; bool noAutoVersion = false; bool staticLib = false; @@ -485,7 +485,7 @@ public: } } - mFilesToProcess[resName.toResourceName()] = FileToProcess{ Source(input), std::move(file) }; + mFilesToProcess.insert(FileToProcess{ std::move(file), Source(input) }); return true; } @@ -640,8 +640,7 @@ public: } } - for (auto& pair : mFilesToProcess) { - FileToProcess& file = pair.second; + for (const FileToProcess& file : mFilesToProcess) { if (file.file.name.type != ResourceType::kRaw && util::stringEndsWith<char>(file.source.path, ".xml.flat")) { if (mOptions.verbose) { @@ -760,7 +759,7 @@ public: return 1; } - for (std::string& extraPackage : mOptions.extraJavaPackages) { + for (const std::string& extraPackage : mOptions.extraJavaPackages) { if (!writeJavaFile(&mFinalTable, actualPackage, util::utf8ToUtf16(extraPackage), options)) { return 1; @@ -795,16 +794,24 @@ private: std::unique_ptr<TableMerger> mTableMerger; struct FileToProcess { - Source source; ResourceFile file; + Source source; + }; + + struct FileToProcessComparator { + bool operator()(const FileToProcess& a, const FileToProcess& b) { + return std::tie(a.file.name, a.file.config) < std::tie(b.file.name, b.file.config); + } }; - std::map<ResourceName, FileToProcess> mFilesToProcess; + + std::set<FileToProcess, FileToProcessComparator> mFilesToProcess; }; int link(const std::vector<StringPiece>& args) { LinkOptions options; Maybe<std::string> privateSymbolsPackage; Maybe<std::string> minSdkVersion, targetSdkVersion; + std::vector<std::string> extraJavaPackages; Flags flags = Flags() .requiredFlag("-o", "Output path", &options.outputPath) .requiredFlag("--manifest", "Path to the Android manifest to build", @@ -833,7 +840,7 @@ int link(const std::vector<StringPiece>& args) { "If not specified, public and private symbols will use the application's " "package name", &privateSymbolsPackage) .optionalFlagList("--extra-packages", "Generate the same R.java but with different " - "package names", &options.extraJavaPackages) + "package names", &extraJavaPackages) .optionalSwitch("-v", "Enables verbose logging", &options.verbose); if (!flags.parse("aapt2 link", args, &std::cerr)) { @@ -852,6 +859,14 @@ int link(const std::vector<StringPiece>& args) { options.targetSdkVersionDefault = util::utf8ToUtf16(targetSdkVersion.value()); } + // Populate the set of extra packages for which to generate R.java. + for (std::string& extraPackage : extraJavaPackages) { + // A given package can actually be a colon separated list of packages. + for (StringPiece package : util::split(extraPackage, ':')) { + options.extraJavaPackages.insert(package.toString()); + } + } + LinkCommand cmd(options); return cmd.run(flags.getArgs()); } diff --git a/tools/aapt2/link/ReferenceLinkerVisitor.h b/tools/aapt2/link/ReferenceLinkerVisitor.h index c70531ba8296..a4cb596eb5ec 100644 --- a/tools/aapt2/link/ReferenceLinkerVisitor.h +++ b/tools/aapt2/link/ReferenceLinkerVisitor.h @@ -80,7 +80,7 @@ public: return; } - DiagMessage errorMsg; + DiagMessage errorMsg(reference->getSource()); errorMsg << "reference to " << reference->name.value(); if (realName) { errorMsg << " (aka " << realName.value() << ")"; @@ -92,7 +92,7 @@ public: } if (!mSymbols->findById(reference->id.value())) { - mContext->getDiagnostics()->error(DiagMessage() + mContext->getDiagnostics()->error(DiagMessage(reference->getSource()) << "reference to " << reference->id.value() << " was not found"); mError = true; diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp index 147b9bf16248..caab9b8a4684 100644 --- a/tools/aapt2/link/XmlReferenceLinker.cpp +++ b/tools/aapt2/link/XmlReferenceLinker.cpp @@ -33,6 +33,7 @@ class XmlReferenceLinkerVisitor : public xml::PackageAwareVisitor { private: IAaptContext* mContext; ISymbolTable* mSymbols; + Source mSource; std::set<int>* mSdkLevelsFound; ReferenceLinkerVisitor mReferenceLinkerVisitor; bool mError = false; @@ -40,13 +41,14 @@ private: public: using xml::PackageAwareVisitor::visit; - XmlReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, + XmlReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, const Source& source, std::set<int>* sdkLevelsFound) : - mContext(context), mSymbols(symbols), mSdkLevelsFound(sdkLevelsFound), + mContext(context), mSymbols(symbols), mSource(source), mSdkLevelsFound(sdkLevelsFound), mReferenceLinkerVisitor(context, symbols, this) { } void visit(xml::Element* el) override { + const Source source = mSource.withLine(el->lineNumber); for (xml::Attribute& attr : el->attributes) { Maybe<std::u16string> maybePackage = util::extractPackageFromNamespace(attr.namespaceUri); @@ -76,15 +78,16 @@ public: !(attribute->typeMask & android::ResTable_map::TYPE_STRING)) { // We won't be able to encode this as a string. mContext->getDiagnostics()->error( - DiagMessage() << "'" << attr.value << "' " - << "is incompatible with attribute " - << package << ":" << attr.name << " " << *attribute); + DiagMessage(source) << "'" << attr.value << "' " + << "is incompatible with attribute " + << package << ":" << attr.name << " " + << *attribute); mError = true; } } else { - mContext->getDiagnostics()->error( - DiagMessage() << "attribute '" << package << ":" << attr.name - << "' was not found"); + mContext->getDiagnostics()->error(DiagMessage(source) + << "attribute '" << package << ":" + << attr.name << "' was not found"); mError = true; } @@ -95,6 +98,7 @@ public: if (attr.compiledValue) { // With a compiledValue, we must resolve the reference and assign it an ID. + attr.compiledValue->setSource(source); attr.compiledValue->accept(&mReferenceLinkerVisitor); } } @@ -123,7 +127,8 @@ public: bool XmlReferenceLinker::consume(IAaptContext* context, XmlResource* resource) { mSdkLevelsFound.clear(); - XmlReferenceLinkerVisitor visitor(context, context->getExternalSymbols(), &mSdkLevelsFound); + XmlReferenceLinkerVisitor visitor(context, context->getExternalSymbols(), resource->file.source, + &mSdkLevelsFound); if (resource->root) { resource->root->accept(&visitor); return !visitor.hasError(); diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp index 9a8b263f5f97..bb33ea78f496 100644 --- a/tools/aapt2/process/SymbolTable.cpp +++ b/tools/aapt2/process/SymbolTable.cpp @@ -77,16 +77,8 @@ const ISymbolTable::Symbol* SymbolTableWrapper::findByName(const ResourceName& n } -static std::shared_ptr<ISymbolTable::Symbol> lookupIdInTable(const android::ResTable& table, - ResourceId id) { - android::Res_value val = {}; - ssize_t block = table.getResource(id.id, &val, true); - if (block >= 0) { - std::shared_ptr<ISymbolTable::Symbol> s = std::make_shared<ISymbolTable::Symbol>(); - s->id = id; - return s; - } - +static std::shared_ptr<ISymbolTable::Symbol> lookupAttributeInTable(const android::ResTable& table, + ResourceId id) { // Try as a bag. const android::ResTable::bag_entry* entry; ssize_t count = table.lockBag(id.id, &entry); @@ -148,14 +140,23 @@ const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTa for (const auto& asset : mAssets) { const android::ResTable& table = asset->getResources(false); StringPiece16 typeStr = toString(name.type); + uint32_t typeSpecFlags = 0; ResourceId resId = table.identifierForName(name.entry.data(), name.entry.size(), typeStr.data(), typeStr.size(), - name.package.data(), name.package.size()); + name.package.data(), name.package.size(), + &typeSpecFlags); if (!resId.isValid()) { continue; } - std::shared_ptr<Symbol> s = lookupIdInTable(table, resId); + std::shared_ptr<Symbol> s; + if (name.type == ResourceType::kAttr) { + s = lookupAttributeInTable(table, resId); + } else { + s = std::make_shared<Symbol>(); + s->id = resId; + } + if (s) { mCache.put(name, s); return s.get(); @@ -173,7 +174,28 @@ const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTa for (const auto& asset : mAssets) { const android::ResTable& table = asset->getResources(false); - std::shared_ptr<Symbol> s = lookupIdInTable(table, id); + android::ResTable::resource_name name; + if (!table.getResourceName(id.id, true, &name)) { + continue; + } + + bool isAttr = false; + if (name.type) { + if (const ResourceType* t = parseResourceType(StringPiece16(name.type, name.typeLen))) { + isAttr = (*t == ResourceType::kAttr); + } + } else if (name.type8) { + isAttr = (StringPiece(name.type8, name.typeLen) == "attr"); + } + + std::shared_ptr<Symbol> s; + if (isAttr) { + s = lookupAttributeInTable(table, id); + } else { + s = std::make_shared<Symbol>(); + s->id = id; + } + if (s) { mIdCache.put(id, s); return s.get(); diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp index 0d17e8467d32..304833481be0 100644 --- a/tools/aapt2/unflatten/BinaryResourceParser.cpp +++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp @@ -304,6 +304,11 @@ bool BinaryResourceParser::parsePackage(const ResChunk_header* chunk) { return false; } + // There can be multiple packages in a table, so + // clear the type and key pool in case they were set from a previous package. + mTypePool.uninit(); + mKeyPool.uninit(); + ResChunkPullParser parser(getChunkData(&packageHeader->header), getChunkDataLen(&packageHeader->header)); while (ResChunkPullParser::isGoodEvent(parser.next())) { diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h index 80552a540ec3..324afb3c7de3 100644 --- a/tools/aapt2/util/Util.h +++ b/tools/aapt2/util/Util.h @@ -229,11 +229,12 @@ public: private: friend class Tokenizer<Char>; - iterator(BasicStringPiece<Char> s, Char sep, BasicStringPiece<Char> tok); + iterator(BasicStringPiece<Char> s, Char sep, BasicStringPiece<Char> tok, bool end); - BasicStringPiece<Char> str; - Char separator; - BasicStringPiece<Char> token; + BasicStringPiece<Char> mStr; + Char mSeparator; + BasicStringPiece<Char> mToken; + bool mEnd; }; Tokenizer(BasicStringPiece<Char> str, Char sep); @@ -252,36 +253,38 @@ inline Tokenizer<Char> tokenize(BasicStringPiece<Char> str, Char sep) { template <typename Char> typename Tokenizer<Char>::iterator& Tokenizer<Char>::iterator::operator++() { - const Char* start = token.end(); - const Char* end = str.end(); + const Char* start = mToken.end(); + const Char* end = mStr.end(); if (start == end) { - token.assign(token.end(), 0); + mEnd = true; + mToken.assign(mToken.end(), 0); return *this; } start += 1; const Char* current = start; while (current != end) { - if (*current == separator) { - token.assign(start, current - start); + if (*current == mSeparator) { + mToken.assign(start, current - start); return *this; } ++current; } - token.assign(start, end - start); + mToken.assign(start, end - start); return *this; } template <typename Char> inline BasicStringPiece<Char> Tokenizer<Char>::iterator::operator*() { - return token; + return mToken; } template <typename Char> inline bool Tokenizer<Char>::iterator::operator==(const iterator& rhs) const { // We check equality here a bit differently. // We need to know that the addresses are the same. - return token.begin() == rhs.token.begin() && token.end() == rhs.token.end(); + return mToken.begin() == rhs.mToken.begin() && mToken.end() == rhs.mToken.end() && + mEnd == rhs.mEnd; } template <typename Char> @@ -291,8 +294,8 @@ inline bool Tokenizer<Char>::iterator::operator!=(const iterator& rhs) const { template <typename Char> inline Tokenizer<Char>::iterator::iterator(BasicStringPiece<Char> s, Char sep, - BasicStringPiece<Char> tok) : - str(s), separator(sep), token(tok) { + BasicStringPiece<Char> tok, bool end) : + mStr(s), mSeparator(sep), mToken(tok), mEnd(end) { } template <typename Char> @@ -307,8 +310,8 @@ inline typename Tokenizer<Char>::iterator Tokenizer<Char>::end() { template <typename Char> inline Tokenizer<Char>::Tokenizer(BasicStringPiece<Char> str, Char sep) : - mBegin(++iterator(str, sep, BasicStringPiece<Char>(str.begin() - 1, 0))), - mEnd(str, sep, BasicStringPiece<Char>(str.end(), 0)) { + mBegin(++iterator(str, sep, BasicStringPiece<Char>(str.begin() - 1, 0), false)), + mEnd(str, sep, BasicStringPiece<Char>(str.end(), 0), true) { } inline uint16_t hostToDevice16(uint16_t value) { diff --git a/tools/aapt2/util/Util_test.cpp b/tools/aapt2/util/Util_test.cpp index 9db9fb7f112a..9208e07e635b 100644 --- a/tools/aapt2/util/Util_test.cpp +++ b/tools/aapt2/util/Util_test.cpp @@ -101,6 +101,15 @@ TEST(UtilTest, TokenizeInput) { ASSERT_EQ(tokenizer.end(), iter); } +TEST(UtilTest, TokenizeEmptyString) { + auto tokenizer = util::tokenize(StringPiece16(u""), u'|'); + auto iter = tokenizer.begin(); + ASSERT_NE(tokenizer.end(), iter); + ASSERT_EQ(StringPiece16(), *iter); + ++iter; + ASSERT_EQ(tokenizer.end(), iter); +} + TEST(UtilTest, TokenizeAtEnd) { auto tokenizer = util::tokenize(StringPiece16(u"one."), u'.'); auto iter = tokenizer.begin(); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java index 868c6d328e8e..b67afebf9a89 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java @@ -16,10 +16,13 @@ package com.android.layoutlib.bridge.bars; +import com.android.ide.common.rendering.api.LayoutLog; +import com.android.ide.common.rendering.api.LayoutlibCallback; import com.android.ide.common.rendering.api.RenderResources; import com.android.ide.common.rendering.api.ResourceValue; import com.android.ide.common.rendering.api.SessionParams; import com.android.ide.common.rendering.api.StyleResourceValue; +import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.impl.ResourceHelper; import com.android.resources.ResourceType; @@ -45,6 +48,8 @@ public class AppCompatActionBar extends BridgeActionBar { private Object mWindowDecorActionBar; private static final String WINDOW_ACTION_BAR_CLASS = "android.support.v7.internal.app.WindowDecorActionBar"; + // This is used on v23.1.1 and later. + private static final String WINDOW_ACTION_BAR_CLASS_NEW = "android.support.v7.app.WindowDecorActionBar"; private Class<?> mWindowActionBarClass; /** @@ -70,14 +75,26 @@ public class AppCompatActionBar extends BridgeActionBar { try { Class[] constructorParams = {View.class}; Object[] constructorArgs = {getDecorContent()}; - mWindowDecorActionBar = params.getLayoutlibCallback().loadView(WINDOW_ACTION_BAR_CLASS, - constructorParams, constructorArgs); + LayoutlibCallback callback = params.getLayoutlibCallback(); + // First try to load the class as was available before appcompat v23.1.1, without + // logging warnings. + try { + mWindowDecorActionBar = callback.loadClass(WINDOW_ACTION_BAR_CLASS, + constructorParams, constructorArgs); + } catch (ClassNotFoundException ignore) { + } + if (mWindowDecorActionBar == null) { + // If failed, load the new class, while logging warnings. + mWindowDecorActionBar = callback.loadView(WINDOW_ACTION_BAR_CLASS_NEW, + constructorParams, constructorArgs); + } mWindowActionBarClass = mWindowDecorActionBar == null ? null : mWindowDecorActionBar.getClass(); setupActionBar(); } catch (Exception e) { - e.printStackTrace(); + Bridge.getLog().warning(LayoutLog.TAG_BROKEN, + "Failed to load AppCompat ActionBar with unknown error.", e); } } |