diff options
Diffstat (limited to 'tools')
133 files changed, 3966 insertions, 3149 deletions
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 9976d00fa872..288b2a320de6 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -494,7 +494,7 @@ struct ImpliedFeature { struct Feature { Feature() : required(false), version(-1) {} - Feature(bool required, int32_t version = -1) : required(required), version(version) {} + explicit Feature(bool required, int32_t version = -1) : required(required), version(version) {} /** * Whether the feature is required. @@ -750,7 +750,7 @@ int doDump(Bundle* bundle) } // Source for AndroidManifest.xml - const String8 manifestFile = String8::format("%s@AndroidManifest.xml", filename); + const String8 manifestFile("AndroidManifest.xml"); // The dynamicRefTable can be null if there are no resources for this asset cookie. // This fine. @@ -850,14 +850,16 @@ int doDump(Bundle* bundle) depth++; const char16_t* ctag16 = tree.getElementName(&len); if (ctag16 == NULL) { - fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n"); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR: failed to get XML element name (bad string pool)"); goto bail; } String8 tag(ctag16); //printf("Depth %d tag %s\n", depth, tag.string()); if (depth == 1) { if (tag != "manifest") { - fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR: manifest does not start with <manifest> tag"); goto bail; } String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL); @@ -867,12 +869,14 @@ int doDump(Bundle* bundle) String8 error; String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR: %s\n", error.string()); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:name': %s", error.string()); goto bail; } if (name == "") { - fprintf(stderr, "ERROR: missing 'android:name' for permission\n"); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR: missing 'android:name' for permission"); goto bail; } printf("permission: %s\n", @@ -881,12 +885,14 @@ int doDump(Bundle* bundle) String8 error; String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR: %s\n", error.string()); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:name' attribute: %s", error.string()); goto bail; } if (name == "") { - fprintf(stderr, "ERROR: missing 'android:name' for uses-permission\n"); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR: missing 'android:name' for uses-permission"); goto bail; } printUsesPermission(name, @@ -896,13 +902,14 @@ int doDump(Bundle* bundle) String8 error; String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR: %s\n", error.string()); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:name' attribute: %s", error.string()); goto bail; } if (name == "") { - fprintf(stderr, "ERROR: missing 'android:name' for " - "uses-permission-sdk-23\n"); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR: missing 'android:name' for uses-permission-sdk-23"); goto bail; } printUsesPermissionSdk23( @@ -1163,14 +1170,16 @@ int doDump(Bundle* bundle) const char16_t* ctag16 = tree.getElementName(&len); if (ctag16 == NULL) { - fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n"); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR: failed to get XML element name (bad string pool)"); goto bail; } String8 tag(ctag16); //printf("Depth %d, %s\n", depth, tag.string()); if (depth == 1) { if (tag != "manifest") { - fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR: manifest does not start with <manifest> tag"); goto bail; } pkg = AaptXml::getAttribute(tree, NULL, "package", NULL); @@ -1179,7 +1188,8 @@ int doDump(Bundle* bundle) int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:versionCode' attribute: %s", error.string()); goto bail; } @@ -1191,7 +1201,8 @@ int doDump(Bundle* bundle) String8 versionName = AaptXml::getResolvedAttribute(res, tree, VERSION_NAME_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:versionName' attribute: %s", error.string()); goto bail; } @@ -1212,7 +1223,8 @@ int doDump(Bundle* bundle) int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree, INSTALL_LOCATION_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n", + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:installLocation' attribute: %s", error.string()); goto bail; } @@ -1278,14 +1290,15 @@ int doDump(Bundle* bundle) String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", - error.string()); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:icon' attribute: %s", error.string()); goto bail; } int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:testOnly' attribute: %s", error.string()); goto bail; } @@ -1293,8 +1306,8 @@ int doDump(Bundle* bundle) String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n", - error.string()); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:banner' attribute: %s", error.string()); goto bail; } printf("application: label='%s' ", @@ -1312,8 +1325,8 @@ int doDump(Bundle* bundle) int32_t isGame = AaptXml::getResolvedIntegerAttribute(res, tree, ISGAME_ATTR, 0, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:isGame' attribute: %s\n", - error.string()); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:isGame' attribute: %s", error.string()); goto bail; } if (isGame != 0) { @@ -1323,7 +1336,8 @@ int doDump(Bundle* bundle) int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree, DEBUGGABLE_ATTR, 0, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:debuggable' attribute: %s", error.string()); goto bail; } @@ -1352,8 +1366,8 @@ int doDump(Bundle* bundle) String8 name = AaptXml::getResolvedAttribute(res, tree, MIN_SDK_VERSION_ATTR, &error); if (error != "") { - fprintf(stderr, - "ERROR getting 'android:minSdkVersion' attribute: %s\n", + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:minSdkVersion' attribute: %s", error.string()); goto bail; } @@ -1374,8 +1388,8 @@ int doDump(Bundle* bundle) String8 name = AaptXml::getResolvedAttribute(res, tree, TARGET_SDK_VERSION_ATTR, &error); if (error != "") { - fprintf(stderr, - "ERROR getting 'android:targetSdkVersion' attribute: %s\n", + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:targetSdkVersion' attribute: %s", error.string()); goto bail; } @@ -1440,8 +1454,8 @@ int doDump(Bundle* bundle) FeatureGroup group; group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:label' attribute:" - " %s\n", error.string()); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:label' attribute: %s", error.string()); goto bail; } featureGroups.add(group); @@ -1486,13 +1500,14 @@ int doDump(Bundle* bundle) } else if (tag == "uses-permission") { String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", - error.string()); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:name' attribute: %s", error.string()); goto bail; } if (name == "") { - fprintf(stderr, "ERROR: missing 'android:name' for uses-permission\n"); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR: missing 'android:name' for uses-permission"); goto bail; } @@ -1521,14 +1536,14 @@ int doDump(Bundle* bundle) } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") { String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", - error.string()); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:name' attribute: %s", error.string()); goto bail; } if (name == "") { - fprintf(stderr, "ERROR: missing 'android:name' for " - "uses-permission-sdk-23\n"); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR: missing 'android:name' for uses-permission-sdk-23"); goto bail; } @@ -1543,9 +1558,9 @@ int doDump(Bundle* bundle) printf("uses-package:'%s'\n", ResTable::normalizeForOutput(name.string()).string()); } else { - fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", - error.string()); - goto bail; + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:name' attribute: %s", error.string()); + goto bail; } } else if (tag == "original-package") { String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); @@ -1553,9 +1568,9 @@ int doDump(Bundle* bundle) printf("original-package:'%s'\n", ResTable::normalizeForOutput(name.string()).string()); } else { - fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", - error.string()); - goto bail; + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:name' attribute: %s", error.string()); + goto bail; } } else if (tag == "supports-gl-texture") { String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); @@ -1563,15 +1578,15 @@ int doDump(Bundle* bundle) printf("supports-gl-texture:'%s'\n", ResTable::normalizeForOutput(name.string()).string()); } else { - fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", - error.string()); - goto bail; + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:name' attribute: %s", error.string()); + goto bail; } } else if (tag == "compatible-screens") { printCompatibleScreens(tree, &error); if (error != "") { - fprintf(stderr, "ERROR getting compatible screens: %s\n", - error.string()); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting compatible screens: %s", error.string()); goto bail; } depth--; @@ -1608,7 +1623,8 @@ int doDump(Bundle* bundle) withinActivity = true; activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:name' attribute: %s", error.string()); goto bail; } @@ -1616,7 +1632,8 @@ int doDump(Bundle* bundle) activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:label' attribute: %s", error.string()); goto bail; } @@ -1624,7 +1641,8 @@ int doDump(Bundle* bundle) activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:icon' attribute: %s", error.string()); goto bail; } @@ -1632,7 +1650,8 @@ int doDump(Bundle* bundle) activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n", + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:banner' attribute: %s", error.string()); goto bail; } @@ -1659,9 +1678,9 @@ int doDump(Bundle* bundle) } else if (tag == "uses-library") { String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { - fprintf(stderr, + SourcePos(manifestFile, tree.getLineNumber()).error( "ERROR getting 'android:name' attribute for uses-library" - " %s\n", error.string()); + " %s", error.string()); goto bail; } int req = AaptXml::getIntegerAttribute(tree, @@ -1674,9 +1693,9 @@ int doDump(Bundle* bundle) receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { - fprintf(stderr, + SourcePos(manifestFile, tree.getLineNumber()).error( "ERROR getting 'android:name' attribute for receiver:" - " %s\n", error.string()); + " %s", error.string()); goto bail; } @@ -1687,9 +1706,9 @@ int doDump(Bundle* bundle) hasBindDeviceAdminPermission = true; } } else { - fprintf(stderr, + SourcePos(manifestFile, tree.getLineNumber()).error( "ERROR getting 'android:permission' attribute for" - " receiver '%s': %s\n", + " receiver '%s': %s", receiverName.string(), error.string()); } } else if (tag == "service") { @@ -1697,8 +1716,9 @@ int doDump(Bundle* bundle) serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:name' attribute for " - "service:%s\n", error.string()); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:name' attribute for " + "service:%s", error.string()); goto bail; } @@ -1723,8 +1743,9 @@ int doDump(Bundle* bundle) hasBindDreamServicePermission = true; } } else { - fprintf(stderr, "ERROR getting 'android:permission' attribute for " - "service '%s': %s\n", serviceName.string(), error.string()); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:permission' attribute for " + "service '%s': %s", serviceName.string(), error.string()); } } else if (tag == "provider") { withinProvider = true; @@ -1732,26 +1753,27 @@ int doDump(Bundle* bundle) bool exported = AaptXml::getResolvedIntegerAttribute(res, tree, EXPORTED_ATTR, &error); if (error != "") { - fprintf(stderr, + SourcePos(manifestFile, tree.getLineNumber()).error( "ERROR getting 'android:exported' attribute for provider:" - " %s\n", error.string()); + " %s", error.string()); goto bail; } bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute( res, tree, GRANT_URI_PERMISSIONS_ATTR, &error); if (error != "") { - fprintf(stderr, + SourcePos(manifestFile, tree.getLineNumber()).error( "ERROR getting 'android:grantUriPermissions' attribute for " - "provider: %s\n", error.string()); + "provider: %s", error.string()); goto bail; } String8 permission = AaptXml::getResolvedAttribute(res, tree, PERMISSION_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:permission' attribute for " - "provider: %s\n", error.string()); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:permission' attribute for " + "provider: %s", error.string()); goto bail; } @@ -1762,8 +1784,9 @@ int doDump(Bundle* bundle) String8 metaDataName = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:name' attribute for " - "meta-data:%s\n", error.string()); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:name' attribute for " + "meta-data: %s", error.string()); goto bail; } printf("meta-data: name='%s' ", @@ -1776,9 +1799,10 @@ int doDump(Bundle* bundle) printResolvedResourceAttribute(res, tree, RESOURCE_ATTR, String8("resource"), &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:value' or " + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:value' or " "'android:resource' attribute for " - "meta-data:%s\n", error.string()); + "meta-data: %s", error.string()); goto bail; } } @@ -1788,7 +1812,8 @@ int doDump(Bundle* bundle) if (name != "" && error == "") { supportedInput.add(name); } else { - fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:name' attribute: %s", error.string()); goto bail; } @@ -1847,8 +1872,9 @@ int doDump(Bundle* bundle) } else if (withinService && tag == "meta-data") { String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:name' attribute for " - "meta-data tag in service '%s': %s\n", serviceName.string(), + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:name' attribute for " + "meta-data tag in service '%s': %s", serviceName.string(), error.string()); goto bail; } @@ -1863,8 +1889,9 @@ int doDump(Bundle* bundle) String8 xmlPath = AaptXml::getResolvedAttribute(res, tree, RESOURCE_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:resource' attribute for " - "meta-data tag in service '%s': %s\n", + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:resource' attribute for " + "meta-data tag in service '%s': %s", serviceName.string(), error.string()); goto bail; } @@ -1872,7 +1899,8 @@ int doDump(Bundle* bundle) Vector<String8> categories = getNfcAidCategories(assets, xmlPath, offHost, &error); if (error != "") { - fprintf(stderr, "ERROR getting AID category for service '%s'\n", + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting AID category for service '%s'", serviceName.string()); goto bail; } @@ -1893,8 +1921,8 @@ int doDump(Bundle* bundle) if (tag == "action") { action = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", - error.string()); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'android:name' attribute: %s", error.string()); goto bail; } @@ -1949,8 +1977,8 @@ int doDump(Bundle* bundle) if (tag == "category") { String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'name' attribute: %s\n", - error.string()); + SourcePos(manifestFile, tree.getLineNumber()).error( + "ERROR getting 'name' attribute: %s", error.string()); goto bail; } if (withinActivity) { @@ -2261,6 +2289,10 @@ int doDump(Bundle* bundle) result = NO_ERROR; bail: + if (SourcePos::hasErrors()) { + SourcePos::printErrors(stderr); + } + if (asset) { delete asset; } diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk index 4b5ea65d8fed..bbb9a51ad407 100644 --- a/tools/aapt2/Android.mk +++ b/tools/aapt2/Android.mk @@ -39,6 +39,7 @@ sources := \ link/PrivateAttributeMover.cpp \ link/ReferenceLinker.cpp \ link/TableMerger.cpp \ + link/VersionCollapser.cpp \ link/XmlReferenceLinker.cpp \ process/SymbolTable.cpp \ proto/ProtoHelpers.cpp \ @@ -87,6 +88,7 @@ testSources := \ link/ProductFilter_test.cpp \ link/ReferenceLinker_test.cpp \ link/TableMerger_test.cpp \ + link/VersionCollapser_test.cpp \ link/XmlReferenceLinker_test.cpp \ process/SymbolTable_test.cpp \ proto/TableProtoSerializer_test.cpp \ @@ -101,6 +103,7 @@ testSources := \ java/JavaClassGenerator_test.cpp \ java/ManifestClassGenerator_test.cpp \ Locale_test.cpp \ + NameMangler_test.cpp \ Resource_test.cpp \ ResourceParser_test.cpp \ ResourceTable_test.cpp \ diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h index 30047f71cc04..1d39b722b053 100644 --- a/tools/aapt2/AppInfo.h +++ b/tools/aapt2/AppInfo.h @@ -17,6 +17,8 @@ #ifndef AAPT_APP_INFO_H #define AAPT_APP_INFO_H +#include "util/Maybe.h" + #include <string> namespace aapt { @@ -29,7 +31,12 @@ struct AppInfo { /** * App's package name. */ - std::u16string package; + std::string package; + + /** + * The App's minimum SDK version. + */ + Maybe<std::string> minSdkVersion; }; } // namespace aapt diff --git a/tools/aapt2/ConfigDescription.cpp b/tools/aapt2/ConfigDescription.cpp index 13f8b3b54f68..c1697e794c1e 100644 --- a/tools/aapt2/ConfigDescription.cpp +++ b/tools/aapt2/ConfigDescription.cpp @@ -783,4 +783,10 @@ void ConfigDescription::applyVersionForCompatibility(ConfigDescription* config) } } +ConfigDescription ConfigDescription::copyWithoutSdkVersion() const { + ConfigDescription copy = *this; + copy.sdkVersion = 0; + return copy; +} + } // namespace aapt diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h index 5749816f5124..ef0d147d9d98 100644 --- a/tools/aapt2/ConfigDescription.h +++ b/tools/aapt2/ConfigDescription.h @@ -65,6 +65,8 @@ struct ConfigDescription : public android::ResTable_config { bool operator!=(const ConfigDescription& o) const; bool operator>=(const ConfigDescription& o) const; bool operator>(const ConfigDescription& o) const; + + ConfigDescription copyWithoutSdkVersion() const; }; inline ConfigDescription::ConfigDescription() { diff --git a/tools/aapt2/ConfigDescription_test.cpp b/tools/aapt2/ConfigDescription_test.cpp index e68d6be536df..455a57f1a10d 100644 --- a/tools/aapt2/ConfigDescription_test.cpp +++ b/tools/aapt2/ConfigDescription_test.cpp @@ -16,10 +16,9 @@ #include "ConfigDescription.h" #include "SdkConstants.h" - +#include "test/Test.h" #include "util/StringPiece.h" -#include <gtest/gtest.h> #include <string> namespace aapt { diff --git a/tools/aapt2/Flags.cpp b/tools/aapt2/Flags.cpp index 666e8a8efff1..2033a4ce45dc 100644 --- a/tools/aapt2/Flags.cpp +++ b/tools/aapt2/Flags.cpp @@ -101,7 +101,7 @@ void Flags::usage(const StringPiece& command, std::ostream* out) { // Split the description by newlines and write out the argument (which is empty after // the first line) followed by the description line. This will make sure that multiline // descriptions are still right justified and aligned. - for (StringPiece line : util::tokenize<char>(flag.description, '\n')) { + for (StringPiece line : util::tokenize(flag.description, '\n')) { *out << " " << std::setw(kWidth) << std::left << argLine << line << "\n"; argLine = " "; } diff --git a/tools/aapt2/Locale.cpp b/tools/aapt2/Locale.cpp index be576613b9b2..f7956c0e8d70 100644 --- a/tools/aapt2/Locale.cpp +++ b/tools/aapt2/Locale.cpp @@ -223,33 +223,6 @@ ssize_t LocaleValue::initFromParts(std::vector<std::string>::iterator iter, return static_cast<ssize_t>(iter - startIter); } - -std::string LocaleValue::toDirName() const { - std::string dirName; - if (language[0]) { - dirName += language; - } else { - return dirName; - } - - if (script[0]) { - dirName += "-s"; - dirName += script; - } - - if (region[0]) { - dirName += "-r"; - dirName += region; - } - - if (variant[0]) { - dirName += "-v"; - dirName += variant; - } - - return dirName; -} - void LocaleValue::initFromResTable(const ResTable_config& config) { config.unpackLanguage(language); config.unpackRegion(region); diff --git a/tools/aapt2/Locale.h b/tools/aapt2/Locale.h index b1c80ab27641..33f80ada2987 100644 --- a/tools/aapt2/Locale.h +++ b/tools/aapt2/Locale.h @@ -57,8 +57,6 @@ struct LocaleValue { */ void writeTo(android::ResTable_config* out) const; - std::string toDirName() const; - inline int compare(const LocaleValue& other) const; inline bool operator<(const LocaleValue& o) const; diff --git a/tools/aapt2/Locale_test.cpp b/tools/aapt2/Locale_test.cpp index 758e1e31c0e7..e4b8ce7379e6 100644 --- a/tools/aapt2/Locale_test.cpp +++ b/tools/aapt2/Locale_test.cpp @@ -23,7 +23,7 @@ namespace aapt { static ::testing::AssertionResult TestLanguage(const char* input, const char* lang) { - std::vector<std::string> parts = util::splitAndLowercase(std::string(input), '-'); + std::vector<std::string> parts = util::splitAndLowercase(input, '-'); LocaleValue lv; ssize_t count = lv.initFromParts(std::begin(parts), std::end(parts)); if (count < 0) { @@ -45,7 +45,7 @@ static ::testing::AssertionResult TestLanguage(const char* input, const char* la static ::testing::AssertionResult TestLanguageRegion(const char* input, const char* lang, const char* region) { - std::vector<std::string> parts = util::splitAndLowercase(std::string(input), '-'); + std::vector<std::string> parts = util::splitAndLowercase(input, '-'); LocaleValue lv; ssize_t count = lv.initFromParts(std::begin(parts), std::end(parts)); if (count < 0) { diff --git a/tools/aapt2/NameMangler.h b/tools/aapt2/NameMangler.h index 054b9ee116f4..505a982e5eab 100644 --- a/tools/aapt2/NameMangler.h +++ b/tools/aapt2/NameMangler.h @@ -18,7 +18,6 @@ #define AAPT_NAME_MANGLER_H #include "Resource.h" - #include "util/Maybe.h" #include <set> @@ -31,12 +30,12 @@ struct NameManglerPolicy { * Represents the package we are trying to build. References pointing * to this package are not mangled, and mangled references inherit this package name. */ - std::u16string targetPackageName; + std::string targetPackageName; /** * We must know which references to mangle, and which to keep (android vs. com.android.support). */ - std::set<std::u16string> packagesToMangle; + std::set<std::string> packagesToMangle; }; class NameMangler { @@ -53,14 +52,11 @@ public: return {}; } - return ResourceName{ - mPolicy.targetPackageName, - name.type, - mangleEntry(name.package, name.entry) - }; + std::string mangledEntryName = mangleEntry(name.package, name.entry); + return ResourceName(mPolicy.targetPackageName, name.type, mangledEntryName); } - bool shouldMangle(const std::u16string& package) const { + bool shouldMangle(const std::string& package) const { if (package.empty() || mPolicy.targetPackageName == package) { return false; } @@ -72,8 +68,8 @@ public: * The mangled name should contain symbols that are illegal to define in XML, * so that there will never be name mangling collisions. */ - static std::u16string mangleEntry(const std::u16string& package, const std::u16string& name) { - return package + u"$" + name; + static std::string mangleEntry(const std::string& package, const std::string& name) { + return package + "$" + name; } /** @@ -81,8 +77,8 @@ public: * and the package in `outPackage`. Returns true if the name was unmangled or * false if the name was never mangled to begin with. */ - static bool unmangle(std::u16string* outName, std::u16string* outPackage) { - size_t pivot = outName->find(u'$'); + static bool unmangle(std::string* outName, std::string* outPackage) { + size_t pivot = outName->find('$'); if (pivot == std::string::npos) { return false; } diff --git a/tools/aapt2/NameMangler_test.cpp b/tools/aapt2/NameMangler_test.cpp index 6103655c15e0..f624df28e7bc 100644 --- a/tools/aapt2/NameMangler_test.cpp +++ b/tools/aapt2/NameMangler_test.cpp @@ -15,31 +15,32 @@ */ #include "NameMangler.h" +#include "test/Test.h" -#include <gtest/gtest.h> #include <string> namespace aapt { TEST(NameManglerTest, MangleName) { - std::u16string package = u"android.appcompat"; - std::u16string name = u"Platform.AppCompat"; + std::string package = "android.appcompat"; + std::string name = "Platform.AppCompat"; - NameMangler::mangle(package, &name); - EXPECT_EQ(name, u"android.appcompat$Platform.AppCompat"); + std::string mangledName = NameMangler::mangleEntry(package, name); + EXPECT_EQ(mangledName, "android.appcompat$Platform.AppCompat"); - std::u16string newPackage; - ASSERT_TRUE(NameMangler::unmangle(&name, &newPackage)); - EXPECT_EQ(name, u"Platform.AppCompat"); - EXPECT_EQ(newPackage, u"android.appcompat"); + std::string unmangledPackage; + std::string unmangledName = mangledName; + ASSERT_TRUE(NameMangler::unmangle(&unmangledName, &unmangledPackage)); + EXPECT_EQ(unmangledName, "Platform.AppCompat"); + EXPECT_EQ(unmangledPackage, "android.appcompat"); } TEST(NameManglerTest, IgnoreUnmangledName) { - std::u16string package; - std::u16string name = u"foo_bar"; + std::string package; + std::string name = "foo_bar"; EXPECT_FALSE(NameMangler::unmangle(&name, &package)); - EXPECT_EQ(name, u"foo_bar"); + EXPECT_EQ(name, "foo_bar"); } } // namespace aapt diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp index 9328b697719d..b7a091ec4679 100644 --- a/tools/aapt2/Resource.cpp +++ b/tools/aapt2/Resource.cpp @@ -22,62 +22,62 @@ namespace aapt { -StringPiece16 toString(ResourceType type) { +StringPiece toString(ResourceType type) { switch (type) { - case ResourceType::kAnim: return u"anim"; - case ResourceType::kAnimator: return u"animator"; - case ResourceType::kArray: return u"array"; - case ResourceType::kAttr: return u"attr"; - case ResourceType::kAttrPrivate: return u"^attr-private"; - case ResourceType::kBool: return u"bool"; - case ResourceType::kColor: return u"color"; - case ResourceType::kDimen: return u"dimen"; - case ResourceType::kDrawable: return u"drawable"; - case ResourceType::kFraction: return u"fraction"; - case ResourceType::kId: return u"id"; - case ResourceType::kInteger: return u"integer"; - case ResourceType::kInterpolator: return u"interpolator"; - case ResourceType::kLayout: return u"layout"; - case ResourceType::kMenu: return u"menu"; - case ResourceType::kMipmap: return u"mipmap"; - case ResourceType::kPlurals: return u"plurals"; - case ResourceType::kRaw: return u"raw"; - case ResourceType::kString: return u"string"; - case ResourceType::kStyle: return u"style"; - case ResourceType::kStyleable: return u"styleable"; - case ResourceType::kTransition: return u"transition"; - case ResourceType::kXml: return u"xml"; + case ResourceType::kAnim: return "anim"; + case ResourceType::kAnimator: return "animator"; + case ResourceType::kArray: return "array"; + case ResourceType::kAttr: return "attr"; + case ResourceType::kAttrPrivate: return "^attr-private"; + case ResourceType::kBool: return "bool"; + case ResourceType::kColor: return "color"; + case ResourceType::kDimen: return "dimen"; + case ResourceType::kDrawable: return "drawable"; + case ResourceType::kFraction: return "fraction"; + case ResourceType::kId: return "id"; + case ResourceType::kInteger: return "integer"; + case ResourceType::kInterpolator: return "interpolator"; + case ResourceType::kLayout: return "layout"; + case ResourceType::kMenu: return "menu"; + case ResourceType::kMipmap: return "mipmap"; + case ResourceType::kPlurals: return "plurals"; + case ResourceType::kRaw: return "raw"; + case ResourceType::kString: return "string"; + case ResourceType::kStyle: return "style"; + case ResourceType::kStyleable: return "styleable"; + case ResourceType::kTransition: return "transition"; + case ResourceType::kXml: return "xml"; } return {}; } -static const std::map<StringPiece16, ResourceType> sResourceTypeMap { - { u"anim", ResourceType::kAnim }, - { u"animator", ResourceType::kAnimator }, - { u"array", ResourceType::kArray }, - { u"attr", ResourceType::kAttr }, - { u"^attr-private", ResourceType::kAttrPrivate }, - { u"bool", ResourceType::kBool }, - { u"color", ResourceType::kColor }, - { u"dimen", ResourceType::kDimen }, - { u"drawable", ResourceType::kDrawable }, - { u"fraction", ResourceType::kFraction }, - { u"id", ResourceType::kId }, - { u"integer", ResourceType::kInteger }, - { u"interpolator", ResourceType::kInterpolator }, - { u"layout", ResourceType::kLayout }, - { u"menu", ResourceType::kMenu }, - { u"mipmap", ResourceType::kMipmap }, - { u"plurals", ResourceType::kPlurals }, - { u"raw", ResourceType::kRaw }, - { u"string", ResourceType::kString }, - { u"style", ResourceType::kStyle }, - { u"styleable", ResourceType::kStyleable }, - { u"transition", ResourceType::kTransition }, - { u"xml", ResourceType::kXml }, +static const std::map<StringPiece, ResourceType> sResourceTypeMap { + { "anim", ResourceType::kAnim }, + { "animator", ResourceType::kAnimator }, + { "array", ResourceType::kArray }, + { "attr", ResourceType::kAttr }, + { "^attr-private", ResourceType::kAttrPrivate }, + { "bool", ResourceType::kBool }, + { "color", ResourceType::kColor }, + { "dimen", ResourceType::kDimen }, + { "drawable", ResourceType::kDrawable }, + { "fraction", ResourceType::kFraction }, + { "id", ResourceType::kId }, + { "integer", ResourceType::kInteger }, + { "interpolator", ResourceType::kInterpolator }, + { "layout", ResourceType::kLayout }, + { "menu", ResourceType::kMenu }, + { "mipmap", ResourceType::kMipmap }, + { "plurals", ResourceType::kPlurals }, + { "raw", ResourceType::kRaw }, + { "string", ResourceType::kString }, + { "style", ResourceType::kStyle }, + { "styleable", ResourceType::kStyleable }, + { "transition", ResourceType::kTransition }, + { "xml", ResourceType::kXml }, }; -const ResourceType* parseResourceType(const StringPiece16& str) { +const ResourceType* parseResourceType(const StringPiece& str) { auto iter = sResourceTypeMap.find(str); if (iter == std::end(sResourceTypeMap)) { return nullptr; diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h index 03ca42b286d6..22d75a2fb315 100644 --- a/tools/aapt2/Resource.h +++ b/tools/aapt2/Resource.h @@ -24,6 +24,7 @@ #include <iomanip> #include <limits> +#include <sstream> #include <string> #include <tuple> #include <vector> @@ -60,28 +61,28 @@ enum class ResourceType { kXml, }; -StringPiece16 toString(ResourceType type); +StringPiece toString(ResourceType type); /** * Returns a pointer to a valid ResourceType, or nullptr if * the string was invalid. */ -const ResourceType* parseResourceType(const StringPiece16& str); +const ResourceType* parseResourceType(const StringPiece& str); /** * A resource's name. This can uniquely identify * a resource in the ResourceTable. */ struct ResourceName { - std::u16string package; + std::string package; ResourceType type; - std::u16string entry; + std::string entry; ResourceName() : type(ResourceType::kRaw) {} - ResourceName(const StringPiece16& p, ResourceType t, const StringPiece16& e); + ResourceName(const StringPiece& p, ResourceType t, const StringPiece& e); bool isValid() const; - std::u16string toString() const; + std::string toString() const; }; /** @@ -91,15 +92,15 @@ struct ResourceName { * of the original string. */ struct ResourceNameRef { - StringPiece16 package; + StringPiece package; ResourceType type; - StringPiece16 entry; + StringPiece entry; ResourceNameRef() = default; ResourceNameRef(const ResourceNameRef&) = default; ResourceNameRef(ResourceNameRef&&) = default; ResourceNameRef(const ResourceName& rhs); - ResourceNameRef(const StringPiece16& p, ResourceType t, const StringPiece16& e); + ResourceNameRef(const StringPiece& p, ResourceType t, const StringPiece& e); ResourceNameRef& operator=(const ResourceNameRef& rhs) = default; ResourceNameRef& operator=(ResourceNameRef&& rhs) = default; ResourceNameRef& operator=(const ResourceName& rhs); @@ -252,7 +253,7 @@ inline ::std::ostream& operator<<(::std::ostream& out, const ResourceType& val) // ResourceName implementation. // -inline ResourceName::ResourceName(const StringPiece16& p, ResourceType t, const StringPiece16& e) : +inline ResourceName::ResourceName(const StringPiece& p, ResourceType t, const StringPiece& e) : package(p.toString()), type(t), entry(e.toString()) { } @@ -275,14 +276,6 @@ inline bool operator!=(const ResourceName& lhs, const ResourceName& rhs) { != std::tie(rhs.package, rhs.type, rhs.entry); } -inline std::u16string ResourceName::toString() const { - std::u16string result; - if (!package.empty()) { - result = package + u":"; - } - return result + aapt::toString(type).toString() + u"/" + entry; -} - inline ::std::ostream& operator<<(::std::ostream& out, const ResourceName& name) { if (!name.package.empty()) { out << name.package << ":"; @@ -290,6 +283,11 @@ inline ::std::ostream& operator<<(::std::ostream& out, const ResourceName& name) return out << name.type << "/" << name.entry; } +inline std::string ResourceName::toString() const { + std::stringstream stream; + stream << *this; + return stream.str(); +} // // ResourceNameRef implementation. @@ -299,8 +297,8 @@ inline ResourceNameRef::ResourceNameRef(const ResourceName& rhs) : package(rhs.package), type(rhs.type), entry(rhs.entry) { } -inline ResourceNameRef::ResourceNameRef(const StringPiece16& p, ResourceType t, - const StringPiece16& e) : +inline ResourceNameRef::ResourceNameRef(const StringPiece& p, ResourceType t, + const StringPiece& e) : package(p), type(t), entry(e) { } @@ -312,7 +310,7 @@ inline ResourceNameRef& ResourceNameRef::operator=(const ResourceName& rhs) { } inline ResourceName ResourceNameRef::toResourceName() const { - return { package.toString(), type, entry.toString() }; + return ResourceName(package, type, entry); } inline bool ResourceNameRef::isValid() const { diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index a84c306e2733..45d3db9c3c4a 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -28,33 +28,33 @@ namespace aapt { -constexpr const char16_t* sXliffNamespaceUri = u"urn:oasis:names:tc:xliff:document:1.2"; +constexpr const char* sXliffNamespaceUri = "urn:oasis:names:tc:xliff:document:1.2"; /** * Returns true if the element is <skip> or <eat-comment> and can be safely ignored. */ -static bool shouldIgnoreElement(const StringPiece16& ns, const StringPiece16& name) { - return ns.empty() && (name == u"skip" || name == u"eat-comment"); +static bool shouldIgnoreElement(const StringPiece& ns, const StringPiece& name) { + return ns.empty() && (name == "skip" || name == "eat-comment"); } -static uint32_t parseFormatType(const StringPiece16& piece) { - if (piece == u"reference") return android::ResTable_map::TYPE_REFERENCE; - else if (piece == u"string") return android::ResTable_map::TYPE_STRING; - else if (piece == u"integer") return android::ResTable_map::TYPE_INTEGER; - else if (piece == u"boolean") return android::ResTable_map::TYPE_BOOLEAN; - else if (piece == u"color") return android::ResTable_map::TYPE_COLOR; - else if (piece == u"float") return android::ResTable_map::TYPE_FLOAT; - else if (piece == u"dimension") return android::ResTable_map::TYPE_DIMENSION; - else if (piece == u"fraction") return android::ResTable_map::TYPE_FRACTION; - else if (piece == u"enum") return android::ResTable_map::TYPE_ENUM; - else if (piece == u"flags") return android::ResTable_map::TYPE_FLAGS; +static uint32_t parseFormatType(const StringPiece& piece) { + if (piece == "reference") return android::ResTable_map::TYPE_REFERENCE; + else if (piece == "string") return android::ResTable_map::TYPE_STRING; + else if (piece == "integer") return android::ResTable_map::TYPE_INTEGER; + else if (piece == "boolean") return android::ResTable_map::TYPE_BOOLEAN; + else if (piece == "color") return android::ResTable_map::TYPE_COLOR; + else if (piece == "float") return android::ResTable_map::TYPE_FLOAT; + else if (piece == "dimension") return android::ResTable_map::TYPE_DIMENSION; + else if (piece == "fraction") return android::ResTable_map::TYPE_FRACTION; + else if (piece == "enum") return android::ResTable_map::TYPE_ENUM; + else if (piece == "flags") return android::ResTable_map::TYPE_FLAGS; return 0; } -static uint32_t parseFormatAttribute(const StringPiece16& str) { +static uint32_t parseFormatAttribute(const StringPiece& str) { uint32_t mask = 0; - for (StringPiece16 part : util::tokenize(str, u'|')) { - StringPiece16 trimmedPart = util::trimWhitespace(part); + for (StringPiece part : util::tokenize(str, '|')) { + StringPiece trimmedPart = util::trimWhitespace(part); uint32_t type = parseFormatType(trimmedPart); if (type == 0) { return 0; @@ -74,14 +74,14 @@ struct ParsedResource { Source source; ResourceId id; Maybe<SymbolState> symbolState; - std::u16string comment; + std::string comment; std::unique_ptr<Value> value; std::list<ParsedResource> childResources; }; // Recursively adds resources to the ResourceTable. static bool addResourcesToTable(ResourceTable* table, IDiagnostics* diag, ParsedResource* res) { - StringPiece16 trimmedComment = util::trimWhitespace(res->comment); + StringPiece trimmedComment = util::trimWhitespace(res->comment); if (trimmedComment.size() != res->comment.size()) { // Only if there was a change do we re-assign. res->comment = trimmedComment.toString(); @@ -130,7 +130,7 @@ ResourceParser::ResourceParser(IDiagnostics* diag, ResourceTable* table, const S /** * Build a string from XML that converts nested elements into Span objects. */ -bool ResourceParser::flattenXmlSubtree(xml::XmlPullParser* parser, std::u16string* outRawString, +bool ResourceParser::flattenXmlSubtree(xml::XmlPullParser* parser, std::string* outRawString, StyleString* outStyleString) { std::vector<Span> spanStack; @@ -176,12 +176,12 @@ bool ResourceParser::flattenXmlSubtree(xml::XmlPullParser* parser, std::u16strin depth++; // Build a span object out of the nested element. - std::u16string spanName = parser->getElementName(); + std::string spanName = parser->getElementName(); const auto endAttrIter = parser->endAttributes(); for (auto attrIter = parser->beginAttributes(); attrIter != endAttrIter; ++attrIter) { - spanName += u";"; + spanName += ";"; spanName += attrIter->name; - spanName += u"="; + spanName += "="; spanName += attrIter->value; } @@ -214,7 +214,7 @@ bool ResourceParser::parse(xml::XmlPullParser* parser) { continue; } - if (!parser->getElementNamespace().empty() || parser->getElementName() != u"resources") { + if (!parser->getElementNamespace().empty() || parser->getElementName() != "resources") { mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber())) << "root element must be <resources>"); return false; @@ -236,7 +236,7 @@ bool ResourceParser::parseResources(xml::XmlPullParser* parser) { std::set<ResourceName> strippedResources; bool error = false; - std::u16string comment; + std::string comment; const size_t depth = parser->getDepth(); while (xml::XmlPullParser::nextChildNode(parser, depth)) { const xml::XmlPullParser::Event event = parser->getEvent(); @@ -261,9 +261,9 @@ bool ResourceParser::parseResources(xml::XmlPullParser* parser) { continue; } - std::u16string elementName = parser->getElementName(); - if (elementName == u"skip" || elementName == u"eat-comment") { - comment = u""; + std::string elementName = parser->getElementName(); + if (elementName == "skip" || elementName == "eat-comment") { + comment = ""; continue; } @@ -273,8 +273,8 @@ bool ResourceParser::parseResources(xml::XmlPullParser* parser) { parsedResource.comment = std::move(comment); // Extract the product name if it exists. - if (Maybe<StringPiece16> maybeProduct = xml::findNonEmptyAttribute(parser, u"product")) { - parsedResource.product = util::utf16ToUtf8(maybeProduct.value()); + if (Maybe<StringPiece> maybeProduct = xml::findNonEmptyAttribute(parser, "product")) { + parsedResource.product = maybeProduct.value().toString(); } // Parse the resource regardless of product. @@ -310,43 +310,43 @@ bool ResourceParser::parseResource(xml::XmlPullParser* parser, ParsedResource* o using BagParseFunc = std::function<bool(ResourceParser*, xml::XmlPullParser*, ParsedResource*)>; - static const auto elToItemMap = ImmutableMap<std::u16string, ItemTypeFormat>::createPreSorted({ - { u"bool", { ResourceType::kBool, android::ResTable_map::TYPE_BOOLEAN } }, - { u"color", { ResourceType::kColor, android::ResTable_map::TYPE_COLOR } }, - { u"dimen", { ResourceType::kDimen, android::ResTable_map::TYPE_FLOAT + static const auto elToItemMap = ImmutableMap<std::string, ItemTypeFormat>::createPreSorted({ + { "bool", { ResourceType::kBool, android::ResTable_map::TYPE_BOOLEAN } }, + { "color", { ResourceType::kColor, android::ResTable_map::TYPE_COLOR } }, + { "dimen", { ResourceType::kDimen, android::ResTable_map::TYPE_FLOAT | android::ResTable_map::TYPE_FRACTION | android::ResTable_map::TYPE_DIMENSION } }, - { u"drawable", { ResourceType::kDrawable, android::ResTable_map::TYPE_COLOR } }, - { u"fraction", { ResourceType::kFraction, android::ResTable_map::TYPE_FLOAT + { "drawable", { ResourceType::kDrawable, android::ResTable_map::TYPE_COLOR } }, + { "fraction", { ResourceType::kFraction, android::ResTable_map::TYPE_FLOAT | android::ResTable_map::TYPE_FRACTION | android::ResTable_map::TYPE_DIMENSION } }, - { u"integer", { ResourceType::kInteger, android::ResTable_map::TYPE_INTEGER } }, - { u"string", { ResourceType::kString, android::ResTable_map::TYPE_STRING } }, + { "integer", { ResourceType::kInteger, android::ResTable_map::TYPE_INTEGER } }, + { "string", { ResourceType::kString, android::ResTable_map::TYPE_STRING } }, }); - static const auto elToBagMap = ImmutableMap<std::u16string, BagParseFunc>::createPreSorted({ - { u"add-resource", std::mem_fn(&ResourceParser::parseAddResource) }, - { u"array", std::mem_fn(&ResourceParser::parseArray) }, - { u"attr", std::mem_fn(&ResourceParser::parseAttr) }, - { u"declare-styleable", std::mem_fn(&ResourceParser::parseDeclareStyleable) }, - { u"integer-array", std::mem_fn(&ResourceParser::parseIntegerArray) }, - { u"java-symbol", std::mem_fn(&ResourceParser::parseSymbol) }, - { u"plurals", std::mem_fn(&ResourceParser::parsePlural) }, - { u"public", std::mem_fn(&ResourceParser::parsePublic) }, - { u"public-group", std::mem_fn(&ResourceParser::parsePublicGroup) }, - { u"string-array", std::mem_fn(&ResourceParser::parseStringArray) }, - { u"style", std::mem_fn(&ResourceParser::parseStyle) }, - { u"symbol", std::mem_fn(&ResourceParser::parseSymbol) }, + static const auto elToBagMap = ImmutableMap<std::string, BagParseFunc>::createPreSorted({ + { "add-resource", std::mem_fn(&ResourceParser::parseAddResource) }, + { "array", std::mem_fn(&ResourceParser::parseArray) }, + { "attr", std::mem_fn(&ResourceParser::parseAttr) }, + { "declare-styleable", std::mem_fn(&ResourceParser::parseDeclareStyleable) }, + { "integer-array", std::mem_fn(&ResourceParser::parseIntegerArray) }, + { "java-symbol", std::mem_fn(&ResourceParser::parseSymbol) }, + { "plurals", std::mem_fn(&ResourceParser::parsePlural) }, + { "public", std::mem_fn(&ResourceParser::parsePublic) }, + { "public-group", std::mem_fn(&ResourceParser::parsePublicGroup) }, + { "string-array", std::mem_fn(&ResourceParser::parseStringArray) }, + { "style", std::mem_fn(&ResourceParser::parseStyle) }, + { "symbol", std::mem_fn(&ResourceParser::parseSymbol) }, }); - std::u16string resourceType = parser->getElementName(); + std::string resourceType = parser->getElementName(); // The value format accepted for this resource. uint32_t resourceFormat = 0u; - if (resourceType == u"item") { + if (resourceType == "item") { // Items have their type encoded in the type attribute. - if (Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type")) { + if (Maybe<StringPiece> maybeType = xml::findNonEmptyAttribute(parser, "type")) { resourceType = maybeType.value().toString(); } else { mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber())) @@ -354,7 +354,7 @@ bool ResourceParser::parseResource(xml::XmlPullParser* parser, ParsedResource* o return false; } - if (Maybe<StringPiece16> maybeFormat = xml::findNonEmptyAttribute(parser, u"format")) { + if (Maybe<StringPiece> maybeFormat = xml::findNonEmptyAttribute(parser, "format")) { // An explicit format for this resource was specified. The resource will retain // its type in its name, but the accepted value for this type is overridden. resourceFormat = parseFormatType(maybeFormat.value()); @@ -368,9 +368,9 @@ bool ResourceParser::parseResource(xml::XmlPullParser* parser, ParsedResource* o // Get the name of the resource. This will be checked later, because not all // XML elements require a name. - Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name"); + Maybe<StringPiece> maybeName = xml::findNonEmptyAttribute(parser, "name"); - if (resourceType == u"id") { + if (resourceType == "id") { if (!maybeName) { mDiag->error(DiagMessage(outResource->source) << "<" << parser->getElementName() << "> missing 'name' attribute"); @@ -411,7 +411,7 @@ bool ResourceParser::parseResource(xml::XmlPullParser* parser, ParsedResource* o const auto bagIter = elToBagMap.find(resourceType); if (bagIter != elToBagMap.end()) { // Ensure we have a name (unless this is a <public-group>). - if (resourceType != u"public-group") { + if (resourceType != "public-group") { if (!maybeName) { mDiag->error(DiagMessage(outResource->source) << "<" << parser->getElementName() << "> missing 'name' attribute"); @@ -480,7 +480,7 @@ std::unique_ptr<Item> ResourceParser::parseXml(xml::XmlPullParser* parser, const const bool allowRawValue) { const size_t beginXmlLine = parser->getLineNumber(); - std::u16string rawValue; + std::string rawValue; StyleString styleString; if (!flattenXmlSubtree(parser, &rawValue, &styleString)) { return {}; @@ -505,7 +505,7 @@ std::unique_ptr<Item> ResourceParser::parseXml(xml::XmlPullParser* parser, const if (processedItem) { // Fix up the reference. if (Reference* ref = valueCast<Reference>(processedItem.get())) { - transformReferenceFromNamespace(parser, u"", ref); + transformReferenceFromNamespace(parser, "", ref); } return processedItem; } @@ -527,7 +527,7 @@ std::unique_ptr<Item> ResourceParser::parseXml(xml::XmlPullParser* parser, const bool ResourceParser::parseString(xml::XmlPullParser* parser, ParsedResource* outResource) { bool formatted = true; - if (Maybe<StringPiece16> formattedAttr = xml::findAttribute(parser, u"formatted")) { + if (Maybe<StringPiece> formattedAttr = xml::findAttribute(parser, "formatted")) { if (!ResourceUtils::tryParseBool(formattedAttr.value(), &formatted)) { mDiag->error(DiagMessage(outResource->source) << "invalid value for 'formatted'. Must be a boolean"); @@ -536,7 +536,7 @@ bool ResourceParser::parseString(xml::XmlPullParser* parser, ParsedResource* out } bool translateable = mOptions.translatable; - if (Maybe<StringPiece16> translateableAttr = xml::findAttribute(parser, u"translatable")) { + if (Maybe<StringPiece> translateableAttr = xml::findAttribute(parser, "translatable")) { if (!ResourceUtils::tryParseBool(translateableAttr.value(), &translateable)) { mDiag->error(DiagMessage(outResource->source) << "invalid value for 'translatable'. Must be a boolean"); @@ -574,7 +574,7 @@ bool ResourceParser::parseString(xml::XmlPullParser* parser, ParsedResource* out } bool ResourceParser::parsePublic(xml::XmlPullParser* parser, ParsedResource* outResource) { - Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type"); + Maybe<StringPiece> maybeType = xml::findNonEmptyAttribute(parser, "type"); if (!maybeType) { mDiag->error(DiagMessage(outResource->source) << "<public> must have a 'type' attribute"); return false; @@ -589,10 +589,10 @@ bool ResourceParser::parsePublic(xml::XmlPullParser* parser, ParsedResource* out outResource->name.type = *parsedType; - if (Maybe<StringPiece16> maybeId = xml::findNonEmptyAttribute(parser, u"id")) { + if (Maybe<StringPiece> maybeId = xml::findNonEmptyAttribute(parser, "id")) { android::Res_value val; - bool result = android::ResTable::stringToInt(maybeId.value().data(), - maybeId.value().size(), &val); + std::u16string idStr16 = util::utf8ToUtf16(maybeId.value()); + bool result = android::ResTable::stringToInt(idStr16.data(), idStr16.size(), &val); ResourceId resourceId(val.data); if (!result || !resourceId.isValid()) { mDiag->error(DiagMessage(outResource->source) @@ -612,7 +612,7 @@ bool ResourceParser::parsePublic(xml::XmlPullParser* parser, ParsedResource* out } bool ResourceParser::parsePublicGroup(xml::XmlPullParser* parser, ParsedResource* outResource) { - Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type"); + Maybe<StringPiece> maybeType = xml::findNonEmptyAttribute(parser, "type"); if (!maybeType) { mDiag->error(DiagMessage(outResource->source) << "<public-group> must have a 'type' attribute"); @@ -626,7 +626,7 @@ bool ResourceParser::parsePublicGroup(xml::XmlPullParser* parser, ParsedResource return false; } - Maybe<StringPiece16> maybeId = xml::findNonEmptyAttribute(parser, u"first-id"); + Maybe<StringPiece> maybeId = xml::findNonEmptyAttribute(parser, "first-id"); if (!maybeId) { mDiag->error(DiagMessage(outResource->source) << "<public-group> must have a 'first-id' attribute"); @@ -634,8 +634,8 @@ bool ResourceParser::parsePublicGroup(xml::XmlPullParser* parser, ParsedResource } android::Res_value val; - bool result = android::ResTable::stringToInt(maybeId.value().data(), - maybeId.value().size(), &val); + std::u16string idStr16 = util::utf8ToUtf16(maybeId.value()); + bool result = android::ResTable::stringToInt(idStr16.data(), idStr16.size(), &val); ResourceId nextId(val.data); if (!result || !nextId.isValid()) { mDiag->error(DiagMessage(outResource->source) @@ -643,7 +643,7 @@ bool ResourceParser::parsePublicGroup(xml::XmlPullParser* parser, ParsedResource return false; } - std::u16string comment; + std::string comment; bool error = false; const size_t depth = parser->getDepth(); while (xml::XmlPullParser::nextChildNode(parser, depth)) { @@ -656,23 +656,23 @@ bool ResourceParser::parsePublicGroup(xml::XmlPullParser* parser, ParsedResource } const Source itemSource = mSource.withLine(parser->getLineNumber()); - const std::u16string& elementNamespace = parser->getElementNamespace(); - const std::u16string& elementName = parser->getElementName(); - if (elementNamespace.empty() && elementName == u"public") { - Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name"); + const std::string& elementNamespace = parser->getElementNamespace(); + const std::string& elementName = parser->getElementName(); + if (elementNamespace.empty() && elementName == "public") { + Maybe<StringPiece> maybeName = xml::findNonEmptyAttribute(parser, "name"); if (!maybeName) { mDiag->error(DiagMessage(itemSource) << "<public> must have a 'name' attribute"); error = true; continue; } - if (xml::findNonEmptyAttribute(parser, u"id")) { + if (xml::findNonEmptyAttribute(parser, "id")) { mDiag->error(DiagMessage(itemSource) << "'id' is ignored within <public-group>"); error = true; continue; } - if (xml::findNonEmptyAttribute(parser, u"type")) { + if (xml::findNonEmptyAttribute(parser, "type")) { mDiag->error(DiagMessage(itemSource) << "'type' is ignored within <public-group>"); error = true; continue; @@ -698,7 +698,7 @@ bool ResourceParser::parsePublicGroup(xml::XmlPullParser* parser, ParsedResource } bool ResourceParser::parseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* outResource) { - Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type"); + Maybe<StringPiece> maybeType = xml::findNonEmptyAttribute(parser, "type"); if (!maybeType) { mDiag->error(DiagMessage(outResource->source) << "<" << parser->getElementName() << "> must have a 'type' attribute"); @@ -751,7 +751,7 @@ bool ResourceParser::parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* o uint32_t typeMask = 0; - Maybe<StringPiece16> maybeFormat = xml::findAttribute(parser, u"format"); + Maybe<StringPiece> maybeFormat = xml::findAttribute(parser, "format"); if (maybeFormat) { typeMask = parseFormatAttribute(maybeFormat.value()); if (typeMask == 0) { @@ -763,11 +763,12 @@ bool ResourceParser::parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* o Maybe<int32_t> maybeMin, maybeMax; - if (Maybe<StringPiece16> maybeMinStr = xml::findAttribute(parser, u"min")) { - StringPiece16 minStr = util::trimWhitespace(maybeMinStr.value()); + if (Maybe<StringPiece> maybeMinStr = xml::findAttribute(parser, "min")) { + StringPiece minStr = util::trimWhitespace(maybeMinStr.value()); if (!minStr.empty()) { + std::u16string minStr16 = util::utf8ToUtf16(minStr); android::Res_value value; - if (android::ResTable::stringToInt(minStr.data(), minStr.size(), &value)) { + if (android::ResTable::stringToInt(minStr16.data(), minStr16.size(), &value)) { maybeMin = static_cast<int32_t>(value.data); } } @@ -779,11 +780,12 @@ bool ResourceParser::parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* o } } - if (Maybe<StringPiece16> maybeMaxStr = xml::findAttribute(parser, u"max")) { - StringPiece16 maxStr = util::trimWhitespace(maybeMaxStr.value()); + if (Maybe<StringPiece> maybeMaxStr = xml::findAttribute(parser, "max")) { + StringPiece maxStr = util::trimWhitespace(maybeMaxStr.value()); if (!maxStr.empty()) { + std::u16string maxStr16 = util::utf8ToUtf16(maxStr); android::Res_value value; - if (android::ResTable::stringToInt(maxStr.data(), maxStr.size(), &value)) { + if (android::ResTable::stringToInt(maxStr16.data(), maxStr16.size(), &value)) { maybeMax = static_cast<int32_t>(value.data); } } @@ -809,7 +811,7 @@ bool ResourceParser::parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* o std::set<Attribute::Symbol, SymbolComparator> items; - std::u16string comment; + std::string comment; bool error = false; const size_t depth = parser->getDepth(); while (xml::XmlPullParser::nextChildNode(parser, depth)) { @@ -822,10 +824,10 @@ bool ResourceParser::parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* o } const Source itemSource = mSource.withLine(parser->getLineNumber()); - const std::u16string& elementNamespace = parser->getElementNamespace(); - const std::u16string& elementName = parser->getElementName(); - if (elementNamespace.empty() && (elementName == u"flag" || elementName == u"enum")) { - if (elementName == u"enum") { + const std::string& elementNamespace = parser->getElementNamespace(); + const std::string& elementName = parser->getElementName(); + if (elementNamespace.empty() && (elementName == "flag" || elementName == "enum")) { + if (elementName == "enum") { if (typeMask & android::ResTable_map::TYPE_FLAGS) { mDiag->error(DiagMessage(itemSource) << "can not define an <enum>; already defined a <flag>"); @@ -834,7 +836,7 @@ bool ResourceParser::parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* o } typeMask |= android::ResTable_map::TYPE_ENUM; - } else if (elementName == u"flag") { + } else if (elementName == "flag") { if (typeMask & android::ResTable_map::TYPE_ENUM) { mDiag->error(DiagMessage(itemSource) << "can not define a <flag>; already defined an <enum>"); @@ -896,24 +898,24 @@ bool ResourceParser::parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* o } Maybe<Attribute::Symbol> ResourceParser::parseEnumOrFlagItem(xml::XmlPullParser* parser, - const StringPiece16& tag) { + const StringPiece& tag) { const Source source = mSource.withLine(parser->getLineNumber()); - Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name"); + Maybe<StringPiece> maybeName = xml::findNonEmptyAttribute(parser, "name"); if (!maybeName) { mDiag->error(DiagMessage(source) << "no attribute 'name' found for tag <" << tag << ">"); return {}; } - Maybe<StringPiece16> maybeValue = xml::findNonEmptyAttribute(parser, u"value"); + Maybe<StringPiece> maybeValue = xml::findNonEmptyAttribute(parser, "value"); if (!maybeValue) { mDiag->error(DiagMessage(source) << "no attribute 'value' found for tag <" << tag << ">"); return {}; } + std::u16string value16 = util::utf8ToUtf16(maybeValue.value()); android::Res_value val; - if (!android::ResTable::stringToInt(maybeValue.value().data(), - maybeValue.value().size(), &val)) { + if (!android::ResTable::stringToInt(value16.data(), value16.size(), &val)) { mDiag->error(DiagMessage(source) << "invalid value '" << maybeValue.value() << "' for <" << tag << ">; must be an integer"); return {}; @@ -923,25 +925,25 @@ Maybe<Attribute::Symbol> ResourceParser::parseEnumOrFlagItem(xml::XmlPullParser* Reference(ResourceNameRef({}, ResourceType::kId, maybeName.value())), val.data }; } -static Maybe<Reference> parseXmlAttributeName(StringPiece16 str) { +static Maybe<Reference> parseXmlAttributeName(StringPiece str) { str = util::trimWhitespace(str); - const char16_t* start = str.data(); - const char16_t* const end = start + str.size(); - const char16_t* p = start; + const char* start = str.data(); + const char* const end = start + str.size(); + const char* p = start; Reference ref; - if (p != end && *p == u'*') { + if (p != end && *p == '*') { ref.privateReference = true; start++; p++; } - StringPiece16 package; - StringPiece16 name; + StringPiece package; + StringPiece name; while (p != end) { - if (*p == u':') { - package = StringPiece16(start, p - start); - name = StringPiece16(p + 1, end - (p + 1)); + if (*p == ':') { + package = StringPiece(start, p - start); + name = StringPiece(p + 1, end - (p + 1)); break; } p++; @@ -955,7 +957,7 @@ static Maybe<Reference> parseXmlAttributeName(StringPiece16 str) { bool ResourceParser::parseStyleItem(xml::XmlPullParser* parser, Style* style) { const Source source = mSource.withLine(parser->getLineNumber()); - Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name"); + Maybe<StringPiece> maybeName = xml::findNonEmptyAttribute(parser, "name"); if (!maybeName) { mDiag->error(DiagMessage(source) << "<item> must have a 'name' attribute"); return false; @@ -967,7 +969,7 @@ bool ResourceParser::parseStyleItem(xml::XmlPullParser* parser, Style* style) { return false; } - transformReferenceFromNamespace(parser, u"", &maybeKey.value()); + transformReferenceFromNamespace(parser, "", &maybeKey.value()); maybeKey.value().setSource(source); std::unique_ptr<Item> value = parseXml(parser, 0, kAllowRawString); @@ -985,7 +987,7 @@ bool ResourceParser::parseStyle(xml::XmlPullParser* parser, ParsedResource* outR std::unique_ptr<Style> style = util::make_unique<Style>(); - Maybe<StringPiece16> maybeParent = xml::findAttribute(parser, u"parent"); + Maybe<StringPiece> maybeParent = xml::findAttribute(parser, "parent"); if (maybeParent) { // If the parent is empty, we don't have a parent, but we also don't infer either. if (!maybeParent.value().empty()) { @@ -998,12 +1000,12 @@ bool ResourceParser::parseStyle(xml::XmlPullParser* parser, ParsedResource* outR // Transform the namespace prefix to the actual package name, and mark the reference as // private if appropriate. - transformReferenceFromNamespace(parser, u"", &style->parent.value()); + transformReferenceFromNamespace(parser, "", &style->parent.value()); } } else { // No parent was specified, so try inferring it from the style name. - std::u16string styleName = outResource->name.entry; + std::string styleName = outResource->name.entry; size_t pos = styleName.find_last_of(u'.'); if (pos != std::string::npos) { style->parentInferred = true; @@ -1020,9 +1022,9 @@ bool ResourceParser::parseStyle(xml::XmlPullParser* parser, ParsedResource* outR continue; } - const std::u16string& elementNamespace = parser->getElementNamespace(); - const std::u16string& elementName = parser->getElementName(); - if (elementNamespace == u"" && elementName == u"item") { + const std::string& elementNamespace = parser->getElementNamespace(); + const std::string& elementName = parser->getElementName(); + if (elementNamespace == "" && elementName == "item") { error |= !parseStyleItem(parser, style.get()); } else if (!shouldIgnoreElement(elementNamespace, elementName)) { @@ -1059,7 +1061,7 @@ bool ResourceParser::parseArrayImpl(xml::XmlPullParser* parser, ParsedResource* std::unique_ptr<Array> array = util::make_unique<Array>(); bool translateable = mOptions.translatable; - if (Maybe<StringPiece16> translateableAttr = xml::findAttribute(parser, u"translatable")) { + if (Maybe<StringPiece> translateableAttr = xml::findAttribute(parser, "translatable")) { if (!ResourceUtils::tryParseBool(translateableAttr.value(), &translateable)) { mDiag->error(DiagMessage(outResource->source) << "invalid value for 'translatable'. Must be a boolean"); @@ -1077,9 +1079,9 @@ bool ResourceParser::parseArrayImpl(xml::XmlPullParser* parser, ParsedResource* } const Source itemSource = mSource.withLine(parser->getLineNumber()); - const std::u16string& elementNamespace = parser->getElementNamespace(); - const std::u16string& elementName = parser->getElementName(); - if (elementNamespace.empty() && elementName == u"item") { + const std::string& elementNamespace = parser->getElementNamespace(); + const std::string& elementName = parser->getElementName(); + if (elementNamespace.empty() && elementName == "item") { std::unique_ptr<Item> item = parseXml(parser, typeMask, kNoRawString); if (!item) { mDiag->error(DiagMessage(itemSource) << "could not parse array item"); @@ -1118,10 +1120,10 @@ bool ResourceParser::parsePlural(xml::XmlPullParser* parser, ParsedResource* out } const Source itemSource = mSource.withLine(parser->getLineNumber()); - const std::u16string& elementNamespace = parser->getElementNamespace(); - const std::u16string& elementName = parser->getElementName(); - if (elementNamespace.empty() && elementName == u"item") { - Maybe<StringPiece16> maybeQuantity = xml::findNonEmptyAttribute(parser, u"quantity"); + const std::string& elementNamespace = parser->getElementNamespace(); + const std::string& elementName = parser->getElementName(); + if (elementNamespace.empty() && elementName == "item") { + Maybe<StringPiece> maybeQuantity = xml::findNonEmptyAttribute(parser, "quantity"); if (!maybeQuantity) { mDiag->error(DiagMessage(itemSource) << "<item> in <plurals> requires attribute " << "'quantity'"); @@ -1129,19 +1131,19 @@ bool ResourceParser::parsePlural(xml::XmlPullParser* parser, ParsedResource* out continue; } - StringPiece16 trimmedQuantity = util::trimWhitespace(maybeQuantity.value()); + StringPiece trimmedQuantity = util::trimWhitespace(maybeQuantity.value()); size_t index = 0; - if (trimmedQuantity == u"zero") { + if (trimmedQuantity == "zero") { index = Plural::Zero; - } else if (trimmedQuantity == u"one") { + } else if (trimmedQuantity == "one") { index = Plural::One; - } else if (trimmedQuantity == u"two") { + } else if (trimmedQuantity == "two") { index = Plural::Two; - } else if (trimmedQuantity == u"few") { + } else if (trimmedQuantity == "few") { index = Plural::Few; - } else if (trimmedQuantity == u"many") { + } else if (trimmedQuantity == "many") { index = Plural::Many; - } else if (trimmedQuantity == u"other") { + } else if (trimmedQuantity == "other") { index = Plural::Other; } else { mDiag->error(DiagMessage(itemSource) @@ -1196,7 +1198,7 @@ bool ResourceParser::parseDeclareStyleable(xml::XmlPullParser* parser, std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>(); - std::u16string comment; + std::string comment; bool error = false; const size_t depth = parser->getDepth(); while (xml::XmlPullParser::nextChildNode(parser, depth)) { @@ -1209,10 +1211,10 @@ bool ResourceParser::parseDeclareStyleable(xml::XmlPullParser* parser, } const Source itemSource = mSource.withLine(parser->getLineNumber()); - const std::u16string& elementNamespace = parser->getElementNamespace(); - const std::u16string& elementName = parser->getElementName(); - if (elementNamespace.empty() && elementName == u"attr") { - Maybe<StringPiece16> maybeName = xml::findNonEmptyAttribute(parser, u"name"); + const std::string& elementNamespace = parser->getElementNamespace(); + const std::string& elementName = parser->getElementName(); + if (elementNamespace.empty() && elementName == "attr") { + Maybe<StringPiece> maybeName = xml::findNonEmptyAttribute(parser, "name"); if (!maybeName) { mDiag->error(DiagMessage(itemSource) << "<attr> tag must have a 'name' attribute"); error = true; @@ -1230,7 +1232,7 @@ bool ResourceParser::parseDeclareStyleable(xml::XmlPullParser* parser, } Reference& childRef = maybeRef.value(); - xml::transformReferenceFromNamespace(parser, u"", &childRef); + xml::transformReferenceFromNamespace(parser, "", &childRef); // Create the ParsedResource that will add the attribute to the table. ParsedResource childResource; diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h index ee5b33788312..ece3090609aa 100644 --- a/tools/aapt2/ResourceParser.h +++ b/tools/aapt2/ResourceParser.h @@ -63,7 +63,7 @@ private: * contains the escaped and whitespace trimmed text, while `outRawString` * contains the unescaped text. Returns true on success. */ - bool flattenXmlSubtree(xml::XmlPullParser* parser, std::u16string* outRawString, + bool flattenXmlSubtree(xml::XmlPullParser* parser, std::string* outRawString, StyleString* outStyleString); /* @@ -89,7 +89,7 @@ private: bool parseAttr(xml::XmlPullParser* parser, ParsedResource* outResource); bool parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* outResource, bool weak); Maybe<Attribute::Symbol> parseEnumOrFlagItem(xml::XmlPullParser* parser, - const StringPiece16& tag); + const StringPiece& tag); bool parseStyle(xml::XmlPullParser* parser, ParsedResource* outResource); bool parseStyleItem(xml::XmlPullParser* parser, Style* style); bool parseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* outResource); diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp index 3450de9078bb..b456c0460147 100644 --- a/tools/aapt2/ResourceParser_test.cpp +++ b/tools/aapt2/ResourceParser_test.cpp @@ -18,10 +18,9 @@ #include "ResourceTable.h" #include "ResourceUtils.h" #include "ResourceValues.h" -#include "test/Context.h" +#include "test/Test.h" #include "xml/XmlPullParser.h" -#include <gtest/gtest.h> #include <sstream> #include <string> @@ -69,18 +68,18 @@ TEST_F(ResourceParserTest, ParseQuotedString) { std::string input = "<string name=\"foo\"> \" hey there \" </string>"; ASSERT_TRUE(testParse(input)); - String* str = test::getValue<String>(&mTable, u"@string/foo"); + String* str = test::getValue<String>(&mTable, "@string/foo"); ASSERT_NE(nullptr, str); - EXPECT_EQ(std::u16string(u" hey there "), *str->value); + EXPECT_EQ(std::string(" hey there "), *str->value); } TEST_F(ResourceParserTest, ParseEscapedString) { std::string input = "<string name=\"foo\">\\?123</string>"; ASSERT_TRUE(testParse(input)); - String* str = test::getValue<String>(&mTable, u"@string/foo"); + String* str = test::getValue<String>(&mTable, "@string/foo"); ASSERT_NE(nullptr, str); - EXPECT_EQ(std::u16string(u"?123"), *str->value); + EXPECT_EQ(std::string("?123"), *str->value); } TEST_F(ResourceParserTest, ParseFormattedString) { @@ -97,9 +96,9 @@ TEST_F(ResourceParserTest, IgnoreXliffTags) { " There are <xliff:g id=\"count\">%1$d</xliff:g> apples</string>"; ASSERT_TRUE(testParse(input)); - String* str = test::getValue<String>(&mTable, u"@string/foo"); + String* str = test::getValue<String>(&mTable, "@string/foo"); ASSERT_NE(nullptr, str); - EXPECT_EQ(StringPiece16(u"There are %1$d apples"), StringPiece16(*str->value)); + EXPECT_EQ(StringPiece("There are %1$d apples"), StringPiece(*str->value)); } TEST_F(ResourceParserTest, ParseNull) { @@ -110,7 +109,7 @@ TEST_F(ResourceParserTest, ParseNull) { // a non-existing value, and this causes problems in styles when trying to resolve // an attribute. Null values must be encoded as android::Res_value::TYPE_REFERENCE // with a data value of 0. - BinaryPrimitive* integer = test::getValue<BinaryPrimitive>(&mTable, u"@integer/foo"); + BinaryPrimitive* integer = test::getValue<BinaryPrimitive>(&mTable, "@integer/foo"); ASSERT_NE(nullptr, integer); EXPECT_EQ(uint16_t(android::Res_value::TYPE_REFERENCE), integer->value.dataType); EXPECT_EQ(0u, integer->value.data); @@ -120,7 +119,7 @@ TEST_F(ResourceParserTest, ParseEmpty) { std::string input = "<integer name=\"foo\">@empty</integer>"; ASSERT_TRUE(testParse(input)); - BinaryPrimitive* integer = test::getValue<BinaryPrimitive>(&mTable, u"@integer/foo"); + BinaryPrimitive* integer = test::getValue<BinaryPrimitive>(&mTable, "@integer/foo"); ASSERT_NE(nullptr, integer); EXPECT_EQ(uint16_t(android::Res_value::TYPE_NULL), integer->value.dataType); EXPECT_EQ(uint32_t(android::Res_value::DATA_NULL_EMPTY), integer->value.data); @@ -131,11 +130,11 @@ TEST_F(ResourceParserTest, ParseAttr) { "<attr name=\"bar\"/>"; ASSERT_TRUE(testParse(input)); - Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo"); + Attribute* attr = test::getValue<Attribute>(&mTable, "@attr/foo"); ASSERT_NE(nullptr, attr); EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask); - attr = test::getValue<Attribute>(&mTable, u"@attr/bar"); + attr = test::getValue<Attribute>(&mTable, "@attr/bar"); ASSERT_NE(nullptr, attr); EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->typeMask); } @@ -151,20 +150,20 @@ TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoC </declare-styleable>)EOF"; ASSERT_TRUE(testParse(input, watchConfig)); - EXPECT_EQ(nullptr, test::getValueForConfig<Attribute>(&mTable, u"@attr/foo", watchConfig)); - EXPECT_EQ(nullptr, test::getValueForConfig<Attribute>(&mTable, u"@attr/baz", watchConfig)); - EXPECT_EQ(nullptr, test::getValueForConfig<Styleable>(&mTable, u"@styleable/bar", watchConfig)); + EXPECT_EQ(nullptr, test::getValueForConfig<Attribute>(&mTable, "@attr/foo", watchConfig)); + EXPECT_EQ(nullptr, test::getValueForConfig<Attribute>(&mTable, "@attr/baz", watchConfig)); + EXPECT_EQ(nullptr, test::getValueForConfig<Styleable>(&mTable, "@styleable/bar", watchConfig)); - EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, u"@attr/foo")); - EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, u"@attr/baz")); - EXPECT_NE(nullptr, test::getValue<Styleable>(&mTable, u"@styleable/bar")); + EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, "@attr/foo")); + EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, "@attr/baz")); + EXPECT_NE(nullptr, test::getValue<Styleable>(&mTable, "@styleable/bar")); } TEST_F(ResourceParserTest, ParseAttrWithMinMax) { std::string input = "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"integer\"/>"; ASSERT_TRUE(testParse(input)); - Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo"); + Attribute* attr = test::getValue<Attribute>(&mTable, "@attr/foo"); ASSERT_NE(nullptr, attr); EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_INTEGER), attr->typeMask); EXPECT_EQ(10, attr->minInt); @@ -183,7 +182,7 @@ TEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) { "<attr name=\"foo\" format=\"string\"/>"; ASSERT_TRUE(testParse(input)); - Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo"); + Attribute* attr = test::getValue<Attribute>(&mTable, "@attr/foo"); ASSERT_NE(nullptr, attr); EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask); } @@ -197,7 +196,7 @@ TEST_F(ResourceParserTest, ParseDoubleUseOfAttr) { "</declare-styleable>"; ASSERT_TRUE(testParse(input)); - Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo"); + Attribute* attr = test::getValue<Attribute>(&mTable, "@attr/foo"); ASSERT_NE(nullptr, attr); EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->typeMask); } @@ -210,21 +209,21 @@ TEST_F(ResourceParserTest, ParseEnumAttr) { "</attr>"; ASSERT_TRUE(testParse(input)); - Attribute* enumAttr = test::getValue<Attribute>(&mTable, u"@attr/foo"); + Attribute* enumAttr = test::getValue<Attribute>(&mTable, "@attr/foo"); ASSERT_NE(enumAttr, nullptr); EXPECT_EQ(enumAttr->typeMask, android::ResTable_map::TYPE_ENUM); ASSERT_EQ(enumAttr->symbols.size(), 3u); AAPT_ASSERT_TRUE(enumAttr->symbols[0].symbol.name); - EXPECT_EQ(enumAttr->symbols[0].symbol.name.value().entry, u"bar"); + EXPECT_EQ(enumAttr->symbols[0].symbol.name.value().entry, "bar"); EXPECT_EQ(enumAttr->symbols[0].value, 0u); AAPT_ASSERT_TRUE(enumAttr->symbols[1].symbol.name); - EXPECT_EQ(enumAttr->symbols[1].symbol.name.value().entry, u"bat"); + EXPECT_EQ(enumAttr->symbols[1].symbol.name.value().entry, "bat"); EXPECT_EQ(enumAttr->symbols[1].value, 1u); AAPT_ASSERT_TRUE(enumAttr->symbols[2].symbol.name); - EXPECT_EQ(enumAttr->symbols[2].symbol.name.value().entry, u"baz"); + EXPECT_EQ(enumAttr->symbols[2].symbol.name.value().entry, "baz"); EXPECT_EQ(enumAttr->symbols[2].value, 2u); } @@ -236,25 +235,25 @@ TEST_F(ResourceParserTest, ParseFlagAttr) { "</attr>"; ASSERT_TRUE(testParse(input)); - Attribute* flagAttr = test::getValue<Attribute>(&mTable, u"@attr/foo"); + Attribute* flagAttr = test::getValue<Attribute>(&mTable, "@attr/foo"); ASSERT_NE(nullptr, flagAttr); EXPECT_EQ(flagAttr->typeMask, android::ResTable_map::TYPE_FLAGS); ASSERT_EQ(flagAttr->symbols.size(), 3u); AAPT_ASSERT_TRUE(flagAttr->symbols[0].symbol.name); - EXPECT_EQ(flagAttr->symbols[0].symbol.name.value().entry, u"bar"); + EXPECT_EQ(flagAttr->symbols[0].symbol.name.value().entry, "bar"); EXPECT_EQ(flagAttr->symbols[0].value, 0u); AAPT_ASSERT_TRUE(flagAttr->symbols[1].symbol.name); - EXPECT_EQ(flagAttr->symbols[1].symbol.name.value().entry, u"bat"); + EXPECT_EQ(flagAttr->symbols[1].symbol.name.value().entry, "bat"); EXPECT_EQ(flagAttr->symbols[1].value, 1u); AAPT_ASSERT_TRUE(flagAttr->symbols[2].symbol.name); - EXPECT_EQ(flagAttr->symbols[2].symbol.name.value().entry, u"baz"); + EXPECT_EQ(flagAttr->symbols[2].symbol.name.value().entry, "baz"); EXPECT_EQ(flagAttr->symbols[2].value, 2u); std::unique_ptr<BinaryPrimitive> flagValue = ResourceUtils::tryParseFlagSymbol(flagAttr, - u"baz|bat"); + "baz|bat"); ASSERT_NE(nullptr, flagValue); EXPECT_EQ(flagValue->value.data, 1u | 2u); } @@ -276,32 +275,32 @@ TEST_F(ResourceParserTest, ParseStyle) { "</style>"; ASSERT_TRUE(testParse(input)); - Style* style = test::getValue<Style>(&mTable, u"@style/foo"); + Style* style = test::getValue<Style>(&mTable, "@style/foo"); ASSERT_NE(nullptr, style); AAPT_ASSERT_TRUE(style->parent); AAPT_ASSERT_TRUE(style->parent.value().name); - EXPECT_EQ(test::parseNameOrDie(u"@style/fu"), style->parent.value().name.value()); + EXPECT_EQ(test::parseNameOrDie("@style/fu"), style->parent.value().name.value()); ASSERT_EQ(3u, style->entries.size()); AAPT_ASSERT_TRUE(style->entries[0].key.name); - EXPECT_EQ(test::parseNameOrDie(u"@attr/bar"), style->entries[0].key.name.value()); + EXPECT_EQ(test::parseNameOrDie("@attr/bar"), style->entries[0].key.name.value()); AAPT_ASSERT_TRUE(style->entries[1].key.name); - EXPECT_EQ(test::parseNameOrDie(u"@attr/bat"), style->entries[1].key.name.value()); + EXPECT_EQ(test::parseNameOrDie("@attr/bat"), style->entries[1].key.name.value()); AAPT_ASSERT_TRUE(style->entries[2].key.name); - EXPECT_EQ(test::parseNameOrDie(u"@attr/baz"), style->entries[2].key.name.value()); + EXPECT_EQ(test::parseNameOrDie("@attr/baz"), style->entries[2].key.name.value()); } TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) { std::string input = "<style name=\"foo\" parent=\"com.app:Theme\"/>"; ASSERT_TRUE(testParse(input)); - Style* style = test::getValue<Style>(&mTable, u"@style/foo"); + Style* style = test::getValue<Style>(&mTable, "@style/foo"); ASSERT_NE(nullptr, style); AAPT_ASSERT_TRUE(style->parent); AAPT_ASSERT_TRUE(style->parent.value().name); - EXPECT_EQ(test::parseNameOrDie(u"@com.app:style/Theme"), style->parent.value().name.value()); + EXPECT_EQ(test::parseNameOrDie("@com.app:style/Theme"), style->parent.value().name.value()); } TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) { @@ -309,11 +308,11 @@ TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) { " name=\"foo\" parent=\"app:Theme\"/>"; ASSERT_TRUE(testParse(input)); - Style* style = test::getValue<Style>(&mTable, u"@style/foo"); + Style* style = test::getValue<Style>(&mTable, "@style/foo"); ASSERT_NE(nullptr, style); AAPT_ASSERT_TRUE(style->parent); AAPT_ASSERT_TRUE(style->parent.value().name); - EXPECT_EQ(test::parseNameOrDie(u"@android:style/Theme"), style->parent.value().name.value()); + EXPECT_EQ(test::parseNameOrDie("@android:style/Theme"), style->parent.value().name.value()); } TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) { @@ -323,21 +322,21 @@ TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) { "</style>"; ASSERT_TRUE(testParse(input)); - Style* style = test::getValue<Style>(&mTable, u"@style/foo"); + Style* style = test::getValue<Style>(&mTable, "@style/foo"); ASSERT_NE(nullptr, style); ASSERT_EQ(1u, style->entries.size()); - EXPECT_EQ(test::parseNameOrDie(u"@android:attr/bar"), style->entries[0].key.name.value()); + EXPECT_EQ(test::parseNameOrDie("@android:attr/bar"), style->entries[0].key.name.value()); } TEST_F(ResourceParserTest, ParseStyleWithInferredParent) { std::string input = "<style name=\"foo.bar\"/>"; ASSERT_TRUE(testParse(input)); - Style* style = test::getValue<Style>(&mTable, u"@style/foo.bar"); + Style* style = test::getValue<Style>(&mTable, "@style/foo.bar"); ASSERT_NE(nullptr, style); AAPT_ASSERT_TRUE(style->parent); AAPT_ASSERT_TRUE(style->parent.value().name); - EXPECT_EQ(style->parent.value().name.value(), test::parseNameOrDie(u"@style/foo")); + EXPECT_EQ(style->parent.value().name.value(), test::parseNameOrDie("@style/foo")); EXPECT_TRUE(style->parentInferred); } @@ -345,7 +344,7 @@ TEST_F(ResourceParserTest, ParseStyleWithInferredParentOverridenByEmptyParentAtt std::string input = "<style name=\"foo.bar\" parent=\"\"/>"; ASSERT_TRUE(testParse(input)); - Style* style = test::getValue<Style>(&mTable, u"@style/foo.bar"); + Style* style = test::getValue<Style>(&mTable, "@style/foo.bar"); ASSERT_NE(nullptr, style); AAPT_EXPECT_FALSE(style->parent); EXPECT_FALSE(style->parentInferred); @@ -355,7 +354,7 @@ TEST_F(ResourceParserTest, ParseStyleWithPrivateParentReference) { std::string input = R"EOF(<style name="foo" parent="*android:style/bar" />)EOF"; ASSERT_TRUE(testParse(input)); - Style* style = test::getValue<Style>(&mTable, u"@style/foo"); + Style* style = test::getValue<Style>(&mTable, "@style/foo"); ASSERT_NE(nullptr, style); AAPT_ASSERT_TRUE(style->parent); EXPECT_TRUE(style->parent.value().privateReference); @@ -365,7 +364,7 @@ TEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) { std::string input = "<string name=\"foo\">@+id/bar</string>"; ASSERT_TRUE(testParse(input)); - Id* id = test::getValue<Id>(&mTable, u"@id/bar"); + Id* id = test::getValue<Id>(&mTable, "@id/bar"); ASSERT_NE(id, nullptr); } @@ -380,31 +379,31 @@ TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) { ASSERT_TRUE(testParse(input)); Maybe<ResourceTable::SearchResult> result = - mTable.findResource(test::parseNameOrDie(u"@styleable/foo")); + mTable.findResource(test::parseNameOrDie("@styleable/foo")); AAPT_ASSERT_TRUE(result); EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state); - Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/bar"); + Attribute* attr = test::getValue<Attribute>(&mTable, "@attr/bar"); ASSERT_NE(attr, nullptr); EXPECT_TRUE(attr->isWeak()); - attr = test::getValue<Attribute>(&mTable, u"@attr/bat"); + attr = test::getValue<Attribute>(&mTable, "@attr/bat"); ASSERT_NE(attr, nullptr); EXPECT_TRUE(attr->isWeak()); - attr = test::getValue<Attribute>(&mTable, u"@attr/baz"); + attr = test::getValue<Attribute>(&mTable, "@attr/baz"); ASSERT_NE(attr, nullptr); EXPECT_TRUE(attr->isWeak()); EXPECT_EQ(1u, attr->symbols.size()); - EXPECT_NE(nullptr, test::getValue<Id>(&mTable, u"@id/foo")); + EXPECT_NE(nullptr, test::getValue<Id>(&mTable, "@id/foo")); - Styleable* styleable = test::getValue<Styleable>(&mTable, u"@styleable/foo"); + Styleable* styleable = test::getValue<Styleable>(&mTable, "@styleable/foo"); ASSERT_NE(styleable, nullptr); ASSERT_EQ(3u, styleable->entries.size()); - EXPECT_EQ(test::parseNameOrDie(u"@attr/bar"), styleable->entries[0].name.value()); - EXPECT_EQ(test::parseNameOrDie(u"@attr/bat"), styleable->entries[1].name.value()); + EXPECT_EQ(test::parseNameOrDie("@attr/bar"), styleable->entries[0].name.value()); + EXPECT_EQ(test::parseNameOrDie("@attr/bat"), styleable->entries[1].name.value()); } TEST_F(ResourceParserTest, ParsePrivateAttributesDeclareStyleable) { @@ -413,17 +412,17 @@ TEST_F(ResourceParserTest, ParsePrivateAttributesDeclareStyleable) { " <attr name=\"privAndroid:bat\" />\n" "</declare-styleable>"; ASSERT_TRUE(testParse(input)); - Styleable* styleable = test::getValue<Styleable>(&mTable, u"@styleable/foo"); + Styleable* styleable = test::getValue<Styleable>(&mTable, "@styleable/foo"); ASSERT_NE(nullptr, styleable); ASSERT_EQ(2u, styleable->entries.size()); EXPECT_TRUE(styleable->entries[0].privateReference); AAPT_ASSERT_TRUE(styleable->entries[0].name); - EXPECT_EQ(std::u16string(u"android"), styleable->entries[0].name.value().package); + EXPECT_EQ(std::string("android"), styleable->entries[0].name.value().package); EXPECT_TRUE(styleable->entries[1].privateReference); AAPT_ASSERT_TRUE(styleable->entries[1].name); - EXPECT_EQ(std::u16string(u"android"), styleable->entries[1].name.value().package); + EXPECT_EQ(std::string("android"), styleable->entries[1].name.value().package); } TEST_F(ResourceParserTest, ParseArray) { @@ -434,7 +433,7 @@ TEST_F(ResourceParserTest, ParseArray) { "</array>"; ASSERT_TRUE(testParse(input)); - Array* array = test::getValue<Array>(&mTable, u"@array/foo"); + Array* array = test::getValue<Array>(&mTable, "@array/foo"); ASSERT_NE(array, nullptr); ASSERT_EQ(3u, array->items.size()); @@ -448,7 +447,7 @@ TEST_F(ResourceParserTest, ParseStringArray) { " <item>\"Werk\"</item>\n" "</string-array>\n"; ASSERT_TRUE(testParse(input)); - EXPECT_NE(nullptr, test::getValue<Array>(&mTable, u"@array/foo")); + EXPECT_NE(nullptr, test::getValue<Array>(&mTable, "@array/foo")); } TEST_F(ResourceParserTest, ParsePlural) { @@ -464,9 +463,9 @@ TEST_F(ResourceParserTest, ParseCommentsWithResource) { "<string name=\"foo\">Hi</string>"; ASSERT_TRUE(testParse(input)); - String* value = test::getValue<String>(&mTable, u"@string/foo"); + String* value = test::getValue<String>(&mTable, "@string/foo"); ASSERT_NE(nullptr, value); - EXPECT_EQ(value->getComment(), u"This is a comment"); + EXPECT_EQ(value->getComment(), "This is a comment"); } TEST_F(ResourceParserTest, DoNotCombineMultipleComments) { @@ -476,9 +475,9 @@ TEST_F(ResourceParserTest, DoNotCombineMultipleComments) { ASSERT_TRUE(testParse(input)); - String* value = test::getValue<String>(&mTable, u"@string/foo"); + String* value = test::getValue<String>(&mTable, "@string/foo"); ASSERT_NE(nullptr, value); - EXPECT_EQ(value->getComment(), u"Two"); + EXPECT_EQ(value->getComment(), "Two"); } TEST_F(ResourceParserTest, IgnoreCommentBeforeEndTag) { @@ -490,9 +489,9 @@ TEST_F(ResourceParserTest, IgnoreCommentBeforeEndTag) { ASSERT_TRUE(testParse(input)); - String* value = test::getValue<String>(&mTable, u"@string/foo"); + String* value = test::getValue<String>(&mTable, "@string/foo"); ASSERT_NE(nullptr, value); - EXPECT_EQ(value->getComment(), u"One"); + EXPECT_EQ(value->getComment(), "One"); } TEST_F(ResourceParserTest, ParseNestedComments) { @@ -510,17 +509,17 @@ TEST_F(ResourceParserTest, ParseNestedComments) { </attr>)EOF"; ASSERT_TRUE(testParse(input)); - Styleable* styleable = test::getValue<Styleable>(&mTable, u"@styleable/foo"); + Styleable* styleable = test::getValue<Styleable>(&mTable, "@styleable/foo"); ASSERT_NE(nullptr, styleable); ASSERT_EQ(1u, styleable->entries.size()); - EXPECT_EQ(StringPiece16(u"The name of the bar"), styleable->entries.front().getComment()); + EXPECT_EQ(StringPiece("The name of the bar"), styleable->entries.front().getComment()); - Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/foo"); + Attribute* attr = test::getValue<Attribute>(&mTable, "@attr/foo"); ASSERT_NE(nullptr, attr); ASSERT_EQ(1u, attr->symbols.size()); - EXPECT_EQ(StringPiece16(u"The very first"), attr->symbols.front().symbol.getComment()); + EXPECT_EQ(StringPiece("The very first"), attr->symbols.front().symbol.getComment()); } /* @@ -531,7 +530,7 @@ TEST_F(ResourceParserTest, ParsePublicIdAsDefinition) { std::string input = "<public type=\"id\" name=\"foo\"/>"; ASSERT_TRUE(testParse(input)); - Id* id = test::getValue<Id>(&mTable, u"@id/foo"); + Id* id = test::getValue<Id>(&mTable, "@id/foo"); ASSERT_NE(nullptr, id); } @@ -546,22 +545,22 @@ TEST_F(ResourceParserTest, KeepAllProducts) { )EOF"; ASSERT_TRUE(testParse(input)); - EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/foo", + EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "@string/foo", ConfigDescription::defaultConfig(), "phone")); - EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/foo", + EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "@string/foo", ConfigDescription::defaultConfig(), "no-sdcard")); - EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/bar", + EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "@string/bar", ConfigDescription::defaultConfig(), "")); - EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/baz", + EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "@string/baz", ConfigDescription::defaultConfig(), "")); - EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/bit", + EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "@string/bit", ConfigDescription::defaultConfig(), "phablet")); - EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, u"@string/bot", + EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "@string/bot", ConfigDescription::defaultConfig(), "default")); } @@ -575,7 +574,7 @@ TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) { ASSERT_TRUE(testParse(input)); Maybe<ResourceTable::SearchResult> result = mTable.findResource( - test::parseNameOrDie(u"@attr/foo")); + test::parseNameOrDie("@attr/foo")); AAPT_ASSERT_TRUE(result); AAPT_ASSERT_TRUE(result.value().package->id); @@ -586,7 +585,7 @@ TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) { result.value().entry->id.value()); EXPECT_EQ(ResourceId(0x01010040), actualId); - result = mTable.findResource(test::parseNameOrDie(u"@attr/bar")); + result = mTable.findResource(test::parseNameOrDie("@attr/bar")); AAPT_ASSERT_TRUE(result); AAPT_ASSERT_TRUE(result.value().package->id); @@ -611,7 +610,7 @@ TEST_F(ResourceParserTest, AddResourcesElementShouldAddEntryWithUndefinedSymbol) ASSERT_TRUE(testParse(input)); Maybe<ResourceTable::SearchResult> result = mTable.findResource( - test::parseNameOrDie(u"@string/bar")); + test::parseNameOrDie("@string/bar")); AAPT_ASSERT_TRUE(result); const ResourceEntry* entry = result.value().entry; ASSERT_NE(nullptr, entry); @@ -622,7 +621,7 @@ TEST_F(ResourceParserTest, ParseItemElementWithFormat) { std::string input = R"EOF(<item name="foo" type="integer" format="float">0.3</item>)EOF"; ASSERT_TRUE(testParse(input)); - BinaryPrimitive* val = test::getValue<BinaryPrimitive>(&mTable, u"@integer/foo"); + BinaryPrimitive* val = test::getValue<BinaryPrimitive>(&mTable, "@integer/foo"); ASSERT_NE(nullptr, val); EXPECT_EQ(uint32_t(android::Res_value::TYPE_FLOAT), val->value.dataType); diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp index e700ed98365a..4d418d9c2e93 100644 --- a/tools/aapt2/ResourceTable.cpp +++ b/tools/aapt2/ResourceTable.cpp @@ -35,11 +35,11 @@ static bool lessThanType(const std::unique_ptr<ResourceTableType>& lhs, Resource template <typename T> static bool lessThanStructWithName(const std::unique_ptr<T>& lhs, - const StringPiece16& rhs) { + const StringPiece& rhs) { return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0; } -ResourceTablePackage* ResourceTable::findPackage(const StringPiece16& name) { +ResourceTablePackage* ResourceTable::findPackage(const StringPiece& name) { const auto last = packages.end(); auto iter = std::lower_bound(packages.begin(), last, name, lessThanStructWithName<ResourceTablePackage>); @@ -58,7 +58,7 @@ ResourceTablePackage* ResourceTable::findPackageById(uint8_t id) { return nullptr; } -ResourceTablePackage* ResourceTable::createPackage(const StringPiece16& name, Maybe<uint8_t> id) { +ResourceTablePackage* ResourceTable::createPackage(const StringPiece& name, Maybe<uint8_t> id) { ResourceTablePackage* package = findOrCreatePackage(name); if (id && !package->id) { package->id = id; @@ -71,7 +71,7 @@ ResourceTablePackage* ResourceTable::createPackage(const StringPiece16& name, Ma return package; } -ResourceTablePackage* ResourceTable::findOrCreatePackage(const StringPiece16& name) { +ResourceTablePackage* ResourceTable::findOrCreatePackage(const StringPiece& name) { const auto last = packages.end(); auto iter = std::lower_bound(packages.begin(), last, name, lessThanStructWithName<ResourceTablePackage>); @@ -102,7 +102,7 @@ ResourceTableType* ResourceTablePackage::findOrCreateType(ResourceType type) { return types.emplace(iter, new ResourceTableType(type))->get(); } -ResourceEntry* ResourceTableType::findEntry(const StringPiece16& name) { +ResourceEntry* ResourceTableType::findEntry(const StringPiece& name) { const auto last = entries.end(); auto iter = std::lower_bound(entries.begin(), last, name, lessThanStructWithName<ResourceEntry>); @@ -112,7 +112,7 @@ ResourceEntry* ResourceTableType::findEntry(const StringPiece16& name) { return nullptr; } -ResourceEntry* ResourceTableType::findOrCreateEntry(const StringPiece16& name) { +ResourceEntry* ResourceTableType::findOrCreateEntry(const StringPiece& name) { auto last = entries.end(); auto iter = std::lower_bound(entries.begin(), last, name, lessThanStructWithName<ResourceEntry>); @@ -261,8 +261,8 @@ int ResourceTable::resolveValueCollision(Value* existing, Value* incoming) { return 0; } -static constexpr const char16_t* kValidNameChars = u"._-"; -static constexpr const char16_t* kValidNameMangledChars = u"._-$"; +static constexpr const char* kValidNameChars = "._-"; +static constexpr const char* kValidNameMangledChars = "._-$"; bool ResourceTable::addResource(const ResourceNameRef& name, const ConfigDescription& config, @@ -286,7 +286,7 @@ bool ResourceTable::addResource(const ResourceNameRef& name, bool ResourceTable::addFileReference(const ResourceNameRef& name, const ConfigDescription& config, const Source& source, - const StringPiece16& path, + const StringPiece& path, IDiagnostics* diag) { return addFileReferenceImpl(name, config, source, path, nullptr, kValidNameChars, diag); } @@ -294,7 +294,7 @@ bool ResourceTable::addFileReference(const ResourceNameRef& name, bool ResourceTable::addFileReferenceAllowMangled(const ResourceNameRef& name, const ConfigDescription& config, const Source& source, - const StringPiece16& path, + const StringPiece& path, io::IFile* file, IDiagnostics* diag) { return addFileReferenceImpl(name, config, source, path, file, kValidNameMangledChars, diag); @@ -303,9 +303,9 @@ bool ResourceTable::addFileReferenceAllowMangled(const ResourceNameRef& name, bool ResourceTable::addFileReferenceImpl(const ResourceNameRef& name, const ConfigDescription& config, const Source& source, - const StringPiece16& path, + const StringPiece& path, io::IFile* file, - const char16_t* validChars, + const char* validChars, IDiagnostics* diag) { std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>( stringPool.makeRef(path)); @@ -339,7 +339,7 @@ bool ResourceTable::addResourceImpl(const ResourceNameRef& name, const ConfigDescription& config, const StringPiece& product, std::unique_ptr<Value> value, - const char16_t* validChars, + const char* validChars, std::function<int(Value*,Value*)> conflictResolver, IDiagnostics* diag) { assert(value && "value can't be nullptr"); @@ -353,7 +353,7 @@ bool ResourceTable::addResourceImpl(const ResourceNameRef& name, << "' has invalid entry name '" << name.entry << "'. Invalid character '" - << StringPiece16(badCharIter, 1) + << StringPiece(badCharIter, 1) << "'"); return false; } @@ -438,7 +438,7 @@ bool ResourceTable::setSymbolStateAllowMangled(const ResourceNameRef& name, } bool ResourceTable::setSymbolStateImpl(const ResourceNameRef& name, const ResourceId resId, - const Symbol& symbol, const char16_t* validChars, + const Symbol& symbol, const char* validChars, IDiagnostics* diag) { assert(diag && "diagnostics can't be nullptr"); @@ -450,7 +450,7 @@ bool ResourceTable::setSymbolStateImpl(const ResourceNameRef& name, const Resour << "' has invalid entry name '" << name.entry << "'. Invalid character '" - << StringPiece16(badCharIter, 1) + << StringPiece(badCharIter, 1) << "'"); return false; } diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h index 5690ea6fa614..a5efe355a76c 100644 --- a/tools/aapt2/ResourceTable.h +++ b/tools/aapt2/ResourceTable.h @@ -48,7 +48,7 @@ enum class SymbolState { struct Symbol { SymbolState state = SymbolState::kUndefined; Source source; - std::u16string comment; + std::string comment; }; class ResourceConfigValue { @@ -86,7 +86,7 @@ public: * this determines the order of this resource * when doing lookups. */ - const std::u16string name; + const std::string name; /** * The entry ID for this resource. @@ -103,7 +103,7 @@ public: */ std::vector<std::unique_ptr<ResourceConfigValue>> values; - ResourceEntry(const StringPiece16& name) : name(name.toString()) { } + ResourceEntry(const StringPiece& name) : name(name.toString()) { } ResourceConfigValue* findValue(const ConfigDescription& config); ResourceConfigValue* findValue(const ConfigDescription& config, const StringPiece& product); @@ -147,8 +147,8 @@ public: explicit ResourceTableType(const ResourceType type) : type(type) { } - ResourceEntry* findEntry(const StringPiece16& name); - ResourceEntry* findOrCreateEntry(const StringPiece16& name); + ResourceEntry* findEntry(const StringPiece& name); + ResourceEntry* findOrCreateEntry(const StringPiece& name); private: DISALLOW_COPY_AND_ASSIGN(ResourceTableType); @@ -165,7 +165,7 @@ class ResourceTablePackage { public: PackageType type = PackageType::App; Maybe<uint8_t> id; - std::u16string name; + std::string name; std::vector<std::unique_ptr<ResourceTableType>> types; @@ -209,13 +209,13 @@ public: bool addFileReference(const ResourceNameRef& name, const ConfigDescription& config, const Source& source, - const StringPiece16& path, + const StringPiece& path, IDiagnostics* diag); bool addFileReferenceAllowMangled(const ResourceNameRef& name, const ConfigDescription& config, const Source& source, - const StringPiece16& path, + const StringPiece& path, io::IFile* file, IDiagnostics* diag); @@ -276,21 +276,21 @@ public: * exist. The empty string is a valid package and typically is used to represent the * 'current' package before it is known to the ResourceTable. */ - ResourceTablePackage* findPackage(const StringPiece16& name); + ResourceTablePackage* findPackage(const StringPiece& name); ResourceTablePackage* findPackageById(uint8_t id); - ResourceTablePackage* createPackage(const StringPiece16& name, Maybe<uint8_t> id = {}); + ResourceTablePackage* createPackage(const StringPiece& name, Maybe<uint8_t> id = {}); private: - ResourceTablePackage* findOrCreatePackage(const StringPiece16& name); + ResourceTablePackage* findOrCreatePackage(const StringPiece& name); bool addFileReferenceImpl(const ResourceNameRef& name, const ConfigDescription& config, const Source& source, - const StringPiece16& path, + const StringPiece& path, io::IFile* file, - const char16_t* validChars, + const char* validChars, IDiagnostics* diag); bool addResourceImpl(const ResourceNameRef& name, @@ -298,14 +298,14 @@ private: const ConfigDescription& config, const StringPiece& product, std::unique_ptr<Value> value, - const char16_t* validChars, + const char* validChars, std::function<int(Value*,Value*)> conflictResolver, IDiagnostics* diag); bool setSymbolStateImpl(const ResourceNameRef& name, ResourceId resId, const Symbol& symbol, - const char16_t* validChars, + const char* validChars, IDiagnostics* diag); DISALLOW_COPY_AND_ASSIGN(ResourceTable); diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp index d6c52ab83d93..cf6660cbff28 100644 --- a/tools/aapt2/ResourceTable_test.cpp +++ b/tools/aapt2/ResourceTable_test.cpp @@ -17,12 +17,10 @@ #include "Diagnostics.h" #include "ResourceTable.h" #include "ResourceValues.h" +#include "test/Test.h" #include "util/Util.h" -#include "test/Builders.h" - #include <algorithm> -#include <gtest/gtest.h> #include <ostream> #include <string> @@ -32,13 +30,13 @@ TEST(ResourceTableTest, FailToAddResourceWithBadName) { ResourceTable table; EXPECT_FALSE(table.addResource( - ResourceNameRef(u"android", ResourceType::kId, u"hey,there"), + test::parseNameOrDie("@android:id/hey,there"), ConfigDescription{}, "", test::ValueBuilder<Id>().setSource("test.xml", 21u).build(), test::getDiagnostics())); EXPECT_FALSE(table.addResource( - ResourceNameRef(u"android", ResourceType::kId, u"hey:there"), + test::parseNameOrDie("@android:id/hey:there"), ConfigDescription{}, "", test::ValueBuilder<Id>().setSource("test.xml", 21u).build(), test::getDiagnostics())); @@ -47,14 +45,13 @@ TEST(ResourceTableTest, FailToAddResourceWithBadName) { TEST(ResourceTableTest, AddOneResource) { ResourceTable table; - EXPECT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/id"), - ConfigDescription{}, - "", - test::ValueBuilder<Id>() - .setSource("test/path/file.xml", 23u).build(), - test::getDiagnostics())); + EXPECT_TRUE(table.addResource( + test::parseNameOrDie("@android:attr/id"), + ConfigDescription{}, "", + test::ValueBuilder<Id>().setSource("test/path/file.xml", 23u).build(), + test::getDiagnostics())); - ASSERT_NE(nullptr, test::getValue<Id>(&table, u"@android:attr/id")); + ASSERT_NE(nullptr, test::getValue<Id>(&table, "@android:attr/id")); } TEST(ResourceTableTest, AddMultipleResources) { @@ -65,56 +62,58 @@ TEST(ResourceTableTest, AddMultipleResources) { memcpy(languageConfig.language, "pl", sizeof(languageConfig.language)); EXPECT_TRUE(table.addResource( - test::parseNameOrDie(u"@android:attr/layout_width"), - config, - "", + test::parseNameOrDie("@android:attr/layout_width"), + config, "", test::ValueBuilder<Id>().setSource("test/path/file.xml", 10u).build(), test::getDiagnostics())); EXPECT_TRUE(table.addResource( - test::parseNameOrDie(u"@android:attr/id"), - config, - "", + test::parseNameOrDie("@android:attr/id"), + config, "", test::ValueBuilder<Id>().setSource("test/path/file.xml", 12u).build(), test::getDiagnostics())); EXPECT_TRUE(table.addResource( - test::parseNameOrDie(u"@android:string/ok"), - config, - "", + test::parseNameOrDie("@android:string/ok"), + config, "", test::ValueBuilder<Id>().setSource("test/path/file.xml", 14u).build(), test::getDiagnostics())); EXPECT_TRUE(table.addResource( - test::parseNameOrDie(u"@android:string/ok"), - languageConfig, - "", + test::parseNameOrDie("@android:string/ok"), + languageConfig, "", test::ValueBuilder<BinaryPrimitive>(android::Res_value{}) .setSource("test/path/file.xml", 20u) .build(), test::getDiagnostics())); - ASSERT_NE(nullptr, test::getValue<Id>(&table, u"@android:attr/layout_width")); - ASSERT_NE(nullptr, test::getValue<Id>(&table, u"@android:attr/id")); - ASSERT_NE(nullptr, test::getValue<Id>(&table, u"@android:string/ok")); - ASSERT_NE(nullptr, test::getValueForConfig<BinaryPrimitive>(&table, u"@android:string/ok", + ASSERT_NE(nullptr, test::getValue<Id>(&table, "@android:attr/layout_width")); + ASSERT_NE(nullptr, test::getValue<Id>(&table, "@android:attr/id")); + ASSERT_NE(nullptr, test::getValue<Id>(&table, "@android:string/ok")); + ASSERT_NE(nullptr, test::getValueForConfig<BinaryPrimitive>(&table, "@android:string/ok", languageConfig)); } TEST(ResourceTableTest, OverrideWeakResourceValue) { ResourceTable table; - ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/foo"), ConfigDescription{}, - "", util::make_unique<Attribute>(true), test::getDiagnostics())); + ASSERT_TRUE(table.addResource( + test::parseNameOrDie("@android:attr/foo"), + ConfigDescription{}, "", + util::make_unique<Attribute>(true), + test::getDiagnostics())); - Attribute* attr = test::getValue<Attribute>(&table, u"@android:attr/foo"); + Attribute* attr = test::getValue<Attribute>(&table, "@android:attr/foo"); ASSERT_NE(nullptr, attr); EXPECT_TRUE(attr->isWeak()); - ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/foo"), ConfigDescription{}, - "", util::make_unique<Attribute>(false), test::getDiagnostics())); + ASSERT_TRUE(table.addResource( + test::parseNameOrDie("@android:attr/foo"), + ConfigDescription{}, "", + util::make_unique<Attribute>(false), + test::getDiagnostics())); - attr = test::getValue<Attribute>(&table, u"@android:attr/foo"); + attr = test::getValue<Attribute>(&table, "@android:attr/foo"); ASSERT_NE(nullptr, attr); EXPECT_FALSE(attr->isWeak()); } @@ -122,26 +121,24 @@ TEST(ResourceTableTest, OverrideWeakResourceValue) { TEST(ResourceTableTest, ProductVaryingValues) { ResourceTable table; - EXPECT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/foo"), - test::parseConfigOrDie("land"), - "tablet", + EXPECT_TRUE(table.addResource(test::parseNameOrDie("@android:string/foo"), + test::parseConfigOrDie("land"), "tablet", util::make_unique<Id>(), test::getDiagnostics())); - EXPECT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/foo"), - test::parseConfigOrDie("land"), - "phone", + EXPECT_TRUE(table.addResource(test::parseNameOrDie("@android:string/foo"), + test::parseConfigOrDie("land"), "phone", util::make_unique<Id>(), test::getDiagnostics())); - EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/foo", + EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, "@android:string/foo", test::parseConfigOrDie("land"), "tablet")); - EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/foo", + EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, "@android:string/foo", test::parseConfigOrDie("land"), "phone")); Maybe<ResourceTable::SearchResult> sr = table.findResource( - test::parseNameOrDie(u"@android:string/foo")); + test::parseNameOrDie("@android:string/foo")); AAPT_ASSERT_TRUE(sr); std::vector<ResourceConfigValue*> values = sr.value().entry->findAllValues( test::parseConfigOrDie("land")); diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index a0a7efc46476..31d6435a6184 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -16,6 +16,7 @@ #include "NameMangler.h" #include "ResourceUtils.h" +#include "SdkConstants.h" #include "flatten/ResourceTypeExtensions.h" #include "util/Files.h" #include "util/Util.h" @@ -26,19 +27,52 @@ namespace aapt { namespace ResourceUtils { -bool extractResourceName(const StringPiece16& str, StringPiece16* outPackage, - StringPiece16* outType, StringPiece16* outEntry) { +Maybe<ResourceName> toResourceName(const android::ResTable::resource_name& nameIn) { + ResourceName nameOut; + if (!nameIn.package) { + return {}; + } + + nameOut.package = util::utf16ToUtf8(StringPiece16(nameIn.package, nameIn.packageLen)); + + const ResourceType* type; + if (nameIn.type) { + type = parseResourceType(util::utf16ToUtf8(StringPiece16(nameIn.type, nameIn.typeLen))); + } else if (nameIn.type8) { + type = parseResourceType(StringPiece(nameIn.type8, nameIn.typeLen)); + } else { + return {}; + } + + if (!type) { + return {}; + } + + nameOut.type = *type; + + if (nameIn.name) { + nameOut.entry = util::utf16ToUtf8(StringPiece16(nameIn.name, nameIn.nameLen)); + } else if (nameIn.name8) { + nameOut.entry = StringPiece(nameIn.name8, nameIn.nameLen).toString(); + } else { + return {}; + } + return nameOut; +} + +bool extractResourceName(const StringPiece& str, StringPiece* outPackage, + StringPiece* outType, StringPiece* 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; + const char* start = str.data(); + const char* end = start + str.size(); + const char* current = start; while (current != end) { - if (outType->size() == 0 && *current == u'/') { + if (outType->size() == 0 && *current == '/') { hasTypeSeparator = true; outType->assign(start, current - start); start = current + 1; - } else if (outPackage->size() == 0 && *current == u':') { + } else if (outPackage->size() == 0 && *current == ':') { hasPackageSeparator = true; outPackage->assign(start, current - start); start = current + 1; @@ -50,21 +84,21 @@ bool extractResourceName(const StringPiece16& str, StringPiece16* outPackage, return !(hasPackageSeparator && outPackage->empty()) && !(hasTypeSeparator && outType->empty()); } -bool parseResourceName(const StringPiece16& str, ResourceNameRef* outRef, bool* outPrivate) { +bool parseResourceName(const StringPiece& str, ResourceNameRef* outRef, bool* outPrivate) { if (str.empty()) { return false; } size_t offset = 0; bool priv = false; - if (str.data()[0] == u'*') { + if (str.data()[0] == '*') { priv = true; offset = 1; } - StringPiece16 package; - StringPiece16 type; - StringPiece16 entry; + StringPiece package; + StringPiece type; + StringPiece entry; if (!extractResourceName(str.substr(offset, str.size() - offset), &package, &type, &entry)) { return false; } @@ -90,18 +124,18 @@ bool parseResourceName(const StringPiece16& str, ResourceNameRef* outRef, bool* return true; } -bool tryParseReference(const StringPiece16& str, ResourceNameRef* outRef, bool* outCreate, +bool tryParseReference(const StringPiece& str, ResourceNameRef* outRef, bool* outCreate, bool* outPrivate) { - StringPiece16 trimmedStr(util::trimWhitespace(str)); + StringPiece trimmedStr(util::trimWhitespace(str)); if (trimmedStr.empty()) { return false; } bool create = false; bool priv = false; - if (trimmedStr.data()[0] == u'@') { + if (trimmedStr.data()[0] == '@') { size_t offset = 1; - if (trimmedStr.data()[1] == u'+') { + if (trimmedStr.data()[1] == '+') { create = true; offset += 1; } @@ -136,26 +170,26 @@ bool tryParseReference(const StringPiece16& str, ResourceNameRef* outRef, bool* return false; } -bool isReference(const StringPiece16& str) { +bool isReference(const StringPiece& str) { return tryParseReference(str, nullptr, nullptr, nullptr); } -bool tryParseAttributeReference(const StringPiece16& str, ResourceNameRef* outRef) { - StringPiece16 trimmedStr(util::trimWhitespace(str)); +bool tryParseAttributeReference(const StringPiece& str, ResourceNameRef* outRef) { + StringPiece trimmedStr(util::trimWhitespace(str)); if (trimmedStr.empty()) { return false; } - if (*trimmedStr.data() == u'?') { - StringPiece16 package; - StringPiece16 type; - StringPiece16 entry; + if (*trimmedStr.data() == '?') { + StringPiece package; + StringPiece type; + StringPiece entry; if (!extractResourceName(trimmedStr.substr(1, trimmedStr.size() - 1), &package, &type, &entry)) { return false; } - if (!type.empty() && type != u"attr") { + if (!type.empty() && type != "attr") { return false; } @@ -173,7 +207,7 @@ bool tryParseAttributeReference(const StringPiece16& str, ResourceNameRef* outRe return false; } -bool isAttributeReference(const StringPiece16& str) { +bool isAttributeReference(const StringPiece& str) { return tryParseAttributeReference(str, nullptr); } @@ -185,23 +219,23 @@ bool isAttributeReference(const StringPiece16& str) { * <[*]package>:[style/]<entry> * [[*]package:style/]<entry> */ -Maybe<Reference> parseStyleParentReference(const StringPiece16& str, std::string* outError) { +Maybe<Reference> parseStyleParentReference(const StringPiece& str, std::string* outError) { if (str.empty()) { return {}; } - StringPiece16 name = str; + StringPiece name = str; bool hasLeadingIdentifiers = false; bool privateRef = false; // Skip over these identifiers. A style's parent is a normal reference. - if (name.data()[0] == u'@' || name.data()[0] == u'?') { + if (name.data()[0] == '@' || name.data()[0] == '?') { hasLeadingIdentifiers = true; name = name.substr(1, name.size() - 1); } - if (name.data()[0] == u'*') { + if (name.data()[0] == '*') { privateRef = true; name = name.substr(1, name.size() - 1); } @@ -209,7 +243,7 @@ Maybe<Reference> parseStyleParentReference(const StringPiece16& str, std::string ResourceNameRef ref; ref.type = ResourceType::kStyle; - StringPiece16 typeStr; + StringPiece typeStr; extractResourceName(name, &ref.package, &typeStr, &ref.entry); if (!typeStr.empty()) { // If we have a type, make sure it is a Style. @@ -234,7 +268,7 @@ Maybe<Reference> parseStyleParentReference(const StringPiece16& str, std::string return result; } -std::unique_ptr<Reference> tryParseReference(const StringPiece16& str, bool* outCreate) { +std::unique_ptr<Reference> tryParseReference(const StringPiece& str, bool* outCreate) { ResourceNameRef ref; bool privateRef = false; if (tryParseReference(str, &ref, outCreate, &privateRef)) { @@ -252,14 +286,14 @@ std::unique_ptr<Reference> tryParseReference(const StringPiece16& str, bool* out return {}; } -std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece16& str) { - StringPiece16 trimmedStr(util::trimWhitespace(str)); +std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece& str) { + StringPiece trimmedStr(util::trimWhitespace(str)); android::Res_value value = { }; - if (trimmedStr == u"@null") { + if (trimmedStr == "@null") { // TYPE_NULL with data set to 0 is interpreted by the runtime as an error. // Instead we set the data type to TYPE_REFERENCE with a value of 0. value.dataType = android::Res_value::TYPE_REFERENCE; - } else if (trimmedStr == u"@empty") { + } else if (trimmedStr == "@empty") { // TYPE_NULL with value of DATA_NULL_EMPTY is handled fine by the runtime. value.dataType = android::Res_value::TYPE_NULL; value.data = android::Res_value::DATA_NULL_EMPTY; @@ -270,8 +304,8 @@ std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece16& str) { } std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute* enumAttr, - const StringPiece16& str) { - StringPiece16 trimmedStr(util::trimWhitespace(str)); + const StringPiece& str) { + StringPiece trimmedStr(util::trimWhitespace(str)); for (const Attribute::Symbol& symbol : enumAttr->symbols) { // Enum symbols are stored as @package:id/symbol resources, // so we need to match against the 'entry' part of the identifier. @@ -287,7 +321,7 @@ std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute* enumAttr, } std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* flagAttr, - const StringPiece16& str) { + const StringPiece& str) { android::Res_value flags = { }; flags.dataType = android::Res_value::TYPE_INT_HEX; flags.data = 0u; @@ -297,8 +331,8 @@ std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* flagAttr, return util::make_unique<BinaryPrimitive>(flags); } - for (StringPiece16 part : util::tokenize(str, u'|')) { - StringPiece16 trimmedPart = util::trimWhitespace(part); + for (StringPiece part : util::tokenize(str, '|')) { + StringPiece trimmedPart = util::trimWhitespace(part); bool flagSet = false; for (const Attribute::Symbol& symbol : flagAttr->symbols) { @@ -319,24 +353,24 @@ std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* flagAttr, return util::make_unique<BinaryPrimitive>(flags); } -static uint32_t parseHex(char16_t c, bool* outError) { - if (c >= u'0' && c <= u'9') { - return c - u'0'; - } else if (c >= u'a' && c <= u'f') { - return c - u'a' + 0xa; - } else if (c >= u'A' && c <= u'F') { - return c - u'A' + 0xa; +static uint32_t parseHex(char c, bool* outError) { + if (c >= '0' && c <= '9') { + return c - '0'; + } else if (c >= 'a' && c <= 'f') { + return c - 'a' + 0xa; + } else if (c >= 'A' && c <= 'F') { + return c - 'A' + 0xa; } else { *outError = true; return 0xffffffffu; } } -std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece16& str) { - StringPiece16 colorStr(util::trimWhitespace(str)); - const char16_t* start = colorStr.data(); +std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece& str) { + StringPiece colorStr(util::trimWhitespace(str)); + const char* start = colorStr.data(); const size_t len = colorStr.size(); - if (len == 0 || start[0] != u'#') { + if (len == 0 || start[0] != '#') { return {}; } @@ -386,14 +420,14 @@ std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece16& str) { return error ? std::unique_ptr<BinaryPrimitive>() : util::make_unique<BinaryPrimitive>(value); } -bool tryParseBool(const StringPiece16& str, bool* outValue) { - StringPiece16 trimmedStr(util::trimWhitespace(str)); - if (trimmedStr == u"true" || trimmedStr == u"TRUE" || trimmedStr == u"True") { +bool tryParseBool(const StringPiece& str, bool* outValue) { + StringPiece trimmedStr(util::trimWhitespace(str)); + if (trimmedStr == "true" || trimmedStr == "TRUE" || trimmedStr == "True") { if (outValue) { *outValue = true; } return true; - } else if (trimmedStr == u"false" || trimmedStr == u"FALSE" || trimmedStr == u"False") { + } else if (trimmedStr == "false" || trimmedStr == "FALSE" || trimmedStr == "False") { if (outValue) { *outValue = false; } @@ -402,7 +436,24 @@ bool tryParseBool(const StringPiece16& str, bool* outValue) { return false; } -std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece16& str) { +Maybe<int> tryParseSdkVersion(const StringPiece& str) { + StringPiece trimmedStr(util::trimWhitespace(str)); + + std::u16string str16 = util::utf8ToUtf16(trimmedStr); + android::Res_value value; + if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) { + return static_cast<int>(value.data); + } + + // Try parsing the code name. + std::pair<StringPiece, int> entry = getDevelopmentSdkCodeNameAndVersion(); + if (entry.first == trimmedStr) { + return entry.second; + } + return {}; +} + +std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece& str) { bool result = false; if (tryParseBool(str, &result)) { android::Res_value value = {}; @@ -418,17 +469,19 @@ std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece16& str) { return {}; } -std::unique_ptr<BinaryPrimitive> tryParseInt(const StringPiece16& str) { +std::unique_ptr<BinaryPrimitive> tryParseInt(const StringPiece& str) { + std::u16string str16 = util::utf8ToUtf16(str); android::Res_value value; - if (!android::ResTable::stringToInt(str.data(), str.size(), &value)) { + if (!android::ResTable::stringToInt(str16.data(), str16.size(), &value)) { return {}; } return util::make_unique<BinaryPrimitive>(value); } -std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece16& str) { +std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece& str) { + std::u16string str16 = util::utf8ToUtf16(str); android::Res_value value; - if (!android::ResTable::stringToFloat(str.data(), str.size(), &value)) { + if (!android::ResTable::stringToFloat(str16.data(), str16.size(), &value)) { return {}; } return util::make_unique<BinaryPrimitive>(value); @@ -474,7 +527,8 @@ uint32_t androidTypeToAttributeTypeMask(uint16_t type) { } std::unique_ptr<Item> parseItemForAttribute( - const StringPiece16& value, uint32_t typeMask, + const StringPiece& value, + uint32_t typeMask, std::function<void(const ResourceName&)> onCreateReference) { std::unique_ptr<BinaryPrimitive> nullOrEmpty = tryParseNullOrEmpty(value); if (nullOrEmpty) { @@ -533,7 +587,7 @@ std::unique_ptr<Item> parseItemForAttribute( * allows. */ std::unique_ptr<Item> parseItemForAttribute( - const StringPiece16& str, const Attribute* attr, + const StringPiece& str, const Attribute* attr, std::function<void(const ResourceName&)> onCreateReference) { const uint32_t typeMask = attr->typeMask; std::unique_ptr<Item> value = parseItemForAttribute(str, typeMask, onCreateReference); diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h index a0fbcc6e700b..871ed7ca1e3b 100644 --- a/tools/aapt2/ResourceUtils.h +++ b/tools/aapt2/ResourceUtils.h @@ -28,6 +28,11 @@ namespace aapt { namespace ResourceUtils { +/** + * Convert an android::ResTable::resource_name to an aapt::ResourceName struct. + */ +Maybe<ResourceName> toResourceName(const android::ResTable::resource_name& name); + /* * Extracts the package, type, and name from a string of the format: * @@ -37,15 +42,15 @@ namespace ResourceUtils { * individual extracted piece to verify that the pieces are valid. * Returns false if there was no package but a ':' was present. */ -bool extractResourceName(const StringPiece16& str, StringPiece16* outPackage, - StringPiece16* outType, StringPiece16* outEntry); +bool extractResourceName(const StringPiece& str, StringPiece* outPackage, + StringPiece* outType, StringPiece* outEntry); /** * Returns true if the string was parsed as a resource name ([*][package:]type/name), with * `outResource` set to the parsed resource name and `outPrivate` set to true if a '*' prefix * was present. */ -bool parseResourceName(const StringPiece16& str, ResourceNameRef* outResource, +bool parseResourceName(const StringPiece& str, ResourceNameRef* outResource, bool* outPrivate = nullptr); /* @@ -55,29 +60,34 @@ bool parseResourceName(const StringPiece16& str, ResourceNameRef* outResource, * If '+' was present in the reference, `outCreate` is set to true. * If '*' was present in the reference, `outPrivate` is set to true. */ -bool tryParseReference(const StringPiece16& str, ResourceNameRef* outReference, +bool tryParseReference(const StringPiece& str, ResourceNameRef* outReference, bool* outCreate = nullptr, bool* outPrivate = nullptr); /* * Returns true if the string is in the form of a resource reference (@[+][package:]type/name). */ -bool isReference(const StringPiece16& str); +bool isReference(const StringPiece& str); /* * 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); +bool tryParseAttributeReference(const StringPiece& str, ResourceNameRef* outReference); /** * Returns true if the string is in the form of an attribute reference(?[package:][type/]name). */ -bool isAttributeReference(const StringPiece16& str); +bool isAttributeReference(const StringPiece& str); /** * Returns true if the value is a boolean, putting the result in `outValue`. */ -bool tryParseBool(const StringPiece16& str, bool* outValue); +bool tryParseBool(const StringPiece& str, bool* outValue); + +/** + * Parses an SDK version, which can be an integer, or a letter from A-Z. + */ +Maybe<int> tryParseSdkVersion(const StringPiece& str); /* * Returns a Reference, or None Maybe instance if the string `str` was parsed as a @@ -88,58 +98,58 @@ bool tryParseBool(const StringPiece16& str, bool* outValue); * ?[package:]style/<entry> or * <package>:[style/]<entry> */ -Maybe<Reference> parseStyleParentReference(const StringPiece16& str, std::string* outError); +Maybe<Reference> parseStyleParentReference(const StringPiece& str, std::string* outError); /* * Returns a Reference object if the string was parsed as a resource or attribute reference, * ( @[+][package:]type/name | ?[package:]type/name ) setting outCreate to true if * the '+' was present in the string. */ -std::unique_ptr<Reference> tryParseReference(const StringPiece16& str, bool* outCreate = nullptr); +std::unique_ptr<Reference> tryParseReference(const StringPiece& str, bool* outCreate = nullptr); /* * Returns a BinaryPrimitve object representing @null or @empty if the string was parsed * as one. */ -std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece16& str); +std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece& str); /* * Returns a BinaryPrimitve object representing a color if the string was parsed * as one. */ -std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece16& str); +std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece& str); /* * Returns a BinaryPrimitve object representing a boolean if the string was parsed * as one. */ -std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece16& str); +std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece& str); /* * Returns a BinaryPrimitve object representing an integer if the string was parsed * as one. */ -std::unique_ptr<BinaryPrimitive> tryParseInt(const StringPiece16& str); +std::unique_ptr<BinaryPrimitive> tryParseInt(const StringPiece& str); /* * Returns a BinaryPrimitve object representing a floating point number * (float, dimension, etc) if the string was parsed as one. */ -std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece16& str); +std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece& str); /* * Returns a BinaryPrimitve object representing an enum symbol if the string was parsed * as one. */ std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute* enumAttr, - const StringPiece16& str); + const StringPiece& str); /* * Returns a BinaryPrimitve object representing a flag symbol if the string was parsed * as one. */ std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* enumAttr, - const StringPiece16& str); + const StringPiece& str); /* * Try to convert a string to an Item for the given attribute. The attribute will * restrict what values the string can be converted to. @@ -147,11 +157,11 @@ std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* enumAttr, * reference to an ID that must be created (@+id/foo). */ std::unique_ptr<Item> parseItemForAttribute( - const StringPiece16& value, const Attribute* attr, + const StringPiece& value, const Attribute* attr, std::function<void(const ResourceName&)> onCreateReference = {}); std::unique_ptr<Item> parseItemForAttribute( - const StringPiece16& value, uint32_t typeMask, + const StringPiece& value, uint32_t typeMask, std::function<void(const ResourceName&)> onCreateReference = {}); uint32_t androidTypeToAttributeTypeMask(uint16_t type); diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp index 7425f97ef8de..fb76914cc495 100644 --- a/tools/aapt2/ResourceUtils_test.cpp +++ b/tools/aapt2/ResourceUtils_test.cpp @@ -16,69 +16,66 @@ #include "Resource.h" #include "ResourceUtils.h" -#include "test/Builders.h" -#include "test/Common.h" - -#include <gtest/gtest.h> +#include "test/Test.h" namespace aapt { TEST(ResourceUtilsTest, ParseBool) { bool val = false; - EXPECT_TRUE(ResourceUtils::tryParseBool(u"true", &val)); + EXPECT_TRUE(ResourceUtils::tryParseBool("true", &val)); EXPECT_TRUE(val); - EXPECT_TRUE(ResourceUtils::tryParseBool(u"TRUE", &val)); + EXPECT_TRUE(ResourceUtils::tryParseBool("TRUE", &val)); EXPECT_TRUE(val); - EXPECT_TRUE(ResourceUtils::tryParseBool(u"True", &val)); + EXPECT_TRUE(ResourceUtils::tryParseBool("True", &val)); EXPECT_TRUE(val); - EXPECT_TRUE(ResourceUtils::tryParseBool(u"false", &val)); + EXPECT_TRUE(ResourceUtils::tryParseBool("false", &val)); EXPECT_FALSE(val); - EXPECT_TRUE(ResourceUtils::tryParseBool(u"FALSE", &val)); + EXPECT_TRUE(ResourceUtils::tryParseBool("FALSE", &val)); EXPECT_FALSE(val); - EXPECT_TRUE(ResourceUtils::tryParseBool(u"False", &val)); + EXPECT_TRUE(ResourceUtils::tryParseBool("False", &val)); EXPECT_FALSE(val); } TEST(ResourceUtilsTest, ParseResourceName) { ResourceNameRef actual; bool actualPriv = false; - EXPECT_TRUE(ResourceUtils::parseResourceName(u"android:color/foo", &actual, &actualPriv)); - EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kColor, u"foo"), actual); + EXPECT_TRUE(ResourceUtils::parseResourceName("android:color/foo", &actual, &actualPriv)); + EXPECT_EQ(ResourceNameRef("android", ResourceType::kColor, "foo"), actual); EXPECT_FALSE(actualPriv); - EXPECT_TRUE(ResourceUtils::parseResourceName(u"color/foo", &actual, &actualPriv)); - EXPECT_EQ(ResourceNameRef({}, ResourceType::kColor, u"foo"), actual); + EXPECT_TRUE(ResourceUtils::parseResourceName("color/foo", &actual, &actualPriv)); + EXPECT_EQ(ResourceNameRef({}, ResourceType::kColor, "foo"), actual); EXPECT_FALSE(actualPriv); - EXPECT_TRUE(ResourceUtils::parseResourceName(u"*android:color/foo", &actual, &actualPriv)); - EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kColor, u"foo"), actual); + EXPECT_TRUE(ResourceUtils::parseResourceName("*android:color/foo", &actual, &actualPriv)); + EXPECT_EQ(ResourceNameRef("android", ResourceType::kColor, "foo"), actual); EXPECT_TRUE(actualPriv); - EXPECT_FALSE(ResourceUtils::parseResourceName(StringPiece16(), &actual, &actualPriv)); + EXPECT_FALSE(ResourceUtils::parseResourceName(StringPiece(), &actual, &actualPriv)); } TEST(ResourceUtilsTest, ParseReferenceWithNoPackage) { - ResourceNameRef expected({}, ResourceType::kColor, u"foo"); + ResourceNameRef expected({}, ResourceType::kColor, "foo"); ResourceNameRef actual; bool create = false; bool privateRef = false; - EXPECT_TRUE(ResourceUtils::tryParseReference(u"@color/foo", &actual, &create, &privateRef)); + EXPECT_TRUE(ResourceUtils::tryParseReference("@color/foo", &actual, &create, &privateRef)); EXPECT_EQ(expected, actual); EXPECT_FALSE(create); EXPECT_FALSE(privateRef); } TEST(ResourceUtilsTest, ParseReferenceWithPackage) { - ResourceNameRef expected(u"android", ResourceType::kColor, u"foo"); + ResourceNameRef expected("android", ResourceType::kColor, "foo"); ResourceNameRef actual; bool create = false; bool privateRef = false; - EXPECT_TRUE(ResourceUtils::tryParseReference(u"@android:color/foo", &actual, &create, + EXPECT_TRUE(ResourceUtils::tryParseReference("@android:color/foo", &actual, &create, &privateRef)); EXPECT_EQ(expected, actual); EXPECT_FALSE(create); @@ -86,11 +83,11 @@ TEST(ResourceUtilsTest, ParseReferenceWithPackage) { } TEST(ResourceUtilsTest, ParseReferenceWithSurroundingWhitespace) { - ResourceNameRef expected(u"android", ResourceType::kColor, u"foo"); + ResourceNameRef expected("android", ResourceType::kColor, "foo"); ResourceNameRef actual; bool create = false; bool privateRef = false; - EXPECT_TRUE(ResourceUtils::tryParseReference(u"\t @android:color/foo\n \n\t", &actual, + EXPECT_TRUE(ResourceUtils::tryParseReference("\t @android:color/foo\n \n\t", &actual, &create, &privateRef)); EXPECT_EQ(expected, actual); EXPECT_FALSE(create); @@ -98,11 +95,11 @@ TEST(ResourceUtilsTest, ParseReferenceWithSurroundingWhitespace) { } TEST(ResourceUtilsTest, ParseAutoCreateIdReference) { - ResourceNameRef expected(u"android", ResourceType::kId, u"foo"); + ResourceNameRef expected("android", ResourceType::kId, "foo"); ResourceNameRef actual; bool create = false; bool privateRef = false; - EXPECT_TRUE(ResourceUtils::tryParseReference(u"@+android:id/foo", &actual, &create, + EXPECT_TRUE(ResourceUtils::tryParseReference("@+android:id/foo", &actual, &create, &privateRef)); EXPECT_EQ(expected, actual); EXPECT_TRUE(create); @@ -110,11 +107,11 @@ TEST(ResourceUtilsTest, ParseAutoCreateIdReference) { } TEST(ResourceUtilsTest, ParsePrivateReference) { - ResourceNameRef expected(u"android", ResourceType::kId, u"foo"); + ResourceNameRef expected("android", ResourceType::kId, "foo"); ResourceNameRef actual; bool create = false; bool privateRef = false; - EXPECT_TRUE(ResourceUtils::tryParseReference(u"@*android:id/foo", &actual, &create, + EXPECT_TRUE(ResourceUtils::tryParseReference("@*android:id/foo", &actual, &create, &privateRef)); EXPECT_EQ(expected, actual); EXPECT_FALSE(create); @@ -125,68 +122,68 @@ TEST(ResourceUtilsTest, FailToParseAutoCreateNonIdReference) { bool create = false; bool privateRef = false; ResourceNameRef actual; - EXPECT_FALSE(ResourceUtils::tryParseReference(u"@+android:color/foo", &actual, &create, + EXPECT_FALSE(ResourceUtils::tryParseReference("@+android:color/foo", &actual, &create, &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")); + EXPECT_TRUE(ResourceUtils::isAttributeReference("?android")); + EXPECT_TRUE(ResourceUtils::isAttributeReference("?android:foo")); + EXPECT_TRUE(ResourceUtils::isAttributeReference("?attr/foo")); + EXPECT_TRUE(ResourceUtils::isAttributeReference("?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")); + EXPECT_FALSE(ResourceUtils::isAttributeReference("?style/foo")); + EXPECT_FALSE(ResourceUtils::isAttributeReference("?android:style/foo")); + EXPECT_FALSE(ResourceUtils::isAttributeReference("?android:")); + EXPECT_FALSE(ResourceUtils::isAttributeReference("?android:attr/")); + EXPECT_FALSE(ResourceUtils::isAttributeReference("?:attr/")); + EXPECT_FALSE(ResourceUtils::isAttributeReference("?:attr/foo")); + EXPECT_FALSE(ResourceUtils::isAttributeReference("?:/")); + EXPECT_FALSE(ResourceUtils::isAttributeReference("?:/foo")); + EXPECT_FALSE(ResourceUtils::isAttributeReference("?attr/")); + EXPECT_FALSE(ResourceUtils::isAttributeReference("?/foo")); } TEST(ResourceUtilsTest, ParseStyleParentReference) { - const ResourceName kAndroidStyleFooName(u"android", ResourceType::kStyle, u"foo"); - const ResourceName kStyleFooName({}, ResourceType::kStyle, u"foo"); + const ResourceName kAndroidStyleFooName("android", ResourceType::kStyle, "foo"); + const ResourceName kStyleFooName({}, ResourceType::kStyle, "foo"); std::string errStr; - Maybe<Reference> ref = ResourceUtils::parseStyleParentReference(u"@android:style/foo", &errStr); + Maybe<Reference> ref = ResourceUtils::parseStyleParentReference("@android:style/foo", &errStr); AAPT_ASSERT_TRUE(ref); EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName); - ref = ResourceUtils::parseStyleParentReference(u"@style/foo", &errStr); + ref = ResourceUtils::parseStyleParentReference("@style/foo", &errStr); AAPT_ASSERT_TRUE(ref); EXPECT_EQ(ref.value().name.value(), kStyleFooName); - ref = ResourceUtils::parseStyleParentReference(u"?android:style/foo", &errStr); + ref = ResourceUtils::parseStyleParentReference("?android:style/foo", &errStr); AAPT_ASSERT_TRUE(ref); EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName); - ref = ResourceUtils::parseStyleParentReference(u"?style/foo", &errStr); + ref = ResourceUtils::parseStyleParentReference("?style/foo", &errStr); AAPT_ASSERT_TRUE(ref); EXPECT_EQ(ref.value().name.value(), kStyleFooName); - ref = ResourceUtils::parseStyleParentReference(u"android:style/foo", &errStr); + ref = ResourceUtils::parseStyleParentReference("android:style/foo", &errStr); AAPT_ASSERT_TRUE(ref); EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName); - ref = ResourceUtils::parseStyleParentReference(u"android:foo", &errStr); + ref = ResourceUtils::parseStyleParentReference("android:foo", &errStr); AAPT_ASSERT_TRUE(ref); EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName); - ref = ResourceUtils::parseStyleParentReference(u"@android:foo", &errStr); + ref = ResourceUtils::parseStyleParentReference("@android:foo", &errStr); AAPT_ASSERT_TRUE(ref); EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName); - ref = ResourceUtils::parseStyleParentReference(u"foo", &errStr); + ref = ResourceUtils::parseStyleParentReference("foo", &errStr); AAPT_ASSERT_TRUE(ref); EXPECT_EQ(ref.value().name.value(), kStyleFooName); - ref = ResourceUtils::parseStyleParentReference(u"*android:style/foo", &errStr); + ref = ResourceUtils::parseStyleParentReference("*android:style/foo", &errStr); AAPT_ASSERT_TRUE(ref); EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName); EXPECT_TRUE(ref.value().privateReference); @@ -195,11 +192,11 @@ TEST(ResourceUtilsTest, ParseStyleParentReference) { TEST(ResourceUtilsTest, ParseEmptyFlag) { std::unique_ptr<Attribute> attr = test::AttributeBuilder(false) .setTypeMask(android::ResTable_map::TYPE_FLAGS) - .addItem(u"one", 0x01) - .addItem(u"two", 0x02) + .addItem("one", 0x01) + .addItem("two", 0x02) .build(); - std::unique_ptr<BinaryPrimitive> result = ResourceUtils::tryParseFlagSymbol(attr.get(), u""); + std::unique_ptr<BinaryPrimitive> result = ResourceUtils::tryParseFlagSymbol(attr.get(), ""); ASSERT_NE(nullptr, result); EXPECT_EQ(0u, result->value.data); } diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp index c10b134cb36e..4a865799372d 100644 --- a/tools/aapt2/ResourceValues.cpp +++ b/tools/aapt2/ResourceValues.cpp @@ -21,6 +21,7 @@ #include "io/File.h" #include "util/Util.h" +#include <algorithm> #include <androidfw/ResourceTypes.h> #include <limits> @@ -302,18 +303,42 @@ Attribute::Attribute(bool w, uint32_t t) : mWeak = w; } +template <typename T> +T* addPointer(T& val) { + return &val; +} + bool Attribute::equals(const Value* value) const { const Attribute* other = valueCast<Attribute>(value); if (!other) { return false; } - return this->typeMask == other->typeMask && this->minInt == other->minInt && - this->maxInt == other->maxInt && - std::equal(this->symbols.begin(), this->symbols.end(), - other->symbols.begin(), - [](const Symbol& a, const Symbol& b) -> bool { - return a.symbol.equals(&b.symbol) && a.value == b.value; + if (symbols.size() != other->symbols.size()) { + return false; + } + + if (typeMask != other->typeMask || minInt != other->minInt || maxInt != other->maxInt) { + return false; + } + + std::vector<const Symbol*> sortedA; + std::transform(symbols.begin(), symbols.end(), + std::back_inserter(sortedA), addPointer<const Symbol>); + std::sort(sortedA.begin(), sortedA.end(), [](const Symbol* a, const Symbol* b) -> bool { + return a->symbol.name < b->symbol.name; + }); + + std::vector<const Symbol*> sortedB; + std::transform(other->symbols.begin(), other->symbols.end(), + std::back_inserter(sortedB), addPointer<const Symbol>); + std::sort(sortedB.begin(), sortedB.end(), [](const Symbol* a, const Symbol* b) -> bool { + return a->symbol.name < b->symbol.name; + }); + + return std::equal(sortedA.begin(), sortedA.end(), sortedB.begin(), + [](const Symbol* a, const Symbol* b) -> bool { + return a->symbol.equals(&b->symbol) && a->value == b->value; }); } @@ -526,9 +551,28 @@ bool Style::equals(const Value* value) const { (parent && other->parent && !parent.value().equals(&other->parent.value()))) { return false; } - return std::equal(entries.begin(), entries.end(), other->entries.begin(), - [](const Entry& a, const Entry& b) -> bool { - return a.key.equals(&b.key) && a.value->equals(b.value.get()); + + if (entries.size() != other->entries.size()) { + return false; + } + + std::vector<const Entry*> sortedA; + std::transform(entries.begin(), entries.end(), + std::back_inserter(sortedA), addPointer<const Entry>); + std::sort(sortedA.begin(), sortedA.end(), [](const Entry* a, const Entry* b) -> bool { + return a->key.name < b->key.name; + }); + + std::vector<const Entry*> sortedB; + std::transform(other->entries.begin(), other->entries.end(), + std::back_inserter(sortedB), addPointer<const Entry>); + std::sort(sortedB.begin(), sortedB.end(), [](const Entry* a, const Entry* b) -> bool { + return a->key.name < b->key.name; + }); + + return std::equal(sortedA.begin(), sortedA.end(), sortedB.begin(), + [](const Entry* a, const Entry* b) -> bool { + return a->key.equals(&b->key) && a->value->equals(b->value.get()); }); } @@ -563,6 +607,8 @@ void Style::print(std::ostream* out) const { static ::std::ostream& operator<<(::std::ostream& out, const Style::Entry& value) { if (value.key.name) { out << value.key.name.value(); + } else if (value.key.id) { + out << value.key.id.value(); } else { out << "???"; } @@ -577,6 +623,10 @@ bool Array::equals(const Value* value) const { return false; } + if (items.size() != other->items.size()) { + return false; + } + return std::equal(items.begin(), items.end(), other->items.begin(), [](const std::unique_ptr<Item>& a, const std::unique_ptr<Item>& b) -> bool { return a->equals(b.get()); @@ -605,6 +655,10 @@ bool Plural::equals(const Value* value) const { return false; } + if (values.size() != other->values.size()) { + return false; + } + return std::equal(values.begin(), values.end(), other->values.begin(), [](const std::unique_ptr<Item>& a, const std::unique_ptr<Item>& b) -> bool { if (bool(a) != bool(b)) { @@ -659,6 +713,11 @@ bool Styleable::equals(const Value* value) const { if (!other) { return false; } + + if (entries.size() != other->entries.size()) { + return false; + } + return std::equal(entries.begin(), entries.end(), other->entries.begin(), [](const Reference& a, const Reference& b) -> bool { return a.equals(&b); diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h index aa1b550bdf09..8ae71ad82a60 100644 --- a/tools/aapt2/ResourceValues.h +++ b/tools/aapt2/ResourceValues.h @@ -84,15 +84,15 @@ struct Value { /** * Returns the comment that was associated with this resource. */ - StringPiece16 getComment() const { + const std::string& getComment() const { return mComment; } - void setComment(const StringPiece16& str) { + void setComment(const StringPiece& str) { mComment = str.toString(); } - void setComment(std::u16string&& str) { + void setComment(std::string&& str) { mComment = std::move(str); } @@ -115,7 +115,7 @@ struct Value { protected: Source mSource; - std::u16string mComment; + std::string mComment; bool mWeak = false; bool mTranslateable = true; }; diff --git a/tools/aapt2/Resource_test.cpp b/tools/aapt2/Resource_test.cpp index 48dc521d843c..06cddc789588 100644 --- a/tools/aapt2/Resource_test.cpp +++ b/tools/aapt2/Resource_test.cpp @@ -14,102 +14,101 @@ * limitations under the License. */ -#include <gtest/gtest.h> - #include "Resource.h" +#include "test/Test.h" namespace aapt { TEST(ResourceTypeTest, ParseResourceTypes) { - const ResourceType* type = parseResourceType(u"anim"); + const ResourceType* type = parseResourceType("anim"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kAnim); - type = parseResourceType(u"animator"); + type = parseResourceType("animator"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kAnimator); - type = parseResourceType(u"array"); + type = parseResourceType("array"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kArray); - type = parseResourceType(u"attr"); + type = parseResourceType("attr"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kAttr); - type = parseResourceType(u"^attr-private"); + type = parseResourceType("^attr-private"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kAttrPrivate); - type = parseResourceType(u"bool"); + type = parseResourceType("bool"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kBool); - type = parseResourceType(u"color"); + type = parseResourceType("color"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kColor); - type = parseResourceType(u"dimen"); + type = parseResourceType("dimen"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kDimen); - type = parseResourceType(u"drawable"); + type = parseResourceType("drawable"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kDrawable); - type = parseResourceType(u"fraction"); + type = parseResourceType("fraction"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kFraction); - type = parseResourceType(u"id"); + type = parseResourceType("id"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kId); - type = parseResourceType(u"integer"); + type = parseResourceType("integer"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kInteger); - type = parseResourceType(u"interpolator"); + type = parseResourceType("interpolator"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kInterpolator); - type = parseResourceType(u"layout"); + type = parseResourceType("layout"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kLayout); - type = parseResourceType(u"menu"); + type = parseResourceType("menu"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kMenu); - type = parseResourceType(u"mipmap"); + type = parseResourceType("mipmap"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kMipmap); - type = parseResourceType(u"plurals"); + type = parseResourceType("plurals"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kPlurals); - type = parseResourceType(u"raw"); + type = parseResourceType("raw"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kRaw); - type = parseResourceType(u"string"); + type = parseResourceType("string"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kString); - type = parseResourceType(u"style"); + type = parseResourceType("style"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kStyle); - type = parseResourceType(u"transition"); + type = parseResourceType("transition"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kTransition); - type = parseResourceType(u"xml"); + type = parseResourceType("xml"); ASSERT_NE(type, nullptr); EXPECT_EQ(*type, ResourceType::kXml); - type = parseResourceType(u"blahaha"); + type = parseResourceType("blahaha"); EXPECT_EQ(type, nullptr); } diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp index c2a22bf2a373..91e755de2514 100644 --- a/tools/aapt2/SdkConstants.cpp +++ b/tools/aapt2/SdkConstants.cpp @@ -23,6 +23,9 @@ namespace aapt { +static const char* sDevelopmentSdkCodeName = "O"; +static int sDevelopmentSdkLevel = 26; + static const std::vector<std::pair<uint16_t, size_t>> sAttrIdMap = { { 0x021c, 1 }, { 0x021d, 2 }, @@ -60,671 +63,671 @@ size_t findAttributeSdkLevel(ResourceId id) { return iter->second; } -static const std::unordered_map<std::u16string, size_t> sAttrMap = { - { u"marqueeRepeatLimit", 2 }, - { u"windowNoDisplay", 3 }, - { u"backgroundDimEnabled", 3 }, - { u"inputType", 3 }, - { u"isDefault", 3 }, - { u"windowDisablePreview", 3 }, - { u"privateImeOptions", 3 }, - { u"editorExtras", 3 }, - { u"settingsActivity", 3 }, - { u"fastScrollEnabled", 3 }, - { u"reqTouchScreen", 3 }, - { u"reqKeyboardType", 3 }, - { u"reqHardKeyboard", 3 }, - { u"reqNavigation", 3 }, - { u"windowSoftInputMode", 3 }, - { u"imeFullscreenBackground", 3 }, - { u"noHistory", 3 }, - { u"headerDividersEnabled", 3 }, - { u"footerDividersEnabled", 3 }, - { u"candidatesTextStyleSpans", 3 }, - { u"smoothScrollbar", 3 }, - { u"reqFiveWayNav", 3 }, - { u"keyBackground", 3 }, - { u"keyTextSize", 3 }, - { u"labelTextSize", 3 }, - { u"keyTextColor", 3 }, - { u"keyPreviewLayout", 3 }, - { u"keyPreviewOffset", 3 }, - { u"keyPreviewHeight", 3 }, - { u"verticalCorrection", 3 }, - { u"popupLayout", 3 }, - { u"state_long_pressable", 3 }, - { u"keyWidth", 3 }, - { u"keyHeight", 3 }, - { u"horizontalGap", 3 }, - { u"verticalGap", 3 }, - { u"rowEdgeFlags", 3 }, - { u"codes", 3 }, - { u"popupKeyboard", 3 }, - { u"popupCharacters", 3 }, - { u"keyEdgeFlags", 3 }, - { u"isModifier", 3 }, - { u"isSticky", 3 }, - { u"isRepeatable", 3 }, - { u"iconPreview", 3 }, - { u"keyOutputText", 3 }, - { u"keyLabel", 3 }, - { u"keyIcon", 3 }, - { u"keyboardMode", 3 }, - { u"isScrollContainer", 3 }, - { u"fillEnabled", 3 }, - { u"updatePeriodMillis", 3 }, - { u"initialLayout", 3 }, - { u"voiceSearchMode", 3 }, - { u"voiceLanguageModel", 3 }, - { u"voicePromptText", 3 }, - { u"voiceLanguage", 3 }, - { u"voiceMaxResults", 3 }, - { u"bottomOffset", 3 }, - { u"topOffset", 3 }, - { u"allowSingleTap", 3 }, - { u"handle", 3 }, - { u"content", 3 }, - { u"animateOnClick", 3 }, - { u"configure", 3 }, - { u"hapticFeedbackEnabled", 3 }, - { u"innerRadius", 3 }, - { u"thickness", 3 }, - { u"sharedUserLabel", 3 }, - { u"dropDownWidth", 3 }, - { u"dropDownAnchor", 3 }, - { u"imeOptions", 3 }, - { u"imeActionLabel", 3 }, - { u"imeActionId", 3 }, - { u"imeExtractEnterAnimation", 3 }, - { u"imeExtractExitAnimation", 3 }, - { u"tension", 4 }, - { u"extraTension", 4 }, - { u"anyDensity", 4 }, - { u"searchSuggestThreshold", 4 }, - { u"includeInGlobalSearch", 4 }, - { u"onClick", 4 }, - { u"targetSdkVersion", 4 }, - { u"maxSdkVersion", 4 }, - { u"testOnly", 4 }, - { u"contentDescription", 4 }, - { u"gestureStrokeWidth", 4 }, - { u"gestureColor", 4 }, - { u"uncertainGestureColor", 4 }, - { u"fadeOffset", 4 }, - { u"fadeDuration", 4 }, - { u"gestureStrokeType", 4 }, - { u"gestureStrokeLengthThreshold", 4 }, - { u"gestureStrokeSquarenessThreshold", 4 }, - { u"gestureStrokeAngleThreshold", 4 }, - { u"eventsInterceptionEnabled", 4 }, - { u"fadeEnabled", 4 }, - { u"backupAgent", 4 }, - { u"allowBackup", 4 }, - { u"glEsVersion", 4 }, - { u"queryAfterZeroResults", 4 }, - { u"dropDownHeight", 4 }, - { u"smallScreens", 4 }, - { u"normalScreens", 4 }, - { u"largeScreens", 4 }, - { u"progressBarStyleInverse", 4 }, - { u"progressBarStyleSmallInverse", 4 }, - { u"progressBarStyleLargeInverse", 4 }, - { u"searchSettingsDescription", 4 }, - { u"textColorPrimaryInverseDisableOnly", 4 }, - { u"autoUrlDetect", 4 }, - { u"resizeable", 4 }, - { u"required", 5 }, - { u"accountType", 5 }, - { u"contentAuthority", 5 }, - { u"userVisible", 5 }, - { u"windowShowWallpaper", 5 }, - { u"wallpaperOpenEnterAnimation", 5 }, - { u"wallpaperOpenExitAnimation", 5 }, - { u"wallpaperCloseEnterAnimation", 5 }, - { u"wallpaperCloseExitAnimation", 5 }, - { u"wallpaperIntraOpenEnterAnimation", 5 }, - { u"wallpaperIntraOpenExitAnimation", 5 }, - { u"wallpaperIntraCloseEnterAnimation", 5 }, - { u"wallpaperIntraCloseExitAnimation", 5 }, - { u"supportsUploading", 5 }, - { u"killAfterRestore", 5 }, - { u"restoreNeedsApplication", 5 }, - { u"smallIcon", 5 }, - { u"accountPreferences", 5 }, - { u"textAppearanceSearchResultSubtitle", 5 }, - { u"textAppearanceSearchResultTitle", 5 }, - { u"summaryColumn", 5 }, - { u"detailColumn", 5 }, - { u"detailSocialSummary", 5 }, - { u"thumbnail", 5 }, - { u"detachWallpaper", 5 }, - { u"finishOnCloseSystemDialogs", 5 }, - { u"scrollbarFadeDuration", 5 }, - { u"scrollbarDefaultDelayBeforeFade", 5 }, - { u"fadeScrollbars", 5 }, - { u"colorBackgroundCacheHint", 5 }, - { u"dropDownHorizontalOffset", 5 }, - { u"dropDownVerticalOffset", 5 }, - { u"quickContactBadgeStyleWindowSmall", 6 }, - { u"quickContactBadgeStyleWindowMedium", 6 }, - { u"quickContactBadgeStyleWindowLarge", 6 }, - { u"quickContactBadgeStyleSmallWindowSmall", 6 }, - { u"quickContactBadgeStyleSmallWindowMedium", 6 }, - { u"quickContactBadgeStyleSmallWindowLarge", 6 }, - { u"author", 7 }, - { u"autoStart", 7 }, - { u"expandableListViewWhiteStyle", 8 }, - { u"installLocation", 8 }, - { u"vmSafeMode", 8 }, - { u"webTextViewStyle", 8 }, - { u"restoreAnyVersion", 8 }, - { u"tabStripLeft", 8 }, - { u"tabStripRight", 8 }, - { u"tabStripEnabled", 8 }, - { u"logo", 9 }, - { u"xlargeScreens", 9 }, - { u"immersive", 9 }, - { u"overScrollMode", 9 }, - { u"overScrollHeader", 9 }, - { u"overScrollFooter", 9 }, - { u"filterTouchesWhenObscured", 9 }, - { u"textSelectHandleLeft", 9 }, - { u"textSelectHandleRight", 9 }, - { u"textSelectHandle", 9 }, - { u"textSelectHandleWindowStyle", 9 }, - { u"popupAnimationStyle", 9 }, - { u"screenSize", 9 }, - { u"screenDensity", 9 }, - { u"allContactsName", 11 }, - { u"windowActionBar", 11 }, - { u"actionBarStyle", 11 }, - { u"navigationMode", 11 }, - { u"displayOptions", 11 }, - { u"subtitle", 11 }, - { u"customNavigationLayout", 11 }, - { u"hardwareAccelerated", 11 }, - { u"measureWithLargestChild", 11 }, - { u"animateFirstView", 11 }, - { u"dropDownSpinnerStyle", 11 }, - { u"actionDropDownStyle", 11 }, - { u"actionButtonStyle", 11 }, - { u"showAsAction", 11 }, - { u"previewImage", 11 }, - { u"actionModeBackground", 11 }, - { u"actionModeCloseDrawable", 11 }, - { u"windowActionModeOverlay", 11 }, - { u"valueFrom", 11 }, - { u"valueTo", 11 }, - { u"valueType", 11 }, - { u"propertyName", 11 }, - { u"ordering", 11 }, - { u"fragment", 11 }, - { u"windowActionBarOverlay", 11 }, - { u"fragmentOpenEnterAnimation", 11 }, - { u"fragmentOpenExitAnimation", 11 }, - { u"fragmentCloseEnterAnimation", 11 }, - { u"fragmentCloseExitAnimation", 11 }, - { u"fragmentFadeEnterAnimation", 11 }, - { u"fragmentFadeExitAnimation", 11 }, - { u"actionBarSize", 11 }, - { u"imeSubtypeLocale", 11 }, - { u"imeSubtypeMode", 11 }, - { u"imeSubtypeExtraValue", 11 }, - { u"splitMotionEvents", 11 }, - { u"listChoiceBackgroundIndicator", 11 }, - { u"spinnerMode", 11 }, - { u"animateLayoutChanges", 11 }, - { u"actionBarTabStyle", 11 }, - { u"actionBarTabBarStyle", 11 }, - { u"actionBarTabTextStyle", 11 }, - { u"actionOverflowButtonStyle", 11 }, - { u"actionModeCloseButtonStyle", 11 }, - { u"titleTextStyle", 11 }, - { u"subtitleTextStyle", 11 }, - { u"iconifiedByDefault", 11 }, - { u"actionLayout", 11 }, - { u"actionViewClass", 11 }, - { u"activatedBackgroundIndicator", 11 }, - { u"state_activated", 11 }, - { u"listPopupWindowStyle", 11 }, - { u"popupMenuStyle", 11 }, - { u"textAppearanceLargePopupMenu", 11 }, - { u"textAppearanceSmallPopupMenu", 11 }, - { u"breadCrumbTitle", 11 }, - { u"breadCrumbShortTitle", 11 }, - { u"listDividerAlertDialog", 11 }, - { u"textColorAlertDialogListItem", 11 }, - { u"loopViews", 11 }, - { u"dialogTheme", 11 }, - { u"alertDialogTheme", 11 }, - { u"dividerVertical", 11 }, - { u"homeAsUpIndicator", 11 }, - { u"enterFadeDuration", 11 }, - { u"exitFadeDuration", 11 }, - { u"selectableItemBackground", 11 }, - { u"autoAdvanceViewId", 11 }, - { u"useIntrinsicSizeAsMinimum", 11 }, - { u"actionModeCutDrawable", 11 }, - { u"actionModeCopyDrawable", 11 }, - { u"actionModePasteDrawable", 11 }, - { u"textEditPasteWindowLayout", 11 }, - { u"textEditNoPasteWindowLayout", 11 }, - { u"textIsSelectable", 11 }, - { u"windowEnableSplitTouch", 11 }, - { u"indeterminateProgressStyle", 11 }, - { u"progressBarPadding", 11 }, - { u"animationResolution", 11 }, - { u"state_accelerated", 11 }, - { u"baseline", 11 }, - { u"homeLayout", 11 }, - { u"opacity", 11 }, - { u"alpha", 11 }, - { u"transformPivotX", 11 }, - { u"transformPivotY", 11 }, - { u"translationX", 11 }, - { u"translationY", 11 }, - { u"scaleX", 11 }, - { u"scaleY", 11 }, - { u"rotation", 11 }, - { u"rotationX", 11 }, - { u"rotationY", 11 }, - { u"showDividers", 11 }, - { u"dividerPadding", 11 }, - { u"borderlessButtonStyle", 11 }, - { u"dividerHorizontal", 11 }, - { u"itemPadding", 11 }, - { u"buttonBarStyle", 11 }, - { u"buttonBarButtonStyle", 11 }, - { u"segmentedButtonStyle", 11 }, - { u"staticWallpaperPreview", 11 }, - { u"allowParallelSyncs", 11 }, - { u"isAlwaysSyncable", 11 }, - { u"verticalScrollbarPosition", 11 }, - { u"fastScrollAlwaysVisible", 11 }, - { u"fastScrollThumbDrawable", 11 }, - { u"fastScrollPreviewBackgroundLeft", 11 }, - { u"fastScrollPreviewBackgroundRight", 11 }, - { u"fastScrollTrackDrawable", 11 }, - { u"fastScrollOverlayPosition", 11 }, - { u"customTokens", 11 }, - { u"nextFocusForward", 11 }, - { u"firstDayOfWeek", 11 }, - { u"showWeekNumber", 11 }, - { u"minDate", 11 }, - { u"maxDate", 11 }, - { u"shownWeekCount", 11 }, - { u"selectedWeekBackgroundColor", 11 }, - { u"focusedMonthDateColor", 11 }, - { u"unfocusedMonthDateColor", 11 }, - { u"weekNumberColor", 11 }, - { u"weekSeparatorLineColor", 11 }, - { u"selectedDateVerticalBar", 11 }, - { u"weekDayTextAppearance", 11 }, - { u"dateTextAppearance", 11 }, - { u"solidColor", 11 }, - { u"spinnersShown", 11 }, - { u"calendarViewShown", 11 }, - { u"state_multiline", 11 }, - { u"detailsElementBackground", 11 }, - { u"textColorHighlightInverse", 11 }, - { u"textColorLinkInverse", 11 }, - { u"editTextColor", 11 }, - { u"editTextBackground", 11 }, - { u"horizontalScrollViewStyle", 11 }, - { u"layerType", 11 }, - { u"alertDialogIcon", 11 }, - { u"windowMinWidthMajor", 11 }, - { u"windowMinWidthMinor", 11 }, - { u"queryHint", 11 }, - { u"fastScrollTextColor", 11 }, - { u"largeHeap", 11 }, - { u"windowCloseOnTouchOutside", 11 }, - { u"datePickerStyle", 11 }, - { u"calendarViewStyle", 11 }, - { u"textEditSidePasteWindowLayout", 11 }, - { u"textEditSideNoPasteWindowLayout", 11 }, - { u"actionMenuTextAppearance", 11 }, - { u"actionMenuTextColor", 11 }, - { u"textCursorDrawable", 12 }, - { u"resizeMode", 12 }, - { u"requiresSmallestWidthDp", 12 }, - { u"compatibleWidthLimitDp", 12 }, - { u"largestWidthLimitDp", 12 }, - { u"state_hovered", 13 }, - { u"state_drag_can_accept", 13 }, - { u"state_drag_hovered", 13 }, - { u"stopWithTask", 13 }, - { u"switchTextOn", 13 }, - { u"switchTextOff", 13 }, - { u"switchPreferenceStyle", 13 }, - { u"switchTextAppearance", 13 }, - { u"track", 13 }, - { u"switchMinWidth", 13 }, - { u"switchPadding", 13 }, - { u"thumbTextPadding", 13 }, - { u"textSuggestionsWindowStyle", 13 }, - { u"textEditSuggestionItemLayout", 13 }, - { u"rowCount", 13 }, - { u"rowOrderPreserved", 13 }, - { u"columnCount", 13 }, - { u"columnOrderPreserved", 13 }, - { u"useDefaultMargins", 13 }, - { u"alignmentMode", 13 }, - { u"layout_row", 13 }, - { u"layout_rowSpan", 13 }, - { u"layout_columnSpan", 13 }, - { u"actionModeSelectAllDrawable", 13 }, - { u"isAuxiliary", 13 }, - { u"accessibilityEventTypes", 13 }, - { u"packageNames", 13 }, - { u"accessibilityFeedbackType", 13 }, - { u"notificationTimeout", 13 }, - { u"accessibilityFlags", 13 }, - { u"canRetrieveWindowContent", 13 }, - { u"listPreferredItemHeightLarge", 13 }, - { u"listPreferredItemHeightSmall", 13 }, - { u"actionBarSplitStyle", 13 }, - { u"actionProviderClass", 13 }, - { u"backgroundStacked", 13 }, - { u"backgroundSplit", 13 }, - { u"textAllCaps", 13 }, - { u"colorPressedHighlight", 13 }, - { u"colorLongPressedHighlight", 13 }, - { u"colorFocusedHighlight", 13 }, - { u"colorActivatedHighlight", 13 }, - { u"colorMultiSelectHighlight", 13 }, - { u"drawableStart", 13 }, - { u"drawableEnd", 13 }, - { u"actionModeStyle", 13 }, - { u"minResizeWidth", 13 }, - { u"minResizeHeight", 13 }, - { u"actionBarWidgetTheme", 13 }, - { u"uiOptions", 13 }, - { u"subtypeLocale", 13 }, - { u"subtypeExtraValue", 13 }, - { u"actionBarDivider", 13 }, - { u"actionBarItemBackground", 13 }, - { u"actionModeSplitBackground", 13 }, - { u"textAppearanceListItem", 13 }, - { u"textAppearanceListItemSmall", 13 }, - { u"targetDescriptions", 13 }, - { u"directionDescriptions", 13 }, - { u"overridesImplicitlyEnabledSubtype", 13 }, - { u"listPreferredItemPaddingLeft", 13 }, - { u"listPreferredItemPaddingRight", 13 }, - { u"requiresFadingEdge", 13 }, - { u"publicKey", 13 }, - { u"parentActivityName", 16 }, - { u"isolatedProcess", 16 }, - { u"importantForAccessibility", 16 }, - { u"keyboardLayout", 16 }, - { u"fontFamily", 16 }, - { u"mediaRouteButtonStyle", 16 }, - { u"mediaRouteTypes", 16 }, - { u"supportsRtl", 17 }, - { u"textDirection", 17 }, - { u"textAlignment", 17 }, - { u"layoutDirection", 17 }, - { u"paddingStart", 17 }, - { u"paddingEnd", 17 }, - { u"layout_marginStart", 17 }, - { u"layout_marginEnd", 17 }, - { u"layout_toStartOf", 17 }, - { u"layout_toEndOf", 17 }, - { u"layout_alignStart", 17 }, - { u"layout_alignEnd", 17 }, - { u"layout_alignParentStart", 17 }, - { u"layout_alignParentEnd", 17 }, - { u"listPreferredItemPaddingStart", 17 }, - { u"listPreferredItemPaddingEnd", 17 }, - { u"singleUser", 17 }, - { u"presentationTheme", 17 }, - { u"subtypeId", 17 }, - { u"initialKeyguardLayout", 17 }, - { u"widgetCategory", 17 }, - { u"permissionGroupFlags", 17 }, - { u"labelFor", 17 }, - { u"permissionFlags", 17 }, - { u"checkedTextViewStyle", 17 }, - { u"showOnLockScreen", 17 }, - { u"format12Hour", 17 }, - { u"format24Hour", 17 }, - { u"timeZone", 17 }, - { u"mipMap", 18 }, - { u"mirrorForRtl", 18 }, - { u"windowOverscan", 18 }, - { u"requiredForAllUsers", 18 }, - { u"indicatorStart", 18 }, - { u"indicatorEnd", 18 }, - { u"childIndicatorStart", 18 }, - { u"childIndicatorEnd", 18 }, - { u"restrictedAccountType", 18 }, - { u"requiredAccountType", 18 }, - { u"canRequestTouchExplorationMode", 18 }, - { u"canRequestEnhancedWebAccessibility", 18 }, - { u"canRequestFilterKeyEvents", 18 }, - { u"layoutMode", 18 }, - { u"keySet", 19 }, - { u"targetId", 19 }, - { u"fromScene", 19 }, - { u"toScene", 19 }, - { u"transition", 19 }, - { u"transitionOrdering", 19 }, - { u"fadingMode", 19 }, - { u"startDelay", 19 }, - { u"ssp", 19 }, - { u"sspPrefix", 19 }, - { u"sspPattern", 19 }, - { u"addPrintersActivity", 19 }, - { u"vendor", 19 }, - { u"category", 19 }, - { u"isAsciiCapable", 19 }, - { u"autoMirrored", 19 }, - { u"supportsSwitchingToNextInputMethod", 19 }, - { u"requireDeviceUnlock", 19 }, - { u"apduServiceBanner", 19 }, - { u"accessibilityLiveRegion", 19 }, - { u"windowTranslucentStatus", 19 }, - { u"windowTranslucentNavigation", 19 }, - { u"advancedPrintOptionsActivity", 19 }, - { u"banner", 20 }, - { u"windowSwipeToDismiss", 20 }, - { u"isGame", 20 }, - { u"allowEmbedded", 20 }, - { u"setupActivity", 20 }, - { u"fastScrollStyle", 21 }, - { u"windowContentTransitions", 21 }, - { u"windowContentTransitionManager", 21 }, - { u"translationZ", 21 }, - { u"tintMode", 21 }, - { u"controlX1", 21 }, - { u"controlY1", 21 }, - { u"controlX2", 21 }, - { u"controlY2", 21 }, - { u"transitionName", 21 }, - { u"transitionGroup", 21 }, - { u"viewportWidth", 21 }, - { u"viewportHeight", 21 }, - { u"fillColor", 21 }, - { u"pathData", 21 }, - { u"strokeColor", 21 }, - { u"strokeWidth", 21 }, - { u"trimPathStart", 21 }, - { u"trimPathEnd", 21 }, - { u"trimPathOffset", 21 }, - { u"strokeLineCap", 21 }, - { u"strokeLineJoin", 21 }, - { u"strokeMiterLimit", 21 }, - { u"colorControlNormal", 21 }, - { u"colorControlActivated", 21 }, - { u"colorButtonNormal", 21 }, - { u"colorControlHighlight", 21 }, - { u"persistableMode", 21 }, - { u"titleTextAppearance", 21 }, - { u"subtitleTextAppearance", 21 }, - { u"slideEdge", 21 }, - { u"actionBarTheme", 21 }, - { u"textAppearanceListItemSecondary", 21 }, - { u"colorPrimary", 21 }, - { u"colorPrimaryDark", 21 }, - { u"colorAccent", 21 }, - { u"nestedScrollingEnabled", 21 }, - { u"windowEnterTransition", 21 }, - { u"windowExitTransition", 21 }, - { u"windowSharedElementEnterTransition", 21 }, - { u"windowSharedElementExitTransition", 21 }, - { u"windowAllowReturnTransitionOverlap", 21 }, - { u"windowAllowEnterTransitionOverlap", 21 }, - { u"sessionService", 21 }, - { u"stackViewStyle", 21 }, - { u"switchStyle", 21 }, - { u"elevation", 21 }, - { u"excludeId", 21 }, - { u"excludeClass", 21 }, - { u"hideOnContentScroll", 21 }, - { u"actionOverflowMenuStyle", 21 }, - { u"documentLaunchMode", 21 }, - { u"maxRecents", 21 }, - { u"autoRemoveFromRecents", 21 }, - { u"stateListAnimator", 21 }, - { u"toId", 21 }, - { u"fromId", 21 }, - { u"reversible", 21 }, - { u"splitTrack", 21 }, - { u"targetName", 21 }, - { u"excludeName", 21 }, - { u"matchOrder", 21 }, - { u"windowDrawsSystemBarBackgrounds", 21 }, - { u"statusBarColor", 21 }, - { u"navigationBarColor", 21 }, - { u"contentInsetStart", 21 }, - { u"contentInsetEnd", 21 }, - { u"contentInsetLeft", 21 }, - { u"contentInsetRight", 21 }, - { u"paddingMode", 21 }, - { u"layout_rowWeight", 21 }, - { u"layout_columnWeight", 21 }, - { u"translateX", 21 }, - { u"translateY", 21 }, - { u"selectableItemBackgroundBorderless", 21 }, - { u"elegantTextHeight", 21 }, - { u"searchKeyphraseId", 21 }, - { u"searchKeyphrase", 21 }, - { u"searchKeyphraseSupportedLocales", 21 }, - { u"windowTransitionBackgroundFadeDuration", 21 }, - { u"overlapAnchor", 21 }, - { u"progressTint", 21 }, - { u"progressTintMode", 21 }, - { u"progressBackgroundTint", 21 }, - { u"progressBackgroundTintMode", 21 }, - { u"secondaryProgressTint", 21 }, - { u"secondaryProgressTintMode", 21 }, - { u"indeterminateTint", 21 }, - { u"indeterminateTintMode", 21 }, - { u"backgroundTint", 21 }, - { u"backgroundTintMode", 21 }, - { u"foregroundTint", 21 }, - { u"foregroundTintMode", 21 }, - { u"buttonTint", 21 }, - { u"buttonTintMode", 21 }, - { u"thumbTint", 21 }, - { u"thumbTintMode", 21 }, - { u"fullBackupOnly", 21 }, - { u"propertyXName", 21 }, - { u"propertyYName", 21 }, - { u"relinquishTaskIdentity", 21 }, - { u"tileModeX", 21 }, - { u"tileModeY", 21 }, - { u"actionModeShareDrawable", 21 }, - { u"actionModeFindDrawable", 21 }, - { u"actionModeWebSearchDrawable", 21 }, - { u"transitionVisibilityMode", 21 }, - { u"minimumHorizontalAngle", 21 }, - { u"minimumVerticalAngle", 21 }, - { u"maximumAngle", 21 }, - { u"searchViewStyle", 21 }, - { u"closeIcon", 21 }, - { u"goIcon", 21 }, - { u"searchIcon", 21 }, - { u"voiceIcon", 21 }, - { u"commitIcon", 21 }, - { u"suggestionRowLayout", 21 }, - { u"queryBackground", 21 }, - { u"submitBackground", 21 }, - { u"buttonBarPositiveButtonStyle", 21 }, - { u"buttonBarNeutralButtonStyle", 21 }, - { u"buttonBarNegativeButtonStyle", 21 }, - { u"popupElevation", 21 }, - { u"actionBarPopupTheme", 21 }, - { u"multiArch", 21 }, - { u"touchscreenBlocksFocus", 21 }, - { u"windowElevation", 21 }, - { u"launchTaskBehindTargetAnimation", 21 }, - { u"launchTaskBehindSourceAnimation", 21 }, - { u"restrictionType", 21 }, - { u"dayOfWeekBackground", 21 }, - { u"dayOfWeekTextAppearance", 21 }, - { u"headerMonthTextAppearance", 21 }, - { u"headerDayOfMonthTextAppearance", 21 }, - { u"headerYearTextAppearance", 21 }, - { u"yearListItemTextAppearance", 21 }, - { u"yearListSelectorColor", 21 }, - { u"calendarTextColor", 21 }, - { u"recognitionService", 21 }, - { u"timePickerStyle", 21 }, - { u"timePickerDialogTheme", 21 }, - { u"headerTimeTextAppearance", 21 }, - { u"headerAmPmTextAppearance", 21 }, - { u"numbersTextColor", 21 }, - { u"numbersBackgroundColor", 21 }, - { u"numbersSelectorColor", 21 }, - { u"amPmTextColor", 21 }, - { u"amPmBackgroundColor", 21 }, - { u"searchKeyphraseRecognitionFlags", 21 }, - { u"checkMarkTint", 21 }, - { u"checkMarkTintMode", 21 }, - { u"popupTheme", 21 }, - { u"toolbarStyle", 21 }, - { u"windowClipToOutline", 21 }, - { u"datePickerDialogTheme", 21 }, - { u"showText", 21 }, - { u"windowReturnTransition", 21 }, - { u"windowReenterTransition", 21 }, - { u"windowSharedElementReturnTransition", 21 }, - { u"windowSharedElementReenterTransition", 21 }, - { u"resumeWhilePausing", 21 }, - { u"datePickerMode", 21 }, - { u"timePickerMode", 21 }, - { u"inset", 21 }, - { u"letterSpacing", 21 }, - { u"fontFeatureSettings", 21 }, - { u"outlineProvider", 21 }, - { u"contentAgeHint", 21 }, - { u"country", 21 }, - { u"windowSharedElementsUseOverlay", 21 }, - { u"reparent", 21 }, - { u"reparentWithOverlay", 21 }, - { u"ambientShadowAlpha", 21 }, - { u"spotShadowAlpha", 21 }, - { u"navigationIcon", 21 }, - { u"navigationContentDescription", 21 }, - { u"fragmentExitTransition", 21 }, - { u"fragmentEnterTransition", 21 }, - { u"fragmentSharedElementEnterTransition", 21 }, - { u"fragmentReturnTransition", 21 }, - { u"fragmentSharedElementReturnTransition", 21 }, - { u"fragmentReenterTransition", 21 }, - { u"fragmentAllowEnterTransitionOverlap", 21 }, - { u"fragmentAllowReturnTransitionOverlap", 21 }, - { u"patternPathData", 21 }, - { u"strokeAlpha", 21 }, - { u"fillAlpha", 21 }, - { u"windowActivityTransitions", 21 }, - { u"colorEdgeEffect", 21 } +static const std::unordered_map<std::string, size_t> sAttrMap = { + { "marqueeRepeatLimit", 2 }, + { "windowNoDisplay", 3 }, + { "backgroundDimEnabled", 3 }, + { "inputType", 3 }, + { "isDefault", 3 }, + { "windowDisablePreview", 3 }, + { "privateImeOptions", 3 }, + { "editorExtras", 3 }, + { "settingsActivity", 3 }, + { "fastScrollEnabled", 3 }, + { "reqTouchScreen", 3 }, + { "reqKeyboardType", 3 }, + { "reqHardKeyboard", 3 }, + { "reqNavigation", 3 }, + { "windowSoftInputMode", 3 }, + { "imeFullscreenBackground", 3 }, + { "noHistory", 3 }, + { "headerDividersEnabled", 3 }, + { "footerDividersEnabled", 3 }, + { "candidatesTextStyleSpans", 3 }, + { "smoothScrollbar", 3 }, + { "reqFiveWayNav", 3 }, + { "keyBackground", 3 }, + { "keyTextSize", 3 }, + { "labelTextSize", 3 }, + { "keyTextColor", 3 }, + { "keyPreviewLayout", 3 }, + { "keyPreviewOffset", 3 }, + { "keyPreviewHeight", 3 }, + { "verticalCorrection", 3 }, + { "popupLayout", 3 }, + { "state_long_pressable", 3 }, + { "keyWidth", 3 }, + { "keyHeight", 3 }, + { "horizontalGap", 3 }, + { "verticalGap", 3 }, + { "rowEdgeFlags", 3 }, + { "codes", 3 }, + { "popupKeyboard", 3 }, + { "popupCharacters", 3 }, + { "keyEdgeFlags", 3 }, + { "isModifier", 3 }, + { "isSticky", 3 }, + { "isRepeatable", 3 }, + { "iconPreview", 3 }, + { "keyOutputText", 3 }, + { "keyLabel", 3 }, + { "keyIcon", 3 }, + { "keyboardMode", 3 }, + { "isScrollContainer", 3 }, + { "fillEnabled", 3 }, + { "updatePeriodMillis", 3 }, + { "initialLayout", 3 }, + { "voiceSearchMode", 3 }, + { "voiceLanguageModel", 3 }, + { "voicePromptText", 3 }, + { "voiceLanguage", 3 }, + { "voiceMaxResults", 3 }, + { "bottomOffset", 3 }, + { "topOffset", 3 }, + { "allowSingleTap", 3 }, + { "handle", 3 }, + { "content", 3 }, + { "animateOnClick", 3 }, + { "configure", 3 }, + { "hapticFeedbackEnabled", 3 }, + { "innerRadius", 3 }, + { "thickness", 3 }, + { "sharedUserLabel", 3 }, + { "dropDownWidth", 3 }, + { "dropDownAnchor", 3 }, + { "imeOptions", 3 }, + { "imeActionLabel", 3 }, + { "imeActionId", 3 }, + { "imeExtractEnterAnimation", 3 }, + { "imeExtractExitAnimation", 3 }, + { "tension", 4 }, + { "extraTension", 4 }, + { "anyDensity", 4 }, + { "searchSuggestThreshold", 4 }, + { "includeInGlobalSearch", 4 }, + { "onClick", 4 }, + { "targetSdkVersion", 4 }, + { "maxSdkVersion", 4 }, + { "testOnly", 4 }, + { "contentDescription", 4 }, + { "gestureStrokeWidth", 4 }, + { "gestureColor", 4 }, + { "uncertainGestureColor", 4 }, + { "fadeOffset", 4 }, + { "fadeDuration", 4 }, + { "gestureStrokeType", 4 }, + { "gestureStrokeLengthThreshold", 4 }, + { "gestureStrokeSquarenessThreshold", 4 }, + { "gestureStrokeAngleThreshold", 4 }, + { "eventsInterceptionEnabled", 4 }, + { "fadeEnabled", 4 }, + { "backupAgent", 4 }, + { "allowBackup", 4 }, + { "glEsVersion", 4 }, + { "queryAfterZeroResults", 4 }, + { "dropDownHeight", 4 }, + { "smallScreens", 4 }, + { "normalScreens", 4 }, + { "largeScreens", 4 }, + { "progressBarStyleInverse", 4 }, + { "progressBarStyleSmallInverse", 4 }, + { "progressBarStyleLargeInverse", 4 }, + { "searchSettingsDescription", 4 }, + { "textColorPrimaryInverseDisableOnly", 4 }, + { "autoUrlDetect", 4 }, + { "resizeable", 4 }, + { "required", 5 }, + { "accountType", 5 }, + { "contentAuthority", 5 }, + { "userVisible", 5 }, + { "windowShowWallpaper", 5 }, + { "wallpaperOpenEnterAnimation", 5 }, + { "wallpaperOpenExitAnimation", 5 }, + { "wallpaperCloseEnterAnimation", 5 }, + { "wallpaperCloseExitAnimation", 5 }, + { "wallpaperIntraOpenEnterAnimation", 5 }, + { "wallpaperIntraOpenExitAnimation", 5 }, + { "wallpaperIntraCloseEnterAnimation", 5 }, + { "wallpaperIntraCloseExitAnimation", 5 }, + { "supportsUploading", 5 }, + { "killAfterRestore", 5 }, + { "restoreNeedsApplication", 5 }, + { "smallIcon", 5 }, + { "accountPreferences", 5 }, + { "textAppearanceSearchResultSubtitle", 5 }, + { "textAppearanceSearchResultTitle", 5 }, + { "summaryColumn", 5 }, + { "detailColumn", 5 }, + { "detailSocialSummary", 5 }, + { "thumbnail", 5 }, + { "detachWallpaper", 5 }, + { "finishOnCloseSystemDialogs", 5 }, + { "scrollbarFadeDuration", 5 }, + { "scrollbarDefaultDelayBeforeFade", 5 }, + { "fadeScrollbars", 5 }, + { "colorBackgroundCacheHint", 5 }, + { "dropDownHorizontalOffset", 5 }, + { "dropDownVerticalOffset", 5 }, + { "quickContactBadgeStyleWindowSmall", 6 }, + { "quickContactBadgeStyleWindowMedium", 6 }, + { "quickContactBadgeStyleWindowLarge", 6 }, + { "quickContactBadgeStyleSmallWindowSmall", 6 }, + { "quickContactBadgeStyleSmallWindowMedium", 6 }, + { "quickContactBadgeStyleSmallWindowLarge", 6 }, + { "author", 7 }, + { "autoStart", 7 }, + { "expandableListViewWhiteStyle", 8 }, + { "installLocation", 8 }, + { "vmSafeMode", 8 }, + { "webTextViewStyle", 8 }, + { "restoreAnyVersion", 8 }, + { "tabStripLeft", 8 }, + { "tabStripRight", 8 }, + { "tabStripEnabled", 8 }, + { "logo", 9 }, + { "xlargeScreens", 9 }, + { "immersive", 9 }, + { "overScrollMode", 9 }, + { "overScrollHeader", 9 }, + { "overScrollFooter", 9 }, + { "filterTouchesWhenObscured", 9 }, + { "textSelectHandleLeft", 9 }, + { "textSelectHandleRight", 9 }, + { "textSelectHandle", 9 }, + { "textSelectHandleWindowStyle", 9 }, + { "popupAnimationStyle", 9 }, + { "screenSize", 9 }, + { "screenDensity", 9 }, + { "allContactsName", 11 }, + { "windowActionBar", 11 }, + { "actionBarStyle", 11 }, + { "navigationMode", 11 }, + { "displayOptions", 11 }, + { "subtitle", 11 }, + { "customNavigationLayout", 11 }, + { "hardwareAccelerated", 11 }, + { "measureWithLargestChild", 11 }, + { "animateFirstView", 11 }, + { "dropDownSpinnerStyle", 11 }, + { "actionDropDownStyle", 11 }, + { "actionButtonStyle", 11 }, + { "showAsAction", 11 }, + { "previewImage", 11 }, + { "actionModeBackground", 11 }, + { "actionModeCloseDrawable", 11 }, + { "windowActionModeOverlay", 11 }, + { "valueFrom", 11 }, + { "valueTo", 11 }, + { "valueType", 11 }, + { "propertyName", 11 }, + { "ordering", 11 }, + { "fragment", 11 }, + { "windowActionBarOverlay", 11 }, + { "fragmentOpenEnterAnimation", 11 }, + { "fragmentOpenExitAnimation", 11 }, + { "fragmentCloseEnterAnimation", 11 }, + { "fragmentCloseExitAnimation", 11 }, + { "fragmentFadeEnterAnimation", 11 }, + { "fragmentFadeExitAnimation", 11 }, + { "actionBarSize", 11 }, + { "imeSubtypeLocale", 11 }, + { "imeSubtypeMode", 11 }, + { "imeSubtypeExtraValue", 11 }, + { "splitMotionEvents", 11 }, + { "listChoiceBackgroundIndicator", 11 }, + { "spinnerMode", 11 }, + { "animateLayoutChanges", 11 }, + { "actionBarTabStyle", 11 }, + { "actionBarTabBarStyle", 11 }, + { "actionBarTabTextStyle", 11 }, + { "actionOverflowButtonStyle", 11 }, + { "actionModeCloseButtonStyle", 11 }, + { "titleTextStyle", 11 }, + { "subtitleTextStyle", 11 }, + { "iconifiedByDefault", 11 }, + { "actionLayout", 11 }, + { "actionViewClass", 11 }, + { "activatedBackgroundIndicator", 11 }, + { "state_activated", 11 }, + { "listPopupWindowStyle", 11 }, + { "popupMenuStyle", 11 }, + { "textAppearanceLargePopupMen", 11 }, + { "textAppearanceSmallPopupMen", 11 }, + { "breadCrumbTitle", 11 }, + { "breadCrumbShortTitle", 11 }, + { "listDividerAlertDialog", 11 }, + { "textColorAlertDialogListItem", 11 }, + { "loopViews", 11 }, + { "dialogTheme", 11 }, + { "alertDialogTheme", 11 }, + { "dividerVertical", 11 }, + { "homeAsUpIndicator", 11 }, + { "enterFadeDuration", 11 }, + { "exitFadeDuration", 11 }, + { "selectableItemBackground", 11 }, + { "autoAdvanceViewId", 11 }, + { "useIntrinsicSizeAsMinimum", 11 }, + { "actionModeCutDrawable", 11 }, + { "actionModeCopyDrawable", 11 }, + { "actionModePasteDrawable", 11 }, + { "textEditPasteWindowLayout", 11 }, + { "textEditNoPasteWindowLayout", 11 }, + { "textIsSelectable", 11 }, + { "windowEnableSplitTouch", 11 }, + { "indeterminateProgressStyle", 11 }, + { "progressBarPadding", 11 }, + { "animationResolution", 11 }, + { "state_accelerated", 11 }, + { "baseline", 11 }, + { "homeLayout", 11 }, + { "opacity", 11 }, + { "alpha", 11 }, + { "transformPivotX", 11 }, + { "transformPivotY", 11 }, + { "translationX", 11 }, + { "translationY", 11 }, + { "scaleX", 11 }, + { "scaleY", 11 }, + { "rotation", 11 }, + { "rotationX", 11 }, + { "rotationY", 11 }, + { "showDividers", 11 }, + { "dividerPadding", 11 }, + { "borderlessButtonStyle", 11 }, + { "dividerHorizontal", 11 }, + { "itemPadding", 11 }, + { "buttonBarStyle", 11 }, + { "buttonBarButtonStyle", 11 }, + { "segmentedButtonStyle", 11 }, + { "staticWallpaperPreview", 11 }, + { "allowParallelSyncs", 11 }, + { "isAlwaysSyncable", 11 }, + { "verticalScrollbarPosition", 11 }, + { "fastScrollAlwaysVisible", 11 }, + { "fastScrollThumbDrawable", 11 }, + { "fastScrollPreviewBackgroundLeft", 11 }, + { "fastScrollPreviewBackgroundRight", 11 }, + { "fastScrollTrackDrawable", 11 }, + { "fastScrollOverlayPosition", 11 }, + { "customTokens", 11 }, + { "nextFocusForward", 11 }, + { "firstDayOfWeek", 11 }, + { "showWeekNumber", 11 }, + { "minDate", 11 }, + { "maxDate", 11 }, + { "shownWeekCount", 11 }, + { "selectedWeekBackgroundColor", 11 }, + { "focusedMonthDateColor", 11 }, + { "unfocusedMonthDateColor", 11 }, + { "weekNumberColor", 11 }, + { "weekSeparatorLineColor", 11 }, + { "selectedDateVerticalBar", 11 }, + { "weekDayTextAppearance", 11 }, + { "dateTextAppearance", 11 }, + { "solidColor", 11 }, + { "spinnersShown", 11 }, + { "calendarViewShown", 11 }, + { "state_multiline", 11 }, + { "detailsElementBackground", 11 }, + { "textColorHighlightInverse", 11 }, + { "textColorLinkInverse", 11 }, + { "editTextColor", 11 }, + { "editTextBackground", 11 }, + { "horizontalScrollViewStyle", 11 }, + { "layerType", 11 }, + { "alertDialogIcon", 11 }, + { "windowMinWidthMajor", 11 }, + { "windowMinWidthMinor", 11 }, + { "queryHint", 11 }, + { "fastScrollTextColor", 11 }, + { "largeHeap", 11 }, + { "windowCloseOnTouchOutside", 11 }, + { "datePickerStyle", 11 }, + { "calendarViewStyle", 11 }, + { "textEditSidePasteWindowLayout", 11 }, + { "textEditSideNoPasteWindowLayout", 11 }, + { "actionMenuTextAppearance", 11 }, + { "actionMenuTextColor", 11 }, + { "textCursorDrawable", 12 }, + { "resizeMode", 12 }, + { "requiresSmallestWidthDp", 12 }, + { "compatibleWidthLimitDp", 12 }, + { "largestWidthLimitDp", 12 }, + { "state_hovered", 13 }, + { "state_drag_can_accept", 13 }, + { "state_drag_hovered", 13 }, + { "stopWithTask", 13 }, + { "switchTextOn", 13 }, + { "switchTextOff", 13 }, + { "switchPreferenceStyle", 13 }, + { "switchTextAppearance", 13 }, + { "track", 13 }, + { "switchMinWidth", 13 }, + { "switchPadding", 13 }, + { "thumbTextPadding", 13 }, + { "textSuggestionsWindowStyle", 13 }, + { "textEditSuggestionItemLayout", 13 }, + { "rowCount", 13 }, + { "rowOrderPreserved", 13 }, + { "columnCount", 13 }, + { "columnOrderPreserved", 13 }, + { "useDefaultMargins", 13 }, + { "alignmentMode", 13 }, + { "layout_row", 13 }, + { "layout_rowSpan", 13 }, + { "layout_columnSpan", 13 }, + { "actionModeSelectAllDrawable", 13 }, + { "isAuxiliary", 13 }, + { "accessibilityEventTypes", 13 }, + { "packageNames", 13 }, + { "accessibilityFeedbackType", 13 }, + { "notificationTimeout", 13 }, + { "accessibilityFlags", 13 }, + { "canRetrieveWindowContent", 13 }, + { "listPreferredItemHeightLarge", 13 }, + { "listPreferredItemHeightSmall", 13 }, + { "actionBarSplitStyle", 13 }, + { "actionProviderClass", 13 }, + { "backgroundStacked", 13 }, + { "backgroundSplit", 13 }, + { "textAllCaps", 13 }, + { "colorPressedHighlight", 13 }, + { "colorLongPressedHighlight", 13 }, + { "colorFocusedHighlight", 13 }, + { "colorActivatedHighlight", 13 }, + { "colorMultiSelectHighlight", 13 }, + { "drawableStart", 13 }, + { "drawableEnd", 13 }, + { "actionModeStyle", 13 }, + { "minResizeWidth", 13 }, + { "minResizeHeight", 13 }, + { "actionBarWidgetTheme", 13 }, + { "uiOptions", 13 }, + { "subtypeLocale", 13 }, + { "subtypeExtraValue", 13 }, + { "actionBarDivider", 13 }, + { "actionBarItemBackground", 13 }, + { "actionModeSplitBackground", 13 }, + { "textAppearanceListItem", 13 }, + { "textAppearanceListItemSmall", 13 }, + { "targetDescriptions", 13 }, + { "directionDescriptions", 13 }, + { "overridesImplicitlyEnabledSubtype", 13 }, + { "listPreferredItemPaddingLeft", 13 }, + { "listPreferredItemPaddingRight", 13 }, + { "requiresFadingEdge", 13 }, + { "publicKey", 13 }, + { "parentActivityName", 16 }, + { "isolatedProcess", 16 }, + { "importantForAccessibility", 16 }, + { "keyboardLayout", 16 }, + { "fontFamily", 16 }, + { "mediaRouteButtonStyle", 16 }, + { "mediaRouteTypes", 16 }, + { "supportsRtl", 17 }, + { "textDirection", 17 }, + { "textAlignment", 17 }, + { "layoutDirection", 17 }, + { "paddingStart", 17 }, + { "paddingEnd", 17 }, + { "layout_marginStart", 17 }, + { "layout_marginEnd", 17 }, + { "layout_toStartOf", 17 }, + { "layout_toEndOf", 17 }, + { "layout_alignStart", 17 }, + { "layout_alignEnd", 17 }, + { "layout_alignParentStart", 17 }, + { "layout_alignParentEnd", 17 }, + { "listPreferredItemPaddingStart", 17 }, + { "listPreferredItemPaddingEnd", 17 }, + { "singleUser", 17 }, + { "presentationTheme", 17 }, + { "subtypeId", 17 }, + { "initialKeyguardLayout", 17 }, + { "widgetCategory", 17 }, + { "permissionGroupFlags", 17 }, + { "labelFor", 17 }, + { "permissionFlags", 17 }, + { "checkedTextViewStyle", 17 }, + { "showOnLockScreen", 17 }, + { "format12Hour", 17 }, + { "format24Hour", 17 }, + { "timeZone", 17 }, + { "mipMap", 18 }, + { "mirrorForRtl", 18 }, + { "windowOverscan", 18 }, + { "requiredForAllUsers", 18 }, + { "indicatorStart", 18 }, + { "indicatorEnd", 18 }, + { "childIndicatorStart", 18 }, + { "childIndicatorEnd", 18 }, + { "restrictedAccountType", 18 }, + { "requiredAccountType", 18 }, + { "canRequestTouchExplorationMode", 18 }, + { "canRequestEnhancedWebAccessibility", 18 }, + { "canRequestFilterKeyEvents", 18 }, + { "layoutMode", 18 }, + { "keySet", 19 }, + { "targetId", 19 }, + { "fromScene", 19 }, + { "toScene", 19 }, + { "transition", 19 }, + { "transitionOrdering", 19 }, + { "fadingMode", 19 }, + { "startDelay", 19 }, + { "ssp", 19 }, + { "sspPrefix", 19 }, + { "sspPattern", 19 }, + { "addPrintersActivity", 19 }, + { "vendor", 19 }, + { "category", 19 }, + { "isAsciiCapable", 19 }, + { "autoMirrored", 19 }, + { "supportsSwitchingToNextInputMethod", 19 }, + { "requireDeviceUnlock", 19 }, + { "apduServiceBanner", 19 }, + { "accessibilityLiveRegion", 19 }, + { "windowTranslucentStatus", 19 }, + { "windowTranslucentNavigation", 19 }, + { "advancedPrintOptionsActivity", 19 }, + { "banner", 20 }, + { "windowSwipeToDismiss", 20 }, + { "isGame", 20 }, + { "allowEmbedded", 20 }, + { "setupActivity", 20 }, + { "fastScrollStyle", 21 }, + { "windowContentTransitions", 21 }, + { "windowContentTransitionManager", 21 }, + { "translationZ", 21 }, + { "tintMode", 21 }, + { "controlX1", 21 }, + { "controlY1", 21 }, + { "controlX2", 21 }, + { "controlY2", 21 }, + { "transitionName", 21 }, + { "transitionGroup", 21 }, + { "viewportWidth", 21 }, + { "viewportHeight", 21 }, + { "fillColor", 21 }, + { "pathData", 21 }, + { "strokeColor", 21 }, + { "strokeWidth", 21 }, + { "trimPathStart", 21 }, + { "trimPathEnd", 21 }, + { "trimPathOffset", 21 }, + { "strokeLineCap", 21 }, + { "strokeLineJoin", 21 }, + { "strokeMiterLimit", 21 }, + { "colorControlNormal", 21 }, + { "colorControlActivated", 21 }, + { "colorButtonNormal", 21 }, + { "colorControlHighlight", 21 }, + { "persistableMode", 21 }, + { "titleTextAppearance", 21 }, + { "subtitleTextAppearance", 21 }, + { "slideEdge", 21 }, + { "actionBarTheme", 21 }, + { "textAppearanceListItemSecondary", 21 }, + { "colorPrimary", 21 }, + { "colorPrimaryDark", 21 }, + { "colorAccent", 21 }, + { "nestedScrollingEnabled", 21 }, + { "windowEnterTransition", 21 }, + { "windowExitTransition", 21 }, + { "windowSharedElementEnterTransition", 21 }, + { "windowSharedElementExitTransition", 21 }, + { "windowAllowReturnTransitionOverlap", 21 }, + { "windowAllowEnterTransitionOverlap", 21 }, + { "sessionService", 21 }, + { "stackViewStyle", 21 }, + { "switchStyle", 21 }, + { "elevation", 21 }, + { "excludeId", 21 }, + { "excludeClass", 21 }, + { "hideOnContentScroll", 21 }, + { "actionOverflowMenuStyle", 21 }, + { "documentLaunchMode", 21 }, + { "maxRecents", 21 }, + { "autoRemoveFromRecents", 21 }, + { "stateListAnimator", 21 }, + { "toId", 21 }, + { "fromId", 21 }, + { "reversible", 21 }, + { "splitTrack", 21 }, + { "targetName", 21 }, + { "excludeName", 21 }, + { "matchOrder", 21 }, + { "windowDrawsSystemBarBackgrounds", 21 }, + { "statusBarColor", 21 }, + { "navigationBarColor", 21 }, + { "contentInsetStart", 21 }, + { "contentInsetEnd", 21 }, + { "contentInsetLeft", 21 }, + { "contentInsetRight", 21 }, + { "paddingMode", 21 }, + { "layout_rowWeight", 21 }, + { "layout_columnWeight", 21 }, + { "translateX", 21 }, + { "translateY", 21 }, + { "selectableItemBackgroundBorderless", 21 }, + { "elegantTextHeight", 21 }, + { "searchKeyphraseId", 21 }, + { "searchKeyphrase", 21 }, + { "searchKeyphraseSupportedLocales", 21 }, + { "windowTransitionBackgroundFadeDuration", 21 }, + { "overlapAnchor", 21 }, + { "progressTint", 21 }, + { "progressTintMode", 21 }, + { "progressBackgroundTint", 21 }, + { "progressBackgroundTintMode", 21 }, + { "secondaryProgressTint", 21 }, + { "secondaryProgressTintMode", 21 }, + { "indeterminateTint", 21 }, + { "indeterminateTintMode", 21 }, + { "backgroundTint", 21 }, + { "backgroundTintMode", 21 }, + { "foregroundTint", 21 }, + { "foregroundTintMode", 21 }, + { "buttonTint", 21 }, + { "buttonTintMode", 21 }, + { "thumbTint", 21 }, + { "thumbTintMode", 21 }, + { "fullBackupOnly", 21 }, + { "propertyXName", 21 }, + { "propertyYName", 21 }, + { "relinquishTaskIdentity", 21 }, + { "tileModeX", 21 }, + { "tileModeY", 21 }, + { "actionModeShareDrawable", 21 }, + { "actionModeFindDrawable", 21 }, + { "actionModeWebSearchDrawable", 21 }, + { "transitionVisibilityMode", 21 }, + { "minimumHorizontalAngle", 21 }, + { "minimumVerticalAngle", 21 }, + { "maximumAngle", 21 }, + { "searchViewStyle", 21 }, + { "closeIcon", 21 }, + { "goIcon", 21 }, + { "searchIcon", 21 }, + { "voiceIcon", 21 }, + { "commitIcon", 21 }, + { "suggestionRowLayout", 21 }, + { "queryBackground", 21 }, + { "submitBackground", 21 }, + { "buttonBarPositiveButtonStyle", 21 }, + { "buttonBarNeutralButtonStyle", 21 }, + { "buttonBarNegativeButtonStyle", 21 }, + { "popupElevation", 21 }, + { "actionBarPopupTheme", 21 }, + { "multiArch", 21 }, + { "touchscreenBlocksFocus", 21 }, + { "windowElevation", 21 }, + { "launchTaskBehindTargetAnimation", 21 }, + { "launchTaskBehindSourceAnimation", 21 }, + { "restrictionType", 21 }, + { "dayOfWeekBackground", 21 }, + { "dayOfWeekTextAppearance", 21 }, + { "headerMonthTextAppearance", 21 }, + { "headerDayOfMonthTextAppearance", 21 }, + { "headerYearTextAppearance", 21 }, + { "yearListItemTextAppearance", 21 }, + { "yearListSelectorColor", 21 }, + { "calendarTextColor", 21 }, + { "recognitionService", 21 }, + { "timePickerStyle", 21 }, + { "timePickerDialogTheme", 21 }, + { "headerTimeTextAppearance", 21 }, + { "headerAmPmTextAppearance", 21 }, + { "numbersTextColor", 21 }, + { "numbersBackgroundColor", 21 }, + { "numbersSelectorColor", 21 }, + { "amPmTextColor", 21 }, + { "amPmBackgroundColor", 21 }, + { "searchKeyphraseRecognitionFlags", 21 }, + { "checkMarkTint", 21 }, + { "checkMarkTintMode", 21 }, + { "popupTheme", 21 }, + { "toolbarStyle", 21 }, + { "windowClipToOutline", 21 }, + { "datePickerDialogTheme", 21 }, + { "showText", 21 }, + { "windowReturnTransition", 21 }, + { "windowReenterTransition", 21 }, + { "windowSharedElementReturnTransition", 21 }, + { "windowSharedElementReenterTransition", 21 }, + { "resumeWhilePausing", 21 }, + { "datePickerMode", 21 }, + { "timePickerMode", 21 }, + { "inset", 21 }, + { "letterSpacing", 21 }, + { "fontFeatureSettings", 21 }, + { "outlineProvider", 21 }, + { "contentAgeHint", 21 }, + { "country", 21 }, + { "windowSharedElementsUseOverlay", 21 }, + { "reparent", 21 }, + { "reparentWithOverlay", 21 }, + { "ambientShadowAlpha", 21 }, + { "spotShadowAlpha", 21 }, + { "navigationIcon", 21 }, + { "navigationContentDescription", 21 }, + { "fragmentExitTransition", 21 }, + { "fragmentEnterTransition", 21 }, + { "fragmentSharedElementEnterTransition", 21 }, + { "fragmentReturnTransition", 21 }, + { "fragmentSharedElementReturnTransition", 21 }, + { "fragmentReenterTransition", 21 }, + { "fragmentAllowEnterTransitionOverlap", 21 }, + { "fragmentAllowReturnTransitionOverlap", 21 }, + { "patternPathData", 21 }, + { "strokeAlpha", 21 }, + { "fillAlpha", 21 }, + { "windowActivityTransitions", 21 }, + { "colorEdgeEffect", 21 } }; size_t findAttributeSdkLevel(const ResourceName& name) { - if (name.package != u"android" && name.type != ResourceType::kAttr) { + if (name.package != "android" && name.type != ResourceType::kAttr) { return 0; } @@ -735,4 +738,8 @@ size_t findAttributeSdkLevel(const ResourceName& name) { return SDK_LOLLIPOP_MR1; } +std::pair<StringPiece, int> getDevelopmentSdkCodeNameAndVersion() { + return std::make_pair(StringPiece(sDevelopmentSdkCodeName), sDevelopmentSdkLevel); +} + } // namespace aapt diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h index 282ed9a56f5c..f28679fde0de 100644 --- a/tools/aapt2/SdkConstants.h +++ b/tools/aapt2/SdkConstants.h @@ -19,6 +19,8 @@ #include "Resource.h" +#include <utility> + namespace aapt { enum { @@ -47,6 +49,7 @@ enum { size_t findAttributeSdkLevel(ResourceId id); size_t findAttributeSdkLevel(const ResourceName& name); +std::pair<StringPiece, int> getDevelopmentSdkCodeNameAndVersion(); } // namespace aapt diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp index aadb00b6be2a..fe4b96722118 100644 --- a/tools/aapt2/StringPool.cpp +++ b/tools/aapt2/StringPool.cpp @@ -59,11 +59,11 @@ StringPool::Ref& StringPool::Ref::operator=(const StringPool::Ref& rhs) { return *this; } -const std::u16string* StringPool::Ref::operator->() const { +const std::string* StringPool::Ref::operator->() const { return &mEntry->value; } -const std::u16string& StringPool::Ref::operator*() const { +const std::string& StringPool::Ref::operator*() const { return mEntry->value; } @@ -124,15 +124,15 @@ const StringPool::Context& StringPool::StyleRef::getContext() const { return mEntry->str.getContext(); } -StringPool::Ref StringPool::makeRef(const StringPiece16& str) { +StringPool::Ref StringPool::makeRef(const StringPiece& str) { return makeRefImpl(str, Context{}, true); } -StringPool::Ref StringPool::makeRef(const StringPiece16& str, const Context& context) { +StringPool::Ref StringPool::makeRef(const StringPiece& str, const Context& context) { return makeRefImpl(str, context, true); } -StringPool::Ref StringPool::makeRefImpl(const StringPiece16& str, const Context& context, +StringPool::Ref StringPool::makeRefImpl(const StringPiece& str, const Context& context, bool unique) { if (unique) { auto iter = mIndexedStrings.find(str); @@ -147,7 +147,7 @@ StringPool::Ref StringPool::makeRefImpl(const StringPiece16& str, const Context& entry->index = mStrings.size(); entry->ref = 0; mStrings.emplace_back(entry); - mIndexedStrings.insert(std::make_pair(StringPiece16(entry->value), entry)); + mIndexedStrings.insert(std::make_pair(StringPiece(entry->value), entry)); return Ref(entry); } @@ -162,13 +162,12 @@ StringPool::StyleRef StringPool::makeRef(const StyleString& str, const Context& entry->index = mStrings.size(); entry->ref = 0; mStrings.emplace_back(entry); - mIndexedStrings.insert(std::make_pair(StringPiece16(entry->value), entry)); + mIndexedStrings.insert(std::make_pair(StringPiece(entry->value), entry)); StyleEntry* styleEntry = new StyleEntry(); styleEntry->str = Ref(entry); for (const aapt::Span& span : str.spans) { - styleEntry->spans.emplace_back(Span{makeRef(span.name), - span.firstChar, span.lastChar}); + styleEntry->spans.emplace_back(Span{ makeRef(span.name), span.firstChar, span.lastChar }); } styleEntry->ref = 0; mStyles.emplace_back(styleEntry); @@ -182,7 +181,7 @@ StringPool::StyleRef StringPool::makeRef(const StyleRef& ref) { entry->index = mStrings.size(); entry->ref = 0; mStrings.emplace_back(entry); - mIndexedStrings.insert(std::make_pair(StringPiece16(entry->value), entry)); + mIndexedStrings.insert(std::make_pair(StringPiece(entry->value), entry)); StyleEntry* styleEntry = new StyleEntry(); styleEntry->str = Ref(entry); @@ -320,33 +319,40 @@ bool StringPool::flatten(BigBuffer* out, const StringPool& pool, bool utf8) { indices++; if (utf8) { - std::string encoded = util::utf16ToUtf8(entry->value); + const std::string& encoded = entry->value; + const ssize_t utf16Length = utf8_to_utf16_length( + reinterpret_cast<const uint8_t*>(entry->value.data()), entry->value.size()); + assert(utf16Length >= 0); - const size_t totalSize = encodedLengthUnits<char>(entry->value.size()) + const size_t totalSize = encodedLengthUnits<char>(utf16Length) + encodedLengthUnits<char>(encoded.length()) + encoded.size() + 1; char* data = out->nextBlock<char>(totalSize); - // First encode the actual UTF16 string length. - data = encodeLength(data, entry->value.size()); + // First encode the UTF16 string length. + data = encodeLength(data, utf16Length); - // Now encode the size of the converted UTF8 string. + // Now encode the size of the real UTF8 string. data = encodeLength(data, encoded.length()); strncpy(data, encoded.data(), encoded.size()); + } else { - const size_t totalSize = encodedLengthUnits<char16_t>(entry->value.size()) - + entry->value.size() + 1; + const std::u16string encoded = util::utf8ToUtf16(entry->value); + const ssize_t utf16Length = encoded.size(); + + // Total number of 16-bit words to write. + const size_t totalSize = encodedLengthUnits<char16_t>(utf16Length) + encoded.size() + 1; char16_t* data = out->nextBlock<char16_t>(totalSize); // Encode the actual UTF16 string length. - data = encodeLength(data, entry->value.size()); - const size_t byteLength = entry->value.size() * sizeof(char16_t); + data = encodeLength(data, utf16Length); + const size_t byteLength = encoded.size() * sizeof(char16_t); // NOTE: For some reason, strncpy16(data, entry->value.data(), entry->value.size()) // truncates the string. - memcpy(data, entry->value.data(), byteLength); + memcpy(data, encoded.data(), byteLength); // The null-terminating character is already here due to the block of data being set // to 0s on allocation. diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h index 509e3041e081..72ae9d184a78 100644 --- a/tools/aapt2/StringPool.h +++ b/tools/aapt2/StringPool.h @@ -22,7 +22,7 @@ #include "util/StringPiece.h" #include <functional> -#include <map> +#include <unordered_map> #include <memory> #include <string> #include <vector> @@ -30,13 +30,13 @@ namespace aapt { struct Span { - std::u16string name; + std::string name; uint32_t firstChar; uint32_t lastChar; }; struct StyleString { - std::u16string str; + std::string str; std::vector<Span> spans; }; @@ -56,8 +56,8 @@ public: ~Ref(); Ref& operator=(const Ref& rhs); - const std::u16string* operator->() const; - const std::u16string& operator*() const; + const std::string* operator->() const; + const std::string& operator*() const; size_t getIndex() const; const Context& getContext() const; @@ -95,7 +95,7 @@ public: class Entry { public: - std::u16string value; + std::string value; Context context; size_t index; @@ -136,14 +136,14 @@ public: * Adds a string to the pool, unless it already exists. Returns * a reference to the string in the pool. */ - Ref makeRef(const StringPiece16& str); + Ref makeRef(const StringPiece& str); /** * Adds a string to the pool, unless it already exists, with a context * object that can be used when sorting the string pool. Returns * a reference to the string in the pool. */ - Ref makeRef(const StringPiece16& str, const Context& context); + Ref makeRef(const StringPiece& str, const Context& context); /** * Adds a style to the string pool and returns a reference to it. @@ -195,11 +195,11 @@ private: static bool flatten(BigBuffer* out, const StringPool& pool, bool utf8); - Ref makeRefImpl(const StringPiece16& str, const Context& context, bool unique); + Ref makeRefImpl(const StringPiece& str, const Context& context, bool unique); std::vector<std::unique_ptr<Entry>> mStrings; std::vector<std::unique_ptr<StyleEntry>> mStyles; - std::multimap<StringPiece16, Entry*> mIndexedStrings; + std::unordered_multimap<StringPiece, Entry*> mIndexedStrings; }; // diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp index 2b2d348fd17c..1367af72e4c1 100644 --- a/tools/aapt2/StringPool_test.cpp +++ b/tools/aapt2/StringPool_test.cpp @@ -15,9 +15,9 @@ */ #include "StringPool.h" +#include "test/Test.h" #include "util/Util.h" -#include <gtest/gtest.h> #include <string> namespace aapt { @@ -25,37 +25,37 @@ namespace aapt { TEST(StringPoolTest, InsertOneString) { StringPool pool; - StringPool::Ref ref = pool.makeRef(u"wut"); - EXPECT_EQ(*ref, u"wut"); + StringPool::Ref ref = pool.makeRef("wut"); + EXPECT_EQ(*ref, "wut"); } TEST(StringPoolTest, InsertTwoUniqueStrings) { StringPool pool; - StringPool::Ref ref = pool.makeRef(u"wut"); - StringPool::Ref ref2 = pool.makeRef(u"hey"); + StringPool::Ref ref = pool.makeRef("wut"); + StringPool::Ref ref2 = pool.makeRef("hey"); - EXPECT_EQ(*ref, u"wut"); - EXPECT_EQ(*ref2, u"hey"); + EXPECT_EQ(*ref, "wut"); + EXPECT_EQ(*ref2, "hey"); } TEST(StringPoolTest, DoNotInsertNewDuplicateString) { StringPool pool; - StringPool::Ref ref = pool.makeRef(u"wut"); - StringPool::Ref ref2 = pool.makeRef(u"wut"); + StringPool::Ref ref = pool.makeRef("wut"); + StringPool::Ref ref2 = pool.makeRef("wut"); - EXPECT_EQ(*ref, u"wut"); - EXPECT_EQ(*ref2, u"wut"); + EXPECT_EQ(*ref, "wut"); + EXPECT_EQ(*ref2, "wut"); EXPECT_EQ(1u, pool.size()); } TEST(StringPoolTest, MaintainInsertionOrderIndex) { StringPool pool; - StringPool::Ref ref = pool.makeRef(u"z"); - StringPool::Ref ref2 = pool.makeRef(u"a"); - StringPool::Ref ref3 = pool.makeRef(u"m"); + StringPool::Ref ref = pool.makeRef("z"); + StringPool::Ref ref2 = pool.makeRef("a"); + StringPool::Ref ref3 = pool.makeRef("m"); EXPECT_EQ(0u, ref.getIndex()); EXPECT_EQ(1u, ref2.getIndex()); @@ -65,39 +65,39 @@ TEST(StringPoolTest, MaintainInsertionOrderIndex) { TEST(StringPoolTest, PruneStringsWithNoReferences) { StringPool pool; - StringPool::Ref refA = pool.makeRef(u"foo"); + StringPool::Ref refA = pool.makeRef("foo"); { - StringPool::Ref ref = pool.makeRef(u"wut"); - EXPECT_EQ(*ref, u"wut"); + StringPool::Ref ref = pool.makeRef("wut"); + EXPECT_EQ(*ref, "wut"); EXPECT_EQ(2u, pool.size()); } - StringPool::Ref refB = pool.makeRef(u"bar"); + StringPool::Ref refB = pool.makeRef("bar"); EXPECT_EQ(3u, pool.size()); pool.prune(); EXPECT_EQ(2u, pool.size()); StringPool::const_iterator iter = begin(pool); - EXPECT_EQ((*iter)->value, u"foo"); + EXPECT_EQ((*iter)->value, "foo"); EXPECT_LT((*iter)->index, 2u); ++iter; - EXPECT_EQ((*iter)->value, u"bar"); + EXPECT_EQ((*iter)->value, "bar"); EXPECT_LT((*iter)->index, 2u); } TEST(StringPoolTest, SortAndMaintainIndexesInReferences) { StringPool pool; - StringPool::Ref ref = pool.makeRef(u"z"); - StringPool::StyleRef ref2 = pool.makeRef(StyleString{ {u"a"} }); - StringPool::Ref ref3 = pool.makeRef(u"m"); + StringPool::Ref ref = pool.makeRef("z"); + StringPool::StyleRef ref2 = pool.makeRef(StyleString{ {"a"} }); + StringPool::Ref ref3 = pool.makeRef("m"); - EXPECT_EQ(*ref, u"z"); + EXPECT_EQ(*ref, "z"); EXPECT_EQ(0u, ref.getIndex()); - EXPECT_EQ(*(ref2->str), u"a"); + EXPECT_EQ(*(ref2->str), "a"); EXPECT_EQ(1u, ref2.getIndex()); - EXPECT_EQ(*ref3, u"m"); + EXPECT_EQ(*ref3, "m"); EXPECT_EQ(2u, ref3.getIndex()); pool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool { @@ -105,30 +105,30 @@ TEST(StringPoolTest, SortAndMaintainIndexesInReferences) { }); - EXPECT_EQ(*ref, u"z"); + EXPECT_EQ(*ref, "z"); EXPECT_EQ(2u, ref.getIndex()); - EXPECT_EQ(*(ref2->str), u"a"); + EXPECT_EQ(*(ref2->str), "a"); EXPECT_EQ(0u, ref2.getIndex()); - EXPECT_EQ(*ref3, u"m"); + EXPECT_EQ(*ref3, "m"); EXPECT_EQ(1u, ref3.getIndex()); } TEST(StringPoolTest, SortAndStillDedupe) { StringPool pool; - StringPool::Ref ref = pool.makeRef(u"z"); - StringPool::Ref ref2 = pool.makeRef(u"a"); - StringPool::Ref ref3 = pool.makeRef(u"m"); + StringPool::Ref ref = pool.makeRef("z"); + StringPool::Ref ref2 = pool.makeRef("a"); + StringPool::Ref ref3 = pool.makeRef("m"); pool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool { return a.value < b.value; }); - StringPool::Ref ref4 = pool.makeRef(u"z"); - StringPool::Ref ref5 = pool.makeRef(u"a"); - StringPool::Ref ref6 = pool.makeRef(u"m"); + StringPool::Ref ref4 = pool.makeRef("z"); + StringPool::Ref ref5 = pool.makeRef("a"); + StringPool::Ref ref6 = pool.makeRef("m"); EXPECT_EQ(ref4.getIndex(), ref.getIndex()); EXPECT_EQ(ref5.getIndex(), ref2.getIndex()); @@ -139,20 +139,20 @@ TEST(StringPoolTest, AddStyles) { StringPool pool; StyleString str { - { u"android" }, + { "android" }, { - Span{ { u"b" }, 2, 6 } + Span{ { "b" }, 2, 6 } } }; StringPool::StyleRef ref = pool.makeRef(str); EXPECT_EQ(0u, ref.getIndex()); - EXPECT_EQ(std::u16string(u"android"), *(ref->str)); + EXPECT_EQ(std::string("android"), *(ref->str)); ASSERT_EQ(1u, ref->spans.size()); const StringPool::Span& span = ref->spans.front(); - EXPECT_EQ(*(span.name), u"b"); + EXPECT_EQ(*(span.name), "b"); EXPECT_EQ(2u, span.firstChar); EXPECT_EQ(6u, span.lastChar); } @@ -160,9 +160,9 @@ TEST(StringPoolTest, AddStyles) { TEST(StringPoolTest, DoNotDedupeStyleWithSameStringAsNonStyle) { StringPool pool; - StringPool::Ref ref = pool.makeRef(u"android"); + StringPool::Ref ref = pool.makeRef("android"); - StyleString str { { u"android" } }; + StyleString str { { "android" } }; StringPool::StyleRef styleRef = pool.makeRef(str); EXPECT_NE(ref.getIndex(), styleRef.getIndex()); @@ -184,7 +184,7 @@ TEST(StringPoolTest, FlattenOddCharactersUtf16) { using namespace android; // For NO_ERROR on Windows. StringPool pool; - pool.makeRef(u"\u093f"); + pool.makeRef("\u093f"); BigBuffer buffer(1024); StringPool::flattenUtf16(&buffer, pool); @@ -198,48 +198,65 @@ TEST(StringPoolTest, FlattenOddCharactersUtf16) { EXPECT_EQ(0u, str[1]); } -constexpr const char16_t* sLongString = u"バッテリーを長持ちさせるため、バッテリーセーバーは端末のパフォーマンスを抑え、バイブレーション、位置情報サービス、大半のバックグラウンドデータを制限します。メール、SMSや、同期を使 用するその他のアプリは、起動しても更新されないことがあります。バッテリーセーバーは端末の充電中は自動的にOFFになります。"; +constexpr const char* sLongString = "バッテリーを長持ちさせるため、バッテリーセーバーは端末のパフォーマンスを抑え、バイブレーション、位置情報サービス、大半のバックグラウンドデータを制限します。メール、SMSや、同期を使 用するその他のアプリは、起動しても更新されないことがあります。バッテリーセーバーは端末の充電中は自動的にOFFになります。"; -TEST(StringPoolTest, FlattenUtf8) { +TEST(StringPoolTest, Flatten) { using namespace android; // For NO_ERROR on Windows. StringPool pool; - StringPool::Ref ref1 = pool.makeRef(u"hello"); - StringPool::Ref ref2 = pool.makeRef(u"goodbye"); + StringPool::Ref ref1 = pool.makeRef("hello"); + StringPool::Ref ref2 = pool.makeRef("goodbye"); StringPool::Ref ref3 = pool.makeRef(sLongString); - StringPool::StyleRef ref4 = pool.makeRef(StyleString{ - { u"style" }, - { Span{ { u"b" }, 0, 1 }, Span{ { u"i" }, 2, 3 } } + StringPool::Ref ref4 = pool.makeRef(""); + StringPool::StyleRef ref5 = pool.makeRef(StyleString{ + { "style" }, + { Span{ { "b" }, 0, 1 }, Span{ { "i" }, 2, 3 } } }); EXPECT_EQ(0u, ref1.getIndex()); EXPECT_EQ(1u, ref2.getIndex()); EXPECT_EQ(2u, ref3.getIndex()); EXPECT_EQ(3u, ref4.getIndex()); + EXPECT_EQ(4u, ref5.getIndex()); - BigBuffer buffer(1024); - StringPool::flattenUtf8(&buffer, pool); + BigBuffer buffers[2] = { BigBuffer(1024), BigBuffer(1024) }; + StringPool::flattenUtf8(&buffers[0], pool); + StringPool::flattenUtf16(&buffers[1], pool); + + // Test both UTF-8 and UTF-16 buffers. + for (const BigBuffer& buffer : buffers) { + std::unique_ptr<uint8_t[]> data = util::copy(buffer); - std::unique_ptr<uint8_t[]> data = util::copy(buffer); - { ResStringPool test; ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR); - EXPECT_EQ(util::getString(test, 0), u"hello"); - EXPECT_EQ(util::getString(test, 1), u"goodbye"); - EXPECT_EQ(util::getString(test, 2), sLongString); - EXPECT_EQ(util::getString(test, 3), u"style"); + EXPECT_EQ(std::string("hello"), util::getString(test, 0)); + EXPECT_EQ(StringPiece16(u"hello"), util::getString16(test, 0)); + + EXPECT_EQ(std::string("goodbye"), util::getString(test, 1)); + EXPECT_EQ(StringPiece16(u"goodbye"), util::getString16(test, 1)); + + EXPECT_EQ(StringPiece(sLongString), util::getString(test, 2)); + EXPECT_EQ(util::utf8ToUtf16(sLongString), util::getString16(test, 2).toString()); + + size_t len; + EXPECT_TRUE(test.stringAt(3, &len) != nullptr || test.string8At(3, &len) != nullptr); + + EXPECT_EQ(std::string("style"), util::getString(test, 4)); + EXPECT_EQ(StringPiece16(u"style"), util::getString16(test, 4)); - const ResStringPool_span* span = test.styleAt(3); + const ResStringPool_span* span = test.styleAt(4); ASSERT_NE(nullptr, span); - EXPECT_EQ(util::getString(test, span->name.index), u"b"); + EXPECT_EQ(std::string("b"), util::getString(test, span->name.index)); + EXPECT_EQ(StringPiece16(u"b"), util::getString16(test, span->name.index)); EXPECT_EQ(0u, span->firstChar); EXPECT_EQ(1u, span->lastChar); span++; ASSERT_NE(ResStringPool_span::END, span->name.index); - EXPECT_EQ(util::getString(test, span->name.index), u"i"); + EXPECT_EQ(std::string("i"), util::getString(test, span->name.index)); + EXPECT_EQ(StringPiece16(u"i"), util::getString16(test, span->name.index)); EXPECT_EQ(2u, span->firstChar); EXPECT_EQ(3u, span->lastChar); span++; diff --git a/tools/aapt2/ValueVisitor_test.cpp b/tools/aapt2/ValueVisitor_test.cpp index 1624079727bb..11eab3323576 100644 --- a/tools/aapt2/ValueVisitor_test.cpp +++ b/tools/aapt2/ValueVisitor_test.cpp @@ -14,13 +14,12 @@ * limitations under the License. */ -#include <gtest/gtest.h> -#include <string> - #include "ResourceValues.h" -#include "util/Util.h" #include "ValueVisitor.h" -#include "test/Builders.h" +#include "test/Test.h" +#include "util/Util.h" + +#include <string> namespace aapt { @@ -51,7 +50,7 @@ struct StyleVisitor : public ValueVisitor { }; TEST(ValueVisitorTest, VisitsReference) { - Reference ref(ResourceName{u"android", ResourceType::kAttr, u"foo"}); + Reference ref(ResourceName{"android", ResourceType::kAttr, "foo"}); SingleReferenceVisitor visitor; ref.accept(&visitor); @@ -60,8 +59,8 @@ TEST(ValueVisitorTest, VisitsReference) { TEST(ValueVisitorTest, VisitsReferencesInStyle) { std::unique_ptr<Style> style = test::StyleBuilder() - .setParent(u"@android:style/foo") - .addItem(u"@android:attr/one", test::buildReference(u"@android:id/foo")) + .setParent("@android:style/foo") + .addItem("@android:attr/one", test::buildReference("@android:id/foo")) .build(); StyleVisitor visitor; @@ -74,11 +73,11 @@ TEST(ValueVisitorTest, VisitsReferencesInStyle) { } TEST(ValueVisitorTest, ValueCast) { - std::unique_ptr<Reference> ref = test::buildReference(u"@android:color/white"); + std::unique_ptr<Reference> ref = test::buildReference("@android:color/white"); EXPECT_NE(valueCast<Reference>(ref.get()), nullptr); std::unique_ptr<Style> style = test::StyleBuilder() - .addItem(u"@android:attr/foo", test::buildReference(u"@android:color/black")) + .addItem("@android:attr/foo", test::buildReference("@android:color/black")) .build(); EXPECT_NE(valueCast<Style>(style.get()), nullptr); EXPECT_EQ(valueCast<Reference>(style.get()), nullptr); diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp index 2452a1d29410..39e4489ffda2 100644 --- a/tools/aapt2/compile/Compile.cpp +++ b/tools/aapt2/compile/Compile.cpp @@ -43,8 +43,8 @@ namespace aapt { struct ResourcePathData { Source source; - std::u16string resourceDir; - std::u16string name; + std::string resourceDir; + std::string name; std::string extension; // Original config str. We keep this because when we parse the config, we may add on @@ -96,8 +96,8 @@ static Maybe<ResourcePathData> extractResourcePathData(const std::string& path, return ResourcePathData{ Source(path), - util::utf8ToUtf16(dirStr), - util::utf8ToUtf16(name), + dirStr.toString(), + name.toString(), extension.toString(), configStr.toString(), config @@ -127,7 +127,7 @@ static std::string buildIntermediateFilename(const ResourcePathData& data) { } static bool isHidden(const StringPiece& filename) { - return util::stringStartsWith<char>(filename, "."); + return util::stringStartsWith(filename, "."); } /** @@ -200,7 +200,7 @@ static bool compileTable(IAaptContext* context, const CompileOptions& options, parserOptions.errorOnPositionalArguments = !options.legacyMode; // If the filename includes donottranslate, then the default translatable is false. - parserOptions.translatable = pathData.name.find(u"donottranslate") == std::string::npos; + parserOptions.translatable = pathData.name.find("donottranslate") == std::string::npos; ResourceParser resParser(context->getDiagnostics(), &table, pathData.source, pathData.config, parserOptions); @@ -440,8 +440,8 @@ public: return nullptr; } - const std::u16string& getCompilationPackage() override { - static std::u16string empty; + const std::string& getCompilationPackage() override { + static std::string empty; return empty; } @@ -454,6 +454,10 @@ public: return nullptr; } + int getMinSdkVersion() override { + return 0; + } + private: StdErrDiagnostics mDiagnostics; bool mVerbose = false; @@ -526,7 +530,7 @@ int compile(const std::vector<StringPiece>& args) { context.getDiagnostics()->note(DiagMessage(pathData.source) << "processing"); } - if (pathData.resourceDir == u"values") { + if (pathData.resourceDir == "values") { // Overwrite the extension. pathData.extension = "arsc"; diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp index aa4a5803b8df..341c9b3d5107 100644 --- a/tools/aapt2/compile/IdAssigner.cpp +++ b/tools/aapt2/compile/IdAssigner.cpp @@ -15,7 +15,6 @@ */ #include "ResourceTable.h" - #include "compile/IdAssigner.h" #include "process/IResourceTableConsumer.h" #include "util/Util.h" diff --git a/tools/aapt2/compile/IdAssigner_test.cpp b/tools/aapt2/compile/IdAssigner_test.cpp index e25a17ab125e..802e99a4640b 100644 --- a/tools/aapt2/compile/IdAssigner_test.cpp +++ b/tools/aapt2/compile/IdAssigner_test.cpp @@ -27,10 +27,10 @@ namespace aapt { TEST(IdAssignerTest, AssignIds) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .addSimple(u"@android:attr/foo") - .addSimple(u"@android:attr/bar") - .addSimple(u"@android:id/foo") - .setPackageId(u"android", 0x01) + .addSimple("@android:attr/foo") + .addSimple("@android:attr/bar") + .addSimple("@android:id/foo") + .setPackageId("android", 0x01) .build(); std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); @@ -42,12 +42,12 @@ TEST(IdAssignerTest, AssignIds) { TEST(IdAssignerTest, AssignIdsWithReservedIds) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .addSimple(u"@android:attr/foo", ResourceId(0x01040006)) - .addSimple(u"@android:attr/bar") - .addSimple(u"@android:id/foo") - .addSimple(u"@app:id/biz") - .setPackageId(u"android", 0x01) - .setPackageId(u"app", 0x7f) + .addSimple("@android:attr/foo", ResourceId(0x01040006)) + .addSimple("@android:attr/bar") + .addSimple("@android:id/foo") + .addSimple("@app:id/biz") + .setPackageId("android", 0x01) + .setPackageId("app", 0x7f) .build(); std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); @@ -59,10 +59,10 @@ TEST(IdAssignerTest, AssignIdsWithReservedIds) { TEST(IdAssignerTest, FailWhenNonUniqueIdsAssigned) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .addSimple(u"@android:attr/foo", ResourceId(0x01040006)) - .addSimple(u"@android:attr/bar", ResourceId(0x01040006)) - .setPackageId(u"android", 0x01) - .setPackageId(u"app", 0x7f) + .addSimple("@android:attr/foo", ResourceId(0x01040006)) + .addSimple("@android:attr/bar", ResourceId(0x01040006)) + .setPackageId("android", 0x01) + .setPackageId("app", 0x7f) .build(); std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); diff --git a/tools/aapt2/compile/Png.cpp b/tools/aapt2/compile/Png.cpp index bbf7f411d07e..055d8b5cf444 100644 --- a/tools/aapt2/compile/Png.cpp +++ b/tools/aapt2/compile/Png.cpp @@ -1234,7 +1234,7 @@ bool Png::process(const Source& source, std::istream* input, BigBuffer* outBuffe goto bail; } - if (util::stringEndsWith<char>(source.path, ".9.png")) { + if (util::stringEndsWith(source.path, ".9.png")) { std::string errorMsg; if (!do9Patch(&pngInfo, &errorMsg)) { mDiag->error(DiagMessage() << errorMsg); diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp index d080e16c520b..732101fe9c8c 100644 --- a/tools/aapt2/compile/PseudolocaleGenerator.cpp +++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp @@ -29,7 +29,7 @@ std::unique_ptr<StyledString> pseudolocalizeStyledString(StyledString* string, StringPool* pool) { Pseudolocalizer localizer(method); - const StringPiece16 originalText = *string->value->str; + const StringPiece originalText = *string->value->str; StyleString localized; @@ -147,7 +147,7 @@ struct Visitor : public RawValueVisitor { } void visit(String* string) override { - std::u16string result = mLocalizer.start() + mLocalizer.text(*string->value) + + std::string result = mLocalizer.start() + mLocalizer.text(*string->value) + mLocalizer.end(); std::unique_ptr<String> localized = util::make_unique<String>(mPool->makeRef(result)); localized->setSource(string->getSource()); diff --git a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp index 4cb6ea2db565..1816abc2cf3a 100644 --- a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp +++ b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp @@ -15,21 +15,18 @@ */ #include "compile/PseudolocaleGenerator.h" -#include "test/Builders.h" -#include "test/Common.h" -#include "test/Context.h" +#include "test/Test.h" #include "util/Util.h" #include <androidfw/ResourceTypes.h> -#include <gtest/gtest.h> namespace aapt { TEST(PseudolocaleGeneratorTest, PseudolocalizeStyledString) { StringPool pool; StyleString originalStyle; - originalStyle.str = u"Hello world!"; - originalStyle.spans = { Span{ u"b", 2, 3 }, Span{ u"b", 6, 7 }, Span{ u"i", 1, 10 } }; + originalStyle.str = "Hello world!"; + originalStyle.spans = { Span{ "b", 2, 3 }, Span{ "b", 6, 7 }, Span{ "i", 1, 10 } }; std::unique_ptr<StyledString> newString = pseudolocalizeStyledString( util::make_unique<StyledString>(pool.makeRef(originalStyle)).get(), @@ -38,51 +35,51 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizeStyledString) { EXPECT_EQ(originalStyle.str, *newString->value->str); ASSERT_EQ(originalStyle.spans.size(), newString->value->spans.size()); - EXPECT_EQ(2u, newString->value->spans[0].firstChar); - EXPECT_EQ(3u, newString->value->spans[0].lastChar); - EXPECT_EQ(std::u16string(u"b"), *newString->value->spans[0].name); + EXPECT_EQ(std::string("He").size(), newString->value->spans[0].firstChar); + EXPECT_EQ(std::string("Hel").size(), newString->value->spans[0].lastChar); + EXPECT_EQ(std::string("b"), *newString->value->spans[0].name); - EXPECT_EQ(6u, newString->value->spans[1].firstChar); - EXPECT_EQ(7u, newString->value->spans[1].lastChar); - EXPECT_EQ(std::u16string(u"b"), *newString->value->spans[1].name); + EXPECT_EQ(std::string("Hello ").size(), newString->value->spans[1].firstChar); + EXPECT_EQ(std::string("Hello w").size(), newString->value->spans[1].lastChar); + EXPECT_EQ(std::string("b"), *newString->value->spans[1].name); - EXPECT_EQ(1u, newString->value->spans[2].firstChar); - EXPECT_EQ(10u, newString->value->spans[2].lastChar); - EXPECT_EQ(std::u16string(u"i"), *newString->value->spans[2].name); + EXPECT_EQ(std::string("H").size(), newString->value->spans[2].firstChar); + EXPECT_EQ(std::string("Hello worl").size(), newString->value->spans[2].lastChar); + EXPECT_EQ(std::string("i"), *newString->value->spans[2].name); - originalStyle.spans.push_back(Span{ u"em", 0, 11u }); + originalStyle.spans.push_back(Span{ "em", 0, 11u }); newString = pseudolocalizeStyledString( util::make_unique<StyledString>(pool.makeRef(originalStyle)).get(), Pseudolocalizer::Method::kAccent, &pool); - EXPECT_EQ(std::u16string(u"[Ĥéļļö ŵöŕļð¡ one two]"), *newString->value->str); + EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð¡ one two]"), *newString->value->str); ASSERT_EQ(originalStyle.spans.size(), newString->value->spans.size()); - EXPECT_EQ(3u, newString->value->spans[0].firstChar); - EXPECT_EQ(4u, newString->value->spans[0].lastChar); + EXPECT_EQ(std::string("[Ĥé").size(), newString->value->spans[0].firstChar); + EXPECT_EQ(std::string("[Ĥéļ").size(), newString->value->spans[0].lastChar); - EXPECT_EQ(7u, newString->value->spans[1].firstChar); - EXPECT_EQ(8u, newString->value->spans[1].lastChar); + EXPECT_EQ(std::string("[Ĥéļļö ").size(), newString->value->spans[1].firstChar); + EXPECT_EQ(std::string("[Ĥéļļö ŵ").size(), newString->value->spans[1].lastChar); - EXPECT_EQ(2u, newString->value->spans[2].firstChar); - EXPECT_EQ(11u, newString->value->spans[2].lastChar); + EXPECT_EQ(std::string("[Ĥ").size(), newString->value->spans[2].firstChar); + EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļ").size(), newString->value->spans[2].lastChar); - EXPECT_EQ(1u, newString->value->spans[3].firstChar); - EXPECT_EQ(12u, newString->value->spans[3].lastChar); + EXPECT_EQ(std::string("[").size(), newString->value->spans[3].firstChar); + EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð").size(), newString->value->spans[3].lastChar); } TEST(PseudolocaleGeneratorTest, PseudolocalizeOnlyDefaultConfigs) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .addString(u"@android:string/one", u"one") - .addString(u"@android:string/two", ResourceId{}, test::parseConfigOrDie("en"), u"two") - .addString(u"@android:string/three", u"three") - .addString(u"@android:string/three", ResourceId{}, test::parseConfigOrDie("en-rXA"), - u"three") - .addString(u"@android:string/four", u"four") + .addString("@android:string/one", "one") + .addString("@android:string/two", ResourceId{}, test::parseConfigOrDie("en"), "two") + .addString("@android:string/three", "three") + .addString("@android:string/three", ResourceId{}, test::parseConfigOrDie("en-rXA"), + "three") + .addString("@android:string/four", "four") .build(); - String* val = test::getValue<String>(table.get(), u"@android:string/four"); + String* val = test::getValue<String>(table.get(), "@android:string/four"); val->setTranslateable(false); std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); @@ -90,31 +87,31 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizeOnlyDefaultConfigs) { ASSERT_TRUE(generator.consume(context.get(), table.get())); // Normal pseudolocalization should take place. - ASSERT_NE(nullptr, test::getValueForConfig<String>(table.get(), u"@android:string/one", + ASSERT_NE(nullptr, test::getValueForConfig<String>(table.get(), "@android:string/one", test::parseConfigOrDie("en-rXA"))); - ASSERT_NE(nullptr, test::getValueForConfig<String>(table.get(), u"@android:string/one", + ASSERT_NE(nullptr, test::getValueForConfig<String>(table.get(), "@android:string/one", test::parseConfigOrDie("ar-rXB"))); // No default config for android:string/two, so no pseudlocales should exist. - ASSERT_EQ(nullptr, test::getValueForConfig<String>(table.get(), u"@android:string/two", + ASSERT_EQ(nullptr, test::getValueForConfig<String>(table.get(), "@android:string/two", test::parseConfigOrDie("en-rXA"))); - ASSERT_EQ(nullptr, test::getValueForConfig<String>(table.get(), u"@android:string/two", + ASSERT_EQ(nullptr, test::getValueForConfig<String>(table.get(), "@android:string/two", test::parseConfigOrDie("ar-rXB"))); // Check that we didn't override manual pseudolocalization. - val = test::getValueForConfig<String>(table.get(), u"@android:string/three", + val = test::getValueForConfig<String>(table.get(), "@android:string/three", test::parseConfigOrDie("en-rXA")); ASSERT_NE(nullptr, val); - EXPECT_EQ(std::u16string(u"three"), *val->value); + EXPECT_EQ(std::string("three"), *val->value); - ASSERT_NE(nullptr, test::getValueForConfig<String>(table.get(), u"@android:string/three", + ASSERT_NE(nullptr, test::getValueForConfig<String>(table.get(), "@android:string/three", test::parseConfigOrDie("ar-rXB"))); // Check that four's translateable marker was honored. - ASSERT_EQ(nullptr, test::getValueForConfig<String>(table.get(), u"@android:string/four", + ASSERT_EQ(nullptr, test::getValueForConfig<String>(table.get(), "@android:string/four", test::parseConfigOrDie("en-rXA"))); - ASSERT_EQ(nullptr, test::getValueForConfig<String>(table.get(), u"@android:string/four", + ASSERT_EQ(nullptr, test::getValueForConfig<String>(table.get(), "@android:string/four", test::parseConfigOrDie("ar-rXB"))); } diff --git a/tools/aapt2/compile/Pseudolocalizer.cpp b/tools/aapt2/compile/Pseudolocalizer.cpp index eae52d778744..90d0d853acfa 100644 --- a/tools/aapt2/compile/Pseudolocalizer.cpp +++ b/tools/aapt2/compile/Pseudolocalizer.cpp @@ -20,41 +20,41 @@ namespace aapt { // String basis to generate expansion -static const std::u16string k_expansion_string = u"one two three " +static const std::string k_expansion_string = "one two three " "four five six seven eight nine ten eleven twelve thirteen " "fourteen fiveteen sixteen seventeen nineteen twenty"; // Special unicode characters to override directionality of the words -static const std::u16string k_rlm = u"\u200f"; -static const std::u16string k_rlo = u"\u202e"; -static const std::u16string k_pdf = u"\u202c"; +static const std::string k_rlm = "\u200f"; +static const std::string k_rlo = "\u202e"; +static const std::string k_pdf = "\u202c"; // Placeholder marks -static const std::u16string k_placeholder_open = u"\u00bb"; -static const std::u16string k_placeholder_close = u"\u00ab"; +static const std::string k_placeholder_open = "\u00bb"; +static const std::string k_placeholder_close = "\u00ab"; -static const char16_t k_arg_start = u'{'; -static const char16_t k_arg_end = u'}'; +static const char k_arg_start = '{'; +static const char k_arg_end = '}'; class PseudoMethodNone : public PseudoMethodImpl { public: - std::u16string text(const StringPiece16& text) override { return text.toString(); } - std::u16string placeholder(const StringPiece16& text) override { return text.toString(); } + std::string text(const StringPiece& text) override { return text.toString(); } + std::string placeholder(const StringPiece& text) override { return text.toString(); } }; class PseudoMethodBidi : public PseudoMethodImpl { public: - std::u16string text(const StringPiece16& text) override; - std::u16string placeholder(const StringPiece16& text) override; + std::string text(const StringPiece& text) override; + std::string placeholder(const StringPiece& text) override; }; class PseudoMethodAccent : public PseudoMethodImpl { public: PseudoMethodAccent() : mDepth(0), mWordCount(0), mLength(0) {} - std::u16string start() override; - std::u16string end() override; - std::u16string text(const StringPiece16& text) override; - std::u16string placeholder(const StringPiece16& text) override; + std::string start() override; + std::string end() override; + std::string text(const StringPiece& text) override; + std::string placeholder(const StringPiece& text) override; private: size_t mDepth; size_t mWordCount; @@ -79,12 +79,12 @@ void Pseudolocalizer::setMethod(Method method) { } } -std::u16string Pseudolocalizer::text(const StringPiece16& text) { - std::u16string out; +std::string Pseudolocalizer::text(const StringPiece& text) { + std::string out; size_t depth = mLastDepth; size_t lastpos, pos; const size_t length = text.size(); - const char16_t* str = text.data(); + const char* str = text.data(); bool escaped = false; for (lastpos = pos = 0; pos < length; pos++) { char16_t c = str[pos]; @@ -111,7 +111,7 @@ std::u16string Pseudolocalizer::text(const StringPiece16& text) { } size_t size = nextpos - lastpos; if (size) { - std::u16string chunk = text.substr(lastpos, size).toString(); + std::string chunk = text.substr(lastpos, size).toString(); if (pseudo) { chunk = mImpl->text(chunk); } else if (str[lastpos] == k_arg_start && str[nextpos - 1] == k_arg_end) { @@ -131,67 +131,67 @@ std::u16string Pseudolocalizer::text(const StringPiece16& text) { return out; } -static const char16_t* pseudolocalizeChar(const char16_t c) { +static const char* pseudolocalizeChar(const char c) { switch (c) { - case 'a': return u"\u00e5"; - case 'b': return u"\u0253"; - case 'c': return u"\u00e7"; - case 'd': return u"\u00f0"; - case 'e': return u"\u00e9"; - case 'f': return u"\u0192"; - case 'g': return u"\u011d"; - case 'h': return u"\u0125"; - case 'i': return u"\u00ee"; - case 'j': return u"\u0135"; - case 'k': return u"\u0137"; - case 'l': return u"\u013c"; - case 'm': return u"\u1e3f"; - case 'n': return u"\u00f1"; - case 'o': return u"\u00f6"; - case 'p': return u"\u00fe"; - case 'q': return u"\u0051"; - case 'r': return u"\u0155"; - case 's': return u"\u0161"; - case 't': return u"\u0163"; - case 'u': return u"\u00fb"; - case 'v': return u"\u0056"; - case 'w': return u"\u0175"; - case 'x': return u"\u0445"; - case 'y': return u"\u00fd"; - case 'z': return u"\u017e"; - case 'A': return u"\u00c5"; - case 'B': return u"\u03b2"; - case 'C': return u"\u00c7"; - case 'D': return u"\u00d0"; - case 'E': return u"\u00c9"; - case 'G': return u"\u011c"; - case 'H': return u"\u0124"; - case 'I': return u"\u00ce"; - case 'J': return u"\u0134"; - case 'K': return u"\u0136"; - case 'L': return u"\u013b"; - case 'M': return u"\u1e3e"; - case 'N': return u"\u00d1"; - case 'O': return u"\u00d6"; - case 'P': return u"\u00de"; - case 'Q': return u"\u0071"; - case 'R': return u"\u0154"; - case 'S': return u"\u0160"; - case 'T': return u"\u0162"; - case 'U': return u"\u00db"; - case 'V': return u"\u03bd"; - case 'W': return u"\u0174"; - case 'X': return u"\u00d7"; - case 'Y': return u"\u00dd"; - case 'Z': return u"\u017d"; - case '!': return u"\u00a1"; - case '?': return u"\u00bf"; - case '$': return u"\u20ac"; - default: return NULL; + case 'a': return "\u00e5"; + case 'b': return "\u0253"; + case 'c': return "\u00e7"; + case 'd': return "\u00f0"; + case 'e': return "\u00e9"; + case 'f': return "\u0192"; + case 'g': return "\u011d"; + case 'h': return "\u0125"; + case 'i': return "\u00ee"; + case 'j': return "\u0135"; + case 'k': return "\u0137"; + case 'l': return "\u013c"; + case 'm': return "\u1e3f"; + case 'n': return "\u00f1"; + case 'o': return "\u00f6"; + case 'p': return "\u00fe"; + case 'q': return "\u0051"; + case 'r': return "\u0155"; + case 's': return "\u0161"; + case 't': return "\u0163"; + case 'u': return "\u00fb"; + case 'v': return "\u0056"; + case 'w': return "\u0175"; + case 'x': return "\u0445"; + case 'y': return "\u00fd"; + case 'z': return "\u017e"; + case 'A': return "\u00c5"; + case 'B': return "\u03b2"; + case 'C': return "\u00c7"; + case 'D': return "\u00d0"; + case 'E': return "\u00c9"; + case 'G': return "\u011c"; + case 'H': return "\u0124"; + case 'I': return "\u00ce"; + case 'J': return "\u0134"; + case 'K': return "\u0136"; + case 'L': return "\u013b"; + case 'M': return "\u1e3e"; + case 'N': return "\u00d1"; + case 'O': return "\u00d6"; + case 'P': return "\u00de"; + case 'Q': return "\u0071"; + case 'R': return "\u0154"; + case 'S': return "\u0160"; + case 'T': return "\u0162"; + case 'U': return "\u00db"; + case 'V': return "\u03bd"; + case 'W': return "\u0174"; + case 'X': return "\u00d7"; + case 'Y': return "\u00dd"; + case 'Z': return "\u017d"; + case '!': return "\u00a1"; + case '?': return "\u00bf"; + case '$': return "\u20ac"; + default: return nullptr; } } -static bool isPossibleNormalPlaceholderEnd(const char16_t c) { +static bool isPossibleNormalPlaceholderEnd(const char c) { switch (c) { case 's': return true; case 'S': return true; @@ -218,11 +218,11 @@ static bool isPossibleNormalPlaceholderEnd(const char16_t c) { } } -static std::u16string pseudoGenerateExpansion(const unsigned int length) { - std::u16string result = k_expansion_string; - const char16_t* s = result.data(); +static std::string pseudoGenerateExpansion(const unsigned int length) { + std::string result = k_expansion_string; + const char* s = result.data(); if (result.size() < length) { - result += u" "; + result += " "; result += pseudoGenerateExpansion(length - result.size()); } else { int ext = 0; @@ -238,26 +238,26 @@ static std::u16string pseudoGenerateExpansion(const unsigned int length) { return result; } -std::u16string PseudoMethodAccent::start() { - std::u16string result; +std::string PseudoMethodAccent::start() { + std::string result; if (mDepth == 0) { - result = u"["; + result = "["; } mWordCount = mLength = 0; mDepth++; return result; } -std::u16string PseudoMethodAccent::end() { - std::u16string result; +std::string PseudoMethodAccent::end() { + std::string result; if (mLength) { - result += u" "; + result += " "; result += pseudoGenerateExpansion(mWordCount > 3 ? mLength : mLength / 2); } mWordCount = mLength = 0; mDepth--; if (mDepth == 0) { - result += u"]"; + result += "]"; } return result; } @@ -267,26 +267,26 @@ std::u16string PseudoMethodAccent::end() { * * Note: This leaves placeholder syntax untouched. */ -std::u16string PseudoMethodAccent::text(const StringPiece16& source) +std::string PseudoMethodAccent::text(const StringPiece& source) { - const char16_t* s = source.data(); - std::u16string result; + const char* s = source.data(); + std::string result; const size_t I = source.size(); bool lastspace = true; for (size_t i = 0; i < I; i++) { - char16_t c = s[i]; + char c = s[i]; if (c == '%') { // Placeholder syntax, no need to pseudolocalize - std::u16string chunk; + std::string chunk; bool end = false; chunk.append(&c, 1); - while (!end && i < I) { + while (!end && i + 1 < I) { ++i; c = s[i]; chunk.append(&c, 1); if (isPossibleNormalPlaceholderEnd(c)) { end = true; - } else if (c == 't') { + } else if (i + 1 < I && c == 't') { ++i; c = s[i]; chunk.append(&c, 1); @@ -300,7 +300,7 @@ std::u16string PseudoMethodAccent::text(const StringPiece16& source) bool tag_closed = false; while (!tag_closed && i < I) { if (c == '&') { - std::u16string escapeText; + std::string escapeText; escapeText.append(&c, 1); bool end = false; size_t htmlCodePos = i; @@ -322,7 +322,7 @@ std::u16string PseudoMethodAccent::text(const StringPiece16& source) } } result += escapeText; - if (escapeText != u"<") { + if (escapeText != "<") { tag_closed = true; } continue; @@ -338,11 +338,11 @@ std::u16string PseudoMethodAccent::text(const StringPiece16& source) } } else { // This is a pure text that should be pseudolocalized - const char16_t* p = pseudolocalizeChar(c); + const char* p = pseudolocalizeChar(c); if (p != nullptr) { result += p; } else { - bool space = util::isspace16(c); + bool space = isspace(c); if (lastspace && !space) { mWordCount++; } @@ -356,19 +356,19 @@ std::u16string PseudoMethodAccent::text(const StringPiece16& source) return result; } -std::u16string PseudoMethodAccent::placeholder(const StringPiece16& source) { +std::string PseudoMethodAccent::placeholder(const StringPiece& source) { // Surround a placeholder with brackets return k_placeholder_open + source.toString() + k_placeholder_close; } -std::u16string PseudoMethodBidi::text(const StringPiece16& source) { - const char16_t* s = source.data(); - std::u16string result; +std::string PseudoMethodBidi::text(const StringPiece& source) { + const char* s = source.data(); + std::string result; bool lastspace = true; bool space = true; for (size_t i = 0; i < source.size(); i++) { - char16_t c = s[i]; - space = util::isspace16(c); + char c = s[i]; + space = isspace(c); if (lastspace && !space) { // Word start result += k_rlm + k_rlo; @@ -386,7 +386,7 @@ std::u16string PseudoMethodBidi::text(const StringPiece16& source) { return result; } -std::u16string PseudoMethodBidi::placeholder(const StringPiece16& source) { +std::string PseudoMethodBidi::placeholder(const StringPiece& source) { // Surround a placeholder with directionality change sequence return k_rlm + k_rlo + source.toString() + k_pdf + k_rlm; } diff --git a/tools/aapt2/compile/Pseudolocalizer.h b/tools/aapt2/compile/Pseudolocalizer.h index 8818c1725617..7db88de13536 100644 --- a/tools/aapt2/compile/Pseudolocalizer.h +++ b/tools/aapt2/compile/Pseudolocalizer.h @@ -29,10 +29,10 @@ namespace aapt { class PseudoMethodImpl { public: virtual ~PseudoMethodImpl() {} - virtual std::u16string start() { return {}; } - virtual std::u16string end() { return {}; } - virtual std::u16string text(const StringPiece16& text) = 0; - virtual std::u16string placeholder(const StringPiece16& text) = 0; + virtual std::string start() { return {}; } + virtual std::string end() { return {}; } + virtual std::string text(const StringPiece& text) = 0; + virtual std::string placeholder(const StringPiece& text) = 0; }; class Pseudolocalizer { @@ -45,9 +45,9 @@ public: Pseudolocalizer(Method method); void setMethod(Method method); - std::u16string start() { return mImpl->start(); } - std::u16string end() { return mImpl->end(); } - std::u16string text(const StringPiece16& text); + std::string start() { return mImpl->start(); } + std::string end() { return mImpl->end(); } + std::string text(const StringPiece& text); private: std::unique_ptr<PseudoMethodImpl> mImpl; size_t mLastDepth; diff --git a/tools/aapt2/compile/Pseudolocalizer_test.cpp b/tools/aapt2/compile/Pseudolocalizer_test.cpp index b0bc2c10fbe0..c33e152d1554 100644 --- a/tools/aapt2/compile/Pseudolocalizer_test.cpp +++ b/tools/aapt2/compile/Pseudolocalizer_test.cpp @@ -28,9 +28,8 @@ namespace aapt { static ::testing::AssertionResult simpleHelper(const char* input, const char* expected, Pseudolocalizer::Method method) { Pseudolocalizer pseudo(method); - std::string result = util::utf16ToUtf8( - pseudo.start() + pseudo.text(util::utf8ToUtf16(input)) + pseudo.end()); - if (StringPiece(expected) != result) { + std::string result = pseudo.start() + pseudo.text(input) + pseudo.end(); + if (result != expected) { return ::testing::AssertionFailure() << expected << " != " << result; } return ::testing::AssertionSuccess(); @@ -40,12 +39,9 @@ static ::testing::AssertionResult compoundHelper(const char* in1, const char* in const char* expected, Pseudolocalizer::Method method) { Pseudolocalizer pseudo(method); - std::string result = util::utf16ToUtf8(pseudo.start() + - pseudo.text(util::utf8ToUtf16(in1)) + - pseudo.text(util::utf8ToUtf16(in2)) + - pseudo.text(util::utf8ToUtf16(in3)) + - pseudo.end()); - if (StringPiece(expected) != result) { + std::string result = pseudo.start() + pseudo.text(in1) + pseudo.text(in2) + pseudo.text(in3) + + pseudo.end(); + if (result != expected) { return ::testing::AssertionFailure() << expected << " != " << result; } return ::testing::AssertionSuccess(); @@ -69,7 +65,7 @@ TEST(PseudolocalizerTest, PlaintextAccent) { EXPECT_TRUE(simpleHelper("Battery %1d%%", "[βåţţéŕý »%1d«%% one two]", Pseudolocalizer::Method::kAccent)); - + EXPECT_TRUE(simpleHelper("^1 %", "[^1 % one]", Pseudolocalizer::Method::kAccent)); EXPECT_TRUE(compoundHelper("", "", "", "[]", Pseudolocalizer::Method::kAccent)); EXPECT_TRUE(compoundHelper("Hello,", " world", "", "[Ĥéļļö, ŵöŕļð one two]", Pseudolocalizer::Method::kAccent)); @@ -218,10 +214,10 @@ TEST(PseudolocalizerTest, NestedICU) { TEST(PseudolocalizerTest, RedefineMethod) { Pseudolocalizer pseudo(Pseudolocalizer::Method::kAccent); - std::u16string result = pseudo.text(u"Hello, "); + std::string result = pseudo.text("Hello, "); pseudo.setMethod(Pseudolocalizer::Method::kNone); - result += pseudo.text(u"world!"); - ASSERT_EQ(StringPiece("Ĥéļļö, world!"), util::utf16ToUtf8(result)); + result += pseudo.text("world!"); + ASSERT_EQ(StringPiece("Ĥéļļö, world!"), result); } } // namespace aapt diff --git a/tools/aapt2/compile/XmlIdCollector.cpp b/tools/aapt2/compile/XmlIdCollector.cpp index f40689eaeb47..9fc979c34353 100644 --- a/tools/aapt2/compile/XmlIdCollector.cpp +++ b/tools/aapt2/compile/XmlIdCollector.cpp @@ -35,7 +35,7 @@ struct IdCollector : public xml::Visitor { std::vector<SourcedResourceName>* mOutSymbols; - IdCollector(std::vector<SourcedResourceName>* outSymbols) : mOutSymbols(outSymbols) { + explicit IdCollector(std::vector<SourcedResourceName>* outSymbols) : mOutSymbols(outSymbols) { } void visit(xml::Element* element) override { diff --git a/tools/aapt2/compile/XmlIdCollector_test.cpp b/tools/aapt2/compile/XmlIdCollector_test.cpp index a37ea86c317f..ea1ced3f973b 100644 --- a/tools/aapt2/compile/XmlIdCollector_test.cpp +++ b/tools/aapt2/compile/XmlIdCollector_test.cpp @@ -38,13 +38,13 @@ TEST(XmlIdCollectorTest, CollectsIds) { ASSERT_TRUE(collector.consume(context.get(), doc.get())); EXPECT_EQ(1, std::count(doc->file.exportedSymbols.begin(), doc->file.exportedSymbols.end(), - SourcedResourceName{ test::parseNameOrDie(u"@id/foo"), 3u })); + SourcedResourceName{ test::parseNameOrDie("@id/foo"), 3u })); EXPECT_EQ(1, std::count(doc->file.exportedSymbols.begin(), doc->file.exportedSymbols.end(), - SourcedResourceName{ test::parseNameOrDie(u"@id/bar"), 3u })); + SourcedResourceName{ test::parseNameOrDie("@id/bar"), 3u })); EXPECT_EQ(1, std::count(doc->file.exportedSymbols.begin(), doc->file.exportedSymbols.end(), - SourcedResourceName{ test::parseNameOrDie(u"@id/car"), 6u })); + SourcedResourceName{ test::parseNameOrDie("@id/car"), 6u })); } TEST(XmlIdCollectorTest, DontCollectNonIds) { diff --git a/tools/aapt2/diff/Diff.cpp b/tools/aapt2/diff/Diff.cpp index 20b7b59642ca..9b1f0572123d 100644 --- a/tools/aapt2/diff/Diff.cpp +++ b/tools/aapt2/diff/Diff.cpp @@ -16,6 +16,7 @@ #include "Flags.h" #include "ResourceTable.h" +#include "ValueVisitor.h" #include "io/ZipArchive.h" #include "process/IResourceTableConsumer.h" #include "process/SymbolTable.h" @@ -27,7 +28,7 @@ namespace aapt { class DiffContext : public IAaptContext { public: - const std::u16string& getCompilationPackage() override { + const std::string& getCompilationPackage() override { return mEmpty; } @@ -51,8 +52,12 @@ public: return false; } + int getMinSdkVersion() override { + return 0; + } + private: - std::u16string mEmpty; + std::string mEmpty; StdErrDiagnostics mDiagnostics; NameMangler mNameMangler = NameMangler(NameManglerPolicy{}); SymbolTable mSymbolTable; @@ -381,6 +386,24 @@ static bool emitResourceTableDiff(IAaptContext* context, LoadedApk* apkA, Loaded return diff; } +class ZeroingReferenceVisitor : public ValueVisitor { +public: + using ValueVisitor::visit; + + void visit(Reference* ref) override { + if (ref->name && ref->id) { + if (ref->id.value().packageId() == 0x7f) { + ref->id = {}; + } + } + } +}; + +static void zeroOutAppReferences(ResourceTable* table) { + ZeroingReferenceVisitor visitor; + visitAllValuesInTable(table, &visitor); +} + int diff(const std::vector<StringPiece>& args) { DiffContext context; @@ -401,6 +424,10 @@ int diff(const std::vector<StringPiece>& args) { return 1; } + // Zero out Application IDs in references. + zeroOutAppReferences(apkA->getResourceTable()); + zeroOutAppReferences(apkB->getResourceTable()); + if (emitResourceTableDiff(&context, apkA.get(), apkB.get())) { // We emitted a diff, so return 1 (failure). return 1; diff --git a/tools/aapt2/dump/Dump.cpp b/tools/aapt2/dump/Dump.cpp index 56b9f9a3e081..88c6f64f5510 100644 --- a/tools/aapt2/dump/Dump.cpp +++ b/tools/aapt2/dump/Dump.cpp @@ -20,6 +20,7 @@ #include "io/ZipArchive.h" #include "process/IResourceTableConsumer.h" #include "proto/ProtoSerialize.h" +#include "unflatten/BinaryResourceParser.h" #include "util/Files.h" #include "util/StringPiece.h" @@ -44,18 +45,9 @@ void dumpCompiledFile(const pb::CompiledFile& pbFile, const void* data, size_t l << "Source: " << file->source << "\n"; } -void dumpCompiledTable(const pb::ResourceTable& pbTable, const Source& source, - IAaptContext* context) { - std::unique_ptr<ResourceTable> table = deserializeTableFromPb(pbTable, source, - context->getDiagnostics()); - if (!table) { - return; - } - - Debug::printTable(table.get()); -} - void tryDumpFile(IAaptContext* context, const std::string& filePath) { + std::unique_ptr<ResourceTable> table; + std::string err; std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::create(filePath, &err); if (zip) { @@ -75,37 +67,62 @@ void tryDumpFile(IAaptContext* context, const std::string& filePath) { return; } - std::unique_ptr<ResourceTable> table = deserializeTableFromPb( + table = deserializeTableFromPb( pbTable, Source(filePath), context->getDiagnostics()); - if (table) { - DebugPrintTableOptions debugPrintTableOptions; - debugPrintTableOptions.showSources = true; - Debug::printTable(table.get(), debugPrintTableOptions); + if (!table) { + return; } } - return; - } - Maybe<android::FileMap> file = file::mmapPath(filePath, &err); - if (!file) { - context->getDiagnostics()->error(DiagMessage(filePath) << err); - return; + if (!table) { + file = zip->findFile("resources.arsc"); + if (file) { + std::unique_ptr<io::IData> data = file->openAsData(); + if (!data) { + context->getDiagnostics()->error(DiagMessage(filePath) + << "failed to open resources.arsc"); + return; + } + + table = util::make_unique<ResourceTable>(); + BinaryResourceParser parser(context, table.get(), Source(filePath), + data->data(), data->size()); + if (!parser.parse()) { + return; + } + } + } } - android::FileMap* fileMap = &file.value(); + if (!table) { + Maybe<android::FileMap> file = file::mmapPath(filePath, &err); + if (!file) { + context->getDiagnostics()->error(DiagMessage(filePath) << err); + return; + } + + android::FileMap* fileMap = &file.value(); - // Try as a compiled table. - pb::ResourceTable pbTable; - if (pbTable.ParseFromArray(fileMap->getDataPtr(), fileMap->getDataLength())) { - dumpCompiledTable(pbTable, Source(filePath), context); - return; + // Try as a compiled table. + pb::ResourceTable pbTable; + if (pbTable.ParseFromArray(fileMap->getDataPtr(), fileMap->getDataLength())) { + table = deserializeTableFromPb(pbTable, Source(filePath), context->getDiagnostics()); + } + + if (!table) { + // Try as a compiled file. + CompiledFileInputStream input(fileMap->getDataPtr(), fileMap->getDataLength()); + if (const pb::CompiledFile* pbFile = input.CompiledFile()) { + dumpCompiledFile(*pbFile, input.data(), input.size(), Source(filePath), context); + return; + } + } } - // Try as a compiled file. - CompiledFileInputStream input(fileMap->getDataPtr(), fileMap->getDataLength()); - if (const pb::CompiledFile* pbFile = input.CompiledFile()) { - dumpCompiledFile(*pbFile, input.data(), input.size(), Source(filePath), context); - return; + if (table) { + DebugPrintTableOptions debugPrintTableOptions; + debugPrintTableOptions.showSources = true; + Debug::printTable(table.get(), debugPrintTableOptions); } } @@ -120,8 +137,8 @@ public: return nullptr; } - const std::u16string& getCompilationPackage() override { - static std::u16string empty; + const std::string& getCompilationPackage() override { + static std::string empty; return empty; } @@ -142,6 +159,10 @@ public: mVerbose = val; } + int getMinSdkVersion() override { + return 0; + } + private: StdErrDiagnostics mDiagnostics; bool mVerbose = false; diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp index 28a792820de3..5fbb0fec7256 100644 --- a/tools/aapt2/flatten/TableFlattener.cpp +++ b/tools/aapt2/flatten/TableFlattener.cpp @@ -25,6 +25,7 @@ #include <android-base/macros.h> #include <algorithm> +#include <sstream> #include <type_traits> #include <numeric> @@ -231,7 +232,8 @@ public: } // Copy the package name in device endianness. - strcpy16_htod(pkgHeader->name, arraysize(pkgHeader->name), mPackage->name); + strcpy16_htod(pkgHeader->name, arraysize(pkgHeader->name), + util::utf8ToUtf16(mPackage->name)); // Serialize the types. We do this now so that our type and key strings // are populated. We write those first. @@ -423,9 +425,9 @@ private: // If there is a gap in the type IDs, fill in the StringPool // with empty values until we reach the ID we expect. while (type->id.value() > expectedTypeId) { - std::u16string typeName(u"?"); - typeName += expectedTypeId; - mTypePool.makeRef(typeName); + std::stringstream typeName; + typeName << "?" << expectedTypeId; + mTypePool.makeRef(typeName.str()); expectedTypeId++; } expectedTypeId++; diff --git a/tools/aapt2/flatten/TableFlattener_test.cpp b/tools/aapt2/flatten/TableFlattener_test.cpp index 39c4fd318508..e720e7e3fc4a 100644 --- a/tools/aapt2/flatten/TableFlattener_test.cpp +++ b/tools/aapt2/flatten/TableFlattener_test.cpp @@ -14,15 +14,12 @@ * limitations under the License. */ +#include "ResourceUtils.h" #include "flatten/TableFlattener.h" -#include "test/Builders.h" -#include "test/Context.h" +#include "test/Test.h" #include "unflatten/BinaryResourceParser.h" #include "util/Util.h" - -#include <gtest/gtest.h> - using namespace android; namespace aapt { @@ -31,7 +28,7 @@ class TableFlattenerTest : public ::testing::Test { public: void SetUp() override { mContext = test::ContextBuilder() - .setCompilationPackage(u"com.app.test") + .setCompilationPackage("com.app.test") .setPackageId(0x7f) .build(); } @@ -66,7 +63,7 @@ public: } ::testing::AssertionResult exists(ResTable* table, - const StringPiece16& expectedName, + const StringPiece& expectedName, const ResourceId expectedId, const ConfigDescription& expectedConfig, const uint8_t expectedDataType, const uint32_t expectedData, @@ -108,25 +105,16 @@ public: return ::testing::AssertionFailure() << "failed to find resource name"; } - StringPiece16 package16(actualName.package, actualName.packageLen); - if (package16 != expectedResName.package) { - return ::testing::AssertionFailure() - << "expected package '" << expectedResName.package << "' but got '" - << package16 << "'"; - } - - StringPiece16 type16(actualName.type, actualName.typeLen); - if (type16 != toString(expectedResName.type)) { + Maybe<ResourceName> resName = ResourceUtils::toResourceName(actualName); + if (!resName) { return ::testing::AssertionFailure() - << "expected type '" << expectedResName.type - << "' but got '" << type16 << "'"; - } - - StringPiece16 name16(actualName.name, actualName.nameLen); - if (name16 != expectedResName.entry) { - return ::testing::AssertionFailure() - << "expected name '" << expectedResName.entry - << "' but got '" << name16 << "'"; + << "expected name '" << expectedResName << "' but got '" + << StringPiece16(actualName.package, actualName.packageLen) + << ":" + << StringPiece16(actualName.type, actualName.typeLen) + << "/" + << StringPiece16(actualName.name, actualName.nameLen) + << "'"; } if (expectedConfig != config) { @@ -143,67 +131,67 @@ private: TEST_F(TableFlattenerTest, FlattenFullyLinkedTable) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"com.app.test", 0x7f) - .addSimple(u"@com.app.test:id/one", ResourceId(0x7f020000)) - .addSimple(u"@com.app.test:id/two", ResourceId(0x7f020001)) - .addValue(u"@com.app.test:id/three", ResourceId(0x7f020002), - test::buildReference(u"@com.app.test:id/one", ResourceId(0x7f020000))) - .addValue(u"@com.app.test:integer/one", ResourceId(0x7f030000), + .setPackageId("com.app.test", 0x7f) + .addSimple("@com.app.test:id/one", ResourceId(0x7f020000)) + .addSimple("@com.app.test:id/two", ResourceId(0x7f020001)) + .addValue("@com.app.test:id/three", ResourceId(0x7f020002), + test::buildReference("@com.app.test:id/one", ResourceId(0x7f020000))) + .addValue("@com.app.test:integer/one", ResourceId(0x7f030000), util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u)) - .addValue(u"@com.app.test:integer/one", ResourceId(0x7f030000), - test::parseConfigOrDie("v1"), + .addValue("@com.app.test:integer/one", test::parseConfigOrDie("v1"), + ResourceId(0x7f030000), util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u)) - .addString(u"@com.app.test:string/test", ResourceId(0x7f040000), u"foo") - .addString(u"@com.app.test:layout/bar", ResourceId(0x7f050000), u"res/layout/bar.xml") + .addString("@com.app.test:string/test", ResourceId(0x7f040000), "foo") + .addString("@com.app.test:layout/bar", ResourceId(0x7f050000), "res/layout/bar.xml") .build(); ResTable resTable; ASSERT_TRUE(flatten(table.get(), &resTable)); - EXPECT_TRUE(exists(&resTable, u"@com.app.test:id/one", ResourceId(0x7f020000), {}, + EXPECT_TRUE(exists(&resTable, "@com.app.test:id/one", ResourceId(0x7f020000), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u)); - EXPECT_TRUE(exists(&resTable, u"@com.app.test:id/two", ResourceId(0x7f020001), {}, + EXPECT_TRUE(exists(&resTable, "@com.app.test:id/two", ResourceId(0x7f020001), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u)); - EXPECT_TRUE(exists(&resTable, u"@com.app.test:id/three", ResourceId(0x7f020002), {}, + EXPECT_TRUE(exists(&resTable, "@com.app.test:id/three", ResourceId(0x7f020002), {}, Res_value::TYPE_REFERENCE, 0x7f020000u, 0u)); - EXPECT_TRUE(exists(&resTable, u"@com.app.test:integer/one", ResourceId(0x7f030000), + EXPECT_TRUE(exists(&resTable, "@com.app.test:integer/one", ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u, ResTable_config::CONFIG_VERSION)); - EXPECT_TRUE(exists(&resTable, u"@com.app.test:integer/one", ResourceId(0x7f030000), + EXPECT_TRUE(exists(&resTable, "@com.app.test:integer/one", ResourceId(0x7f030000), test::parseConfigOrDie("v1"), Res_value::TYPE_INT_DEC, 2u, ResTable_config::CONFIG_VERSION)); - StringPiece16 fooStr = u"foo"; + std::u16string fooStr = u"foo"; ssize_t idx = resTable.getTableStringBlock(0)->indexOfString(fooStr.data(), fooStr.size()); ASSERT_GE(idx, 0); - EXPECT_TRUE(exists(&resTable, u"@com.app.test:string/test", ResourceId(0x7f040000), + EXPECT_TRUE(exists(&resTable, "@com.app.test:string/test", ResourceId(0x7f040000), {}, Res_value::TYPE_STRING, (uint32_t) idx, 0u)); - StringPiece16 barPath = u"res/layout/bar.xml"; + std::u16string barPath = u"res/layout/bar.xml"; idx = resTable.getTableStringBlock(0)->indexOfString(barPath.data(), barPath.size()); ASSERT_GE(idx, 0); - EXPECT_TRUE(exists(&resTable, u"@com.app.test:layout/bar", ResourceId(0x7f050000), {}, + EXPECT_TRUE(exists(&resTable, "@com.app.test:layout/bar", ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t) idx, 0u)); } TEST_F(TableFlattenerTest, FlattenEntriesWithGapsInIds) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"com.app.test", 0x7f) - .addSimple(u"@com.app.test:id/one", ResourceId(0x7f020001)) - .addSimple(u"@com.app.test:id/three", ResourceId(0x7f020003)) + .setPackageId("com.app.test", 0x7f) + .addSimple("@com.app.test:id/one", ResourceId(0x7f020001)) + .addSimple("@com.app.test:id/three", ResourceId(0x7f020003)) .build(); ResTable resTable; ASSERT_TRUE(flatten(table.get(), &resTable)); - EXPECT_TRUE(exists(&resTable, u"@com.app.test:id/one", ResourceId(0x7f020001), {}, + EXPECT_TRUE(exists(&resTable, "@com.app.test:id/one", ResourceId(0x7f020001), {}, + Res_value::TYPE_INT_BOOLEAN, 0u, 0u)); + EXPECT_TRUE(exists(&resTable, "@com.app.test:id/three", ResourceId(0x7f020003), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u)); - EXPECT_TRUE(exists(&resTable, u"@com.app.test:id/three", ResourceId(0x7f020003), {}, - Res_value::TYPE_INT_BOOLEAN, 0u, 0u)); } TEST_F(TableFlattenerTest, FlattenMinMaxAttributes) { @@ -212,15 +200,15 @@ TEST_F(TableFlattenerTest, FlattenMinMaxAttributes) { attr.minInt = 10; attr.maxInt = 23; std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"android", 0x01) - .addValue(u"@android:attr/foo", ResourceId(0x01010000), + .setPackageId("android", 0x01) + .addValue("@android:attr/foo", ResourceId(0x01010000), util::make_unique<Attribute>(attr)) .build(); ResourceTable result; ASSERT_TRUE(flatten(table.get(), &result)); - Attribute* actualAttr = test::getValue<Attribute>(&result, u"@android:attr/foo"); + Attribute* actualAttr = test::getValue<Attribute>(&result, "@android:attr/foo"); ASSERT_NE(nullptr, actualAttr); EXPECT_EQ(attr.isWeak(), actualAttr->isWeak()); EXPECT_EQ(attr.typeMask, actualAttr->typeMask); diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/flatten/XmlFlattener.cpp index 570cd9635de3..a74317781b2d 100644 --- a/tools/aapt2/flatten/XmlFlattener.cpp +++ b/tools/aapt2/flatten/XmlFlattener.cpp @@ -22,6 +22,7 @@ #include <androidfw/ResourceTypes.h> #include <algorithm> +#include <map> #include <utils/misc.h> #include <vector> @@ -56,14 +57,15 @@ struct XmlFlattenerVisitor : public xml::Visitor { mBuffer(buffer), mOptions(options) { } - void addString(const StringPiece16& str, uint32_t priority, android::ResStringPool_ref* dest) { - if (!str.empty()) { + void addString(const StringPiece& str, uint32_t priority, android::ResStringPool_ref* dest, + bool treatEmptyStringAsNull = false) { + if (str.empty() && treatEmptyStringAsNull) { + // Some parts of the runtime treat null differently than empty string. + dest->index = util::deviceToHost32(-1); + } else { mStringRefs.push_back(StringFlattenDest{ mPool.makeRef(str, StringPool::Context{ priority }), dest }); - } else { - // The device doesn't think a string of size 0 is the same as null. - dest->index = util::deviceToHost32(-1); } } @@ -117,8 +119,14 @@ struct XmlFlattenerVisitor : public xml::Visitor { flatNode->comment.index = util::hostToDevice32(-1); ResXMLTree_attrExt* flatElem = startWriter.nextBlock<ResXMLTree_attrExt>(); - addString(node->namespaceUri, kLowPriority, &flatElem->ns); - addString(node->name, kLowPriority, &flatElem->name); + + // A missing namespace must be null, not an empty string. Otherwise the runtime + // complains. + addString(node->namespaceUri, kLowPriority, &flatElem->ns, + true /* treatEmptyStringAsNull */); + addString(node->name, kLowPriority, &flatElem->name, + true /* treatEmptyStringAsNull */); + flatElem->attributeStart = util::hostToDevice16(sizeof(*flatElem)); flatElem->attributeSize = util::hostToDevice16(sizeof(ResXMLTree_attribute)); @@ -137,7 +145,8 @@ struct XmlFlattenerVisitor : public xml::Visitor { flatEndNode->comment.index = util::hostToDevice32(-1); ResXMLTree_endElementExt* flatEndElem = endWriter.nextBlock<ResXMLTree_endElementExt>(); - addString(node->namespaceUri, kLowPriority, &flatEndElem->ns); + addString(node->namespaceUri, kLowPriority, &flatEndElem->ns, + true /* treatEmptyStringAsNull */); addString(node->name, kLowPriority, &flatEndElem->name); endWriter.finish(); @@ -196,16 +205,18 @@ struct XmlFlattenerVisitor : public xml::Visitor { xmlAttr->compiledAttribute.value().id.value() == kIdAttr) { flatElem->idIndex = util::hostToDevice16(attributeIndex); } else if (xmlAttr->namespaceUri.empty()) { - if (xmlAttr->name == u"class") { + if (xmlAttr->name == "class") { flatElem->classIndex = util::hostToDevice16(attributeIndex); - } else if (xmlAttr->name == u"style") { + } else if (xmlAttr->name == "style") { flatElem->styleIndex = util::hostToDevice16(attributeIndex); } } attributeIndex++; - // Add the namespaceUri to the list of StringRefs to encode. - addString(xmlAttr->namespaceUri, kLowPriority, &flatAttr->ns); + // Add the namespaceUri to the list of StringRefs to encode. Use null if the namespace + // is empty (doesn't exist). + addString(xmlAttr->namespaceUri, kLowPriority, &flatAttr->ns, + true /* treatEmptyStringAsNull */); flatAttr->rawValue.index = util::hostToDevice32(-1); diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/flatten/XmlFlattener_test.cpp index 4e6eb811e572..9efee1ea1fe6 100644 --- a/tools/aapt2/flatten/XmlFlattener_test.cpp +++ b/tools/aapt2/flatten/XmlFlattener_test.cpp @@ -16,13 +16,11 @@ #include "flatten/XmlFlattener.h" #include "link/Linkers.h" -#include "test/Builders.h" -#include "test/Context.h" +#include "test/Test.h" #include "util/BigBuffer.h" #include "util/Util.h" #include <androidfw/ResourceTypes.h> -#include <gtest/gtest.h> namespace aapt { @@ -30,15 +28,15 @@ class XmlFlattenerTest : public ::testing::Test { public: void SetUp() override { mContext = test::ContextBuilder() - .setCompilationPackage(u"com.app.test") - .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" }) + .setCompilationPackage("com.app.test") + .setNameManglerPolicy(NameManglerPolicy{ "com.app.test" }) .addSymbolSource(test::StaticSymbolSourceBuilder() - .addSymbol(u"@android:attr/id", ResourceId(0x010100d0), + .addSymbol("@android:attr/id", ResourceId(0x010100d0), test::AttributeBuilder().build()) - .addSymbol(u"@com.app.test:id/id", ResourceId(0x7f020000)) - .addSymbol(u"@android:attr/paddingStart", ResourceId(0x010103b3), + .addSymbol("@com.app.test:id/id", ResourceId(0x7f020000)) + .addSymbol("@android:attr/paddingStart", ResourceId(0x010103b3), test::AttributeBuilder().build()) - .addSymbol(u"@android:attr/colorAccent", ResourceId(0x01010435), + .addSymbol("@android:attr/colorAccent", ResourceId(0x01010435), test::AttributeBuilder().build()) .build()) .build(); @@ -207,4 +205,23 @@ TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) { EXPECT_GE(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), 0); } +TEST_F(XmlFlattenerTest, EmptyStringValueInAttributeIsNotNull) { + std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom("<View package=\"\"/>"); + + android::ResXMLTree tree; + ASSERT_TRUE(flatten(doc.get(), &tree)); + + while (tree.next() != android::ResXMLTree::START_TAG) { + ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT); + ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT); + } + + const StringPiece16 kPackage = u"package"; + ssize_t idx = tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()); + ASSERT_GE(idx, 0); + + size_t len; + EXPECT_NE(nullptr, tree.getAttributeStringValue(idx, &len)); +} + } // namespace aapt diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp index b7e7f903a2b1..23ff8abf8950 100644 --- a/tools/aapt2/java/AnnotationProcessor.cpp +++ b/tools/aapt2/java/AnnotationProcessor.cpp @@ -47,23 +47,13 @@ void AnnotationProcessor::appendCommentLine(std::string& comment) { mComment << "\n * " << std::move(comment); } -void AnnotationProcessor::appendComment(const StringPiece16& comment) { - // We need to process line by line to clean-up whitespace and append prefixes. - for (StringPiece16 line : util::tokenize(comment, u'\n')) { - line = util::trimWhitespace(line); - if (!line.empty()) { - std::string utf8Line = util::utf16ToUtf8(line); - appendCommentLine(utf8Line); - } - } -} - void AnnotationProcessor::appendComment(const StringPiece& comment) { + // We need to process line by line to clean-up whitespace and append prefixes. for (StringPiece line : util::tokenize(comment, '\n')) { line = util::trimWhitespace(line); if (!line.empty()) { - std::string utf8Line = line.toString(); - appendCommentLine(utf8Line); + std::string lineCopy = line.toString(); + appendCommentLine(lineCopy); } } } @@ -75,7 +65,7 @@ void AnnotationProcessor::appendNewLine() { 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')) { + for (StringPiece line : util::tokenize(result, '\n')) { *out << prefix << line << "\n"; } *out << prefix << " */" << "\n"; diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h index 8309dd978175..cfc32f3c6477 100644 --- a/tools/aapt2/java/AnnotationProcessor.h +++ b/tools/aapt2/java/AnnotationProcessor.h @@ -57,7 +57,6 @@ public: * Adds more comments. Since resources can have various values with different configurations, * we need to collect all the comments. */ - void appendComment(const StringPiece16& comment); void appendComment(const StringPiece& comment); void appendNewLine(); diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp index 84df0b429fc5..fbaefb1f0c80 100644 --- a/tools/aapt2/java/JavaClassGenerator.cpp +++ b/tools/aapt2/java/JavaClassGenerator.cpp @@ -19,7 +19,6 @@ #include "ResourceTable.h" #include "ResourceValues.h" #include "ValueVisitor.h" - #include "java/AnnotationProcessor.h" #include "java/ClassDefinition.h" #include "java/JavaClassGenerator.h" @@ -39,20 +38,20 @@ JavaClassGenerator::JavaClassGenerator(IAaptContext* context, ResourceTable* tab mContext(context), mTable(table), mOptions(options) { } -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", - u"default", u"do", u"double", u"else", u"enum", u"extends", - u"final", u"finally", u"float", u"for", u"goto", u"if", - u"implements", u"import", u"instanceof", u"int", u"interface", - u"long", u"native", u"new", u"package", u"private", u"protected", - u"public", u"return", u"short", u"static", u"strictfp", u"super", - u"switch", u"synchronized", u"this", u"throw", u"throws", - u"transient", u"try", u"void", u"volatile", u"while", u"true", - u"false", u"null" +static const std::set<StringPiece> sJavaIdentifiers = { + "abstract", "assert", "boolean", "break", "byte", + "case", "catch", "char", "class", "const", "continue", + "default", "do", "double", "else", "enum", "extends", + "final", "finally", "float", "for", "goto", "if", + "implements", "import", "instanceof", "int", "interface", + "long", "native", "new", "package", "private", "protected", + "public", "return", "short", "static", "strictfp", "super", + "switch", "synchronized", "this", "throw", "throws", + "transient", "try", "void", "volatile", "while", "true", + "false", "null" }; -static bool isValidSymbol(const StringPiece16& symbol) { +static bool isValidSymbol(const StringPiece& symbol) { return sJavaIdentifiers.find(symbol) == sJavaIdentifiers.end(); } @@ -60,8 +59,8 @@ static bool isValidSymbol(const StringPiece16& symbol) { * Java symbols can not contain . or -, but those are valid in a resource name. * Replace those with '_'. */ -static std::string transform(const StringPiece16& symbol) { - std::string output = util::utf16ToUtf8(symbol); +static std::string transform(const StringPiece& symbol) { + std::string output = symbol.toString(); for (char& c : output) { if (c == '.' || c == '-') { c = '_'; @@ -83,7 +82,7 @@ static std::string transform(const StringPiece16& symbol) { */ static std::string transformNestedAttr(const ResourceNameRef& attrName, const std::string& styleableClassName, - const StringPiece16& packageNameToGenerate) { + const StringPiece& packageNameToGenerate) { std::string output = styleableClassName; // We may reference IDs from other packages, so prefix the entry name with @@ -204,8 +203,8 @@ static bool lessStyleableAttr(const StyleableAttr& lhs, const StyleableAttr& rhs } } -void JavaClassGenerator::addMembersToStyleableClass(const StringPiece16& packageNameToGenerate, - const std::u16string& entryName, +void JavaClassGenerator::addMembersToStyleableClass(const StringPiece& packageNameToGenerate, + const std::string& entryName, const Styleable* styleable, ClassDefinition* outStyleableClassDef) { const std::string className = transform(entryName); @@ -284,8 +283,8 @@ void JavaClassGenerator::addMembersToStyleableClass(const StringPiece16& package continue; } - StringPiece16 attrCommentLine = entry.symbol->attribute->getComment(); - if (attrCommentLine.contains(StringPiece16(u"@removed"))) { + StringPiece attrCommentLine = entry.symbol->attribute->getComment(); + if (attrCommentLine.contains("@removed")) { // Removed attributes are public but hidden from the documentation, so don't emit // them as part of the class documentation. continue; @@ -354,12 +353,12 @@ void JavaClassGenerator::addMembersToStyleableClass(const StringPiece16& package continue; } - StringPiece16 comment = styleableAttr.attrRef->getComment(); + StringPiece comment = styleableAttr.attrRef->getComment(); if (styleableAttr.symbol->attribute && comment.empty()) { comment = styleableAttr.symbol->attribute->getComment(); } - if (comment.contains(StringPiece16(u"@removed"))) { + if (comment.contains("@removed")) { // Removed attributes are public but hidden from the documentation, so don't emit them // as part of the class documentation. continue; @@ -367,7 +366,7 @@ void JavaClassGenerator::addMembersToStyleableClass(const StringPiece16& package const ResourceName& attrName = styleableAttr.attrRef->name.value(); - StringPiece16 packageName = attrName.package; + StringPiece packageName = attrName.package; if (packageName.empty()) { packageName = mContext->getCompilationPackage(); } @@ -403,7 +402,7 @@ void JavaClassGenerator::addMembersToStyleableClass(const StringPiece16& package } } -bool JavaClassGenerator::addMembersToTypeClass(const StringPiece16& packageNameToGenerate, +bool JavaClassGenerator::addMembersToTypeClass(const StringPiece& packageNameToGenerate, const ResourceTablePackage* package, const ResourceTableType* type, ClassDefinition* outTypeClassDef) { @@ -418,8 +417,8 @@ bool JavaClassGenerator::addMembersToTypeClass(const StringPiece16& packageNameT id = ResourceId(package->id.value(), type->id.value(), entry->id.value()); } - std::u16string unmangledPackage; - std::u16string unmangledName = entry->name; + std::string unmangledPackage; + std::string unmangledName = entry->name; if (NameMangler::unmangle(&unmangledName, &unmangledPackage)) { // The entry name was mangled, and we successfully unmangled it. // Check that we want to emit this symbol. @@ -481,7 +480,7 @@ bool JavaClassGenerator::addMembersToTypeClass(const StringPiece16& packageNameT return true; } -bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate, std::ostream* out) { +bool JavaClassGenerator::generate(const StringPiece& packageNameToGenerate, std::ostream* out) { return generate(packageNameToGenerate, packageNameToGenerate, out); } @@ -494,8 +493,8 @@ static void appendJavaDocAnnotations(const std::vector<std::string>& annotations } } -bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate, - const StringPiece16& outPackageName, std::ostream* out) { +bool JavaClassGenerator::generate(const StringPiece& packageNameToGenerate, + const StringPiece& outPackageName, std::ostream* out) { ClassDefinition rClass("R", ClassQualifier::None, true); @@ -509,8 +508,7 @@ bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate, (mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic); std::unique_ptr<ClassDefinition> classDef = util::make_unique<ClassDefinition>( - util::utf16ToUtf8(toString(type->type)), ClassQualifier::Static, - forceCreationIfEmpty); + toString(type->type), ClassQualifier::Static, forceCreationIfEmpty); bool result = addMembersToTypeClass(packageNameToGenerate, package.get(), type.get(), classDef.get()); @@ -545,8 +543,7 @@ bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate, appendJavaDocAnnotations(mOptions.javadocAnnotations, rClass.getCommentBuilder()); - if (!ClassDefinition::writeJavaFile(&rClass, util::utf16ToUtf8(outPackageName), - mOptions.useFinal, out)) { + if (!ClassDefinition::writeJavaFile(&rClass, outPackageName, mOptions.useFinal, out)) { return false; } diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h index 77e0ed76143a..901a86ed5b1d 100644 --- a/tools/aapt2/java/JavaClassGenerator.h +++ b/tools/aapt2/java/JavaClassGenerator.h @@ -66,22 +66,22 @@ public: * We need to generate these symbols in a separate file. * Returns true on success. */ - bool generate(const StringPiece16& packageNameToGenerate, std::ostream* out); + bool generate(const StringPiece& packageNameToGenerate, std::ostream* out); - bool generate(const StringPiece16& packageNameToGenerate, - const StringPiece16& outputPackageName, + bool generate(const StringPiece& packageNameToGenerate, + const StringPiece& outputPackageName, std::ostream* out); const std::string& getError() const; private: - bool addMembersToTypeClass(const StringPiece16& packageNameToGenerate, + bool addMembersToTypeClass(const StringPiece& packageNameToGenerate, const ResourceTablePackage* package, const ResourceTableType* type, ClassDefinition* outTypeClassDef); - void addMembersToStyleableClass(const StringPiece16& packageNameToGenerate, - const std::u16string& entryName, + void addMembersToStyleableClass(const StringPiece& packageNameToGenerate, + const std::string& entryName, const Styleable* styleable, ClassDefinition* outStyleableClassDef); diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp index 46266b3f3e89..57a8047a6f40 100644 --- a/tools/aapt2/java/JavaClassGenerator_test.cpp +++ b/tools/aapt2/java/JavaClassGenerator_test.cpp @@ -25,40 +25,40 @@ namespace aapt { TEST(JavaClassGeneratorTest, FailWhenEntryIsJavaKeyword) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"android", 0x01) - .addSimple(u"@android:id/class", ResourceId(0x01020000)) + .setPackageId("android", 0x01) + .addSimple("@android:id/class", ResourceId(0x01020000)) .build(); std::unique_ptr<IAaptContext> context = test::ContextBuilder() .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get())) - .setNameManglerPolicy(NameManglerPolicy{ u"android" }) + .setNameManglerPolicy(NameManglerPolicy{ "android" }) .build(); JavaClassGenerator generator(context.get(), table.get(), {}); std::stringstream out; - EXPECT_FALSE(generator.generate(u"android", &out)); + EXPECT_FALSE(generator.generate("android", &out)); } TEST(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"android", 0x01) - .addSimple(u"@android:id/hey-man", ResourceId(0x01020000)) - .addValue(u"@android:attr/cool.attr", ResourceId(0x01010000), + .setPackageId("android", 0x01) + .addSimple("@android:id/hey-man", ResourceId(0x01020000)) + .addValue("@android:attr/cool.attr", ResourceId(0x01010000), test::AttributeBuilder(false).build()) - .addValue(u"@android:styleable/hey.dude", ResourceId(0x01030000), + .addValue("@android:styleable/hey.dude", ResourceId(0x01030000), test::StyleableBuilder() - .addItem(u"@android:attr/cool.attr", ResourceId(0x01010000)) + .addItem("@android:attr/cool.attr", ResourceId(0x01010000)) .build()) .build(); std::unique_ptr<IAaptContext> context = test::ContextBuilder() .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get())) - .setNameManglerPolicy(NameManglerPolicy{ u"android" }) + .setNameManglerPolicy(NameManglerPolicy{ "android" }) .build(); JavaClassGenerator generator(context.get(), table.get(), {}); std::stringstream out; - EXPECT_TRUE(generator.generate(u"android", &out)); + EXPECT_TRUE(generator.generate("android", &out)); std::string output = out.str(); @@ -74,18 +74,18 @@ TEST(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) { TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"android", 0x01) - .addSimple(u"@android:id/one", ResourceId(0x01020000)) - .addSimple(u"@android:id/com.foo$two", ResourceId(0x01020001)) + .setPackageId("android", 0x01) + .addSimple("@android:id/one", ResourceId(0x01020000)) + .addSimple("@android:id/com.foo$two", ResourceId(0x01020001)) .build(); std::unique_ptr<IAaptContext> context = test::ContextBuilder() .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get())) - .setNameManglerPolicy(NameManglerPolicy{ u"android" }) + .setNameManglerPolicy(NameManglerPolicy{ "android" }) .build(); JavaClassGenerator generator(context.get(), table.get(), {}); std::stringstream out; - ASSERT_TRUE(generator.generate(u"android", u"com.android.internal", &out)); + 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;")); @@ -96,18 +96,18 @@ TEST(JavaClassGeneratorTest, CorrectPackageNameIsUsed) { TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"android", 0x01) - .addSimple(u"@android:attr/two", ResourceId(0x01010001)) - .addSimple(u"@android:^attr-private/one", ResourceId(0x01010000)) + .setPackageId("android", 0x01) + .addSimple("@android:attr/two", ResourceId(0x01010001)) + .addSimple("@android:^attr-private/one", ResourceId(0x01010000)) .build(); std::unique_ptr<IAaptContext> context = test::ContextBuilder() .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get())) - .setNameManglerPolicy(NameManglerPolicy{ u"android" }) + .setNameManglerPolicy(NameManglerPolicy{ "android" }) .build(); JavaClassGenerator generator(context.get(), table.get(), {}); std::stringstream out; - ASSERT_TRUE(generator.generate(u"android", &out)); + ASSERT_TRUE(generator.generate("android", &out)); std::string output = out.str(); EXPECT_NE(std::string::npos, output.find("public static final class attr")); @@ -117,17 +117,17 @@ TEST(JavaClassGeneratorTest, AttrPrivateIsWrittenAsAttr) { TEST(JavaClassGeneratorTest, OnlyWritePublicResources) { StdErrDiagnostics diag; std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"android", 0x01) - .addSimple(u"@android:id/one", ResourceId(0x01020000)) - .addSimple(u"@android:id/two", ResourceId(0x01020001)) - .addSimple(u"@android:id/three", ResourceId(0x01020002)) - .setSymbolState(u"@android:id/one", ResourceId(0x01020000), SymbolState::kPublic) - .setSymbolState(u"@android:id/two", ResourceId(0x01020001), SymbolState::kPrivate) + .setPackageId("android", 0x01) + .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) .build(); std::unique_ptr<IAaptContext> context = test::ContextBuilder() .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get())) - .setNameManglerPolicy(NameManglerPolicy{ u"android" }) + .setNameManglerPolicy(NameManglerPolicy{ "android" }) .build(); JavaClassGeneratorOptions options; @@ -135,7 +135,7 @@ TEST(JavaClassGeneratorTest, OnlyWritePublicResources) { { JavaClassGenerator generator(context.get(), table.get(), options); std::stringstream out; - ASSERT_TRUE(generator.generate(u"android", &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")); @@ -146,7 +146,7 @@ TEST(JavaClassGeneratorTest, OnlyWritePublicResources) { { JavaClassGenerator generator(context.get(), table.get(), options); std::stringstream out; - ASSERT_TRUE(generator.generate(u"android", &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;")); @@ -157,7 +157,7 @@ TEST(JavaClassGeneratorTest, OnlyWritePublicResources) { { JavaClassGenerator generator(context.get(), table.get(), options); std::stringstream out; - ASSERT_TRUE(generator.generate(u"android", &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;")); @@ -198,27 +198,27 @@ TEST(JavaClassGeneratorTest, OnlyWritePublicResources) { TEST(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"android", 0x01) - .setPackageId(u"com.lib", 0x02) - .addValue(u"@android:attr/bar", ResourceId(0x01010000), + .setPackageId("android", 0x01) + .setPackageId("com.lib", 0x02) + .addValue("@android:attr/bar", ResourceId(0x01010000), test::AttributeBuilder(false).build()) - .addValue(u"@com.lib:attr/bar", ResourceId(0x02010000), + .addValue("@com.lib:attr/bar", ResourceId(0x02010000), test::AttributeBuilder(false).build()) - .addValue(u"@android:styleable/foo", ResourceId(0x01030000), + .addValue("@android:styleable/foo", ResourceId(0x01030000), test::StyleableBuilder() - .addItem(u"@android:attr/bar", ResourceId(0x01010000)) - .addItem(u"@com.lib:attr/bar", ResourceId(0x02010000)) + .addItem("@android:attr/bar", ResourceId(0x01010000)) + .addItem("@com.lib:attr/bar", ResourceId(0x02010000)) .build()) .build(); std::unique_ptr<IAaptContext> context = test::ContextBuilder() .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get())) - .setNameManglerPolicy(NameManglerPolicy{ u"android" }) + .setNameManglerPolicy(NameManglerPolicy{ "android" }) .build(); JavaClassGenerator generator(context.get(), table.get(), {}); std::stringstream out; - EXPECT_TRUE(generator.generate(u"android", &out)); + EXPECT_TRUE(generator.generate("android", &out)); std::string output = out.str(); EXPECT_NE(std::string::npos, output.find("int foo_bar=")); @@ -227,19 +227,19 @@ TEST(JavaClassGeneratorTest, EmitOtherPackagesAttributesInStyleable) { TEST(JavaClassGeneratorTest, CommentsForSimpleResourcesArePresent) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"android", 0x01) - .addSimple(u"@android:id/foo", ResourceId(0x01010000)) + .setPackageId("android", 0x01) + .addSimple("@android:id/foo", ResourceId(0x01010000)) .build(); - test::getValue<Id>(table.get(), u"@android:id/foo") - ->setComment(std::u16string(u"This is a comment\n@deprecated")); + test::getValue<Id>(table.get(), "@android:id/foo") + ->setComment(std::string("This is a comment\n@deprecated")); std::unique_ptr<IAaptContext> context = test::ContextBuilder() .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get())) - .setNameManglerPolicy(NameManglerPolicy{ u"android" }) + .setNameManglerPolicy(NameManglerPolicy{ "android" }) .build(); JavaClassGenerator generator(context.get(), table.get(), {}); std::stringstream out; - ASSERT_TRUE(generator.generate(u"android", &out)); + ASSERT_TRUE(generator.generate("android", &out)); std::string actual = out.str(); const char* expectedText = @@ -259,59 +259,56 @@ TEST(JavaClassGeneratorTest, CommentsForEnumAndFlagAttributesArePresent) { TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent) { Attribute attr(false); - attr.setComment(StringPiece16(u"This is an attribute")); + attr.setComment(StringPiece("This is an attribute")); Styleable styleable; - styleable.entries.push_back(Reference(test::parseNameOrDie(u"@android:attr/one"))); - styleable.setComment(StringPiece16(u"This is a styleable")); + styleable.entries.push_back(Reference(test::parseNameOrDie("@android:attr/one"))); + styleable.setComment(StringPiece("This is a styleable")); std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"android", 0x01) - .addValue(u"@android:attr/one", util::make_unique<Attribute>(attr)) - .addValue(u"@android:styleable/Container", + .setPackageId("android", 0x01) + .addValue("@android:attr/one", util::make_unique<Attribute>(attr)) + .addValue("@android:styleable/Container", std::unique_ptr<Styleable>(styleable.clone(nullptr))) .build(); std::unique_ptr<IAaptContext> context = test::ContextBuilder() .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get())) - .setNameManglerPolicy(NameManglerPolicy{ u"android" }) + .setNameManglerPolicy(NameManglerPolicy{ "android" }) .build(); JavaClassGeneratorOptions options; options.useFinal = false; JavaClassGenerator generator(context.get(), table.get(), options); std::stringstream out; - ASSERT_TRUE(generator.generate(u"android", &out)); + ASSERT_TRUE(generator.generate("android", &out)); std::string actual = 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(util::utf16ToUtf8(attr.getComment()))); - EXPECT_NE(std::string::npos, actual.find(util::utf16ToUtf8(styleable.getComment()))); + EXPECT_NE(std::string::npos, actual.find(attr.getComment().data())); + EXPECT_NE(std::string::npos, actual.find(styleable.getComment().data())); } TEST(JavaClassGeneratorTest, CommentsForRemovedAttributesAreNotPresentInClass) { Attribute attr(false); - attr.setComment(StringPiece16(u"@removed")); - + attr.setComment(StringPiece("@removed")); std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"android", 0x01) - .addValue(u"@android:attr/one", util::make_unique<Attribute>(attr)) + .setPackageId("android", 0x01) + .addValue("@android:attr/one", util::make_unique<Attribute>(attr)) .build(); std::unique_ptr<IAaptContext> context = test::ContextBuilder() .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get())) - .setNameManglerPolicy(NameManglerPolicy{ u"android" }) + .setNameManglerPolicy(NameManglerPolicy{ "android" }) .build(); JavaClassGeneratorOptions options; options.useFinal = false; JavaClassGenerator generator(context.get(), table.get(), options); std::stringstream out; - ASSERT_TRUE(generator.generate(u"android", &out)); + ASSERT_TRUE(generator.generate("android", &out)); std::string actual = out.str(); - std::cout << actual << std::endl; - EXPECT_EQ(std::string::npos, actual.find("@attr name android:one")); EXPECT_EQ(std::string::npos, actual.find("@attr description")); diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp index be8955ecdf83..5ff11b1303d3 100644 --- a/tools/aapt2/java/ManifestClassGenerator.cpp +++ b/tools/aapt2/java/ManifestClassGenerator.cpp @@ -25,12 +25,12 @@ namespace aapt { -static Maybe<StringPiece16> extractJavaIdentifier(IDiagnostics* diag, const Source& source, - const StringPiece16& value) { - const StringPiece16 sep = u"."; +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()); - StringPiece16 result; + StringPiece result; if (iter != value.end()) { result.assign(iter + sep.size(), value.end() - (iter + sep.size())); } else { @@ -42,15 +42,15 @@ static Maybe<StringPiece16> extractJavaIdentifier(IDiagnostics* diag, const Sour return {}; } - iter = util::findNonAlphaNumericAndNotInSet(result, u"_"); + iter = util::findNonAlphaNumericAndNotInSet(result, "_"); if (iter != result.end()) { diag->error(DiagMessage(source) - << "invalid character '" << StringPiece16(iter, 1) + << "invalid character '" << StringPiece(iter, 1) << "' in '" << result << "'"); return {}; } - if (*result.begin() >= u'0' && *result.begin() <= u'9') { + if (*result.begin() >= '0' && *result.begin() <= '9') { diag->error(DiagMessage(source) << "symbol can not start with a digit"); return {}; } @@ -60,20 +60,20 @@ static Maybe<StringPiece16> extractJavaIdentifier(IDiagnostics* diag, const Sour static bool writeSymbol(const Source& source, IDiagnostics* diag, xml::Element* el, ClassDefinition* classDef) { - xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name"); + xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, "name"); if (!attr) { diag->error(DiagMessage(source) << "<" << el->name << "> must define 'android:name'"); return false; } - Maybe<StringPiece16> result = extractJavaIdentifier(diag, source.withLine(el->lineNumber), - attr->value); + Maybe<StringPiece> result = extractJavaIdentifier(diag, source.withLine(el->lineNumber), + attr->value); if (!result) { return false; } std::unique_ptr<StringMember> stringMember = util::make_unique<StringMember>( - util::utf16ToUtf8(result.value()), util::utf16ToUtf8(attr->value)); + result.value(), attr->value); stringMember->getCommentBuilder()->appendComment(el->comment); classDef->addMember(std::move(stringMember)); @@ -87,7 +87,7 @@ std::unique_ptr<ClassDefinition> generateManifestClass(IDiagnostics* diag, xml:: return {}; } - if (el->name != u"manifest" && !el->namespaceUri.empty()) { + if (el->name != "manifest" && !el->namespaceUri.empty()) { diag->error(DiagMessage(res->file.source) << "no <manifest> root tag defined"); return {}; } @@ -102,9 +102,9 @@ std::unique_ptr<ClassDefinition> generateManifestClass(IDiagnostics* diag, xml:: std::vector<xml::Element*> children = el->getChildElements(); for (xml::Element* childEl : children) { if (childEl->namespaceUri.empty()) { - if (childEl->name == u"permission") { + if (childEl->name == "permission") { error |= !writeSymbol(res->file.source, diag, childEl, permissionClass.get()); - } else if (childEl->name == u"permission-group") { + } else if (childEl->name == "permission-group") { error |= !writeSymbol(res->file.source, diag, childEl, permissionGroupClass.get()); } } diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp index d3bca7068cb2..eecb54464d40 100644 --- a/tools/aapt2/java/ManifestClassGenerator_test.cpp +++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp @@ -15,10 +15,7 @@ */ #include "java/ManifestClassGenerator.h" -#include "test/Builders.h" -#include "test/Context.h" - -#include <gtest/gtest.h> +#include "test/Test.h" namespace aapt { diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp index c610bb0f2ff2..90616605b7aa 100644 --- a/tools/aapt2/java/ProguardRules.cpp +++ b/tools/aapt2/java/ProguardRules.cpp @@ -43,7 +43,7 @@ public: node->namespaceUri); if (maybePackage) { // This is a custom view, let's figure out the class name from this. - std::u16string package = maybePackage.value().package + u"." + node->name; + std::string package = maybePackage.value().package + "." + node->name; if (util::isJavaClassName(package)) { addClass(node->lineNumber, package); } @@ -58,11 +58,11 @@ public: } protected: - void addClass(size_t lineNumber, const std::u16string& className) { + void addClass(size_t lineNumber, const std::string& className) { mKeepSet->addClass(Source(mSource.path, lineNumber), className); } - void addMethod(size_t lineNumber, const std::u16string& methodName) { + void addMethod(size_t lineNumber, const std::string& methodName) { mKeepSet->addMethod(Source(mSource.path, lineNumber), methodName); } @@ -79,19 +79,19 @@ struct LayoutVisitor : public BaseVisitor { bool checkClass = false; bool checkName = false; if (node->namespaceUri.empty()) { - checkClass = node->name == u"view" || node->name == u"fragment"; + checkClass = node->name == "view" || node->name == "fragment"; } else if (node->namespaceUri == xml::kSchemaAndroid) { - checkName = node->name == u"fragment"; + checkName = node->name == "fragment"; } for (const auto& attr : node->attributes) { - if (checkClass && attr.namespaceUri.empty() && attr.name == u"class" && + if (checkClass && attr.namespaceUri.empty() && attr.name == "class" && util::isJavaClassName(attr.value)) { addClass(node->lineNumber, attr.value); } else if (checkName && attr.namespaceUri == xml::kSchemaAndroid && - attr.name == u"name" && util::isJavaClassName(attr.value)) { + attr.name == "name" && util::isJavaClassName(attr.value)) { addClass(node->lineNumber, attr.value); - } else if (attr.namespaceUri == xml::kSchemaAndroid && attr.name == u"onClick") { + } else if (attr.namespaceUri == xml::kSchemaAndroid && attr.name == "onClick") { addMethod(node->lineNumber, attr.value); } } @@ -107,11 +107,11 @@ struct XmlResourceVisitor : public BaseVisitor { virtual void visit(xml::Element* node) override { bool checkFragment = false; if (node->namespaceUri.empty()) { - checkFragment = node->name == u"PreferenceScreen" || node->name == u"header"; + checkFragment = node->name == "PreferenceScreen" || node->name == "header"; } if (checkFragment) { - xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, u"fragment"); + xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, "fragment"); if (attr && util::isJavaClassName(attr->value)) { addClass(node->lineNumber, attr->value); } @@ -127,9 +127,9 @@ struct TransitionVisitor : public BaseVisitor { virtual void visit(xml::Element* node) override { bool checkClass = node->namespaceUri.empty() && - (node->name == u"transition" || node->name == u"pathMotion"); + (node->name == "transition" || node->name == "pathMotion"); if (checkClass) { - xml::Attribute* attr = node->findAttribute({}, u"class"); + xml::Attribute* attr = node->findAttribute({}, "class"); if (attr && util::isJavaClassName(attr->value)) { addClass(node->lineNumber, attr->value); } @@ -140,38 +140,57 @@ struct TransitionVisitor : public BaseVisitor { }; struct ManifestVisitor : public BaseVisitor { - ManifestVisitor(const Source& source, KeepSet* keepSet) : BaseVisitor(source, keepSet) { + ManifestVisitor(const Source& source, KeepSet* keepSet, bool mainDexOnly) + : BaseVisitor(source, keepSet), mMainDexOnly(mainDexOnly) { } virtual void visit(xml::Element* node) override { if (node->namespaceUri.empty()) { bool getName = false; - if (node->name == u"manifest") { - xml::Attribute* attr = node->findAttribute({}, u"package"); + if (node->name == "manifest") { + xml::Attribute* attr = node->findAttribute({}, "package"); if (attr) { mPackage = attr->value; } - } else if (node->name == u"application") { + } else if (node->name == "application") { getName = true; - xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, u"backupAgent"); + xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, "backupAgent"); if (attr) { - Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage, - attr->value); + Maybe<std::string> result = util::getFullyQualifiedClassName(mPackage, + attr->value); if (result) { addClass(node->lineNumber, result.value()); } } - } else if (node->name == u"activity" || node->name == u"service" || - node->name == u"receiver" || node->name == u"provider" || - node->name == u"instrumentation") { + if (mMainDexOnly) { + xml::Attribute* defaultProcess = node->findAttribute(xml::kSchemaAndroid, + "process"); + if (defaultProcess) { + mDefaultProcess = defaultProcess->value; + } + } + } else if (node->name == "activity" || node->name == "service" || + node->name == "receiver" || node->name == "provider" || + node->name == "instrumentation") { getName = true; } if (getName) { - xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, u"name"); - if (attr) { - Maybe<std::u16string> result = util::getFullyQualifiedClassName(mPackage, - attr->value); + xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, "name"); + getName = attr != nullptr; + + if (getName && mMainDexOnly) { + xml::Attribute* componentProcess = node->findAttribute(xml::kSchemaAndroid, + "process"); + + const std::string& process = componentProcess ? componentProcess->value + : mDefaultProcess; + getName = !process.empty() && process[0] != ':'; + } + + if (getName) { + Maybe<std::string> result = util::getFullyQualifiedClassName(mPackage, + attr->value); if (result) { addClass(node->lineNumber, result.value()); } @@ -181,12 +200,15 @@ struct ManifestVisitor : public BaseVisitor { BaseVisitor::visit(node); } - std::u16string mPackage; +private: + std::string mPackage; + const bool mMainDexOnly; + std::string mDefaultProcess; }; bool collectProguardRulesForManifest(const Source& source, xml::XmlResource* res, - KeepSet* keepSet) { - ManifestVisitor visitor(source, keepSet); + KeepSet* keepSet, bool mainDexOnly) { + ManifestVisitor visitor(source, keepSet, mainDexOnly); if (res->root) { res->root->accept(&visitor); return true; diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h index aafffd39d84e..c2d2bd928f90 100644 --- a/tools/aapt2/java/ProguardRules.h +++ b/tools/aapt2/java/ProguardRules.h @@ -31,22 +31,23 @@ namespace proguard { class KeepSet { public: - inline void addClass(const Source& source, const std::u16string& className) { + inline void addClass(const Source& source, const std::string& className) { mKeepSet[className].insert(source); } - inline void addMethod(const Source& source, const std::u16string& methodName) { + inline void addMethod(const Source& source, const std::string& methodName) { mKeepMethodSet[methodName].insert(source); } private: friend bool writeKeepSet(std::ostream* out, const KeepSet& keepSet); - std::map<std::u16string, std::set<Source>> mKeepSet; - std::map<std::u16string, std::set<Source>> mKeepMethodSet; + std::map<std::string, std::set<Source>> mKeepSet; + std::map<std::string, std::set<Source>> mKeepMethodSet; }; -bool collectProguardRulesForManifest(const Source& source, xml::XmlResource* res, KeepSet* keepSet); +bool collectProguardRulesForManifest(const Source& source, xml::XmlResource* res, KeepSet* keepSet, + bool mainDexOnly = false); bool collectProguardRules(const Source& source, xml::XmlResource* res, KeepSet* keepSet); bool writeKeepSet(std::ostream* out, const KeepSet& keepSet); diff --git a/tools/aapt2/link/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp index 459c330cfbdc..8ed27c3b95f6 100644 --- a/tools/aapt2/link/AutoVersioner.cpp +++ b/tools/aapt2/link/AutoVersioner.cpp @@ -27,7 +27,9 @@ namespace aapt { bool shouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config, const int sdkVersionToGenerate) { + // We assume the caller is trying to generate a version greater than the current configuration. assert(sdkVersionToGenerate > config.sdkVersion); + const auto endIter = entry->values.end(); auto iter = entry->values.begin(); for (; iter != endIter; ++iter) { diff --git a/tools/aapt2/link/AutoVersioner_test.cpp b/tools/aapt2/link/AutoVersioner_test.cpp index 9b3a87c4eed0..7764176772b6 100644 --- a/tools/aapt2/link/AutoVersioner_test.cpp +++ b/tools/aapt2/link/AutoVersioner_test.cpp @@ -16,10 +16,7 @@ #include "ConfigDescription.h" #include "link/Linkers.h" -#include "test/Builders.h" -#include "test/Context.h" - -#include <gtest/gtest.h> +#include "test/Test.h" namespace aapt { @@ -28,7 +25,7 @@ TEST(AutoVersionerTest, GenerateVersionedResources) { const ConfigDescription landConfig = test::parseConfigOrDie("land"); const ConfigDescription sw600dpLandConfig = test::parseConfigOrDie("sw600dp-land"); - ResourceEntry entry(u"foo"); + ResourceEntry entry("foo"); entry.values.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, "")); entry.values.push_back(util::make_unique<ResourceConfigValue>(landConfig, "")); entry.values.push_back(util::make_unique<ResourceConfigValue>(sw600dpLandConfig, "")); @@ -42,7 +39,7 @@ TEST(AutoVersionerTest, GenerateVersionedResourceWhenHigherVersionExists) { const ConfigDescription sw600dpV13Config = test::parseConfigOrDie("sw600dp-v13"); const ConfigDescription v21Config = test::parseConfigOrDie("v21"); - ResourceEntry entry(u"foo"); + ResourceEntry entry("foo"); entry.values.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, "")); entry.values.push_back(util::make_unique<ResourceConfigValue>(sw600dpV13Config, "")); entry.values.push_back(util::make_unique<ResourceConfigValue>(v21Config, "")); @@ -53,73 +50,73 @@ TEST(AutoVersionerTest, GenerateVersionedResourceWhenHigherVersionExists) { TEST(AutoVersionerTest, VersionStylesForTable) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"app", 0x7f) - .addValue(u"@app:style/Foo", ResourceId(0x7f020000), test::parseConfigOrDie("v4"), + .setPackageId("app", 0x7f) + .addValue("@app:style/Foo", test::parseConfigOrDie("v4"), ResourceId(0x7f020000), test::StyleBuilder() - .addItem(u"@android:attr/onClick", ResourceId(0x0101026f), + .addItem("@android:attr/onClick", ResourceId(0x0101026f), util::make_unique<Id>()) - .addItem(u"@android:attr/paddingStart", ResourceId(0x010103b3), + .addItem("@android:attr/paddingStart", ResourceId(0x010103b3), util::make_unique<Id>()) - .addItem(u"@android:attr/requiresSmallestWidthDp", + .addItem("@android:attr/requiresSmallestWidthDp", ResourceId(0x01010364), util::make_unique<Id>()) - .addItem(u"@android:attr/colorAccent", ResourceId(0x01010435), + .addItem("@android:attr/colorAccent", ResourceId(0x01010435), util::make_unique<Id>()) .build()) - .addValue(u"@app:style/Foo", ResourceId(0x7f020000), test::parseConfigOrDie("v21"), + .addValue("@app:style/Foo", test::parseConfigOrDie("v21"), ResourceId(0x7f020000), test::StyleBuilder() - .addItem(u"@android:attr/paddingEnd", ResourceId(0x010103b4), + .addItem("@android:attr/paddingEnd", ResourceId(0x010103b4), util::make_unique<Id>()) .build()) .build(); std::unique_ptr<IAaptContext> context = test::ContextBuilder() - .setCompilationPackage(u"app") + .setCompilationPackage("app") .setPackageId(0x7f) .build(); AutoVersioner versioner; ASSERT_TRUE(versioner.consume(context.get(), table.get())); - Style* style = test::getValueForConfig<Style>(table.get(), u"@app:style/Foo", + Style* style = test::getValueForConfig<Style>(table.get(), "@app:style/Foo", test::parseConfigOrDie("v4")); ASSERT_NE(style, nullptr); ASSERT_EQ(style->entries.size(), 1u); AAPT_ASSERT_TRUE(style->entries.front().key.name); EXPECT_EQ(style->entries.front().key.name.value(), - test::parseNameOrDie(u"@android:attr/onClick")); + test::parseNameOrDie("@android:attr/onClick")); - style = test::getValueForConfig<Style>(table.get(), u"@app:style/Foo", + style = test::getValueForConfig<Style>(table.get(), "@app:style/Foo", test::parseConfigOrDie("v13")); ASSERT_NE(style, nullptr); ASSERT_EQ(style->entries.size(), 2u); AAPT_ASSERT_TRUE(style->entries[0].key.name); EXPECT_EQ(style->entries[0].key.name.value(), - test::parseNameOrDie(u"@android:attr/onClick")); + test::parseNameOrDie("@android:attr/onClick")); AAPT_ASSERT_TRUE(style->entries[1].key.name); EXPECT_EQ(style->entries[1].key.name.value(), - test::parseNameOrDie(u"@android:attr/requiresSmallestWidthDp")); + test::parseNameOrDie("@android:attr/requiresSmallestWidthDp")); - style = test::getValueForConfig<Style>(table.get(), u"@app:style/Foo", + style = test::getValueForConfig<Style>(table.get(), "@app:style/Foo", test::parseConfigOrDie("v17")); ASSERT_NE(style, nullptr); ASSERT_EQ(style->entries.size(), 3u); AAPT_ASSERT_TRUE(style->entries[0].key.name); EXPECT_EQ(style->entries[0].key.name.value(), - test::parseNameOrDie(u"@android:attr/onClick")); + test::parseNameOrDie("@android:attr/onClick")); AAPT_ASSERT_TRUE(style->entries[1].key.name); EXPECT_EQ(style->entries[1].key.name.value(), - test::parseNameOrDie(u"@android:attr/requiresSmallestWidthDp")); + test::parseNameOrDie("@android:attr/requiresSmallestWidthDp")); AAPT_ASSERT_TRUE(style->entries[2].key.name); EXPECT_EQ(style->entries[2].key.name.value(), - test::parseNameOrDie(u"@android:attr/paddingStart")); + test::parseNameOrDie("@android:attr/paddingStart")); - style = test::getValueForConfig<Style>(table.get(), u"@app:style/Foo", + style = test::getValueForConfig<Style>(table.get(), "@app:style/Foo", test::parseConfigOrDie("v21")); ASSERT_NE(style, nullptr); ASSERT_EQ(style->entries.size(), 1u); AAPT_ASSERT_TRUE(style->entries.front().key.name); EXPECT_EQ(style->entries.front().key.name.value(), - test::parseNameOrDie(u"@android:attr/paddingEnd")); + test::parseNameOrDie("@android:attr/paddingEnd")); } } // namespace aapt diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp index 49971201fb3c..8093e6a035d9 100644 --- a/tools/aapt2/link/Link.cpp +++ b/tools/aapt2/link/Link.cpp @@ -58,9 +58,10 @@ struct LinkOptions { std::vector<std::string> includePaths; std::vector<std::string> overlayFiles; Maybe<std::string> generateJavaClassPath; - Maybe<std::u16string> customJavaPackage; - std::set<std::u16string> extraJavaPackages; + Maybe<std::string> customJavaPackage; + std::set<std::string> extraJavaPackages; Maybe<std::string> generateProguardRulesPath; + Maybe<std::string> generateMainDexProguardRulesPath; bool noAutoVersion = false; bool noVersionVectors = false; bool staticLib = false; @@ -71,7 +72,7 @@ struct LinkOptions { bool autoAddOverlay = false; bool doNotCompressAnything = false; std::vector<std::string> extensionsToNotCompress; - Maybe<std::u16string> privateSymbols; + Maybe<std::string> privateSymbols; ManifestFixerOptions manifestFixerOptions; std::unordered_set<std::string> products; TableSplitterOptions tableSplitterOptions; @@ -94,11 +95,11 @@ public: mNameMangler = NameMangler(policy); } - const std::u16string& getCompilationPackage() override { + const std::string& getCompilationPackage() override { return mCompilationPackage; } - void setCompilationPackage(const StringPiece16& packageName) { + void setCompilationPackage(const StringPiece& packageName) { mCompilationPackage = packageName.toString(); } @@ -122,13 +123,22 @@ public: mVerbose = val; } + int getMinSdkVersion() override { + return mMinSdkVersion; + } + + void setMinSdkVersion(int minSdk) { + mMinSdkVersion = minSdk; + } + private: StdErrDiagnostics mDiagnostics; NameMangler mNameMangler; - std::u16string mCompilationPackage; + std::string mCompilationPackage; uint8_t mPackageId = 0x0; SymbolTable mSymbols; bool mVerbose = false; + int mMinSdkVersion = 0; }; static bool copyFileToArchive(io::IFile* file, const std::string& outPath, @@ -145,7 +155,7 @@ static bool copyFileToArchive(io::IFile* file, const std::string& outPath, size_t bufferSize = data->size(); // If the file ends with .flat, we must strip off the CompiledFileHeader from it. - if (util::stringEndsWith<char>(file->getSource().path, ".flat")) { + if (util::stringEndsWith(file->getSource().path, ".flat")) { CompiledFileInputStream inputStream(data->data(), data->size()); if (!inputStream.CompiledFile()) { context->getDiagnostics()->error(DiagMessage(file->getSource()) @@ -203,16 +213,6 @@ static bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path, Maybe< return false; } -/*static std::unique_ptr<ResourceTable> loadTable(const Source& source, const void* data, size_t len, - IDiagnostics* diag) { - std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>(); - BinaryResourceParser parser(diag, table.get(), source, data, len); - if (!parser.parse()) { - return {}; - } - return table; -}*/ - static std::unique_ptr<ResourceTable> loadTableFromPb(const Source& source, const void* data, size_t len, IDiagnostics* diag) { @@ -282,6 +282,7 @@ struct ResourceFileFlattenerOptions { bool noVersionVectors = false; bool keepRawValues = false; bool doNotCompressAnything = false; + bool updateProguardSpec = false; std::vector<std::string> extensionsToNotCompress; }; @@ -318,7 +319,7 @@ uint32_t ResourceFileFlattener::getCompressionFlags(const StringPiece& str) { } for (const std::string& extension : mOptions.extensionsToNotCompress) { - if (util::stringEndsWith<char>(str, extension)) { + if (util::stringEndsWith(str, extension)) { return 0; } } @@ -341,7 +342,7 @@ bool ResourceFileFlattener::linkAndVersionXmlFile(const ResourceEntry* entry, return false; } - if (util::stringEndsWith<char>(srcPath, ".flat")) { + if (util::stringEndsWith(srcPath, ".flat")) { outFileOp->xmlToFlatten = loadBinaryXmlSkipFileExport(file->getSource(), data->data(), data->size(), mContext->getDiagnostics()); @@ -363,8 +364,8 @@ bool ResourceFileFlattener::linkAndVersionXmlFile(const ResourceEntry* entry, return false; } - if (!proguard::collectProguardRules(outFileOp->xmlToFlatten->file.source, - outFileOp->xmlToFlatten.get(), mKeepSet)) { + if (mOptions.updateProguardSpec && !proguard::collectProguardRules( + outFileOp->xmlToFlatten->file.source, outFileOp->xmlToFlatten.get(), mKeepSet)) { return false; } @@ -373,7 +374,7 @@ bool ResourceFileFlattener::linkAndVersionXmlFile(const ResourceEntry* entry, // Skip this if it is a vector or animated-vector. xml::Element* el = xml::findRootElement(outFileOp->xmlToFlatten.get()); if (el && el->namespaceUri.empty()) { - if (el->name == u"vector" || el->name == u"animated-vector") { + if (el->name == "vector" || el->name == "animated-vector") { // We are NOT going to version this file. outFileOp->skipVersion = true; return true; @@ -402,8 +403,8 @@ bool ResourceFileFlattener::linkAndVersionXmlFile(const ResourceEntry* entry, << versionedFileDesc.config << "'"); } - std::u16string genPath = util::utf8ToUtf16(ResourceUtils::buildResourceFileName( - versionedFileDesc, mContext->getNameMangler())); + std::string genPath = ResourceUtils::buildResourceFileName( + versionedFileDesc, mContext->getNameMangler()); bool added = table->addFileReferenceAllowMangled(versionedFileDesc.name, versionedFileDesc.config, @@ -427,7 +428,7 @@ bool ResourceFileFlattener::linkAndVersionXmlFile(const ResourceEntry* entry, */ bool ResourceFileFlattener::flatten(ResourceTable* table, IArchiveWriter* archiveWriter) { bool error = false; - std::map<std::pair<ConfigDescription, StringPiece16>, FileOperation> configSortedFiles; + std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> configSortedFiles; for (auto& pkg : table->packages) { for (auto& type : pkg->types) { @@ -452,12 +453,12 @@ bool ResourceFileFlattener::flatten(ResourceTable* table, IArchiveWriter* archiv } FileOperation fileOp; - fileOp.dstPath = util::utf16ToUtf8(*fileRef->path); + fileOp.dstPath = *fileRef->path; const StringPiece srcPath = file->getSource().path; if (type->type != ResourceType::kRaw && - (util::stringEndsWith<char>(srcPath, ".xml.flat") || - util::stringEndsWith<char>(srcPath, ".xml"))) { + (util::stringEndsWith(srcPath, ".xml.flat") || + util::stringEndsWith(srcPath, ".xml"))) { ResourceFile fileDesc; fileDesc.config = configValue->config; fileDesc.name = ResourceName(pkg->name, type->type, entry->name); @@ -475,7 +476,7 @@ bool ResourceFileFlattener::flatten(ResourceTable* table, IArchiveWriter* archiv // we end up copying the string in the std::make_pair() method, then creating // a StringPiece16 from the copy, which would cause us to end up referencing // garbage in the map. - const StringPiece16 entryName(entry->name); + const StringPiece entryName(entry->name); configSortedFiles[std::make_pair(configValue->config, entryName)] = std::move(fileOp); } @@ -576,14 +577,33 @@ public: return true; } - Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes) { + Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes, IDiagnostics* diag) { // Make sure the first element is <manifest> with package attribute. if (xml::Element* manifestEl = xml::findRootElement(xmlRes->root.get())) { - if (manifestEl->namespaceUri.empty() && manifestEl->name == u"manifest") { - if (xml::Attribute* packageAttr = manifestEl->findAttribute({}, u"package")) { - return AppInfo{ packageAttr->value }; + AppInfo appInfo; + + if (!manifestEl->namespaceUri.empty() || manifestEl->name != "manifest") { + diag->error(DiagMessage(xmlRes->file.source) << "root tag must be <manifest>"); + return {}; + } + + xml::Attribute* packageAttr = manifestEl->findAttribute({}, "package"); + if (!packageAttr) { + diag->error(DiagMessage(xmlRes->file.source) + << "<manifest> must have a 'package' attribute"); + return {}; + } + + appInfo.package = packageAttr->value; + + if (xml::Element* usesSdkEl = manifestEl->findChild({}, "uses-sdk")) { + if (xml::Attribute* minSdk = + usesSdkEl->findAttribute(xml::kSchemaAndroid, "minSdkVersion")) { + appInfo.minSdkVersion = minSdk->value; } } + + return appInfo; } return {}; } @@ -612,7 +632,7 @@ public: // Special case the occurrence of an ID that is being generated for the // 'android' package. This is due to legacy reasons. if (valueCast<Id>(configValue->value.get()) && - package->name == u"android") { + package->name == "android") { mContext->getDiagnostics()->warn( DiagMessage(configValue->value->getSource()) << "generated id '" << resName @@ -722,14 +742,14 @@ public: return true; } - bool writeJavaFile(ResourceTable* table, const StringPiece16& packageNameToGenerate, - const StringPiece16& outPackage, JavaClassGeneratorOptions javaOptions) { + bool writeJavaFile(ResourceTable* table, const StringPiece& packageNameToGenerate, + const StringPiece& outPackage, JavaClassGeneratorOptions javaOptions) { if (!mOptions.generateJavaClassPath) { return true; } std::string outPath = mOptions.generateJavaClassPath.value(); - file::appendPath(&outPath, file::packageToPath(util::utf16ToUtf8(outPackage))); + file::appendPath(&outPath, file::packageToPath(outPackage)); if (!file::mkdirs(outPath)) { mContext->getDiagnostics()->error( DiagMessage() << "failed to create directory '" << outPath << "'"); @@ -783,7 +803,7 @@ public: manifestClass->getCommentBuilder()->appendComment(properAnnotation); } - const std::string packageUtf8 = util::utf16ToUtf8(mContext->getCompilationPackage()); + const std::string& packageUtf8 = mContext->getCompilationPackage(); std::string outPath = mOptions.generateJavaClassPath.value(); file::appendPath(&outPath, file::packageToPath(packageUtf8)); @@ -811,12 +831,12 @@ public: return true; } - bool writeProguardFile(const proguard::KeepSet& keepSet) { - if (!mOptions.generateProguardRulesPath) { + bool writeProguardFile(const Maybe<std::string>& out, const proguard::KeepSet& keepSet) { + if (!out) { return true; } - const std::string& outPath = mOptions.generateProguardRulesPath.value(); + const std::string& outPath = out.value(); std::ofstream fout(outPath, std::ofstream::binary); if (!fout) { mContext->getDiagnostics()->error( @@ -891,7 +911,7 @@ public: mOptions.extraJavaPackages.insert(pkg->name); } - pkg->name = u""; + pkg->name = ""; if (override) { result = mTableMerger->mergeOverlay(Source(input), table.get(), collection.get()); } else { @@ -1029,12 +1049,12 @@ public: * Otherwise the files is processed on its own. */ bool mergePath(const std::string& path, bool override) { - if (util::stringEndsWith<char>(path, ".flata") || - util::stringEndsWith<char>(path, ".jar") || - util::stringEndsWith<char>(path, ".jack") || - util::stringEndsWith<char>(path, ".zip")) { + if (util::stringEndsWith(path, ".flata") || + util::stringEndsWith(path, ".jar") || + util::stringEndsWith(path, ".jack") || + util::stringEndsWith(path, ".zip")) { return mergeArchive(path, override); - } else if (util::stringEndsWith<char>(path, ".apk")) { + } else if (util::stringEndsWith(path, ".apk")) { return mergeStaticLibrary(path, override); } @@ -1055,10 +1075,10 @@ public: */ bool mergeFile(io::IFile* file, bool override) { const Source& src = file->getSource(); - if (util::stringEndsWith<char>(src.path, ".arsc.flat")) { + if (util::stringEndsWith(src.path, ".arsc.flat")) { return mergeResourceTable(file, override); - } else if (util::stringEndsWith<char>(src.path, ".flat")){ + } else if (util::stringEndsWith(src.path, ".flat")){ // Try opening the file and looking for an Export header. std::unique_ptr<io::IData> data = file->openAsData(); if (!data) { @@ -1087,11 +1107,11 @@ public: return 1; } - if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get())) { - mContext->setCompilationPackage(maybeAppInfo.value().package); + if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get(), + mContext->getDiagnostics())) { + AppInfo& appInfo = maybeAppInfo.value(); + mContext->setCompilationPackage(appInfo.package); } else { - mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath) - << "no package specified in <manifest> tag"); return 1; } @@ -1105,7 +1125,7 @@ public: mContext->setNameManglerPolicy(NameManglerPolicy{ mContext->getCompilationPackage() }); - if (mContext->getCompilationPackage() == u"android") { + if (mContext->getCompilationPackage() == "android") { mContext->setPackageId(0x01); } else { mContext->setPackageId(0x7f); @@ -1213,6 +1233,7 @@ public: } proguard::KeepSet proguardKeepSet; + proguard::KeepSet proguardMainDexKeepSet; std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter(); if (!archiveWriter) { @@ -1234,9 +1255,18 @@ public: XmlReferenceLinker manifestLinker; if (manifestLinker.consume(mContext, manifestXml.get())) { - if (!proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath), - manifestXml.get(), - &proguardKeepSet)) { + if (mOptions.generateProguardRulesPath && + !proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath), + manifestXml.get(), + &proguardKeepSet)) { + error = true; + } + + if (mOptions.generateMainDexProguardRulesPath && + !proguard::collectProguardRulesForManifest(Source(mOptions.manifestPath), + manifestXml.get(), + &proguardMainDexKeepSet, + true)) { error = true; } @@ -1268,6 +1298,8 @@ public: fileFlattenerOptions.extensionsToNotCompress = mOptions.extensionsToNotCompress; fileFlattenerOptions.noAutoVersion = mOptions.noAutoVersion; fileFlattenerOptions.noVersionVectors = mOptions.noVersionVectors; + fileFlattenerOptions.updateProguardSpec = + static_cast<bool>(mOptions.generateProguardRulesPath); ResourceFileFlattener fileFlattener(fileFlattenerOptions, mContext, &proguardKeepSet); if (!fileFlattener.flatten(&mFinalTable, archiveWriter.get())) { @@ -1283,6 +1315,28 @@ public: } } + Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get(), + mContext->getDiagnostics()); + if (maybeAppInfo && maybeAppInfo.value().minSdkVersion) { + if (Maybe<int> maybeMinSdkVersion = + ResourceUtils::tryParseSdkVersion(maybeAppInfo.value().minSdkVersion.value())) { + mContext->setMinSdkVersion(maybeMinSdkVersion.value()); + } + } + + if (!mOptions.staticLib && mContext->getMinSdkVersion() > 0) { + if (mContext->verbose()) { + mContext->getDiagnostics()->note( + DiagMessage() << "collapsing resource versions for minimum SDK " + << mContext->getMinSdkVersion()); + } + + VersionCollapser collapser; + if (!collapser.consume(mContext, &mFinalTable)) { + return 1; + } + } + if (mOptions.staticLib) { if (!flattenTableToPb(&mFinalTable, archiveWriter.get())) { mContext->getDiagnostics()->error(DiagMessage() @@ -1306,8 +1360,8 @@ public: options.useFinal = false; } - const StringPiece16 actualPackage = mContext->getCompilationPackage(); - StringPiece16 outputPackage = mContext->getCompilationPackage(); + const StringPiece actualPackage = mContext->getCompilationPackage(); + StringPiece outputPackage = mContext->getCompilationPackage(); if (mOptions.customJavaPackage) { // Override the output java package to the custom one. outputPackage = mOptions.customJavaPackage.value(); @@ -1331,17 +1385,19 @@ public: return 1; } - for (const std::u16string& extraPackage : mOptions.extraJavaPackages) { + for (const std::string& extraPackage : mOptions.extraJavaPackages) { if (!writeJavaFile(&mFinalTable, actualPackage, extraPackage, options)) { return 1; } } } - if (mOptions.generateProguardRulesPath) { - if (!writeProguardFile(proguardKeepSet)) { - return 1; - } + if (!writeProguardFile(mOptions.generateProguardRulesPath, proguardKeepSet)) { + return 1; + } + + if (!writeProguardFile(mOptions.generateMainDexProguardRulesPath, proguardMainDexKeepSet)) { + return 1; } if (mContext->verbose()) { @@ -1373,11 +1429,7 @@ private: int link(const std::vector<StringPiece>& args) { LinkContext context; LinkOptions options; - Maybe<std::string> privateSymbolsPackage; - Maybe<std::string> minSdkVersion, targetSdkVersion; - Maybe<std::string> renameManifestPackage, renameInstrumentationTargetPackage; - Maybe<std::string> versionCode, versionName; - Maybe<std::string> customJavaPackage; + std::vector<std::string> overlayArgList; std::vector<std::string> extraJavaPackages; Maybe<std::string> configs; Maybe<std::string> preferredDensity; @@ -1392,11 +1444,14 @@ int link(const std::vector<StringPiece>& args) { .optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths) .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n" "The last conflicting resource given takes precedence.", - &options.overlayFiles) + &overlayArgList) .optionalFlag("--java", "Directory in which to generate R.java", &options.generateJavaClassPath) .optionalFlag("--proguard", "Output file for generated Proguard rules", &options.generateProguardRulesPath) + .optionalFlag("--proguard-main-dex", + "Output file for generated Proguard rules for the main dex", + &options.generateMainDexProguardRulesPath) .optionalSwitch("--no-auto-version", "Disables automatic style and layout SDK versioning", &options.noAutoVersion) @@ -1419,14 +1474,19 @@ int link(const std::vector<StringPiece>& args) { "by -o", &options.outputToDirectory) .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for " - "AndroidManifest.xml", &minSdkVersion) + "AndroidManifest.xml", + &options.manifestFixerOptions.minSdkVersionDefault) .optionalFlag("--target-sdk-version", "Default target SDK version to use for " - "AndroidManifest.xml", &targetSdkVersion) + "AndroidManifest.xml", + &options.manifestFixerOptions.targetSdkVersionDefault) .optionalFlag("--version-code", "Version code (integer) to inject into the " - "AndroidManifest.xml if none is present", &versionCode) + "AndroidManifest.xml if none is present", + &options.manifestFixerOptions.versionCodeDefault) .optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml " - "if none is present", &versionName) - .optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib) + "if none is present", + &options.manifestFixerOptions.versionNameDefault) + .optionalSwitch("--static-lib", "Generate a static Android library", + &options.staticLib) .optionalSwitch("--no-static-lib-packages", "Merge all library resources under the app's package", &options.noStaticLibPackages) @@ -1436,24 +1496,29 @@ int link(const std::vector<StringPiece>& args) { .optionalFlag("--private-symbols", "Package name to use when generating R.java for " "private symbols.\n" "If not specified, public and private symbols will use the application's " - "package name", &privateSymbolsPackage) + "package name", + &options.privateSymbols) .optionalFlag("--custom-package", "Custom Java package under which to generate R.java", - &customJavaPackage) + &options.customJavaPackage) .optionalFlagList("--extra-packages", "Generate the same R.java but with different " - "package names", &extraJavaPackages) + "package names", + &extraJavaPackages) .optionalFlagList("--add-javadoc-annotation", "Adds a JavaDoc annotation to all " - "generated Java classes", &options.javadocAnnotations) + "generated Java classes", + &options.javadocAnnotations) .optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in " - "overlays without <add-resource> tags", &options.autoAddOverlay) + "overlays without <add-resource> tags", + &options.autoAddOverlay) .optionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml", - &renameManifestPackage) + &options.manifestFixerOptions.renameManifestPackage) .optionalFlag("--rename-instrumentation-target-package", "Changes the name of the target package for instrumentation. Most useful " "when used\nin conjunction with --rename-manifest-package", - &renameInstrumentationTargetPackage) + &options.manifestFixerOptions.renameInstrumentationTargetPackage) .optionalFlagList("-0", "File extensions not to compress", &options.extensionsToNotCompress) - .optionalSwitch("-v", "Enables verbose logging", &verbose); + .optionalSwitch("-v", "Enables verbose logging", + &verbose); if (!flags.parse("aapt2 link", args, &std::cerr)) { return 1; @@ -1462,7 +1527,7 @@ int link(const std::vector<StringPiece>& args) { // Expand all argument-files passed into the command line. These start with '@'. std::vector<std::string> argList; for (const std::string& arg : flags.getArgs()) { - if (util::stringStartsWith<char>(arg, "@")) { + if (util::stringStartsWith(arg, "@")) { const std::string path = arg.substr(1, arg.size() - 1); std::string error; if (!file::appendArgsFromFile(path, &argList, &error)) { @@ -1474,56 +1539,34 @@ int link(const std::vector<StringPiece>& args) { } } - if (verbose) { - context.setVerbose(verbose); - } - - if (privateSymbolsPackage) { - options.privateSymbols = util::utf8ToUtf16(privateSymbolsPackage.value()); - } - - if (minSdkVersion) { - options.manifestFixerOptions.minSdkVersionDefault = - util::utf8ToUtf16(minSdkVersion.value()); - } - - if (targetSdkVersion) { - options.manifestFixerOptions.targetSdkVersionDefault = - util::utf8ToUtf16(targetSdkVersion.value()); - } - - if (renameManifestPackage) { - options.manifestFixerOptions.renameManifestPackage = - util::utf8ToUtf16(renameManifestPackage.value()); - } - - if (renameInstrumentationTargetPackage) { - options.manifestFixerOptions.renameInstrumentationTargetPackage = - util::utf8ToUtf16(renameInstrumentationTargetPackage.value()); - } - - if (versionCode) { - options.manifestFixerOptions.versionCodeDefault = util::utf8ToUtf16(versionCode.value()); - } - - if (versionName) { - options.manifestFixerOptions.versionNameDefault = util::utf8ToUtf16(versionName.value()); + // Expand all argument-files passed to -R. + for (const std::string& arg : overlayArgList) { + if (util::stringStartsWith(arg, "@")) { + const std::string path = arg.substr(1, arg.size() - 1); + std::string error; + if (!file::appendArgsFromFile(path, &options.overlayFiles, &error)) { + context.getDiagnostics()->error(DiagMessage(path) << error); + return 1; + } + } else { + options.overlayFiles.push_back(arg); + } } - if (customJavaPackage) { - options.customJavaPackage = util::utf8ToUtf16(customJavaPackage.value()); + if (verbose) { + context.setVerbose(verbose); } // 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(util::utf8ToUtf16(package)); + options.extraJavaPackages.insert(package.toString()); } } if (productList) { - for (StringPiece product : util::tokenize<char>(productList.value(), ',')) { + for (StringPiece product : util::tokenize(productList.value(), ',')) { if (product != "" && product != "default") { options.products.insert(product.toString()); } @@ -1532,7 +1575,7 @@ int link(const std::vector<StringPiece>& args) { AxisConfigFilter filter; if (configs) { - for (const StringPiece& configStr : util::tokenize<char>(configs.value(), ',')) { + for (const StringPiece& configStr : util::tokenize(configs.value(), ',')) { ConfigDescription config; LocaleValue lv; if (lv.initFromFilterString(configStr)) { diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h index ec532aba465f..43b8fb494f2c 100644 --- a/tools/aapt2/link/Linkers.h +++ b/tools/aapt2/link/Linkers.h @@ -44,14 +44,21 @@ struct CallSite { bool shouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config, const int sdkVersionToGenerate); -struct AutoVersioner : public IResourceTableConsumer { +class AutoVersioner : public IResourceTableConsumer { +public: bool consume(IAaptContext* context, ResourceTable* table) override; }; -struct XmlAutoVersioner : public IXmlResourceConsumer { +class XmlAutoVersioner : public IXmlResourceConsumer { +public: bool consume(IAaptContext* context, xml::XmlResource* resource) override; }; +class VersionCollapser : public IResourceTableConsumer { +public: + bool consume(IAaptContext* context, ResourceTable* table) override; +}; + /** * If any attribute resource values are defined as public, this consumer will move all private * attribute resource values to a private ^private-attr type, avoiding backwards compatibility diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index 77a949f1339d..ef095357d9e6 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -20,6 +20,8 @@ #include "xml/XmlActionExecutor.h" #include "xml/XmlDom.h" +#include <unordered_set> + namespace aapt { /** @@ -27,21 +29,15 @@ namespace aapt { */ static bool nameIsJavaClassName(xml::Element* el, xml::Attribute* attr, SourcePathDiagnostics* diag) { - std::u16string className = attr->value; - if (className.find(u'.') == std::u16string::npos) { - // There is no '.', so add one to the beginning. - className = u"."; - className += attr->value; - } - // We allow unqualified class names (ie: .HelloActivity) // Since we don't know the package name, we can just make a fake one here and // the test will be identical as long as the real package name is valid too. - Maybe<std::u16string> fullyQualifiedClassName = - util::getFullyQualifiedClassName(u"a", className); + Maybe<std::string> fullyQualifiedClassName = + util::getFullyQualifiedClassName("a", attr->value); + + StringPiece qualifiedClassName = fullyQualifiedClassName + ? fullyQualifiedClassName.value() : attr->value; - StringPiece16 qualifiedClassName = fullyQualifiedClassName - ? fullyQualifiedClassName.value() : className; if (!util::isJavaClassName(qualifiedClassName)) { diag->error(DiagMessage(el->lineNumber) << "attribute 'android:name' in <" @@ -52,14 +48,14 @@ static bool nameIsJavaClassName(xml::Element* el, xml::Attribute* attr, } static bool optionalNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) { - if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name")) { + if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, "name")) { return nameIsJavaClassName(el, attr, diag); } return true; } static bool requiredNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) { - if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name")) { + if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, "name")) { return nameIsJavaClassName(el, attr, diag); } diag->error(DiagMessage(el->lineNumber) @@ -68,7 +64,7 @@ static bool requiredNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* } static bool verifyManifest(xml::Element* el, SourcePathDiagnostics* diag) { - xml::Attribute* attr = el->findAttribute({}, u"package"); + xml::Attribute* attr = el->findAttribute({}, "package"); if (!attr) { diag->error(DiagMessage(el->lineNumber) << "<manifest> tag is missing 'package' attribute"); return false; @@ -105,31 +101,31 @@ bool ManifestFixer::buildRules(xml::XmlActionExecutor* executor, IDiagnostics* d // Common intent-filter actions. xml::XmlNodeAction intentFilterAction; - intentFilterAction[u"action"]; - intentFilterAction[u"category"]; - intentFilterAction[u"data"]; + intentFilterAction["action"]; + intentFilterAction["category"]; + intentFilterAction["data"]; // Common meta-data actions. xml::XmlNodeAction metaDataAction; // Manifest actions. - xml::XmlNodeAction& manifestAction = (*executor)[u"manifest"]; + xml::XmlNodeAction& manifestAction = (*executor)["manifest"]; manifestAction.action(verifyManifest); manifestAction.action([&](xml::Element* el) -> bool { if (mOptions.versionNameDefault) { - if (el->findAttribute(xml::kSchemaAndroid, u"versionName") == nullptr) { + if (el->findAttribute(xml::kSchemaAndroid, "versionName") == nullptr) { el->attributes.push_back(xml::Attribute{ xml::kSchemaAndroid, - u"versionName", + "versionName", mOptions.versionNameDefault.value() }); } } if (mOptions.versionCodeDefault) { - if (el->findAttribute(xml::kSchemaAndroid, u"versionCode") == nullptr) { + if (el->findAttribute(xml::kSchemaAndroid, "versionCode") == nullptr) { el->attributes.push_back(xml::Attribute{ xml::kSchemaAndroid, - u"versionCode", + "versionCode", mOptions.versionCodeDefault.value() }); } } @@ -137,84 +133,87 @@ bool ManifestFixer::buildRules(xml::XmlActionExecutor* executor, IDiagnostics* d }); // Meta tags. - manifestAction[u"eat-comment"]; + manifestAction["eat-comment"]; // Uses-sdk actions. - manifestAction[u"uses-sdk"].action([&](xml::Element* el) -> bool { + manifestAction["uses-sdk"].action([&](xml::Element* el) -> bool { if (mOptions.minSdkVersionDefault && - el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion") == nullptr) { + el->findAttribute(xml::kSchemaAndroid, "minSdkVersion") == nullptr) { // There was no minSdkVersion defined and we have a default to assign. el->attributes.push_back(xml::Attribute{ - xml::kSchemaAndroid, u"minSdkVersion", + xml::kSchemaAndroid, "minSdkVersion", mOptions.minSdkVersionDefault.value() }); } if (mOptions.targetSdkVersionDefault && - el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion") == nullptr) { + el->findAttribute(xml::kSchemaAndroid, "targetSdkVersion") == nullptr) { // There was no targetSdkVersion defined and we have a default to assign. el->attributes.push_back(xml::Attribute{ - xml::kSchemaAndroid, u"targetSdkVersion", + xml::kSchemaAndroid, "targetSdkVersion", mOptions.targetSdkVersionDefault.value() }); } return true; }); // Instrumentation actions. - manifestAction[u"instrumentation"].action([&](xml::Element* el) -> bool { + manifestAction["instrumentation"].action([&](xml::Element* el) -> bool { if (!mOptions.renameInstrumentationTargetPackage) { return true; } - if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"targetPackage")) { + if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, "targetPackage")) { attr->value = mOptions.renameInstrumentationTargetPackage.value(); } return true; }); - manifestAction[u"original-package"]; - manifestAction[u"protected-broadcast"]; - manifestAction[u"uses-permission"]; - manifestAction[u"permission"]; - manifestAction[u"permission-tree"]; - manifestAction[u"permission-group"]; + manifestAction["original-package"]; + manifestAction["protected-broadcast"]; + manifestAction["uses-permission"]; + manifestAction["permission"]; + manifestAction["permission-tree"]; + manifestAction["permission-group"]; - manifestAction[u"uses-configuration"]; - manifestAction[u"uses-feature"]; - manifestAction[u"supports-screens"]; - manifestAction[u"compatible-screens"]; - manifestAction[u"supports-gl-texture"]; + manifestAction["uses-configuration"]; + manifestAction["uses-feature"]; + manifestAction["supports-screens"]; + manifestAction["compatible-screens"]; + manifestAction["supports-gl-texture"]; // Application actions. - xml::XmlNodeAction& applicationAction = (*executor)[u"manifest"][u"application"]; + xml::XmlNodeAction& applicationAction = manifestAction["application"]; applicationAction.action(optionalNameIsJavaClassName); // Uses library actions. - applicationAction[u"uses-library"]; + applicationAction["uses-library"]; + + // Meta-data. + applicationAction["meta-data"] = metaDataAction; // Activity actions. - applicationAction[u"activity"].action(requiredNameIsJavaClassName); - applicationAction[u"activity"][u"intent-filter"] = intentFilterAction; - applicationAction[u"activity"][u"meta-data"] = metaDataAction; + applicationAction["activity"].action(requiredNameIsJavaClassName); + applicationAction["activity"]["intent-filter"] = intentFilterAction; + applicationAction["activity"]["meta-data"] = metaDataAction; // Activity alias actions. - applicationAction[u"activity-alias"][u"intent-filter"] = intentFilterAction; - applicationAction[u"activity-alias"][u"meta-data"] = metaDataAction; + applicationAction["activity-alias"]["intent-filter"] = intentFilterAction; + applicationAction["activity-alias"]["meta-data"] = metaDataAction; // Service actions. - applicationAction[u"service"].action(requiredNameIsJavaClassName); - applicationAction[u"service"][u"intent-filter"] = intentFilterAction; - applicationAction[u"service"][u"meta-data"] = metaDataAction; + applicationAction["service"].action(requiredNameIsJavaClassName); + applicationAction["service"]["intent-filter"] = intentFilterAction; + applicationAction["service"]["meta-data"] = metaDataAction; // Receiver actions. - applicationAction[u"receiver"].action(requiredNameIsJavaClassName); - applicationAction[u"receiver"][u"intent-filter"] = intentFilterAction; - applicationAction[u"receiver"][u"meta-data"] = metaDataAction; + applicationAction["receiver"].action(requiredNameIsJavaClassName); + applicationAction["receiver"]["intent-filter"] = intentFilterAction; + applicationAction["receiver"]["meta-data"] = metaDataAction; // Provider actions. - applicationAction[u"provider"].action(requiredNameIsJavaClassName); - applicationAction[u"provider"][u"grant-uri-permissions"]; - applicationAction[u"provider"][u"meta-data"] = metaDataAction; - applicationAction[u"provider"][u"path-permissions"]; + applicationAction["provider"].action(requiredNameIsJavaClassName); + applicationAction["provider"]["grant-uri-permissions"]; + applicationAction["provider"]["meta-data"] = metaDataAction; + applicationAction["provider"]["path-permissions"]; return true; } @@ -222,14 +221,17 @@ class FullyQualifiedClassNameVisitor : public xml::Visitor { public: using xml::Visitor::visit; - FullyQualifiedClassNameVisitor(const StringPiece16& package) : mPackage(package) { + explicit FullyQualifiedClassNameVisitor(const StringPiece& package) : mPackage(package) { } void visit(xml::Element* el) override { for (xml::Attribute& attr : el->attributes) { - if (Maybe<std::u16string> newValue = - util::getFullyQualifiedClassName(mPackage, attr.value)) { - attr.value = std::move(newValue.value()); + if (attr.namespaceUri == xml::kSchemaAndroid + && mClassAttributes.find(attr.name) != mClassAttributes.end()) { + if (Maybe<std::string> newValue = + util::getFullyQualifiedClassName(mPackage, attr.value)) { + attr.value = std::move(newValue.value()); + } } } @@ -238,16 +240,17 @@ public: } private: - StringPiece16 mPackage; + StringPiece mPackage; + std::unordered_set<StringPiece> mClassAttributes = { "name" }; }; -static bool renameManifestPackage(const StringPiece16& packageOverride, xml::Element* manifestEl) { - xml::Attribute* attr = manifestEl->findAttribute({}, u"package"); +static bool renameManifestPackage(const StringPiece& packageOverride, xml::Element* manifestEl) { + xml::Attribute* attr = manifestEl->findAttribute({}, "package"); // We've already verified that the manifest element is present, with a package name specified. assert(attr); - std::u16string originalPackage = std::move(attr->value); + std::string originalPackage = std::move(attr->value); attr->value = packageOverride.toString(); FullyQualifiedClassNameVisitor visitor(originalPackage); @@ -257,17 +260,17 @@ static bool renameManifestPackage(const StringPiece16& packageOverride, xml::Ele bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) { xml::Element* root = xml::findRootElement(doc->root.get()); - if (!root || !root->namespaceUri.empty() || root->name != u"manifest") { + if (!root || !root->namespaceUri.empty() || root->name != "manifest") { context->getDiagnostics()->error(DiagMessage(doc->file.source) << "root tag must be <manifest>"); return false; } if ((mOptions.minSdkVersionDefault || mOptions.targetSdkVersionDefault) - && root->findChild({}, u"uses-sdk") == nullptr) { + && root->findChild({}, "uses-sdk") == nullptr) { // Auto insert a <uses-sdk> element. std::unique_ptr<xml::Element> usesSdk = util::make_unique<xml::Element>(); - usesSdk->name = u"uses-sdk"; + usesSdk->name = "uses-sdk"; root->addChild(std::move(usesSdk)); } diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h index 4d9356a933c2..55b587e57013 100644 --- a/tools/aapt2/link/ManifestFixer.h +++ b/tools/aapt2/link/ManifestFixer.h @@ -27,12 +27,12 @@ namespace aapt { struct ManifestFixerOptions { - Maybe<std::u16string> minSdkVersionDefault; - Maybe<std::u16string> targetSdkVersionDefault; - Maybe<std::u16string> renameManifestPackage; - Maybe<std::u16string> renameInstrumentationTargetPackage; - Maybe<std::u16string> versionNameDefault; - Maybe<std::u16string> versionCodeDefault; + Maybe<std::string> minSdkVersionDefault; + Maybe<std::string> targetSdkVersionDefault; + Maybe<std::string> renameManifestPackage; + Maybe<std::string> renameInstrumentationTargetPackage; + Maybe<std::string> versionNameDefault; + Maybe<std::string> versionCodeDefault; }; /** diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp index f993720b9566..6d52c4c02fb3 100644 --- a/tools/aapt2/link/ManifestFixer_test.cpp +++ b/tools/aapt2/link/ManifestFixer_test.cpp @@ -27,25 +27,25 @@ struct ManifestFixerTest : public ::testing::Test { void SetUp() override { mContext = test::ContextBuilder() - .setCompilationPackage(u"android") + .setCompilationPackage("android") .setPackageId(0x01) - .setNameManglerPolicy(NameManglerPolicy{ u"android" }) + .setNameManglerPolicy(NameManglerPolicy{ "android" }) .addSymbolSource(test::StaticSymbolSourceBuilder() - .addSymbol(u"@android:attr/package", ResourceId(0x01010000), + .addSymbol("@android:attr/package", ResourceId(0x01010000), test::AttributeBuilder() .setTypeMask(android::ResTable_map::TYPE_STRING) .build()) - .addSymbol(u"@android:attr/minSdkVersion", ResourceId(0x01010001), + .addSymbol("@android:attr/minSdkVersion", ResourceId(0x01010001), test::AttributeBuilder() .setTypeMask(android::ResTable_map::TYPE_STRING | android::ResTable_map::TYPE_INTEGER) .build()) - .addSymbol(u"@android:attr/targetSdkVersion", ResourceId(0x01010002), + .addSymbol("@android:attr/targetSdkVersion", ResourceId(0x01010002), test::AttributeBuilder() .setTypeMask(android::ResTable_map::TYPE_STRING | android::ResTable_map::TYPE_INTEGER) .build()) - .addSymbol(u"@android:string/str", ResourceId(0x01060000)) + .addSymbol("@android:string/str", ResourceId(0x01060000)) .build()) .build(); } @@ -83,7 +83,7 @@ TEST_F(ManifestFixerTest, EnsureManifestHasPackage) { } TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) { - ManifestFixerOptions options = { std::u16string(u"8"), std::u16string(u"22") }; + ManifestFixerOptions options = { std::string("8"), std::string("22") }; std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" @@ -97,14 +97,14 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) { el = xml::findRootElement(doc.get()); ASSERT_NE(nullptr, el); - el = el->findChild({}, u"uses-sdk"); + el = el->findChild({}, "uses-sdk"); ASSERT_NE(nullptr, el); - attr = el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion"); + attr = el->findAttribute(xml::kSchemaAndroid, "minSdkVersion"); ASSERT_NE(nullptr, attr); - EXPECT_EQ(u"7", attr->value); - attr = el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion"); + EXPECT_EQ("7", attr->value); + attr = el->findAttribute(xml::kSchemaAndroid, "targetSdkVersion"); ASSERT_NE(nullptr, attr); - EXPECT_EQ(u"21", attr->value); + EXPECT_EQ("21", attr->value); doc = verifyWithOptions(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" @@ -115,14 +115,14 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) { el = xml::findRootElement(doc.get()); ASSERT_NE(nullptr, el); - el = el->findChild({}, u"uses-sdk"); + el = el->findChild({}, "uses-sdk"); ASSERT_NE(nullptr, el); - attr = el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion"); + attr = el->findAttribute(xml::kSchemaAndroid, "minSdkVersion"); ASSERT_NE(nullptr, attr); - EXPECT_EQ(u"8", attr->value); - attr = el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion"); + EXPECT_EQ("8", attr->value); + attr = el->findAttribute(xml::kSchemaAndroid, "targetSdkVersion"); ASSERT_NE(nullptr, attr); - EXPECT_EQ(u"21", attr->value); + EXPECT_EQ("21", attr->value); doc = verifyWithOptions(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" @@ -133,14 +133,14 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) { el = xml::findRootElement(doc.get()); ASSERT_NE(nullptr, el); - el = el->findChild({}, u"uses-sdk"); + el = el->findChild({}, "uses-sdk"); ASSERT_NE(nullptr, el); - attr = el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion"); + attr = el->findAttribute(xml::kSchemaAndroid, "minSdkVersion"); ASSERT_NE(nullptr, attr); - EXPECT_EQ(u"8", attr->value); - attr = el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion"); + EXPECT_EQ("8", attr->value); + attr = el->findAttribute(xml::kSchemaAndroid, "targetSdkVersion"); ASSERT_NE(nullptr, attr); - EXPECT_EQ(u"22", attr->value); + EXPECT_EQ("22", attr->value); doc = verifyWithOptions(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" @@ -149,19 +149,19 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) { el = xml::findRootElement(doc.get()); ASSERT_NE(nullptr, el); - el = el->findChild({}, u"uses-sdk"); + el = el->findChild({}, "uses-sdk"); ASSERT_NE(nullptr, el); - attr = el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion"); + attr = el->findAttribute(xml::kSchemaAndroid, "minSdkVersion"); ASSERT_NE(nullptr, attr); - EXPECT_EQ(u"8", attr->value); - attr = el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion"); + EXPECT_EQ("8", attr->value); + attr = el->findAttribute(xml::kSchemaAndroid, "targetSdkVersion"); ASSERT_NE(nullptr, attr); - EXPECT_EQ(u"22", attr->value); + EXPECT_EQ("22", attr->value); } TEST_F(ManifestFixerTest, RenameManifestPackageAndFullyQualifyClasses) { ManifestFixerOptions options; - options.renameManifestPackage = std::u16string(u"com.android"); + options.renameManifestPackage = std::string("com.android"); std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" @@ -178,40 +178,40 @@ TEST_F(ManifestFixerTest, RenameManifestPackageAndFullyQualifyClasses) { xml::Attribute* attr = nullptr; - attr = manifestEl->findAttribute({}, u"package"); + attr = manifestEl->findAttribute({},"package"); ASSERT_NE(nullptr, attr); - EXPECT_EQ(std::u16string(u"com.android"), attr->value); + EXPECT_EQ(std::string("com.android"), attr->value); - xml::Element* applicationEl = manifestEl->findChild({}, u"application"); + xml::Element* applicationEl = manifestEl->findChild({}, "application"); ASSERT_NE(nullptr, applicationEl); - attr = applicationEl->findAttribute(xml::kSchemaAndroid, u"name"); + attr = applicationEl->findAttribute(xml::kSchemaAndroid, "name"); ASSERT_NE(nullptr, attr); - EXPECT_EQ(std::u16string(u"android.MainApplication"), attr->value); + EXPECT_EQ(std::string("android.MainApplication"), attr->value); - attr = applicationEl->findAttribute({}, u"text"); + attr = applicationEl->findAttribute({}, "text"); ASSERT_NE(nullptr, attr); - EXPECT_EQ(std::u16string(u"hello"), attr->value); + EXPECT_EQ(std::string("hello"), attr->value); xml::Element* el; - el = applicationEl->findChild({}, u"activity"); + el = applicationEl->findChild({}, "activity"); ASSERT_NE(nullptr, el); - attr = el->findAttribute(xml::kSchemaAndroid, u"name"); + attr = el->findAttribute(xml::kSchemaAndroid, "name"); ASSERT_NE(nullptr, el); - EXPECT_EQ(std::u16string(u"android.activity.Start"), attr->value); + EXPECT_EQ(std::string("android.activity.Start"), attr->value); - el = applicationEl->findChild({}, u"receiver"); + el = applicationEl->findChild({}, "receiver"); ASSERT_NE(nullptr, el); - attr = el->findAttribute(xml::kSchemaAndroid, u"name"); + attr = el->findAttribute(xml::kSchemaAndroid, "name"); ASSERT_NE(nullptr, el); - EXPECT_EQ(std::u16string(u"com.google.android.Receiver"), attr->value); + EXPECT_EQ(std::string("com.google.android.Receiver"), attr->value); } TEST_F(ManifestFixerTest, RenameManifestInstrumentationPackageAndFullyQualifyTarget) { ManifestFixerOptions options; - options.renameInstrumentationTargetPackage = std::u16string(u"com.android"); + options.renameInstrumentationTargetPackage = std::string("com.android"); std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" @@ -223,18 +223,18 @@ TEST_F(ManifestFixerTest, RenameManifestInstrumentationPackageAndFullyQualifyTar xml::Element* manifestEl = xml::findRootElement(doc.get()); ASSERT_NE(nullptr, manifestEl); - xml::Element* instrumentationEl = manifestEl->findChild({}, u"instrumentation"); + xml::Element* instrumentationEl = manifestEl->findChild({}, "instrumentation"); ASSERT_NE(nullptr, instrumentationEl); - xml::Attribute* attr = instrumentationEl->findAttribute(xml::kSchemaAndroid, u"targetPackage"); + xml::Attribute* attr = instrumentationEl->findAttribute(xml::kSchemaAndroid, "targetPackage"); ASSERT_NE(nullptr, attr); - EXPECT_EQ(std::u16string(u"com.android"), attr->value); + EXPECT_EQ(std::string("com.android"), attr->value); } TEST_F(ManifestFixerTest, UseDefaultVersionNameAndCode) { ManifestFixerOptions options; - options.versionNameDefault = std::u16string(u"Beta"); - options.versionCodeDefault = std::u16string(u"0x10000000"); + options.versionNameDefault = std::string("Beta"); + options.versionCodeDefault = std::string("0x10000000"); std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF( <manifest xmlns:android="http://schemas.android.com/apk/res/android" @@ -244,13 +244,13 @@ TEST_F(ManifestFixerTest, UseDefaultVersionNameAndCode) { xml::Element* manifestEl = xml::findRootElement(doc.get()); ASSERT_NE(nullptr, manifestEl); - xml::Attribute* attr = manifestEl->findAttribute(xml::kSchemaAndroid, u"versionName"); + xml::Attribute* attr = manifestEl->findAttribute(xml::kSchemaAndroid, "versionName"); ASSERT_NE(nullptr, attr); - EXPECT_EQ(std::u16string(u"Beta"), attr->value); + EXPECT_EQ(std::string("Beta"), attr->value); - attr = manifestEl->findAttribute(xml::kSchemaAndroid, u"versionCode"); + attr = manifestEl->findAttribute(xml::kSchemaAndroid, "versionCode"); ASSERT_NE(nullptr, attr); - EXPECT_EQ(std::u16string(u"0x10000000"), attr->value); + EXPECT_EQ(std::string("0x10000000"), attr->value); } } // namespace aapt diff --git a/tools/aapt2/link/PrivateAttributeMover_test.cpp b/tools/aapt2/link/PrivateAttributeMover_test.cpp index dbe0c92253c1..136e10b530da 100644 --- a/tools/aapt2/link/PrivateAttributeMover_test.cpp +++ b/tools/aapt2/link/PrivateAttributeMover_test.cpp @@ -15,10 +15,7 @@ */ #include "link/Linkers.h" -#include "test/Builders.h" -#include "test/Context.h" - -#include <gtest/gtest.h> +#include "test/Test.h" namespace aapt { @@ -26,45 +23,45 @@ TEST(PrivateAttributeMoverTest, MovePrivateAttributes) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .addSimple(u"@android:attr/publicA") - .addSimple(u"@android:attr/privateA") - .addSimple(u"@android:attr/publicB") - .addSimple(u"@android:attr/privateB") - .setSymbolState(u"@android:attr/publicA", ResourceId(0x01010000), SymbolState::kPublic) - .setSymbolState(u"@android:attr/publicB", ResourceId(0x01010000), SymbolState::kPublic) + .addSimple("@android:attr/publicA") + .addSimple("@android:attr/privateA") + .addSimple("@android:attr/publicB") + .addSimple("@android:attr/privateB") + .setSymbolState("@android:attr/publicA", ResourceId(0x01010000), SymbolState::kPublic) + .setSymbolState("@android:attr/publicB", ResourceId(0x01010000), SymbolState::kPublic) .build(); PrivateAttributeMover mover; ASSERT_TRUE(mover.consume(context.get(), table.get())); - ResourceTablePackage* package = table->findPackage(u"android"); + ResourceTablePackage* package = table->findPackage("android"); ASSERT_NE(package, nullptr); ResourceTableType* type = package->findType(ResourceType::kAttr); ASSERT_NE(type, nullptr); ASSERT_EQ(type->entries.size(), 2u); - EXPECT_NE(type->findEntry(u"publicA"), nullptr); - EXPECT_NE(type->findEntry(u"publicB"), nullptr); + EXPECT_NE(type->findEntry("publicA"), nullptr); + EXPECT_NE(type->findEntry("publicB"), nullptr); type = package->findType(ResourceType::kAttrPrivate); ASSERT_NE(type, nullptr); ASSERT_EQ(type->entries.size(), 2u); - EXPECT_NE(type->findEntry(u"privateA"), nullptr); - EXPECT_NE(type->findEntry(u"privateB"), nullptr); + EXPECT_NE(type->findEntry("privateA"), nullptr); + EXPECT_NE(type->findEntry("privateB"), nullptr); } TEST(PrivateAttributeMoverTest, LeavePrivateAttributesWhenNoPublicAttributesDefined) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .addSimple(u"@android:attr/privateA") - .addSimple(u"@android:attr/privateB") + .addSimple("@android:attr/privateA") + .addSimple("@android:attr/privateB") .build(); PrivateAttributeMover mover; ASSERT_TRUE(mover.consume(context.get(), table.get())); - ResourceTablePackage* package = table->findPackage(u"android"); + ResourceTablePackage* package = table->findPackage("android"); ASSERT_NE(package, nullptr); ResourceTableType* type = package->findType(ResourceType::kAttr); diff --git a/tools/aapt2/link/ProductFilter_test.cpp b/tools/aapt2/link/ProductFilter_test.cpp index f4f756ae4519..811323bb8f44 100644 --- a/tools/aapt2/link/ProductFilter_test.cpp +++ b/tools/aapt2/link/ProductFilter_test.cpp @@ -15,10 +15,7 @@ */ #include "link/ProductFilter.h" -#include "test/Builders.h" -#include "test/Context.h" - -#include <gtest/gtest.h> +#include "test/Test.h" namespace aapt { @@ -29,23 +26,23 @@ TEST(ProductFilterTest, SelectTwoProducts) { const ConfigDescription port = test::parseConfigOrDie("port"); ResourceTable table; - ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"), + ASSERT_TRUE(table.addResource(test::parseNameOrDie("@android:string/one"), land, "", test::ValueBuilder<Id>() .setSource(Source("land/default.xml")).build(), context->getDiagnostics())); - ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"), + ASSERT_TRUE(table.addResource(test::parseNameOrDie("@android:string/one"), land, "tablet", test::ValueBuilder<Id>() .setSource(Source("land/tablet.xml")).build(), context->getDiagnostics())); - ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"), + ASSERT_TRUE(table.addResource(test::parseNameOrDie("@android:string/one"), port, "", test::ValueBuilder<Id>() .setSource(Source("port/default.xml")).build(), context->getDiagnostics())); - ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"), + ASSERT_TRUE(table.addResource(test::parseNameOrDie("@android:string/one"), port, "tablet", test::ValueBuilder<Id>() .setSource(Source("port/tablet.xml")).build(), @@ -54,13 +51,13 @@ TEST(ProductFilterTest, SelectTwoProducts) { ProductFilter filter({ "tablet" }); ASSERT_TRUE(filter.consume(context.get(), &table)); - EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one", + EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, "@android:string/one", land, "")); - EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one", + EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, "@android:string/one", land, "tablet")); - EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one", + EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, "@android:string/one", port, "")); - EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one", + EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, "@android:string/one", port, "tablet")); } @@ -68,12 +65,12 @@ TEST(ProductFilterTest, SelectDefaultProduct) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); ResourceTable table; - ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"), + ASSERT_TRUE(table.addResource(test::parseNameOrDie("@android:string/one"), ConfigDescription::defaultConfig(), "", test::ValueBuilder<Id>() .setSource(Source("default.xml")).build(), context->getDiagnostics())); - ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"), + ASSERT_TRUE(table.addResource(test::parseNameOrDie("@android:string/one"), ConfigDescription::defaultConfig(), "tablet", test::ValueBuilder<Id>() .setSource(Source("tablet.xml")).build(), @@ -82,10 +79,10 @@ TEST(ProductFilterTest, SelectDefaultProduct) { ProductFilter filter({}); ASSERT_TRUE(filter.consume(context.get(), &table)); - EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one", + EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, "@android:string/one", ConfigDescription::defaultConfig(), "")); - EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/one", + EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, "@android:string/one", ConfigDescription::defaultConfig(), "tablet")); } @@ -94,17 +91,17 @@ TEST(ProductFilterTest, FailOnAmbiguousProduct) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); ResourceTable table; - ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"), + ASSERT_TRUE(table.addResource(test::parseNameOrDie("@android:string/one"), ConfigDescription::defaultConfig(), "", test::ValueBuilder<Id>() .setSource(Source("default.xml")).build(), context->getDiagnostics())); - ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"), + ASSERT_TRUE(table.addResource(test::parseNameOrDie("@android:string/one"), ConfigDescription::defaultConfig(), "tablet", test::ValueBuilder<Id>() .setSource(Source("tablet.xml")).build(), context->getDiagnostics())); - ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"), + ASSERT_TRUE(table.addResource(test::parseNameOrDie("@android:string/one"), ConfigDescription::defaultConfig(), "no-sdcard", test::ValueBuilder<Id>() .setSource(Source("no-sdcard.xml")).build(), @@ -118,12 +115,12 @@ TEST(ProductFilterTest, FailOnMultipleDefaults) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); ResourceTable table; - ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"), + ASSERT_TRUE(table.addResource(test::parseNameOrDie("@android:string/one"), ConfigDescription::defaultConfig(), "", test::ValueBuilder<Id>() .setSource(Source(".xml")).build(), context->getDiagnostics())); - ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/one"), + ASSERT_TRUE(table.addResource(test::parseNameOrDie("@android:string/one"), ConfigDescription::defaultConfig(), "default", test::ValueBuilder<Id>() .setSource(Source("default.xml")).build(), diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp index 66eb0df048db..fe886de3f7c5 100644 --- a/tools/aapt2/link/ReferenceLinker.cpp +++ b/tools/aapt2/link/ReferenceLinker.cpp @@ -288,7 +288,7 @@ namespace { struct EmptyDeclStack : public xml::IPackageDeclStack { Maybe<xml::ExtractedPackage> transformPackageAlias( - const StringPiece16& alias, const StringPiece16& localPackage) const override { + const StringPiece& alias, const StringPiece& localPackage) const override { if (alias.empty()) { return xml::ExtractedPackage{ localPackage.toString(), true /* private */ }; } diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp index 76b23098a35c..17c26360313e 100644 --- a/tools/aapt2/link/ReferenceLinker_test.cpp +++ b/tools/aapt2/link/ReferenceLinker_test.cpp @@ -23,41 +23,41 @@ namespace aapt { TEST(ReferenceLinkerTest, LinkSimpleReferences) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"com.app.test", 0x7f) - .addReference(u"@com.app.test:string/foo", ResourceId(0x7f020000), - u"@com.app.test:string/bar") + .setPackageId("com.app.test", 0x7f) + .addReference("@com.app.test:string/foo", ResourceId(0x7f020000), + "@com.app.test:string/bar") // Test use of local reference (w/o package name). - .addReference(u"@com.app.test:string/bar", ResourceId(0x7f020001), u"@string/baz") + .addReference("@com.app.test:string/bar", ResourceId(0x7f020001), "@string/baz") - .addReference(u"@com.app.test:string/baz", ResourceId(0x7f020002), - u"@android:string/ok") + .addReference("@com.app.test:string/baz", ResourceId(0x7f020002), + "@android:string/ok") .build(); std::unique_ptr<IAaptContext> context = test::ContextBuilder() - .setCompilationPackage(u"com.app.test") + .setCompilationPackage("com.app.test") .setPackageId(0x7f) - .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" }) + .setNameManglerPolicy(NameManglerPolicy{ "com.app.test" }) .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get())) .addSymbolSource(test::StaticSymbolSourceBuilder() - .addPublicSymbol(u"@android:string/ok", ResourceId(0x01040034)) + .addPublicSymbol("@android:string/ok", ResourceId(0x01040034)) .build()) .build(); ReferenceLinker linker; ASSERT_TRUE(linker.consume(context.get(), table.get())); - Reference* ref = test::getValue<Reference>(table.get(), u"@com.app.test:string/foo"); + Reference* ref = test::getValue<Reference>(table.get(), "@com.app.test:string/foo"); ASSERT_NE(ref, nullptr); AAPT_ASSERT_TRUE(ref->id); EXPECT_EQ(ref->id.value(), ResourceId(0x7f020001)); - ref = test::getValue<Reference>(table.get(), u"@com.app.test:string/bar"); + ref = test::getValue<Reference>(table.get(), "@com.app.test:string/bar"); ASSERT_NE(ref, nullptr); AAPT_ASSERT_TRUE(ref->id); EXPECT_EQ(ref->id.value(), ResourceId(0x7f020002)); - ref = test::getValue<Reference>(table.get(), u"@com.app.test:string/baz"); + ref = test::getValue<Reference>(table.get(), "@com.app.test:string/baz"); ASSERT_NE(ref, nullptr); AAPT_ASSERT_TRUE(ref->id); EXPECT_EQ(ref->id.value(), ResourceId(0x01040034)); @@ -65,39 +65,39 @@ TEST(ReferenceLinkerTest, LinkSimpleReferences) { TEST(ReferenceLinkerTest, LinkStyleAttributes) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"com.app.test", 0x7f) - .addValue(u"@com.app.test:style/Theme", test::StyleBuilder() - .setParent(u"@android:style/Theme.Material") - .addItem(u"@android:attr/foo", ResourceUtils::tryParseColor(u"#ff00ff")) - .addItem(u"@android:attr/bar", {} /* placeholder */) + .setPackageId("com.app.test", 0x7f) + .addValue("@com.app.test:style/Theme", test::StyleBuilder() + .setParent("@android:style/Theme.Material") + .addItem("@android:attr/foo", ResourceUtils::tryParseColor("#ff00ff")) + .addItem("@android:attr/bar", {} /* placeholder */) .build()) .build(); { // We need to fill in the value for the attribute android:attr/bar after we build the // table, because we need access to the string pool. - Style* style = test::getValue<Style>(table.get(), u"@com.app.test:style/Theme"); + Style* style = test::getValue<Style>(table.get(), "@com.app.test:style/Theme"); ASSERT_NE(style, nullptr); style->entries.back().value = util::make_unique<RawString>( - table->stringPool.makeRef(u"one|two")); + table->stringPool.makeRef("one|two")); } std::unique_ptr<IAaptContext> context = test::ContextBuilder() - .setCompilationPackage(u"com.app.test") + .setCompilationPackage("com.app.test") .setPackageId(0x7f) - .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" }) + .setNameManglerPolicy(NameManglerPolicy{ "com.app.test" }) .addSymbolSource(test::StaticSymbolSourceBuilder() - .addPublicSymbol(u"@android:style/Theme.Material", + .addPublicSymbol("@android:style/Theme.Material", ResourceId(0x01060000)) - .addPublicSymbol(u"@android:attr/foo", ResourceId(0x01010001), + .addPublicSymbol("@android:attr/foo", ResourceId(0x01010001), test::AttributeBuilder() .setTypeMask(ResTable_map::TYPE_COLOR) .build()) - .addPublicSymbol(u"@android:attr/bar", ResourceId(0x01010002), + .addPublicSymbol("@android:attr/bar", ResourceId(0x01010002), test::AttributeBuilder() .setTypeMask(ResTable_map::TYPE_FLAGS) - .addItem(u"one", 0x01) - .addItem(u"two", 0x02) + .addItem("one", 0x01) + .addItem("two", 0x02) .build()) .build()) .build(); @@ -105,7 +105,7 @@ TEST(ReferenceLinkerTest, LinkStyleAttributes) { ReferenceLinker linker; ASSERT_TRUE(linker.consume(context.get(), table.get())); - Style* style = test::getValue<Style>(table.get(), u"@com.app.test:style/Theme"); + Style* style = test::getValue<Style>(table.get(), "@com.app.test:style/Theme"); ASSERT_NE(style, nullptr); AAPT_ASSERT_TRUE(style->parent); AAPT_ASSERT_TRUE(style->parent.value().id); @@ -124,11 +124,11 @@ TEST(ReferenceLinkerTest, LinkStyleAttributes) { TEST(ReferenceLinkerTest, LinkMangledReferencesAndAttributes) { std::unique_ptr<IAaptContext> context = test::ContextBuilder() - .setCompilationPackage(u"com.app.test") + .setCompilationPackage("com.app.test") .setPackageId(0x7f) - .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test", { u"com.android.support" } }) + .setNameManglerPolicy(NameManglerPolicy{ "com.app.test", { "com.android.support" } }) .addSymbolSource(test::StaticSymbolSourceBuilder() - .addPublicSymbol(u"@com.app.test:attr/com.android.support$foo", + .addPublicSymbol("@com.app.test:attr/com.android.support$foo", ResourceId(0x7f010000), test::AttributeBuilder() .setTypeMask(ResTable_map::TYPE_COLOR) @@ -137,17 +137,17 @@ TEST(ReferenceLinkerTest, LinkMangledReferencesAndAttributes) { .build(); std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"com.app.test", 0x7f) - .addValue(u"@com.app.test:style/Theme", ResourceId(0x7f020000), - test::StyleBuilder().addItem(u"@com.android.support:attr/foo", - ResourceUtils::tryParseColor(u"#ff0000")) + .setPackageId("com.app.test", 0x7f) + .addValue("@com.app.test:style/Theme", ResourceId(0x7f020000), + test::StyleBuilder().addItem("@com.android.support:attr/foo", + ResourceUtils::tryParseColor("#ff0000")) .build()) .build(); ReferenceLinker linker; ASSERT_TRUE(linker.consume(context.get(), table.get())); - Style* style = test::getValue<Style>(table.get(), u"@com.app.test:style/Theme"); + Style* style = test::getValue<Style>(table.get(), "@com.app.test:style/Theme"); ASSERT_NE(style, nullptr); ASSERT_EQ(1u, style->entries.size()); AAPT_ASSERT_TRUE(style->entries.front().key.id); @@ -156,18 +156,18 @@ TEST(ReferenceLinkerTest, LinkMangledReferencesAndAttributes) { TEST(ReferenceLinkerTest, FailToLinkPrivateSymbols) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"com.app.test", 0x7f) - .addReference(u"@com.app.test:string/foo", ResourceId(0x7f020000), - u"@android:string/hidden") + .setPackageId("com.app.test", 0x7f) + .addReference("@com.app.test:string/foo", ResourceId(0x7f020000), + "@android:string/hidden") .build(); std::unique_ptr<IAaptContext> context = test::ContextBuilder() - .setCompilationPackage(u"com.app.test") + .setCompilationPackage("com.app.test") .setPackageId(0x7f) - .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" }) + .setNameManglerPolicy(NameManglerPolicy{ "com.app.test" }) .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get())) .addSymbolSource(test::StaticSymbolSourceBuilder() - .addSymbol(u"@android:string/hidden", ResourceId(0x01040034)) + .addSymbol("@android:string/hidden", ResourceId(0x01040034)) .build()) .build(); @@ -177,18 +177,18 @@ TEST(ReferenceLinkerTest, FailToLinkPrivateSymbols) { TEST(ReferenceLinkerTest, FailToLinkPrivateMangledSymbols) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"com.app.test", 0x7f) - .addReference(u"@com.app.test:string/foo", ResourceId(0x7f020000), - u"@com.app.lib:string/hidden") + .setPackageId("com.app.test", 0x7f) + .addReference("@com.app.test:string/foo", ResourceId(0x7f020000), + "@com.app.lib:string/hidden") .build(); std::unique_ptr<IAaptContext> context = test::ContextBuilder() - .setCompilationPackage(u"com.app.test") + .setCompilationPackage("com.app.test") .setPackageId(0x7f) - .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test", { u"com.app.lib" } }) + .setNameManglerPolicy(NameManglerPolicy{ "com.app.test", { "com.app.lib" } }) .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get())) .addSymbolSource(test::StaticSymbolSourceBuilder() - .addSymbol(u"@com.app.test:string/com.app.lib$hidden", + .addSymbol("@com.app.test:string/com.app.lib$hidden", ResourceId(0x7f040034)) .build()) @@ -200,19 +200,19 @@ TEST(ReferenceLinkerTest, FailToLinkPrivateMangledSymbols) { TEST(ReferenceLinkerTest, FailToLinkPrivateStyleAttributes) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"com.app.test", 0x7f) - .addValue(u"@com.app.test:style/Theme", test::StyleBuilder() - .addItem(u"@android:attr/hidden", ResourceUtils::tryParseColor(u"#ff00ff")) + .setPackageId("com.app.test", 0x7f) + .addValue("@com.app.test:style/Theme", test::StyleBuilder() + .addItem("@android:attr/hidden", ResourceUtils::tryParseColor("#ff00ff")) .build()) .build(); std::unique_ptr<IAaptContext> context = test::ContextBuilder() - .setCompilationPackage(u"com.app.test") + .setCompilationPackage("com.app.test") .setPackageId(0x7f) - .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" }) + .setNameManglerPolicy(NameManglerPolicy{ "com.app.test" }) .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get())) .addSymbolSource(test::StaticSymbolSourceBuilder() - .addSymbol(u"@android:attr/hidden", ResourceId(0x01010001), + .addSymbol("@android:attr/hidden", ResourceId(0x01010001), test::AttributeBuilder() .setTypeMask( android::ResTable_map::TYPE_COLOR) diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp index 7471e15db41a..2cd2639ec228 100644 --- a/tools/aapt2/link/TableMerger.cpp +++ b/tools/aapt2/link/TableMerger.cpp @@ -67,7 +67,7 @@ bool TableMerger::mergeImpl(const Source& src, ResourceTable* table, callback = [&](const ResourceNameRef& name, const ConfigDescription& config, FileReference* newFile, FileReference* oldFile) -> bool { // The old file's path points inside the APK, so we can use it as is. - io::IFile* f = collection->findFile(util::utf16ToUtf8(*oldFile->path)); + io::IFile* f = collection->findFile(*oldFile->path); if (!f) { mContext->getDiagnostics()->error(DiagMessage(src) << "file '" << *oldFile->path @@ -95,7 +95,7 @@ bool TableMerger::mergeImpl(const Source& src, ResourceTable* table, /** * This will merge and mangle resources from a static library. */ -bool TableMerger::mergeAndMangle(const Source& src, const StringPiece16& packageName, +bool TableMerger::mergeAndMangle(const Source& src, const StringPiece& packageName, ResourceTable* table, io::IFileCollection* collection) { bool error = false; for (auto& package : table->packages) { @@ -112,7 +112,7 @@ bool TableMerger::mergeAndMangle(const Source& src, const StringPiece16& package auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config, FileReference* newFile, FileReference* oldFile) -> bool { // The old file's path points inside the APK, so we can use it as is. - io::IFile* f = collection->findFile(util::utf16ToUtf8(*oldFile->path)); + io::IFile* f = collection->findFile(*oldFile->path); if (!f) { mContext->getDiagnostics()->error(DiagMessage(src) << "file '" << *oldFile->path << "' not found"); @@ -159,8 +159,8 @@ bool TableMerger::doMerge(const Source& src, for (auto& srcEntry : srcType->entries) { ResourceEntry* dstEntry; if (manglePackage) { - std::u16string mangledName = NameMangler::mangleEntry(srcPackage->name, - srcEntry->name); + std::string mangledName = NameMangler::mangleEntry(srcPackage->name, + srcEntry->name); if (allowNewResources) { dstEntry = dstType->findOrCreateEntry(mangledName); } else { @@ -275,13 +275,13 @@ bool TableMerger::doMerge(const Source& src, return !error; } -std::unique_ptr<FileReference> TableMerger::cloneAndMangleFile(const std::u16string& package, +std::unique_ptr<FileReference> TableMerger::cloneAndMangleFile(const std::string& package, const FileReference& fileRef) { - StringPiece16 prefix, entry, suffix; + StringPiece prefix, entry, suffix; if (util::extractResFilePathParts(*fileRef.path, &prefix, &entry, &suffix)) { - std::u16string mangledEntry = NameMangler::mangleEntry(package, entry.toString()); - std::u16string newPath = prefix.toString() + mangledEntry + suffix.toString(); + std::string mangledEntry = NameMangler::mangleEntry(package, entry.toString()); + std::string newPath = prefix.toString() + mangledEntry + suffix.toString(); std::unique_ptr<FileReference> newFileRef = util::make_unique<FileReference>( mMasterTable->stringPool.makeRef(newPath)); newFileRef->setComment(fileRef.getComment()); @@ -293,8 +293,7 @@ std::unique_ptr<FileReference> TableMerger::cloneAndMangleFile(const std::u16str bool TableMerger::mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file, bool overlay) { ResourceTable table; - std::u16string path = util::utf8ToUtf16(ResourceUtils::buildResourceFileName(fileDesc, - nullptr)); + std::string path = ResourceUtils::buildResourceFileName(fileDesc, nullptr); std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>( table.stringPool.makeRef(path)); fileRef->setSource(fileDesc.source); diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h index 80c2a5e69b66..6997f936add1 100644 --- a/tools/aapt2/link/TableMerger.h +++ b/tools/aapt2/link/TableMerger.h @@ -59,7 +59,7 @@ public: */ TableMerger(IAaptContext* context, ResourceTable* outTable, const TableMergerOptions& options); - const std::set<std::u16string>& getMergedPackages() const { + const std::set<std::string>& getMergedPackages() const { return mMergedPackages; } @@ -81,7 +81,7 @@ public: * Merges resources from the given package, mangling the name. This is for static libraries. * An io::IFileCollection is needed in order to find the referenced Files and process them. */ - bool mergeAndMangle(const Source& src, const StringPiece16& package, ResourceTable* table, + bool mergeAndMangle(const Source& src, const StringPiece& package, ResourceTable* table, io::IFileCollection* collection); /** @@ -104,7 +104,7 @@ private: TableMergerOptions mOptions; ResourceTablePackage* mMasterPackage; - std::set<std::u16string> mMergedPackages; + std::set<std::string> mMergedPackages; bool mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file, bool overlay); @@ -117,7 +117,7 @@ private: const bool allowNewResources, FileMergeCallback callback); - std::unique_ptr<FileReference> cloneAndMangleFile(const std::u16string& package, + std::unique_ptr<FileReference> cloneAndMangleFile(const std::string& package, const FileReference& value); }; diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp index 4a80d3f48777..169721722240 100644 --- a/tools/aapt2/link/TableMerger_test.cpp +++ b/tools/aapt2/link/TableMerger_test.cpp @@ -30,13 +30,13 @@ struct TableMergerTest : public ::testing::Test { void SetUp() override { mContext = test::ContextBuilder() // We are compiling this package. - .setCompilationPackage(u"com.app.a") + .setCompilationPackage("com.app.a") // Merge all packages that have this package ID. .setPackageId(0x7f) // Mangle all packages that do not have this package name. - .setNameManglerPolicy(NameManglerPolicy{ u"com.app.a", { u"com.app.b" } }) + .setNameManglerPolicy(NameManglerPolicy{ "com.app.a", { "com.app.b" } }) .build(); } @@ -44,17 +44,17 @@ struct TableMergerTest : public ::testing::Test { TEST_F(TableMergerTest, SimpleMerge) { std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder() - .setPackageId(u"com.app.a", 0x7f) - .addReference(u"@com.app.a:id/foo", u"@com.app.a:id/bar") - .addReference(u"@com.app.a:id/bar", u"@com.app.b:id/foo") - .addValue(u"@com.app.a:styleable/view", test::StyleableBuilder() - .addItem(u"@com.app.b:id/foo") + .setPackageId("com.app.a", 0x7f) + .addReference("@com.app.a:id/foo", "@com.app.a:id/bar") + .addReference("@com.app.a:id/bar", "@com.app.b:id/foo") + .addValue("@com.app.a:styleable/view", test::StyleableBuilder() + .addItem("@com.app.b:id/foo") .build()) .build(); std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder() - .setPackageId(u"com.app.b", 0x7f) - .addSimple(u"@com.app.b:id/foo") + .setPackageId("com.app.b", 0x7f) + .addSimple("@com.app.b:id/foo") .build(); ResourceTable finalTable; @@ -62,20 +62,20 @@ TEST_F(TableMergerTest, SimpleMerge) { io::FileCollection collection; ASSERT_TRUE(merger.merge({}, tableA.get())); - ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get(), &collection)); + ASSERT_TRUE(merger.mergeAndMangle({}, "com.app.b", tableB.get(), &collection)); - EXPECT_TRUE(merger.getMergedPackages().count(u"com.app.b") != 0); + EXPECT_TRUE(merger.getMergedPackages().count("com.app.b") != 0); // Entries from com.app.a should not be mangled. - AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie(u"@com.app.a:id/foo"))); - AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie(u"@com.app.a:id/bar"))); - AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie(u"@com.app.a:styleable/view"))); + AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie("@com.app.a:id/foo"))); + AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie("@com.app.a:id/bar"))); + AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie("@com.app.a:styleable/view"))); // The unmangled name should not be present. - AAPT_EXPECT_FALSE(finalTable.findResource(test::parseNameOrDie(u"@com.app.b:id/foo"))); + AAPT_EXPECT_FALSE(finalTable.findResource(test::parseNameOrDie("@com.app.b:id/foo"))); // Look for the mangled name. - AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie(u"@com.app.a:id/com.app.b$foo"))); + AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie("@com.app.a:id/com.app.b$foo"))); } TEST_F(TableMergerTest, MergeFile) { @@ -86,17 +86,17 @@ TEST_F(TableMergerTest, MergeFile) { ResourceFile fileDesc; fileDesc.config = test::parseConfigOrDie("hdpi-v4"); - fileDesc.name = test::parseNameOrDie(u"@layout/main"); + fileDesc.name = test::parseNameOrDie("@layout/main"); fileDesc.source = Source("res/layout-hdpi/main.xml"); test::TestFile testFile("path/to/res/layout-hdpi/main.xml.flat"); ASSERT_TRUE(merger.mergeFile(fileDesc, &testFile)); FileReference* file = test::getValueForConfig<FileReference>(&finalTable, - u"@com.app.a:layout/main", + "@com.app.a:layout/main", test::parseConfigOrDie("hdpi-v4")); ASSERT_NE(nullptr, file); - EXPECT_EQ(std::u16string(u"res/layout-hdpi-v4/main.xml"), *file->path); + EXPECT_EQ(std::string("res/layout-hdpi-v4/main.xml"), *file->path); } TEST_F(TableMergerTest, MergeFileOverlay) { @@ -106,7 +106,7 @@ TEST_F(TableMergerTest, MergeFileOverlay) { TableMerger merger(mContext.get(), &finalTable, tableMergerOptions); ResourceFile fileDesc; - fileDesc.name = test::parseNameOrDie(u"@xml/foo"); + fileDesc.name = test::parseNameOrDie("@xml/foo"); test::TestFile fileA("path/to/fileA.xml.flat"); test::TestFile fileB("path/to/fileB.xml.flat"); @@ -116,12 +116,12 @@ TEST_F(TableMergerTest, MergeFileOverlay) { TEST_F(TableMergerTest, MergeFileReferences) { std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder() - .setPackageId(u"com.app.a", 0x7f) - .addFileReference(u"@com.app.a:xml/file", u"res/xml/file.xml") + .setPackageId("com.app.a", 0x7f) + .addFileReference("@com.app.a:xml/file", "res/xml/file.xml") .build(); std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder() - .setPackageId(u"com.app.b", 0x7f) - .addFileReference(u"@com.app.b:xml/file", u"res/xml/file.xml") + .setPackageId("com.app.b", 0x7f) + .addFileReference("@com.app.b:xml/file", "res/xml/file.xml") .build(); ResourceTable finalTable; @@ -130,25 +130,25 @@ TEST_F(TableMergerTest, MergeFileReferences) { collection.insertFile("res/xml/file.xml"); ASSERT_TRUE(merger.merge({}, tableA.get())); - ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get(), &collection)); + ASSERT_TRUE(merger.mergeAndMangle({}, "com.app.b", tableB.get(), &collection)); - FileReference* f = test::getValue<FileReference>(&finalTable, u"@com.app.a:xml/file"); + FileReference* f = test::getValue<FileReference>(&finalTable, "@com.app.a:xml/file"); ASSERT_NE(f, nullptr); - EXPECT_EQ(std::u16string(u"res/xml/file.xml"), *f->path); + EXPECT_EQ(std::string("res/xml/file.xml"), *f->path); - f = test::getValue<FileReference>(&finalTable, u"@com.app.a:xml/com.app.b$file"); + f = test::getValue<FileReference>(&finalTable, "@com.app.a:xml/com.app.b$file"); ASSERT_NE(f, nullptr); - EXPECT_EQ(std::u16string(u"res/xml/com.app.b$file.xml"), *f->path); + EXPECT_EQ(std::string("res/xml/com.app.b$file.xml"), *f->path); } TEST_F(TableMergerTest, OverrideResourceWithOverlay) { std::unique_ptr<ResourceTable> base = test::ResourceTableBuilder() - .setPackageId(u"", 0x00) - .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true")) + .setPackageId("", 0x00) + .addValue("@bool/foo", ResourceUtils::tryParseBool("true")) .build(); std::unique_ptr<ResourceTable> overlay = test::ResourceTableBuilder() - .setPackageId(u"", 0x00) - .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"false")) + .setPackageId("", 0x00) + .addValue("@bool/foo", ResourceUtils::tryParseBool("false")) .build(); ResourceTable finalTable; @@ -159,19 +159,19 @@ TEST_F(TableMergerTest, OverrideResourceWithOverlay) { ASSERT_TRUE(merger.merge({}, base.get())); ASSERT_TRUE(merger.mergeOverlay({}, overlay.get())); - BinaryPrimitive* foo = test::getValue<BinaryPrimitive>(&finalTable, u"@com.app.a:bool/foo"); + BinaryPrimitive* foo = test::getValue<BinaryPrimitive>(&finalTable, "@com.app.a:bool/foo"); ASSERT_NE(nullptr, foo); EXPECT_EQ(0x0u, foo->value.data); } TEST_F(TableMergerTest, MergeAddResourceFromOverlay) { std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder() - .setPackageId(u"", 0x7f) - .setSymbolState(u"@bool/foo", {}, SymbolState::kUndefined) + .setPackageId("", 0x7f) + .setSymbolState("@bool/foo", {}, SymbolState::kUndefined) .build(); std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder() - .setPackageId(u"", 0x7f) - .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true")) + .setPackageId("", 0x7f) + .addValue("@bool/foo", ResourceUtils::tryParseBool("true")) .build(); ResourceTable finalTable; @@ -183,11 +183,11 @@ TEST_F(TableMergerTest, MergeAddResourceFromOverlay) { TEST_F(TableMergerTest, MergeAddResourceFromOverlayWithAutoAddOverlay) { std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder() - .setPackageId(u"", 0x7f) + .setPackageId("", 0x7f) .build(); std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder() - .setPackageId(u"", 0x7f) - .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true")) + .setPackageId("", 0x7f) + .addValue("@bool/foo", ResourceUtils::tryParseBool("true")) .build(); ResourceTable finalTable; @@ -201,11 +201,11 @@ TEST_F(TableMergerTest, MergeAddResourceFromOverlayWithAutoAddOverlay) { TEST_F(TableMergerTest, FailToMergeNewResourceWithoutAutoAddOverlay) { std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder() - .setPackageId(u"", 0x7f) + .setPackageId("", 0x7f) .build(); std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder() - .setPackageId(u"", 0x7f) - .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true")) + .setPackageId("", 0x7f) + .addValue("@bool/foo", ResourceUtils::tryParseBool("true")) .build(); ResourceTable finalTable; diff --git a/tools/aapt2/link/VersionCollapser.cpp b/tools/aapt2/link/VersionCollapser.cpp new file mode 100644 index 000000000000..949d656f44a0 --- /dev/null +++ b/tools/aapt2/link/VersionCollapser.cpp @@ -0,0 +1,152 @@ +/* + * 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 "ResourceTable.h" +#include "link/Linkers.h" + +#include <algorithm> +#include <vector> + +namespace aapt { + +template <typename Iterator, typename Pred> +class FilterIterator { +public: + FilterIterator(Iterator begin, Iterator end, Pred pred=Pred()) : + mCurrent(begin), mEnd(end), mPred(pred) { + advance(); + } + + bool hasNext() { + return mCurrent != mEnd; + } + + Iterator nextIter() { + Iterator iter = mCurrent; + ++mCurrent; + advance(); + return iter; + } + + typename Iterator::reference next() { + return *nextIter(); + } + +private: + void advance() { + for (; mCurrent != mEnd; ++mCurrent) { + if (mPred(*mCurrent)) { + return; + } + } + } + + Iterator mCurrent, mEnd; + Pred mPred; +}; + +template <typename Iterator, typename Pred> +FilterIterator<Iterator, Pred> makeFilterIterator(Iterator begin, Iterator end=Iterator(), + Pred pred=Pred()) { + return FilterIterator<Iterator, Pred>(begin, end, pred); +} + +/** + * Every Configuration with an SDK version specified that is less than minSdk will be removed. + * The exception is when there is no exact matching resource for the minSdk. The next smallest + * one will be kept. + */ +static void collapseVersions(int minSdk, ResourceEntry* entry) { + // First look for all sdks less than minSdk. + for (auto iter = entry->values.rbegin(); iter != entry->values.rend(); ++iter) { + // Check if the item was already marked for removal. + if (!(*iter)) { + continue; + } + + const ConfigDescription& config = (*iter)->config; + if (config.sdkVersion <= minSdk) { + // This is the first configuration we've found with a smaller or equal SDK level + // to the minimum. We MUST keep this one, but remove all others we find, which get + // overridden by this one. + + ConfigDescription configWithoutSdk = config; + configWithoutSdk.sdkVersion = 0; + auto pred = [&](const std::unique_ptr<ResourceConfigValue>& val) -> bool { + // Check that the value hasn't already been marked for removal. + if (!val) { + return false; + } + + // Only return Configs that differ in SDK version. + configWithoutSdk.sdkVersion = val->config.sdkVersion; + return configWithoutSdk == val->config && val->config.sdkVersion <= minSdk; + }; + + // Remove the rest that match. + auto filterIter = makeFilterIterator(iter + 1, entry->values.rend(), pred); + while (filterIter.hasNext()) { + filterIter.next() = {}; + } + } + } + + // Now erase the nullptr values. + entry->values.erase(std::remove_if(entry->values.begin(), entry->values.end(), + [](const std::unique_ptr<ResourceConfigValue>& val) -> bool { + return val == nullptr; + }), entry->values.end()); + + // Strip the version qualifiers for every resource with version <= minSdk. This will ensure + // that the resource entries are all packed together in the same ResTable_type struct + // and take up less space in the resources.arsc table. + bool modified = false; + for (std::unique_ptr<ResourceConfigValue>& configValue : entry->values) { + if (configValue->config.sdkVersion != 0 && configValue->config.sdkVersion <= minSdk) { + // Override the resource with a Configuration without an SDK. + std::unique_ptr<ResourceConfigValue> newValue = util::make_unique<ResourceConfigValue>( + configValue->config.copyWithoutSdkVersion(), configValue->product); + newValue->value = std::move(configValue->value); + configValue = std::move(newValue); + + modified = true; + } + } + + if (modified) { + // We've modified the keys (ConfigDescription) by changing the sdkVersion to 0. + // We MUST re-sort to ensure ordering guarantees hold. + std::sort(entry->values.begin(), entry->values.end(), + [](const std::unique_ptr<ResourceConfigValue>& a, + const std::unique_ptr<ResourceConfigValue>& b) -> bool { + return a->config.compare(b->config) < 0; + }); + } +} + +bool VersionCollapser::consume(IAaptContext* context, ResourceTable* table) { + const int minSdk = context->getMinSdkVersion(); + for (auto& package : table->packages) { + for (auto& type : package->types) { + for (auto& entry : type->entries) { + collapseVersions(minSdk, entry.get()); + } + } + } + return true; +} + +} // namespace aapt diff --git a/tools/aapt2/link/VersionCollapser_test.cpp b/tools/aapt2/link/VersionCollapser_test.cpp new file mode 100644 index 000000000000..dd5f1d17bec3 --- /dev/null +++ b/tools/aapt2/link/VersionCollapser_test.cpp @@ -0,0 +1,103 @@ +/* + * 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 "link/Linkers.h" +#include "test/Test.h" + +namespace aapt { + +template <typename T> +using uptr = std::unique_ptr<T>; + +static uptr<ResourceTable> buildTableWithConfigs(const StringPiece& name, + std::initializer_list<std::string> list) { + test::ResourceTableBuilder builder; + for (const std::string& item : list) { + builder.addSimple(name, test::parseConfigOrDie(item)); + } + return builder.build(); +} + +TEST(VersionCollapserTest, CollapseVersions) { + uptr<IAaptContext> context = test::ContextBuilder().setMinSdkVersion(7).build(); + + const StringPiece resName = "@android:string/foo"; + + uptr<ResourceTable> table = + buildTableWithConfigs(resName, + { "land-v4", "land-v5", "sw600dp", "land-v6", + "land-v14", "land-v21" }); + + VersionCollapser collapser; + ASSERT_TRUE(collapser.consume(context.get(), table.get())); + + // These should be removed. + EXPECT_EQ(nullptr, + test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v4"))); + EXPECT_EQ(nullptr, + test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v5"))); + // This one should be removed because it was renamed to 'land', with the version dropped. + EXPECT_EQ(nullptr, + test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v6"))); + + // These should remain. + EXPECT_NE(nullptr, + test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("sw600dp"))); + + // 'land' should be present because it was renamed from 'land-v6'. + EXPECT_NE(nullptr, + test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land"))); + EXPECT_NE(nullptr, + test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v14"))); + EXPECT_NE(nullptr, + test::getValueForConfig<Id>(table.get(), resName, test::parseConfigOrDie("land-v21"))); +} + +TEST(VersionCollapserTest, CollapseVersionsWhenMinSdkIsHighest) { + uptr<IAaptContext> context = test::ContextBuilder().setMinSdkVersion(21).build(); + + const StringPiece resName = "@android:string/foo"; + + uptr<ResourceTable> table = + buildTableWithConfigs(resName, + { "land-v4", "land-v5", "sw600dp", "land-v6", + "land-v14", "land-v21", "land-v22" }); + VersionCollapser collapser; + ASSERT_TRUE(collapser.consume(context.get(), table.get())); + + // These should all be removed. + EXPECT_EQ(nullptr, test::getValueForConfig<Id>(table.get(), resName, + test::parseConfigOrDie("land-v4"))); + EXPECT_EQ(nullptr, test::getValueForConfig<Id>(table.get(), resName, + test::parseConfigOrDie("land-v5"))); + EXPECT_EQ(nullptr, test::getValueForConfig<Id>(table.get(), resName, + test::parseConfigOrDie("land-v6"))); + EXPECT_EQ(nullptr, test::getValueForConfig<Id>(table.get(), resName, + test::parseConfigOrDie("land-v14"))); + + // These should remain. + EXPECT_NE(nullptr, test::getValueForConfig<Id>( + table.get(), resName, test::parseConfigOrDie("sw600dp").copyWithoutSdkVersion())); + + // land-v21 should have been converted to land. + EXPECT_NE(nullptr, test::getValueForConfig<Id>(table.get(), resName, + test::parseConfigOrDie("land"))); + // land-v22 should remain as-is. + EXPECT_NE(nullptr, test::getValueForConfig<Id>(table.get(), resName, + test::parseConfigOrDie("land-v22"))); +} + +} // namespace aapt diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp index 568bc74895c7..02af5e3af7ab 100644 --- a/tools/aapt2/link/XmlReferenceLinker.cpp +++ b/tools/aapt2/link/XmlReferenceLinker.cpp @@ -81,7 +81,7 @@ public: xml::extractPackageFromNamespace(attr.namespaceUri); if (maybePackage) { // There is a valid package name for this attribute. We will look this up. - StringPiece16 package = maybePackage.value().package; + StringPiece package = maybePackage.value().package; if (package.empty()) { // Empty package means the 'current' or 'local' package. package = mContext->getCompilationPackage(); diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp index af9098b9e483..d48de42e5033 100644 --- a/tools/aapt2/link/XmlReferenceLinker_test.cpp +++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include <test/Context.h> #include "link/Linkers.h" #include "test/Test.h" @@ -24,44 +23,44 @@ class XmlReferenceLinkerTest : public ::testing::Test { public: void SetUp() override { mContext = test::ContextBuilder() - .setCompilationPackage(u"com.app.test") + .setCompilationPackage("com.app.test") .setNameManglerPolicy( - NameManglerPolicy{ u"com.app.test", { u"com.android.support" } }) + NameManglerPolicy{ "com.app.test", { "com.android.support" } }) .addSymbolSource(test::StaticSymbolSourceBuilder() - .addPublicSymbol(u"@android:attr/layout_width", ResourceId(0x01010000), + .addPublicSymbol("@android:attr/layout_width", ResourceId(0x01010000), test::AttributeBuilder() .setTypeMask(android::ResTable_map::TYPE_ENUM | android::ResTable_map::TYPE_DIMENSION) - .addItem(u"match_parent", 0xffffffff) + .addItem("match_parent", 0xffffffff) .build()) - .addPublicSymbol(u"@android:attr/background", ResourceId(0x01010001), + .addPublicSymbol("@android:attr/background", ResourceId(0x01010001), test::AttributeBuilder() .setTypeMask(android::ResTable_map::TYPE_COLOR).build()) - .addPublicSymbol(u"@android:attr/attr", ResourceId(0x01010002), + .addPublicSymbol("@android:attr/attr", ResourceId(0x01010002), test::AttributeBuilder().build()) - .addPublicSymbol(u"@android:attr/text", ResourceId(0x01010003), + .addPublicSymbol("@android:attr/text", ResourceId(0x01010003), test::AttributeBuilder() .setTypeMask(android::ResTable_map::TYPE_STRING) .build()) // Add one real symbol that was introduces in v21 - .addPublicSymbol(u"@android:attr/colorAccent", ResourceId(0x01010435), + .addPublicSymbol("@android:attr/colorAccent", ResourceId(0x01010435), test::AttributeBuilder().build()) // Private symbol. - .addSymbol(u"@android:color/hidden", ResourceId(0x01020001)) + .addSymbol("@android:color/hidden", ResourceId(0x01020001)) - .addPublicSymbol(u"@android:id/id", ResourceId(0x01030000)) - .addSymbol(u"@com.app.test:id/id", ResourceId(0x7f030000)) - .addSymbol(u"@com.app.test:color/green", ResourceId(0x7f020000)) - .addSymbol(u"@com.app.test:color/red", ResourceId(0x7f020001)) - .addSymbol(u"@com.app.test:attr/colorAccent", ResourceId(0x7f010000), + .addPublicSymbol("@android:id/id", ResourceId(0x01030000)) + .addSymbol("@com.app.test:id/id", ResourceId(0x7f030000)) + .addSymbol("@com.app.test:color/green", ResourceId(0x7f020000)) + .addSymbol("@com.app.test:color/red", ResourceId(0x7f020001)) + .addSymbol("@com.app.test:attr/colorAccent", ResourceId(0x7f010000), test::AttributeBuilder() .setTypeMask(android::ResTable_map::TYPE_COLOR).build()) - .addPublicSymbol(u"@com.app.test:attr/com.android.support$colorAccent", + .addPublicSymbol("@com.app.test:attr/com.android.support$colorAccent", ResourceId(0x7f010001), test::AttributeBuilder() .setTypeMask(android::ResTable_map::TYPE_COLOR).build()) - .addPublicSymbol(u"@com.app.test:attr/attr", ResourceId(0x7f010002), + .addPublicSymbol("@com.app.test:attr/attr", ResourceId(0x7f010002), test::AttributeBuilder().build()) .build()) .build(); @@ -85,8 +84,7 @@ TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) { xml::Element* viewEl = xml::findRootElement(doc.get()); ASSERT_NE(viewEl, nullptr); - xml::Attribute* xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/android", - u"layout_width"); + xml::Attribute* xmlAttr = viewEl->findAttribute(xml::kSchemaAndroid, "layout_width"); ASSERT_NE(xmlAttr, nullptr); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id); @@ -94,7 +92,7 @@ TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) { ASSERT_NE(xmlAttr->compiledValue, nullptr); ASSERT_NE(valueCast<BinaryPrimitive>(xmlAttr->compiledValue.get()), nullptr); - xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/android", u"background"); + xmlAttr = viewEl->findAttribute(xml::kSchemaAndroid, "background"); ASSERT_NE(xmlAttr, nullptr); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id); @@ -103,17 +101,17 @@ TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) { Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get()); ASSERT_NE(ref, nullptr); AAPT_ASSERT_TRUE(ref->name); - EXPECT_EQ(ref->name.value(), test::parseNameOrDie(u"@color/green")); // Make sure the name - // didn't change. + EXPECT_EQ(ref->name.value(), test::parseNameOrDie("@color/green")); // Make sure the name + // didn't change. AAPT_ASSERT_TRUE(ref->id); EXPECT_EQ(ref->id.value(), ResourceId(0x7f020000)); - xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/android", u"text"); + xmlAttr = viewEl->findAttribute(xml::kSchemaAndroid, "text"); ASSERT_NE(xmlAttr, nullptr); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute); ASSERT_FALSE(xmlAttr->compiledValue); // Strings don't get compiled for memory sake. - xmlAttr = viewEl->findAttribute(u"", u"class"); + xmlAttr = viewEl->findAttribute("", "class"); ASSERT_NE(xmlAttr, nullptr); AAPT_ASSERT_FALSE(xmlAttr->compiledAttribute); ASSERT_EQ(xmlAttr->compiledValue, nullptr); @@ -159,7 +157,7 @@ TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) { ASSERT_NE(viewEl, nullptr); xml::Attribute* xmlAttr = viewEl->findAttribute( - u"http://schemas.android.com/apk/res/com.android.support", u"colorAccent"); + xml::buildPackageNamespace("com.android.support"), "colorAccent"); ASSERT_NE(xmlAttr, nullptr); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id); @@ -178,8 +176,7 @@ TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) { xml::Element* viewEl = xml::findRootElement(doc.get()); ASSERT_NE(viewEl, nullptr); - xml::Attribute* xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res-auto", - u"colorAccent"); + xml::Attribute* xmlAttr = viewEl->findAttribute(xml::kSchemaAuto, "colorAccent"); ASSERT_NE(xmlAttr, nullptr); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id); @@ -206,8 +203,7 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) { ASSERT_NE(viewEl, nullptr); // All attributes and references in this element should be referring to "android" (0x01). - xml::Attribute* xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/android", - u"attr"); + xml::Attribute* xmlAttr = viewEl->findAttribute(xml::kSchemaAndroid, "attr"); ASSERT_NE(xmlAttr, nullptr); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id); @@ -222,7 +218,7 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) { ASSERT_NE(viewEl, nullptr); // All attributes and references in this element should be referring to "com.app.test" (0x7f). - xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/com.app.test", u"attr"); + xmlAttr = viewEl->findAttribute(xml::buildPackageNamespace("com.app.test"), "attr"); ASSERT_NE(xmlAttr, nullptr); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id); @@ -245,8 +241,8 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) { ASSERT_NE(viewEl, nullptr); // All attributes and references in this element should be referring to "com.app.test" (0x7f). - xml::Attribute* xmlAttr = viewEl->findAttribute( - u"http://schemas.android.com/apk/res/com.app.test", u"attr"); + xml::Attribute* xmlAttr = viewEl->findAttribute(xml::buildPackageNamespace("com.app.test"), + "attr"); ASSERT_NE(xmlAttr, nullptr); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id); diff --git a/tools/aapt2/process/IResourceTableConsumer.h b/tools/aapt2/process/IResourceTableConsumer.h index 9affb836340c..69b7d9230db4 100644 --- a/tools/aapt2/process/IResourceTableConsumer.h +++ b/tools/aapt2/process/IResourceTableConsumer.h @@ -37,10 +37,11 @@ struct IAaptContext { virtual SymbolTable* getExternalSymbols() = 0; virtual IDiagnostics* getDiagnostics() = 0; - virtual const std::u16string& getCompilationPackage() = 0; + virtual const std::string& getCompilationPackage() = 0; virtual uint8_t getPackageId() = 0; virtual NameMangler* getNameMangler() = 0; virtual bool verbose() = 0; + virtual int getMinSdkVersion() = 0; }; struct IResourceTableConsumer { diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp index eaaf06f7e530..6c506dfe1749 100644 --- a/tools/aapt2/process/SymbolTable.cpp +++ b/tools/aapt2/process/SymbolTable.cpp @@ -16,6 +16,7 @@ #include "ConfigDescription.h" #include "Resource.h" +#include "ResourceUtils.h" #include "ValueVisitor.h" #include "process/SymbolTable.h" #include "util/Util.h" @@ -84,7 +85,7 @@ const SymbolTable::Symbol* SymbolTable::findById(ResourceId id) { const SymbolTable::Symbol* SymbolTable::findByReference(const Reference& ref) { // First try the ID. This is because when we lookup by ID, we only fill in the ID cache. // Looking up by name fills in the name and ID cache. So a cache miss will cause a failed - // ID lookup, then a successfull name lookup. Subsequent look ups will hit immediately + // ID lookup, then a successful name lookup. Subsequent look ups will hit immediately // because the ID is cached too. // // If we looked up by name first, a cache miss would mean we failed to lookup by name, then @@ -184,18 +185,13 @@ static std::unique_ptr<SymbolTable::Symbol> lookupAttributeInTable(const android return nullptr; } - const ResourceType* parsedType = parseResourceType( - StringPiece16(entryName.type, entryName.typeLen)); - if (!parsedType) { - table.unlockBag(entry); + Maybe<ResourceName> parsedName = ResourceUtils::toResourceName(entryName); + if (!parsedName) { return nullptr; } Attribute::Symbol symbol; - symbol.symbol.name = ResourceName( - StringPiece16(entryName.package, entryName.packageLen), - *parsedType, - StringPiece16(entryName.name, entryName.nameLen)); + symbol.symbol.name = parsedName.value(); symbol.symbol.id = ResourceId(mapEntry.name.ident); symbol.value = mapEntry.value.data; s->attribute->symbols.push_back(std::move(symbol)); @@ -208,11 +204,15 @@ static std::unique_ptr<SymbolTable::Symbol> lookupAttributeInTable(const android std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findByName( const ResourceName& name) { const android::ResTable& table = mAssets.getResources(false); - StringPiece16 typeStr = toString(name.type); + + const std::u16string package16 = util::utf8ToUtf16(name.package); + const std::u16string type16 = util::utf8ToUtf16(toString(name.type)); + const std::u16string entry16 = util::utf8ToUtf16(name.entry); + uint32_t typeSpecFlags = 0; - ResourceId resId = table.identifierForName(name.entry.data(), name.entry.size(), - typeStr.data(), typeStr.size(), - name.package.data(), name.package.size(), + ResourceId resId = table.identifierForName(entry16.data(), entry16.size(), + type16.data(), type16.size(), + package16.data(), package16.size(), &typeSpecFlags); if (!resId.isValid()) { return {}; @@ -238,37 +238,7 @@ static Maybe<ResourceName> getResourceName(const android::ResTable& table, Resou if (!table.getResourceName(id.id, true, &resName)) { return {}; } - - ResourceName name; - if (resName.package) { - name.package = StringPiece16(resName.package, resName.packageLen).toString(); - } - - const ResourceType* type; - if (resName.type) { - type = parseResourceType(StringPiece16(resName.type, resName.typeLen)); - - } else if (resName.type8) { - type = parseResourceType(util::utf8ToUtf16(StringPiece(resName.type8, resName.typeLen))); - } else { - return {}; - } - - if (!type) { - return {}; - } - - name.type = *type; - - if (resName.name) { - name.entry = StringPiece16(resName.name, resName.nameLen).toString(); - } else if (resName.name8) { - name.entry = util::utf8ToUtf16(StringPiece(resName.name8, resName.nameLen)); - } else { - return {}; - } - - return name; + return ResourceUtils::toResourceName(resName); } std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findById(ResourceId id) { diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h index e684bb06f1f5..43f4dd7fb869 100644 --- a/tools/aapt2/process/SymbolTable.h +++ b/tools/aapt2/process/SymbolTable.h @@ -34,7 +34,7 @@ namespace aapt { inline android::hash_t hash_type(const ResourceName& name) { - std::hash<std::u16string> strHash; + std::hash<std::string> strHash; android::hash_t hash = 0; hash = android::JenkinsHashMix(hash, (uint32_t) strHash(name.package)); hash = android::JenkinsHashMix(hash, (uint32_t) name.type); diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp index 34f31be3d0db..68669741cb19 100644 --- a/tools/aapt2/process/SymbolTable_test.cpp +++ b/tools/aapt2/process/SymbolTable_test.cpp @@ -21,31 +21,31 @@ namespace aapt { TEST(ResourceTableSymbolSourceTest, FindSymbols) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .addSimple(u"@android:id/foo", ResourceId(0x01020000)) - .addSimple(u"@android:id/bar") - .addValue(u"@android:attr/foo", ResourceId(0x01010000), + .addSimple("@android:id/foo", ResourceId(0x01020000)) + .addSimple("@android:id/bar") + .addValue("@android:attr/foo", ResourceId(0x01010000), test::AttributeBuilder().build()) .build(); ResourceTableSymbolSource symbolSource(table.get()); - EXPECT_NE(nullptr, symbolSource.findByName(test::parseNameOrDie(u"@android:id/foo"))); - EXPECT_NE(nullptr, symbolSource.findByName(test::parseNameOrDie(u"@android:id/bar"))); + EXPECT_NE(nullptr, symbolSource.findByName(test::parseNameOrDie("@android:id/foo"))); + EXPECT_NE(nullptr, symbolSource.findByName(test::parseNameOrDie("@android:id/bar"))); std::unique_ptr<SymbolTable::Symbol> s = symbolSource.findByName( - test::parseNameOrDie(u"@android:attr/foo")); + test::parseNameOrDie("@android:attr/foo")); ASSERT_NE(nullptr, s); EXPECT_NE(nullptr, s->attribute); } TEST(ResourceTableSymbolSourceTest, FindPrivateAttrSymbol) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .addValue(u"@android:^attr-private/foo", ResourceId(0x01010000), + .addValue("@android:^attr-private/foo", ResourceId(0x01010000), test::AttributeBuilder().build()) .build(); ResourceTableSymbolSource symbolSource(table.get()); std::unique_ptr<SymbolTable::Symbol> s = symbolSource.findByName( - test::parseNameOrDie(u"@android:attr/foo")); + test::parseNameOrDie("@android:attr/foo")); ASSERT_NE(nullptr, s); EXPECT_NE(nullptr, s->attribute); } diff --git a/tools/aapt2/proto/ProtoHelpers.cpp b/tools/aapt2/proto/ProtoHelpers.cpp index 99981c52e26f..2aa8aa500387 100644 --- a/tools/aapt2/proto/ProtoHelpers.cpp +++ b/tools/aapt2/proto/ProtoHelpers.cpp @@ -33,7 +33,7 @@ void serializeStringPoolToPb(const StringPool& pool, pb::StringPool* outPbPool) } void serializeSourceToPb(const Source& source, StringPool* srcPool, pb::Source* outPbSource) { - StringPool::Ref ref = srcPool->makeRef(util::utf8ToUtf16(source.path)); + StringPool::Ref ref = srcPool->makeRef(source.path); outPbSource->set_path_idx(static_cast<uint32_t>(ref.getIndex())); if (source.line) { outPbSource->set_line_no(static_cast<uint32_t>(source.line.value())); @@ -43,7 +43,7 @@ void serializeSourceToPb(const Source& source, StringPool* srcPool, pb::Source* void deserializeSourceFromPb(const pb::Source& pbSource, const android::ResStringPool& srcPool, Source* outSource) { if (pbSource.has_path_idx()) { - outSource->path = util::getString8(srcPool, pbSource.path_idx()).toString(); + outSource->path = util::getString(srcPool, pbSource.path_idx()); } if (pbSource.has_line_no()) { diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp index 1ec48f09f228..98ff87f7af30 100644 --- a/tools/aapt2/proto/TableProtoDeserializer.cpp +++ b/tools/aapt2/proto/TableProtoDeserializer.cpp @@ -30,7 +30,7 @@ class ReferenceIdToNameVisitor : public ValueVisitor { public: using ValueVisitor::visit; - ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceNameRef>* mapping) : + explicit ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceNameRef>* mapping) : mMapping(mapping) { assert(mMapping); } @@ -70,10 +70,9 @@ public: std::map<ResourceId, ResourceNameRef> idIndex; - ResourceTablePackage* pkg = table->createPackage( - util::utf8ToUtf16(pbPackage.package_name()), id); + ResourceTablePackage* pkg = table->createPackage(pbPackage.package_name(), id); for (const pb::Type& pbType : pbPackage.types()) { - const ResourceType* resType = parseResourceType(util::utf8ToUtf16(pbType.name())); + const ResourceType* resType = parseResourceType(pbType.name()); if (!resType) { mDiag->error(DiagMessage(mSource) << "unknown type '" << pbType.name() << "'"); return {}; @@ -82,7 +81,7 @@ public: ResourceTableType* type = pkg->findOrCreateType(*resType); for (const pb::Entry& pbEntry : pbType.entries()) { - ResourceEntry* entry = type->findOrCreateEntry(util::utf8ToUtf16(pbEntry.name())); + ResourceEntry* entry = type->findOrCreateEntry(pbEntry.name()); // Deserialize the symbol status (public/private with source and comments). if (pbEntry.has_symbol_status()) { @@ -93,7 +92,7 @@ public: } if (pbStatus.has_comment()) { - entry->symbolStatus.comment = util::utf8ToUtf16(pbStatus.comment()); + entry->symbolStatus.comment = pbStatus.comment(); } SymbolState visibility = deserializeVisibilityFromPb(pbStatus.visibility()); @@ -179,14 +178,14 @@ private: } else if (pbItem.has_str()) { const uint32_t idx = pbItem.str().idx(); - StringPiece16 str = util::getString(*mValuePool, idx); + const std::string str = util::getString(*mValuePool, idx); const android::ResStringPool_span* spans = mValuePool->styleAt(idx); if (spans && spans->name.index != android::ResStringPool_span::END) { - StyleString styleStr = { str.toString() }; + StyleString styleStr = { str }; while (spans->name.index != android::ResStringPool_span::END) { styleStr.spans.push_back(Span{ - util::getString(*mValuePool, spans->name.index).toString(), + util::getString(*mValuePool, spans->name.index), spans->firstChar, spans->lastChar }); @@ -200,13 +199,13 @@ private: } else if (pbItem.has_raw_str()) { const uint32_t idx = pbItem.raw_str().idx(); - StringPiece16 str = util::getString(*mValuePool, idx); + const std::string str = util::getString(*mValuePool, idx); return util::make_unique<RawString>( pool->makeRef(str, StringPool::Context{ 1, config })); } else if (pbItem.has_file()) { const uint32_t idx = pbItem.file().path_idx(); - StringPiece16 str = util::getString(*mValuePool, idx); + const std::string str = util::getString(*mValuePool, idx); return util::make_unique<FileReference>( pool->makeRef(str, StringPool::Context{ 0, config })); @@ -351,7 +350,7 @@ private: } if (pbRef.has_symbol_idx()) { - StringPiece16 strSymbol = util::getString(*mSymbolPool, pbRef.symbol_idx()); + const std::string strSymbol = util::getString(*mSymbolPool, pbRef.symbol_idx()); ResourceNameRef nameRef; if (!ResourceUtils::parseResourceName(strSymbol, &nameRef, nullptr)) { mDiag->error(DiagMessage(mSource) << "invalid reference name '" @@ -373,7 +372,7 @@ private: } if (pbItem.has_comment()) { - outValue->setComment(util::utf8ToUtf16(pbItem.comment())); + outValue->setComment(pbItem.comment()); } } @@ -446,8 +445,7 @@ std::unique_ptr<ResourceFile> deserializeCompiledFileFromPb(const pb::CompiledFi ResourceNameRef nameRef; // Need to create an lvalue here so that nameRef can point to something real. - std::u16string utf16Name = util::utf8ToUtf16(pbFile.resource_name()); - if (!ResourceUtils::parseResourceName(utf16Name, &nameRef)) { + if (!ResourceUtils::parseResourceName(pbFile.resource_name(), &nameRef)) { diag->error(DiagMessage(source) << "invalid resource name in compiled file header: " << pbFile.resource_name()); return {}; @@ -458,8 +456,7 @@ std::unique_ptr<ResourceFile> deserializeCompiledFileFromPb(const pb::CompiledFi for (const pb::CompiledFile_Symbol& pbSymbol : pbFile.exported_symbols()) { // Need to create an lvalue here so that nameRef can point to something real. - utf16Name = util::utf8ToUtf16(pbSymbol.resource_name()); - if (!ResourceUtils::parseResourceName(utf16Name, &nameRef)) { + if (!ResourceUtils::parseResourceName(pbSymbol.resource_name(), &nameRef)) { diag->error(DiagMessage(source) << "invalid resource name for exported symbol in " "compiled file header: " << pbFile.resource_name()); diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp index 5d1b72b0ebbd..425fca695a0b 100644 --- a/tools/aapt2/proto/TableProtoSerializer.cpp +++ b/tools/aapt2/proto/TableProtoSerializer.cpp @@ -171,7 +171,7 @@ private: void serializeItemCommonToPb(const Item& item, T* pbItem) { serializeSourceToPb(item.getSource(), mSourcePool, pbItem->mutable_source()); if (!item.getComment().empty()) { - pbItem->set_comment(util::utf16ToUtf8(item.getComment())); + pbItem->set_comment(item.getComment()); } } @@ -220,28 +220,28 @@ std::unique_ptr<pb::ResourceTable> serializeTableToPb(ResourceTable* table) { if (package->id) { pbPackage->set_package_id(package->id.value()); } - pbPackage->set_package_name(util::utf16ToUtf8(package->name)); + pbPackage->set_package_name(package->name); for (auto& type : package->types) { pb::Type* pbType = pbPackage->add_types(); if (type->id) { pbType->set_id(type->id.value()); } - pbType->set_name(util::utf16ToUtf8(toString(type->type))); + pbType->set_name(toString(type->type).toString()); for (auto& entry : type->entries) { pb::Entry* pbEntry = pbType->add_entries(); if (entry->id) { pbEntry->set_id(entry->id.value()); } - pbEntry->set_name(util::utf16ToUtf8(entry->name)); + pbEntry->set_name(entry->name); // Write the SymbolStatus struct. pb::SymbolStatus* pbStatus = pbEntry->mutable_symbol_status(); pbStatus->set_visibility(serializeVisibilityToPb(entry->symbolStatus.state)); serializeSourceToPb(entry->symbolStatus.source, &sourcePool, pbStatus->mutable_source()); - pbStatus->set_comment(util::utf16ToUtf8(entry->symbolStatus.comment)); + pbStatus->set_comment(entry->symbolStatus.comment); for (auto& configValue : entry->values) { pb::ConfigValue* pbConfigValue = pbEntry->add_config_values(); @@ -254,7 +254,7 @@ std::unique_ptr<pb::ResourceTable> serializeTableToPb(ResourceTable* table) { serializeSourceToPb(configValue->value->getSource(), &sourcePool, pbValue->mutable_source()); if (!configValue->value->getComment().empty()) { - pbValue->set_comment(util::utf16ToUtf8(configValue->value->getComment())); + pbValue->set_comment(configValue->value->getComment()); } if (configValue->value->isWeak()) { @@ -275,13 +275,13 @@ std::unique_ptr<pb::ResourceTable> serializeTableToPb(ResourceTable* table) { std::unique_ptr<pb::CompiledFile> serializeCompiledFileToPb(const ResourceFile& file) { std::unique_ptr<pb::CompiledFile> pbFile = util::make_unique<pb::CompiledFile>(); - pbFile->set_resource_name(util::utf16ToUtf8(file.name.toString())); + pbFile->set_resource_name(file.name.toString()); pbFile->set_source_path(file.source.path); serializeConfig(file.config, pbFile->mutable_config()); for (const SourcedResourceName& exported : file.exportedSymbols) { pb::CompiledFile_Symbol* pbSymbol = pbFile->add_exported_symbols(); - pbSymbol->set_resource_name(util::utf16ToUtf8(exported.name.toString())); + pbSymbol->set_resource_name(exported.name.toString()); pbSymbol->set_line_no(exported.line); } return pbFile; diff --git a/tools/aapt2/proto/TableProtoSerializer_test.cpp b/tools/aapt2/proto/TableProtoSerializer_test.cpp index dd995d858d77..78b32f740ba3 100644 --- a/tools/aapt2/proto/TableProtoSerializer_test.cpp +++ b/tools/aapt2/proto/TableProtoSerializer_test.cpp @@ -16,49 +16,45 @@ #include "ResourceTable.h" #include "proto/ProtoSerialize.h" -#include "test/Builders.h" -#include "test/Common.h" -#include "test/Context.h" - -#include <gtest/gtest.h> +#include "test/Test.h" namespace aapt { TEST(TableProtoSerializer, SerializeSinglePackage) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .setPackageId(u"com.app.a", 0x7f) - .addFileReference(u"@com.app.a:layout/main", ResourceId(0x7f020000), - u"res/layout/main.xml") - .addReference(u"@com.app.a:layout/other", ResourceId(0x7f020001), - u"@com.app.a:layout/main") - .addString(u"@com.app.a:string/text", {}, u"hi") - .addValue(u"@com.app.a:id/foo", {}, util::make_unique<Id>()) + .setPackageId("com.app.a", 0x7f) + .addFileReference("@com.app.a:layout/main", ResourceId(0x7f020000), + "res/layout/main.xml") + .addReference("@com.app.a:layout/other", ResourceId(0x7f020001), + "@com.app.a:layout/main") + .addString("@com.app.a:string/text", {}, "hi") + .addValue("@com.app.a:id/foo", {}, util::make_unique<Id>()) .build(); Symbol publicSymbol; publicSymbol.state = SymbolState::kPublic; - ASSERT_TRUE(table->setSymbolState(test::parseNameOrDie(u"@com.app.a:layout/main"), + ASSERT_TRUE(table->setSymbolState(test::parseNameOrDie("@com.app.a:layout/main"), ResourceId(0x7f020000), publicSymbol, context->getDiagnostics())); - Id* id = test::getValue<Id>(table.get(), u"@com.app.a:id/foo"); + Id* id = test::getValue<Id>(table.get(), "@com.app.a:id/foo"); ASSERT_NE(nullptr, id); // Make a plural. std::unique_ptr<Plural> plural = util::make_unique<Plural>(); - plural->values[Plural::One] = util::make_unique<String>(table->stringPool.makeRef(u"one")); - ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:plurals/hey"), - ConfigDescription{}, std::string(), std::move(plural), + plural->values[Plural::One] = util::make_unique<String>(table->stringPool.makeRef("one")); + ASSERT_TRUE(table->addResource(test::parseNameOrDie("@com.app.a:plurals/hey"), + ConfigDescription{}, {}, std::move(plural), context->getDiagnostics())); // Make a resource with different products. - ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:integer/one"), - test::parseConfigOrDie("land"), std::string(), + ASSERT_TRUE(table->addResource(test::parseNameOrDie("@com.app.a:integer/one"), + test::parseConfigOrDie("land"), {}, test::buildPrimitive(android::Res_value::TYPE_INT_DEC, 123u), context->getDiagnostics())); - ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:integer/one"), - test::parseConfigOrDie("land"), std::string("tablet"), + ASSERT_TRUE(table->addResource(test::parseNameOrDie("@com.app.a:integer/one"), + test::parseConfigOrDie("land"), "tablet", test::buildPrimitive(android::Res_value::TYPE_INT_DEC, 321u), context->getDiagnostics())); @@ -66,10 +62,10 @@ TEST(TableProtoSerializer, SerializeSinglePackage) { // The reference should point to a resource outside of this table to test that both // name and id get serialized. Reference expectedRef; - expectedRef.name = test::parseNameOrDie(u"@android:layout/main"); + expectedRef.name = test::parseNameOrDie("@android:layout/main"); expectedRef.id = ResourceId(0x01020000); - ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:layout/abc"), - ConfigDescription::defaultConfig(), std::string(), + ASSERT_TRUE(table->addResource(test::parseNameOrDie("@com.app.a:layout/abc"), + ConfigDescription::defaultConfig(), {}, util::make_unique<Reference>(expectedRef), context->getDiagnostics())); @@ -81,28 +77,28 @@ TEST(TableProtoSerializer, SerializeSinglePackage) { context->getDiagnostics()); ASSERT_NE(nullptr, newTable); - Id* newId = test::getValue<Id>(newTable.get(), u"@com.app.a:id/foo"); + Id* newId = test::getValue<Id>(newTable.get(), "@com.app.a:id/foo"); ASSERT_NE(nullptr, newId); EXPECT_EQ(id->isWeak(), newId->isWeak()); Maybe<ResourceTable::SearchResult> result = newTable->findResource( - test::parseNameOrDie(u"@com.app.a:layout/main")); + test::parseNameOrDie("@com.app.a:layout/main")); AAPT_ASSERT_TRUE(result); EXPECT_EQ(SymbolState::kPublic, result.value().type->symbolStatus.state); EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state); // Find the product-dependent values BinaryPrimitive* prim = test::getValueForConfigAndProduct<BinaryPrimitive>( - newTable.get(), u"@com.app.a:integer/one", test::parseConfigOrDie("land"), ""); + newTable.get(), "@com.app.a:integer/one", test::parseConfigOrDie("land"), ""); ASSERT_NE(nullptr, prim); EXPECT_EQ(123u, prim->value.data); prim = test::getValueForConfigAndProduct<BinaryPrimitive>( - newTable.get(), u"@com.app.a:integer/one", test::parseConfigOrDie("land"), "tablet"); + newTable.get(), "@com.app.a:integer/one", test::parseConfigOrDie("land"), "tablet"); ASSERT_NE(nullptr, prim); EXPECT_EQ(321u, prim->value.data); - Reference* actualRef = test::getValue<Reference>(newTable.get(), u"@com.app.a:layout/abc"); + Reference* actualRef = test::getValue<Reference>(newTable.get(), "@com.app.a:layout/abc"); ASSERT_NE(nullptr, actualRef); AAPT_ASSERT_TRUE(actualRef->name); AAPT_ASSERT_TRUE(actualRef->id); @@ -115,9 +111,9 @@ TEST(TableProtoSerializer, SerializeFileHeader) { ResourceFile f; f.config = test::parseConfigOrDie("hdpi-v9"); - f.name = test::parseNameOrDie(u"@com.app.a:layout/main"); + f.name = test::parseNameOrDie("@com.app.a:layout/main"); f.source.path = "res/layout-hdpi-v9/main.xml"; - f.exportedSymbols.push_back(SourcedResourceName{ test::parseNameOrDie(u"@+id/unchecked"), 23u }); + f.exportedSymbols.push_back(SourcedResourceName{ test::parseNameOrDie("@+id/unchecked"), 23u }); const std::string expectedData = "1234"; @@ -136,7 +132,7 @@ TEST(TableProtoSerializer, SerializeFileHeader) { const pb::CompiledFile* newPbFile = inFileStream.CompiledFile(); ASSERT_NE(nullptr, newPbFile); - std::unique_ptr<ResourceFile> file = deserializeCompiledFileFromPb(*newPbFile, Source{ "test" }, + std::unique_ptr<ResourceFile> file = deserializeCompiledFileFromPb(*newPbFile, Source("test"), context->getDiagnostics()); ASSERT_NE(nullptr, file); @@ -145,7 +141,7 @@ TEST(TableProtoSerializer, SerializeFileHeader) { EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(inFileStream.data()) & 0x03); ASSERT_EQ(1u, file->exportedSymbols.size()); - EXPECT_EQ(test::parseNameOrDie(u"@+id/unchecked"), file->exportedSymbols[0].name); + EXPECT_EQ(test::parseNameOrDie("@+id/unchecked"), file->exportedSymbols[0].name); } TEST(TableProtoSerializer, DeserializeCorruptHeaderSafely) { diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp index 4bfdb1205e19..2dfe2a239900 100644 --- a/tools/aapt2/split/TableSplitter.cpp +++ b/tools/aapt2/split/TableSplitter.cpp @@ -40,7 +40,7 @@ static ConfigDescription copyWithoutDensity(const ConfigDescription& config) { */ class SplitValueSelector { public: - SplitValueSelector(const SplitConstraints& constraints) { + explicit SplitValueSelector(const SplitConstraints& constraints) { for (const ConfigDescription& config : constraints.configs) { if (config.density == 0) { mDensityIndependentConfigs.insert(config); diff --git a/tools/aapt2/split/TableSplitter_test.cpp b/tools/aapt2/split/TableSplitter_test.cpp index 74ca32e04a30..a6dfd6243f89 100644 --- a/tools/aapt2/split/TableSplitter_test.cpp +++ b/tools/aapt2/split/TableSplitter_test.cpp @@ -15,24 +15,21 @@ */ #include "split/TableSplitter.h" -#include "test/Builders.h" -#include "test/Common.h" - -#include <gtest/gtest.h> +#include "test/Test.h" namespace aapt { TEST(TableSplitterTest, NoSplitPreferredDensity) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() - .addFileReference(u"@android:drawable/icon", u"res/drawable-mdpi/icon.png", + .addFileReference("@android:drawable/icon", "res/drawable-mdpi/icon.png", test::parseConfigOrDie("mdpi")) - .addFileReference(u"@android:drawable/icon", u"res/drawable-hdpi/icon.png", + .addFileReference("@android:drawable/icon", "res/drawable-hdpi/icon.png", test::parseConfigOrDie("hdpi")) - .addFileReference(u"@android:drawable/icon", u"res/drawable-xhdpi/icon.png", + .addFileReference("@android:drawable/icon", "res/drawable-xhdpi/icon.png", test::parseConfigOrDie("xhdpi")) - .addFileReference(u"@android:drawable/icon", u"res/drawable-xxhdpi/icon.png", + .addFileReference("@android:drawable/icon", "res/drawable-xxhdpi/icon.png", test::parseConfigOrDie("xxhdpi")) - .addSimple(u"@android:string/one", {}) + .addSimple("@android:string/one") .build(); TableSplitterOptions options; @@ -41,24 +38,24 @@ TEST(TableSplitterTest, NoSplitPreferredDensity) { splitter.splitTable(table.get()); EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(table.get(), - u"@android:drawable/icon", + "@android:drawable/icon", test::parseConfigOrDie("mdpi"))); EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(table.get(), - u"@android:drawable/icon", + "@android:drawable/icon", test::parseConfigOrDie("hdpi"))); EXPECT_NE(nullptr, test::getValueForConfig<FileReference>(table.get(), - u"@android:drawable/icon", + "@android:drawable/icon", test::parseConfigOrDie("xhdpi"))); EXPECT_EQ(nullptr, test::getValueForConfig<FileReference>(table.get(), - u"@android:drawable/icon", + "@android:drawable/icon", test::parseConfigOrDie("xxhdpi"))); - EXPECT_NE(nullptr, test::getValue<Id>(table.get(), u"@android:string/one")); + EXPECT_NE(nullptr, test::getValue<Id>(table.get(), "@android:string/one")); } TEST(TableSplitterTest, SplitTableByConfigAndDensity) { ResourceTable table; - const ResourceName foo = test::parseNameOrDie(u"@android:string/foo"); + const ResourceName foo = test::parseNameOrDie("@android:string/foo"); ASSERT_TRUE(table.addResource(foo, test::parseConfigOrDie("land-hdpi"), {}, util::make_unique<Id>(), test::getDiagnostics())); @@ -82,25 +79,25 @@ TEST(TableSplitterTest, SplitTableByConfigAndDensity) { ResourceTable* splitTwo = splitter.getSplits()[1].get(); // Since a split was defined, all densities should be gone from base. - EXPECT_EQ(nullptr, test::getValueForConfig<Id>(&table, u"@android:string/foo", + EXPECT_EQ(nullptr, test::getValueForConfig<Id>(&table, "@android:string/foo", test::parseConfigOrDie("land-hdpi"))); - EXPECT_EQ(nullptr, test::getValueForConfig<Id>(&table, u"@android:string/foo", + EXPECT_EQ(nullptr, test::getValueForConfig<Id>(&table, "@android:string/foo", test::parseConfigOrDie("land-xhdpi"))); - EXPECT_EQ(nullptr, test::getValueForConfig<Id>(&table, u"@android:string/foo", + EXPECT_EQ(nullptr, test::getValueForConfig<Id>(&table, "@android:string/foo", test::parseConfigOrDie("land-xxhdpi"))); - EXPECT_NE(nullptr, test::getValueForConfig<Id>(splitOne, u"@android:string/foo", + EXPECT_NE(nullptr, test::getValueForConfig<Id>(splitOne, "@android:string/foo", test::parseConfigOrDie("land-hdpi"))); - EXPECT_EQ(nullptr, test::getValueForConfig<Id>(splitOne, u"@android:string/foo", + EXPECT_EQ(nullptr, test::getValueForConfig<Id>(splitOne, "@android:string/foo", test::parseConfigOrDie("land-xhdpi"))); - EXPECT_EQ(nullptr, test::getValueForConfig<Id>(splitOne, u"@android:string/foo", + EXPECT_EQ(nullptr, test::getValueForConfig<Id>(splitOne, "@android:string/foo", test::parseConfigOrDie("land-xxhdpi"))); - EXPECT_EQ(nullptr, test::getValueForConfig<Id>(splitTwo, u"@android:string/foo", + EXPECT_EQ(nullptr, test::getValueForConfig<Id>(splitTwo, "@android:string/foo", test::parseConfigOrDie("land-hdpi"))); - EXPECT_NE(nullptr, test::getValueForConfig<Id>(splitTwo, u"@android:string/foo", + EXPECT_NE(nullptr, test::getValueForConfig<Id>(splitTwo, "@android:string/foo", test::parseConfigOrDie("land-xhdpi"))); - EXPECT_EQ(nullptr, test::getValueForConfig<Id>(splitTwo, u"@android:string/foo", + EXPECT_EQ(nullptr, test::getValueForConfig<Id>(splitTwo, "@android:string/foo", test::parseConfigOrDie("land-xxhdpi"))); } diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h index 8eb4bc88168d..c0c016032921 100644 --- a/tools/aapt2/test/Builders.h +++ b/tools/aapt2/test/Builders.h @@ -40,77 +40,81 @@ public: return &mTable->stringPool; } - ResourceTableBuilder& setPackageId(const StringPiece16& packageName, uint8_t id) { + ResourceTableBuilder& setPackageId(const StringPiece& packageName, uint8_t id) { ResourceTablePackage* package = mTable->createPackage(packageName, id); assert(package); return *this; } - ResourceTableBuilder& addSimple(const StringPiece16& name, const ResourceId id = {}) { + ResourceTableBuilder& addSimple(const StringPiece& name, const ResourceId id = {}) { return addValue(name, id, util::make_unique<Id>()); } - ResourceTableBuilder& addReference(const StringPiece16& name, const StringPiece16& ref) { + ResourceTableBuilder& addSimple(const StringPiece& name, const ConfigDescription& config, + const ResourceId id = {}) { + return addValue(name, config, id, util::make_unique<Id>()); + } + + ResourceTableBuilder& addReference(const StringPiece& name, const StringPiece& ref) { return addReference(name, {}, ref); } - ResourceTableBuilder& addReference(const StringPiece16& name, const ResourceId id, - const StringPiece16& ref) { + ResourceTableBuilder& addReference(const StringPiece& name, const ResourceId id, + const StringPiece& ref) { return addValue(name, id, util::make_unique<Reference>(parseNameOrDie(ref))); } - ResourceTableBuilder& addString(const StringPiece16& name, const StringPiece16& str) { + ResourceTableBuilder& addString(const StringPiece& name, const StringPiece& str) { return addString(name, {}, str); } - ResourceTableBuilder& addString(const StringPiece16& name, const ResourceId id, - const StringPiece16& str) { + ResourceTableBuilder& addString(const StringPiece& name, const ResourceId id, + const StringPiece& str) { return addValue(name, id, util::make_unique<String>(mTable->stringPool.makeRef(str))); } - ResourceTableBuilder& addString(const StringPiece16& name, const ResourceId id, - const ConfigDescription& config, const StringPiece16& str) { - return addValue(name, id, config, + ResourceTableBuilder& addString(const StringPiece& name, const ResourceId id, + const ConfigDescription& config, const StringPiece& str) { + return addValue(name, config, id, util::make_unique<String>(mTable->stringPool.makeRef(str))); } - ResourceTableBuilder& addFileReference(const StringPiece16& name, const StringPiece16& path) { + ResourceTableBuilder& addFileReference(const StringPiece& name, const StringPiece& path) { return addFileReference(name, {}, path); } - ResourceTableBuilder& addFileReference(const StringPiece16& name, const ResourceId id, - const StringPiece16& path) { + ResourceTableBuilder& addFileReference(const StringPiece& name, const ResourceId id, + const StringPiece& path) { return addValue(name, id, util::make_unique<FileReference>(mTable->stringPool.makeRef(path))); } - ResourceTableBuilder& addFileReference(const StringPiece16& name, const StringPiece16& path, + ResourceTableBuilder& addFileReference(const StringPiece& name, const StringPiece& path, const ConfigDescription& config) { - return addValue(name, {}, config, + return addValue(name, config, {}, util::make_unique<FileReference>(mTable->stringPool.makeRef(path))); } - ResourceTableBuilder& addValue(const StringPiece16& name, + ResourceTableBuilder& addValue(const StringPiece& name, std::unique_ptr<Value> value) { return addValue(name, {}, std::move(value)); } - ResourceTableBuilder& addValue(const StringPiece16& name, const ResourceId id, - std::unique_ptr<Value> value) { - return addValue(name, id, {}, std::move(value)); + ResourceTableBuilder& addValue(const StringPiece& name, const ResourceId id, + std::unique_ptr<Value> value) { + return addValue(name, {}, id, std::move(value)); } - ResourceTableBuilder& addValue(const StringPiece16& name, const ResourceId id, - const ConfigDescription& config, - std::unique_ptr<Value> value) { + ResourceTableBuilder& addValue(const StringPiece& name, const ConfigDescription& config, + const ResourceId id, std::unique_ptr<Value> value) { ResourceName resName = parseNameOrDie(name); - bool result = mTable->addResourceAllowMangled(resName, id, config, std::string(), + bool result = mTable->addResourceAllowMangled(resName, id, config, {}, std::move(value), &mDiagnostics); assert(result); return *this; } - ResourceTableBuilder& setSymbolState(const StringPiece16& name, ResourceId id, + ResourceTableBuilder& setSymbolState(const StringPiece& name, ResourceId id, SymbolState state) { ResourceName resName = parseNameOrDie(name); Symbol symbol; @@ -125,7 +129,7 @@ public: } }; -inline std::unique_ptr<Reference> buildReference(const StringPiece16& ref, +inline std::unique_ptr<Reference> buildReference(const StringPiece& ref, Maybe<ResourceId> id = {}) { std::unique_ptr<Reference> reference = util::make_unique<Reference>(parseNameOrDie(ref)); reference->id = id; @@ -156,7 +160,7 @@ public: return *this; } - ValueBuilder& setComment(const StringPiece16& str) { + ValueBuilder& setComment(const StringPiece& str) { mValue->setComment(str); return *this; } @@ -180,9 +184,9 @@ public: return *this; } - AttributeBuilder& addItem(const StringPiece16& name, uint32_t value) { + AttributeBuilder& addItem(const StringPiece& name, uint32_t value) { mAttr->symbols.push_back(Attribute::Symbol{ - Reference(ResourceName{ {}, ResourceType::kId, name.toString()}), + Reference(ResourceName({}, ResourceType::kId, name)), value}); return *this; } @@ -197,17 +201,17 @@ private: std::unique_ptr<Style> mStyle = util::make_unique<Style>(); public: - StyleBuilder& setParent(const StringPiece16& str) { + StyleBuilder& setParent(const StringPiece& str) { mStyle->parent = Reference(parseNameOrDie(str)); return *this; } - StyleBuilder& addItem(const StringPiece16& str, std::unique_ptr<Item> value) { + StyleBuilder& addItem(const StringPiece& str, std::unique_ptr<Item> value) { mStyle->entries.push_back(Style::Entry{ Reference(parseNameOrDie(str)), std::move(value) }); return *this; } - StyleBuilder& addItem(const StringPiece16& str, ResourceId id, std::unique_ptr<Item> value) { + StyleBuilder& addItem(const StringPiece& str, ResourceId id, std::unique_ptr<Item> value) { addItem(str, std::move(value)); mStyle->entries.back().key.id = id; return *this; @@ -223,7 +227,7 @@ private: std::unique_ptr<Styleable> mStyleable = util::make_unique<Styleable>(); public: - StyleableBuilder& addItem(const StringPiece16& str, Maybe<ResourceId> id = {}) { + StyleableBuilder& addItem(const StringPiece& str, Maybe<ResourceId> id = {}) { mStyleable->entries.push_back(Reference(parseNameOrDie(str))); mStyleable->entries.back().id = id; return *this; diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h index faccd47783ff..b2eaba6439d8 100644 --- a/tools/aapt2/test/Common.h +++ b/tools/aapt2/test/Common.h @@ -62,7 +62,7 @@ inline IDiagnostics* getDiagnostics() { return &diag; } -inline ResourceName parseNameOrDie(const StringPiece16& str) { +inline ResourceName parseNameOrDie(const StringPiece& str) { ResourceNameRef ref; bool result = ResourceUtils::tryParseReference(str, &ref); assert(result && "invalid resource name"); @@ -77,7 +77,7 @@ inline ConfigDescription parseConfigOrDie(const StringPiece& str) { } template <typename T> T* getValueForConfigAndProduct(ResourceTable* table, - const StringPiece16& resName, + const StringPiece& resName, const ConfigDescription& config, const StringPiece& product) { Maybe<ResourceTable::SearchResult> result = table->findResource(parseNameOrDie(resName)); @@ -90,12 +90,12 @@ template <typename T> T* getValueForConfigAndProduct(ResourceTable* table, return nullptr; } -template <typename T> T* getValueForConfig(ResourceTable* table, const StringPiece16& resName, +template <typename T> T* getValueForConfig(ResourceTable* table, const StringPiece& resName, const ConfigDescription& config) { return getValueForConfigAndProduct<T>(table, resName, config, {}); } -template <typename T> T* getValue(ResourceTable* table, const StringPiece16& resName) { +template <typename T> T* getValue(ResourceTable* table, const StringPiece& resName) { return getValueForConfig<T>(table, resName, {}); } diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h index 96752d33dd9a..b053e07eafde 100644 --- a/tools/aapt2/test/Context.h +++ b/tools/aapt2/test/Context.h @@ -19,7 +19,6 @@ #include "NameMangler.h" #include "util/Util.h" - #include "process/IResourceTableConsumer.h" #include "process/SymbolTable.h" #include "test/Common.h" @@ -40,7 +39,7 @@ public: return &mDiagnostics; } - const std::u16string& getCompilationPackage() override { + const std::string& getCompilationPackage() override { assert(mCompilationPackage && "package name not set"); return mCompilationPackage.value(); } @@ -58,17 +57,19 @@ public: return false; } + int getMinSdkVersion() override { + return mMinSdkVersion; + } + private: friend class ContextBuilder; - Context() : mNameMangler({}) { - } - - Maybe<std::u16string> mCompilationPackage; + Maybe<std::string> mCompilationPackage; Maybe<uint8_t> mPackageId; StdErrDiagnostics mDiagnostics; SymbolTable mSymbols; - NameMangler mNameMangler; + NameMangler mNameMangler = NameMangler({}); + int mMinSdkVersion = 0; }; class ContextBuilder { @@ -76,7 +77,7 @@ private: std::unique_ptr<Context> mContext = std::unique_ptr<Context>(new Context()); public: - ContextBuilder& setCompilationPackage(const StringPiece16& package) { + ContextBuilder& setCompilationPackage(const StringPiece& package) { mContext->mCompilationPackage = package.toString(); return *this; } @@ -96,6 +97,11 @@ public: return *this; } + ContextBuilder& setMinSdkVersion(int minSdk) { + mContext->mMinSdkVersion = minSdk; + return *this; + } + std::unique_ptr<Context> build() { return std::move(mContext); } @@ -103,7 +109,7 @@ public: class StaticSymbolSourceBuilder { public: - StaticSymbolSourceBuilder& addPublicSymbol(const StringPiece16& name, ResourceId id, + StaticSymbolSourceBuilder& addPublicSymbol(const StringPiece& name, ResourceId id, std::unique_ptr<Attribute> attr = {}) { std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>( id, std::move(attr), true); @@ -113,7 +119,7 @@ public: return *this; } - StaticSymbolSourceBuilder& addSymbol(const StringPiece16& name, ResourceId id, + StaticSymbolSourceBuilder& addSymbol(const StringPiece& name, ResourceId id, std::unique_ptr<Attribute> attr = {}) { std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>( id, std::move(attr), false); diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp index ec4675167676..4fd77c83a891 100644 --- a/tools/aapt2/unflatten/BinaryResourceParser.cpp +++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp @@ -47,7 +47,7 @@ private: public: using ValueVisitor::visit; - ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceName>* mapping) : + explicit ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceName>* mapping) : mMapping(mapping) { assert(mMapping); } @@ -61,7 +61,6 @@ public: auto cacheIter = mMapping->find(id); if (cacheIter != mMapping->end()) { reference->name = cacheIter->second; - reference->id = {}; } } }; @@ -179,7 +178,8 @@ bool BinaryResourceParser::parsePackage(const ResChunk_header* chunk) { packageName[i] = util::deviceToHost16(packageHeader->name[i]); } - ResourceTablePackage* package = mTable->createPackage(packageName, (uint8_t) packageId); + ResourceTablePackage* package = mTable->createPackage(util::utf16ToUtf8(packageName), + static_cast<uint8_t>(packageId)); if (!package) { mContext->getDiagnostics()->error(DiagMessage(mSource) << "incompatible package '" << packageName @@ -309,12 +309,12 @@ bool BinaryResourceParser::parseType(const ResourceTablePackage* package, ConfigDescription config; config.copyFromDtoH(type->config); - StringPiece16 typeStr16 = util::getString(mTypePool, type->id - 1); + const std::string typeStr = util::getString(mTypePool, type->id - 1); - const ResourceType* parsedType = parseResourceType(typeStr16); + const ResourceType* parsedType = parseResourceType(typeStr); if (!parsedType) { mContext->getDiagnostics()->error(DiagMessage(mSource) - << "invalid type name '" << typeStr16 + << "invalid type name '" << typeStr << "' for type with ID " << (int) type->id); return false; } @@ -328,7 +328,7 @@ bool BinaryResourceParser::parseType(const ResourceTablePackage* package, const ResourceName name(package->name, *parsedType, util::getString(mKeyPool, - util::deviceToHost32(entry->key.index)).toString()); + util::deviceToHost32(entry->key.index))); const ResourceId resId(package->id.value(), type->id, static_cast<uint16_t>(it.index())); @@ -388,16 +388,16 @@ std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& na const uint32_t data = util::deviceToHost32(value->data); if (value->dataType == Res_value::TYPE_STRING) { - StringPiece16 str = util::getString(mValuePool, data); + const std::string str = util::getString(mValuePool, data); const ResStringPool_span* spans = mValuePool.styleAt(data); // Check if the string has a valid style associated with it. if (spans != nullptr && spans->name.index != ResStringPool_span::END) { - StyleString styleStr = { str.toString() }; + StyleString styleStr = { str }; while (spans->name.index != ResStringPool_span::END) { styleStr.spans.push_back(Span{ - util::getString(mValuePool, spans->name.index).toString(), + util::getString(mValuePool, spans->name.index), spans->firstChar, spans->lastChar }); @@ -407,7 +407,7 @@ std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& na styleStr, StringPool::Context{1, config})); } else { if (name.type != ResourceType::kString && - util::stringStartsWith<char16_t>(str, u"res/")) { + util::stringStartsWith(str, "res/")) { // This must be a FileReference. return util::make_unique<FileReference>(mTable->stringPool.makeRef( str, StringPool::Context{ 0, config })); diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp index f5e49f11c082..c9b381124ddf 100644 --- a/tools/aapt2/util/Files.cpp +++ b/tools/aapt2/util/Files.cpp @@ -157,7 +157,7 @@ void appendPath(std::string* base, StringPiece part) { std::string packageToPath(const StringPiece& package) { std::string outPath; - for (StringPiece part : util::tokenize<char>(package, '.')) { + for (StringPiece part : util::tokenize(package, '.')) { appendPath(&outPath, part); } return outPath; @@ -199,7 +199,7 @@ bool appendArgsFromFile(const StringPiece& path, std::vector<std::string>* outAr return false; } - for (StringPiece line : util::tokenize<char>(contents, ' ')) { + for (StringPiece line : util::tokenize(contents, ' ')) { line = util::trimWhitespace(line); if (!line.empty()) { outArgList->push_back(line.toString()); diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h index 595db960d5e5..a31bc89fa98b 100644 --- a/tools/aapt2/util/Maybe.h +++ b/tools/aapt2/util/Maybe.h @@ -88,6 +88,8 @@ public: */ const T& value() const; + T valueOrDefault(const T& def) const; + private: template <typename U> friend class Maybe; @@ -263,6 +265,14 @@ const T& Maybe<T>::value() const { } template <typename T> +T Maybe<T>::valueOrDefault(const T& def) const { + if (mNothing) { + return def; + } + return reinterpret_cast<const T&>(mStorage); +} + +template <typename T> void Maybe<T>::destroy() { reinterpret_cast<T&>(mStorage).~T(); } @@ -306,6 +316,19 @@ typename std::enable_if< return !(a == b); } +template <typename T, typename U> +typename std::enable_if< + has_lt_op<T, U>::value, + bool +>::type operator<(const Maybe<T>& a, const Maybe<U>& b) { + if (a && b) { + return a.value() < b.value(); + } else if (!a && !b) { + return false; + } + return !a; +} + } // namespace aapt #endif // AAPT_MAYBE_H diff --git a/tools/aapt2/util/StringPiece.h b/tools/aapt2/util/StringPiece.h index f91bccc93019..f5c985bd9fff 100644 --- a/tools/aapt2/util/StringPiece.h +++ b/tools/aapt2/util/StringPiece.h @@ -19,6 +19,7 @@ #include <ostream> #include <string> +#include <utils/JenkinsHash.h> #include <utils/String8.h> #include <utils/Unicode.h> @@ -257,4 +258,17 @@ inline ::std::ostream& operator<<(::std::ostream& out, const std::u16string& str return out.write(utf8.string(), utf8.size()); } +namespace std { + +template <typename TChar> +struct hash<aapt::BasicStringPiece<TChar>> { + size_t operator()(const aapt::BasicStringPiece<TChar>& str) const { + uint32_t hashCode = android::JenkinsHashMixBytes( + 0, reinterpret_cast<const uint8_t*>(str.data()), sizeof(TChar) * str.size()); + return static_cast<size_t>(hashCode); + } +}; + +} // namespace std + #endif // AAPT_STRING_PIECE_H diff --git a/tools/aapt2/util/StringPiece_test.cpp b/tools/aapt2/util/StringPiece_test.cpp index 853a9a46fde8..a87065acd3a0 100644 --- a/tools/aapt2/util/StringPiece_test.cpp +++ b/tools/aapt2/util/StringPiece_test.cpp @@ -34,16 +34,16 @@ TEST(StringPieceTest, CompareNonNullTerminatedPiece) { } TEST(StringPieceTest, PiecesHaveCorrectSortOrder) { - std::u16string testing(u"testing"); - std::u16string banana(u"banana"); - std::u16string car(u"car"); + std::string testing("testing"); + std::string banana("banana"); + std::string car("car"); - EXPECT_TRUE(StringPiece16(testing) > banana); - EXPECT_TRUE(StringPiece16(testing) > car); - EXPECT_TRUE(StringPiece16(banana) < testing); - EXPECT_TRUE(StringPiece16(banana) < car); - EXPECT_TRUE(StringPiece16(car) < testing); - EXPECT_TRUE(StringPiece16(car) > banana); + EXPECT_TRUE(StringPiece(testing) > banana); + EXPECT_TRUE(StringPiece(testing) > car); + EXPECT_TRUE(StringPiece(banana) < testing); + EXPECT_TRUE(StringPiece(banana) < car); + EXPECT_TRUE(StringPiece(car) < testing); + EXPECT_TRUE(StringPiece(car) > banana); } TEST(StringPieceTest, PiecesHaveCorrectSortOrderUtf8) { diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp index 5a87c334c59e..3c0e9bdeab0d 100644 --- a/tools/aapt2/util/Util.cpp +++ b/tools/aapt2/util/Util.cpp @@ -54,23 +54,18 @@ std::vector<std::string> splitAndLowercase(const StringPiece& str, char sep) { return splitAndTransform(str, sep, ::tolower); } -StringPiece16 trimWhitespace(const StringPiece16& str) { - if (str.size() == 0 || str.data() == nullptr) { - return str; - } - - const char16_t* start = str.data(); - const char16_t* end = str.data() + str.length(); - - while (start != end && util::isspace16(*start)) { - start++; +bool stringStartsWith(const StringPiece& str, const StringPiece& prefix) { + if (str.size() < prefix.size()) { + return false; } + return str.substr(0, prefix.size()) == prefix; +} - while (end != start && util::isspace16(*(end - 1))) { - end--; +bool stringEndsWith(const StringPiece& str, const StringPiece& suffix) { + if (str.size() < suffix.size()) { + return false; } - - return StringPiece16(start, end - start); + return str.substr(str.size() - suffix.size(), suffix.size()) == suffix; } StringPiece trimWhitespace(const StringPiece& str) { @@ -92,11 +87,11 @@ StringPiece trimWhitespace(const StringPiece& str) { return StringPiece(start, end - start); } -StringPiece16::const_iterator findNonAlphaNumericAndNotInSet(const StringPiece16& str, - const StringPiece16& allowedChars) { +StringPiece::const_iterator findNonAlphaNumericAndNotInSet(const StringPiece& str, + const StringPiece& allowedChars) { const auto endIter = str.end(); for (auto iter = str.begin(); iter != endIter; ++iter) { - char16_t c = *iter; + char c = *iter; if ((c >= u'a' && c <= u'z') || (c >= u'A' && c <= u'Z') || (c >= u'0' && c <= u'9')) { @@ -104,7 +99,7 @@ StringPiece16::const_iterator findNonAlphaNumericAndNotInSet(const StringPiece16 } bool match = false; - for (char16_t i : allowedChars) { + for (char i : allowedChars) { if (c == i) { match = true; break; @@ -118,51 +113,51 @@ StringPiece16::const_iterator findNonAlphaNumericAndNotInSet(const StringPiece16 return endIter; } -bool isJavaClassName(const StringPiece16& str) { +bool isJavaClassName(const StringPiece& str) { size_t pieces = 0; - for (const StringPiece16& piece : tokenize(str, u'.')) { + for (const StringPiece& piece : tokenize(str, '.')) { pieces++; if (piece.empty()) { return false; } // Can't have starting or trailing $ character. - if (piece.data()[0] == u'$' || piece.data()[piece.size() - 1] == u'$') { + if (piece.data()[0] == '$' || piece.data()[piece.size() - 1] == '$') { return false; } - if (findNonAlphaNumericAndNotInSet(piece, u"$_") != piece.end()) { + if (findNonAlphaNumericAndNotInSet(piece, "$_") != piece.end()) { return false; } } return pieces >= 2; } -bool isJavaPackageName(const StringPiece16& str) { +bool isJavaPackageName(const StringPiece& str) { if (str.empty()) { return false; } size_t pieces = 0; - for (const StringPiece16& piece : tokenize(str, u'.')) { + for (const StringPiece& piece : tokenize(str, '.')) { pieces++; if (piece.empty()) { return false; } - if (piece.data()[0] == u'_' || piece.data()[piece.size() - 1] == u'_') { + if (piece.data()[0] == '_' || piece.data()[piece.size() - 1] == '_') { return false; } - if (findNonAlphaNumericAndNotInSet(piece, u"_") != piece.end()) { + if (findNonAlphaNumericAndNotInSet(piece, "_") != piece.end()) { return false; } } return pieces >= 1; } -Maybe<std::u16string> getFullyQualifiedClassName(const StringPiece16& package, - const StringPiece16& className) { +Maybe<std::string> getFullyQualifiedClassName(const StringPiece& package, + const StringPiece& className) { if (className.empty()) { return {}; } @@ -175,11 +170,11 @@ Maybe<std::u16string> getFullyQualifiedClassName(const StringPiece16& package, return {}; } - if (className.data()[0] != u'.') { - return {}; + std::string result(package.data(), package.size()); + if (className.data()[0] != '.') { + result += '.'; } - std::u16string result(package.data(), package.size()); result.append(className.data(), className.size()); if (!isJavaClassName(result)) { return {}; @@ -187,23 +182,23 @@ Maybe<std::u16string> getFullyQualifiedClassName(const StringPiece16& package, return result; } -static size_t consumeDigits(const char16_t* start, const char16_t* end) { - const char16_t* c = start; - for (; c != end && *c >= u'0' && *c <= u'9'; c++) {} +static size_t consumeDigits(const char* start, const char* end) { + const char* c = start; + for (; c != end && *c >= '0' && *c <= '9'; c++) {} return static_cast<size_t>(c - start); } -bool verifyJavaStringFormat(const StringPiece16& str) { - const char16_t* c = str.begin(); - const char16_t* const end = str.end(); +bool verifyJavaStringFormat(const StringPiece& str) { + const char* c = str.begin(); + const char* const end = str.end(); size_t argCount = 0; bool nonpositional = false; while (c != end) { - if (*c == u'%' && c + 1 < end) { + if (*c == '%' && c + 1 < end) { c++; - if (*c == u'%') { + if (*c == '%') { c++; continue; } @@ -213,11 +208,11 @@ bool verifyJavaStringFormat(const StringPiece16& str) { size_t numDigits = consumeDigits(c, end); if (numDigits > 0) { c += numDigits; - if (c != end && *c != u'$') { + if (c != end && *c != '$') { // The digits were a size, but not a positional argument. nonpositional = true; } - } else if (*c == u'<') { + } else if (*c == '<') { // Reusing last argument, bad idea since positions can be moved around // during translation. nonpositional = true; @@ -225,7 +220,7 @@ bool verifyJavaStringFormat(const StringPiece16& str) { c++; // Optionally we can have a $ after - if (c != end && *c == u'$') { + if (c != end && *c == '$') { c++; } } else { @@ -233,13 +228,13 @@ bool verifyJavaStringFormat(const StringPiece16& str) { } // Ignore size, width, flags, etc. - while (c != end && (*c == u'-' || - *c == u'#' || - *c == u'+' || - *c == u' ' || - *c == u',' || - *c == u'(' || - (*c >= u'0' && *c <= '9'))) { + while (c != end && (*c == '-' || + *c == '#' || + *c == '+' || + *c == ' ' || + *c == ',' || + *c == '(' || + (*c >= '0' && *c <= '9'))) { c++; } @@ -286,11 +281,11 @@ bool verifyJavaStringFormat(const StringPiece16& str) { return true; } -static Maybe<char16_t> parseUnicodeCodepoint(const char16_t** start, const char16_t* end) { - char16_t code = 0; +static Maybe<std::string> parseUnicodeCodepoint(const char** start, const char* end) { + char32_t code = 0; for (size_t i = 0; i < 4 && *start != end; i++, (*start)++) { - char16_t c = **start; - int a; + char c = **start; + char32_t a; if (c >= '0' && c <= '9') { a = c - '0'; } else if (c >= 'a' && c <= 'f') { @@ -298,51 +293,60 @@ static Maybe<char16_t> parseUnicodeCodepoint(const char16_t** start, const char1 } else if (c >= 'A' && c <= 'F') { a = c - 'A' + 10; } else { - return make_nothing<char16_t>(); + return {}; } code = (code << 4) | a; } - return make_value(code); + + ssize_t len = utf32_to_utf8_length(&code, 1); + if (len < 0) { + return {}; + } + + std::string resultUtf8; + resultUtf8.resize(len); + utf32_to_utf8(&code, 1, &*resultUtf8.begin(), len + 1); + return resultUtf8; } -StringBuilder& StringBuilder::append(const StringPiece16& str) { +StringBuilder& StringBuilder::append(const StringPiece& str) { if (!mError.empty()) { return *this; } - const char16_t* const end = str.end(); - const char16_t* start = str.begin(); - const char16_t* current = start; + const char* const end = str.end(); + const char* start = str.begin(); + const char* current = start; while (current != end) { if (mLastCharWasEscape) { switch (*current) { - case u't': - mStr += u'\t'; + case 't': + mStr += '\t'; break; - case u'n': - mStr += u'\n'; + case 'n': + mStr += '\n'; break; - case u'#': - mStr += u'#'; + case '#': + mStr += '#'; break; - case u'@': - mStr += u'@'; + case '@': + mStr += '@'; break; - case u'?': - mStr += u'?'; + case '?': + mStr += '?'; break; - case u'"': - mStr += u'"'; + case '"': + mStr += '"'; break; - case u'\'': - mStr += u'\''; + case '\'': + mStr += '\''; break; - case u'\\': - mStr += u'\\'; + case '\\': + mStr += '\\'; break; - case u'u': { + case 'u': { current++; - Maybe<char16_t> c = parseUnicodeCodepoint(¤t, end); + Maybe<std::string> c = parseUnicodeCodepoint(¤t, end); if (!c) { mError = "invalid unicode escape sequence"; return *this; @@ -358,7 +362,7 @@ StringBuilder& StringBuilder::append(const StringPiece16& str) { } mLastCharWasEscape = false; start = current + 1; - } else if (*current == u'"') { + } else if (*current == '"') { if (!mQuote && mTrailingSpace) { // We found an opening quote, and we have // trailing space, so we should append that @@ -367,7 +371,7 @@ StringBuilder& StringBuilder::append(const StringPiece16& str) { // We had trailing whitespace, so // replace with a single space. if (!mStr.empty()) { - mStr += u' '; + mStr += ' '; } mTrailingSpace = false; } @@ -375,17 +379,17 @@ StringBuilder& StringBuilder::append(const StringPiece16& str) { mQuote = !mQuote; mStr.append(start, current - start); start = current + 1; - } else if (*current == u'\'' && !mQuote) { + } else if (*current == '\'' && !mQuote) { // This should be escaped. mError = "unescaped apostrophe"; return *this; - } else if (*current == u'\\') { + } else if (*current == '\\') { // This is an escape sequence, convert to the real value. if (!mQuote && mTrailingSpace) { // We had trailing whitespace, so // replace with a single space. if (!mStr.empty()) { - mStr += u' '; + mStr += ' '; } mTrailingSpace = false; } @@ -394,7 +398,7 @@ StringBuilder& StringBuilder::append(const StringPiece16& str) { mLastCharWasEscape = true; } else if (!mQuote) { // This is not quoted text, so look for whitespace. - if (isspace16(*current)) { + if (isspace(*current)) { // We found whitespace, see if we have seen some // before. if (!mTrailingSpace) { @@ -410,7 +414,7 @@ StringBuilder& StringBuilder::append(const StringPiece16& str) { // We saw trailing space before, so replace all // that trailing space with one space. if (!mStr.empty()) { - mStr += u' '; + mStr += ' '; } mTrailingSpace = false; } @@ -441,10 +445,8 @@ std::string utf16ToUtf8(const StringPiece16& utf16) { } std::string utf8; - // Make room for '\0' explicitly. - utf8.resize(utf8Length + 1); - utf16_to_utf8(utf16.data(), utf16.length(), &*utf8.begin(), utf8Length + 1); utf8.resize(utf8Length); + utf16_to_utf8(utf16.data(), utf16.length(), &*utf8.begin(), utf8Length + 1); return utf8; } @@ -467,15 +469,58 @@ std::unique_ptr<uint8_t[]> copy(const BigBuffer& buffer) { return data; } -bool extractResFilePathParts(const StringPiece16& path, StringPiece16* outPrefix, - StringPiece16* outEntry, StringPiece16* outSuffix) { - if (!stringStartsWith<char16_t>(path, u"res/")) { +typename Tokenizer::iterator& Tokenizer::iterator::operator++() { + const char* start = mToken.end(); + const char* end = mStr.end(); + if (start == end) { + mEnd = true; + mToken.assign(mToken.end(), 0); + return *this; + } + + start += 1; + const char* current = start; + while (current != end) { + if (*current == mSeparator) { + mToken.assign(start, current - start); + return *this; + } + ++current; + } + mToken.assign(start, end - start); + return *this; +} + +bool Tokenizer::iterator::operator==(const iterator& rhs) const { + // We check equality here a bit differently. + // We need to know that the addresses are the same. + return mToken.begin() == rhs.mToken.begin() && mToken.end() == rhs.mToken.end() && + mEnd == rhs.mEnd; +} + +bool Tokenizer::iterator::operator!=(const iterator& rhs) const { + return !(*this == rhs); +} + +Tokenizer::iterator::iterator(StringPiece s, char sep, StringPiece tok, bool end) : + mStr(s), mSeparator(sep), mToken(tok), mEnd(end) { +} + +Tokenizer::Tokenizer(StringPiece str, char sep) : + mBegin(++iterator(str, sep, StringPiece(str.begin() - 1, 0), false)), + mEnd(str, sep, StringPiece(str.end(), 0), true) { +} + +bool extractResFilePathParts(const StringPiece& path, StringPiece* outPrefix, + StringPiece* outEntry, StringPiece* outSuffix) { + const StringPiece resPrefix("res/"); + if (!stringStartsWith(path, resPrefix)) { return false; } - StringPiece16::const_iterator lastOccurence = path.end(); - for (auto iter = path.begin() + StringPiece16(u"res/").size(); iter != path.end(); ++iter) { - if (*iter == u'/') { + StringPiece::const_iterator lastOccurence = path.end(); + for (auto iter = path.begin() + resPrefix.size(); iter != path.end(); ++iter) { + if (*iter == '/') { lastOccurence = iter; } } @@ -484,12 +529,30 @@ bool extractResFilePathParts(const StringPiece16& path, StringPiece16* outPrefix return false; } - auto iter = std::find(lastOccurence, path.end(), u'.'); - *outSuffix = StringPiece16(iter, path.end() - iter); - *outEntry = StringPiece16(lastOccurence + 1, iter - lastOccurence - 1); - *outPrefix = StringPiece16(path.begin(), lastOccurence - path.begin() + 1); + auto iter = std::find(lastOccurence, path.end(), '.'); + *outSuffix = StringPiece(iter, path.end() - iter); + *outEntry = StringPiece(lastOccurence + 1, iter - lastOccurence - 1); + *outPrefix = StringPiece(path.begin(), lastOccurence - path.begin() + 1); return true; } +StringPiece16 getString16(const android::ResStringPool& pool, size_t idx) { + size_t len; + const char16_t* str = pool.stringAt(idx, &len); + if (str != nullptr) { + return StringPiece16(str, len); + } + return StringPiece16(); +} + +std::string getString(const android::ResStringPool& pool, size_t idx) { + size_t len; + const char* str = pool.string8At(idx, &len); + if (str != nullptr) { + return std::string(str, len); + } + return utf16ToUtf8(getString16(pool, idx)); +} + } // namespace util } // namespace aapt diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h index 0dacbd773488..4a10987b4400 100644 --- a/tools/aapt2/util/Util.h +++ b/tools/aapt2/util/Util.h @@ -37,30 +37,18 @@ std::vector<std::string> splitAndLowercase(const StringPiece& str, char sep); /** * Returns true if the string starts with prefix. */ -template <typename T> -bool stringStartsWith(const BasicStringPiece<T>& str, const BasicStringPiece<T>& prefix) { - if (str.size() < prefix.size()) { - return false; - } - return str.substr(0, prefix.size()) == prefix; -} +bool stringStartsWith(const StringPiece& str, const StringPiece& prefix); /** * Returns true if the string ends with suffix. */ -template <typename T> -bool stringEndsWith(const BasicStringPiece<T>& str, const BasicStringPiece<T>& suffix) { - if (str.size() < suffix.size()) { - return false; - } - return str.substr(str.size() - suffix.size(), suffix.size()) == suffix; -} +bool stringEndsWith(const StringPiece& str, const StringPiece& suffix); /** * Creates a new StringPiece16 that points to a substring * of the original string without leading or trailing whitespace. */ -StringPiece16 trimWhitespace(const StringPiece16& str); +StringPiece trimWhitespace(const StringPiece& str); StringPiece trimWhitespace(const StringPiece& str); @@ -76,18 +64,18 @@ inline bool isspace16(char16_t c) { * Returns an iterator to the first character that is not alpha-numeric and that * is not in the allowedChars set. */ -StringPiece16::const_iterator findNonAlphaNumericAndNotInSet(const StringPiece16& str, - const StringPiece16& allowedChars); +StringPiece::const_iterator findNonAlphaNumericAndNotInSet(const StringPiece& str, + const StringPiece& allowedChars); /** * Tests that the string is a valid Java class name. */ -bool isJavaClassName(const StringPiece16& str); +bool isJavaClassName(const StringPiece& str); /** * Tests that the string is a valid Java package name. */ -bool isJavaPackageName(const StringPiece16& str); +bool isJavaPackageName(const StringPiece& str); /** * Converts the class name to a fully qualified class name from the given `package`. Ex: @@ -97,9 +85,8 @@ bool isJavaPackageName(const StringPiece16& str); * .a.b --> package.a.b * asdf.adsf --> asdf.adsf */ -Maybe<std::u16string> getFullyQualifiedClassName(const StringPiece16& package, - const StringPiece16& className); - +Maybe<std::string> getFullyQualifiedClassName(const StringPiece& package, + const StringPiece& className); /** * Makes a std::unique_ptr<> with the template parameter inferred by the compiler. @@ -147,25 +134,17 @@ inline ::std::function<::std::ostream&(::std::ostream&)> formatSize(size_t size) } /** - * Helper method to extract a string from a StringPool. + * Helper method to extract a UTF-16 string from a StringPool. If the string is stored as UTF-8, + * the conversion to UTF-16 happens within ResStringPool. */ -inline StringPiece16 getString(const android::ResStringPool& pool, size_t idx) { - size_t len; - const char16_t* str = pool.stringAt(idx, &len); - if (str != nullptr) { - return StringPiece16(str, len); - } - return StringPiece16(); -} +StringPiece16 getString16(const android::ResStringPool& pool, size_t idx); -inline StringPiece getString8(const android::ResStringPool& pool, size_t idx) { - size_t len; - const char* str = pool.string8At(idx, &len); - if (str != nullptr) { - return StringPiece(str, len); - } - return StringPiece(); -} +/** + * Helper method to extract a UTF-8 string from a StringPool. If the string is stored as UTF-16, + * the conversion from UTF-16 to UTF-8 does not happen in ResStringPool and is done by this method, + * which maintains no state or cache. This means we must return an std::string copy. + */ +std::string getString(const android::ResStringPool& pool, size_t idx); /** * Checks that the Java string format contains no non-positional arguments (arguments without @@ -173,24 +152,24 @@ inline StringPiece getString8(const android::ResStringPool& pool, size_t idx) { * because translations may rearrange the order of the arguments in the string, which will * break the string interpolation. */ -bool verifyJavaStringFormat(const StringPiece16& str); +bool verifyJavaStringFormat(const StringPiece& str); class StringBuilder { public: - StringBuilder& append(const StringPiece16& str); - const std::u16string& str() const; + StringBuilder& append(const StringPiece& str); + const std::string& str() const; const std::string& error() const; operator bool() const; private: - std::u16string mStr; + std::string mStr; bool mQuote = false; bool mTrailingSpace = false; bool mLastCharWasEscape = false; std::string mError; }; -inline const std::u16string& StringBuilder::str() const { +inline const std::string& StringBuilder::str() const { return mStr; } @@ -206,7 +185,7 @@ inline StringBuilder::operator bool() const { * Converts a UTF8 string to a UTF16 string. */ std::u16string utf8ToUtf16(const StringPiece& utf8); -std::string utf16ToUtf8(const StringPiece16& utf8); +std::string utf16ToUtf8(const StringPiece16& utf16); /** * Writes the entire BigBuffer to the output stream. @@ -222,7 +201,6 @@ std::unique_ptr<uint8_t[]> copy(const BigBuffer& buffer); * A Tokenizer implemented as an iterable collection. It does not allocate * any memory on the heap nor use standard containers. */ -template <typename Char> class Tokenizer { public: class iterator { @@ -231,96 +209,41 @@ public: iterator& operator=(const iterator&) = default; iterator& operator++(); - BasicStringPiece<Char> operator*(); + + StringPiece operator*() { + return mToken; + } bool operator==(const iterator& rhs) const; bool operator!=(const iterator& rhs) const; private: - friend class Tokenizer<Char>; + friend class Tokenizer; - iterator(BasicStringPiece<Char> s, Char sep, BasicStringPiece<Char> tok, bool end); + iterator(StringPiece s, char sep, StringPiece tok, bool end); - BasicStringPiece<Char> mStr; - Char mSeparator; - BasicStringPiece<Char> mToken; + StringPiece mStr; + char mSeparator; + StringPiece mToken; bool mEnd; }; - Tokenizer(BasicStringPiece<Char> str, Char sep); - iterator begin(); - iterator end(); - -private: - const iterator mBegin; - const iterator mEnd; -}; - -template <typename Char> -inline Tokenizer<Char> tokenize(BasicStringPiece<Char> str, Char sep) { - return Tokenizer<Char>(str, sep); -} + Tokenizer(StringPiece str, char sep); -template <typename Char> -typename Tokenizer<Char>::iterator& Tokenizer<Char>::iterator::operator++() { - const Char* start = mToken.end(); - const Char* end = mStr.end(); - if (start == end) { - mEnd = true; - mToken.assign(mToken.end(), 0); - return *this; + iterator begin() { + return mBegin; } - start += 1; - const Char* current = start; - while (current != end) { - if (*current == mSeparator) { - mToken.assign(start, current - start); - return *this; - } - ++current; + iterator end() { + return mEnd; } - mToken.assign(start, end - start); - return *this; -} - -template <typename Char> -inline BasicStringPiece<Char> Tokenizer<Char>::iterator::operator*() { - 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 mToken.begin() == rhs.mToken.begin() && mToken.end() == rhs.mToken.end() && - mEnd == rhs.mEnd; -} - -template <typename Char> -inline bool Tokenizer<Char>::iterator::operator!=(const iterator& rhs) const { - return !(*this == rhs); -} -template <typename Char> -inline Tokenizer<Char>::iterator::iterator(BasicStringPiece<Char> s, Char sep, - BasicStringPiece<Char> tok, bool end) : - mStr(s), mSeparator(sep), mToken(tok), mEnd(end) { -} - -template <typename Char> -inline typename Tokenizer<Char>::iterator Tokenizer<Char>::begin() { - return mBegin; -} - -template <typename Char> -inline typename Tokenizer<Char>::iterator Tokenizer<Char>::end() { - return mEnd; -} +private: + const iterator mBegin; + const iterator mEnd; +}; -template <typename Char> -inline Tokenizer<Char>::Tokenizer(BasicStringPiece<Char> str, Char sep) : - mBegin(++iterator(str, sep, BasicStringPiece<Char>(str.begin() - 1, 0), false)), - mEnd(str, sep, BasicStringPiece<Char>(str.end(), 0), true) { +inline Tokenizer tokenize(StringPiece str, char sep) { + return Tokenizer(str, sep); } inline uint16_t hostToDevice16(uint16_t value) { @@ -348,8 +271,8 @@ inline uint32_t deviceToHost32(uint32_t value) { * * Returns true if successful. */ -bool extractResFilePathParts(const StringPiece16& path, StringPiece16* outPrefix, - StringPiece16* outEntry, StringPiece16* outSuffix); +bool extractResFilePathParts(const StringPiece& path, StringPiece* outPrefix, + StringPiece* outEntry, StringPiece* outSuffix); } // namespace util diff --git a/tools/aapt2/util/Util_test.cpp b/tools/aapt2/util/Util_test.cpp index 1e0c7fa9152d..0e27213a8d8a 100644 --- a/tools/aapt2/util/Util_test.cpp +++ b/tools/aapt2/util/Util_test.cpp @@ -14,191 +14,191 @@ * limitations under the License. */ -#include "test/Common.h" +#include "test/Test.h" #include "util/StringPiece.h" #include "util/Util.h" -#include <gtest/gtest.h> #include <string> namespace aapt { TEST(UtilTest, TrimOnlyWhitespace) { - const std::u16string full = u"\n "; + const std::string full = "\n "; - StringPiece16 trimmed = util::trimWhitespace(full); + StringPiece trimmed = util::trimWhitespace(full); EXPECT_TRUE(trimmed.empty()); EXPECT_EQ(0u, trimmed.size()); } TEST(UtilTest, StringEndsWith) { - EXPECT_TRUE(util::stringEndsWith<char>("hello.xml", ".xml")); + EXPECT_TRUE(util::stringEndsWith("hello.xml", ".xml")); } TEST(UtilTest, StringStartsWith) { - EXPECT_TRUE(util::stringStartsWith<char>("hello.xml", "he")); + EXPECT_TRUE(util::stringStartsWith("hello.xml", "he")); } TEST(UtilTest, StringBuilderSplitEscapeSequence) { - EXPECT_EQ(StringPiece16(u"this is a new\nline."), - util::StringBuilder().append(u"this is a new\\") - .append(u"nline.") + EXPECT_EQ(StringPiece("this is a new\nline."), + util::StringBuilder().append("this is a new\\") + .append("nline.") .str()); } TEST(UtilTest, StringBuilderWhitespaceRemoval) { - EXPECT_EQ(StringPiece16(u"hey guys this is so cool"), - util::StringBuilder().append(u" hey guys ") - .append(u" this is so cool ") + EXPECT_EQ(StringPiece("hey guys this is so cool"), + util::StringBuilder().append(" hey guys ") + .append(" this is so cool ") .str()); - EXPECT_EQ(StringPiece16(u" wow, so many \t spaces. what?"), - util::StringBuilder().append(u" \" wow, so many \t ") - .append(u"spaces. \"what? ") + EXPECT_EQ(StringPiece(" wow, so many \t spaces. what?"), + util::StringBuilder().append(" \" wow, so many \t ") + .append("spaces. \"what? ") .str()); - EXPECT_EQ(StringPiece16(u"where is the pie?"), - util::StringBuilder().append(u" where \t ") - .append(u" \nis the "" pie?") + EXPECT_EQ(StringPiece("where is the pie?"), + util::StringBuilder().append(" where \t ") + .append(" \nis the "" pie?") .str()); } TEST(UtilTest, StringBuilderEscaping) { - EXPECT_EQ(StringPiece16(u"hey guys\n this \t is so\\ cool"), - util::StringBuilder().append(u" hey guys\\n ") - .append(u" this \\t is so\\\\ cool ") + EXPECT_EQ(StringPiece("hey guys\n this \t is so\\ cool"), + util::StringBuilder().append(" hey guys\\n ") + .append(" this \\t is so\\\\ cool ") .str()); - EXPECT_EQ(StringPiece16(u"@?#\\\'"), - util::StringBuilder().append(u"\\@\\?\\#\\\\\\'") + EXPECT_EQ(StringPiece("@?#\\\'"), + util::StringBuilder().append("\\@\\?\\#\\\\\\'") .str()); } TEST(UtilTest, StringBuilderMisplacedQuote) { util::StringBuilder builder{}; - EXPECT_FALSE(builder.append(u"they're coming!")); + EXPECT_FALSE(builder.append("they're coming!")); } TEST(UtilTest, StringBuilderUnicodeCodes) { - EXPECT_EQ(StringPiece16(u"\u00AF\u0AF0 woah"), - util::StringBuilder().append(u"\\u00AF\\u0AF0 woah") + EXPECT_EQ(std::string("\u00AF\u0AF0 woah"), + util::StringBuilder().append("\\u00AF\\u0AF0 woah") .str()); - EXPECT_FALSE(util::StringBuilder().append(u"\\u00 yo")); + EXPECT_FALSE(util::StringBuilder().append("\\u00 yo")); } TEST(UtilTest, TokenizeInput) { - auto tokenizer = util::tokenize(StringPiece16(u"this| is|the|end"), u'|'); + auto tokenizer = util::tokenize(StringPiece("this| is|the|end"), '|'); auto iter = tokenizer.begin(); - ASSERT_EQ(*iter, StringPiece16(u"this")); + ASSERT_EQ(*iter, StringPiece("this")); ++iter; - ASSERT_EQ(*iter, StringPiece16(u" is")); + ASSERT_EQ(*iter, StringPiece(" is")); ++iter; - ASSERT_EQ(*iter, StringPiece16(u"the")); + ASSERT_EQ(*iter, StringPiece("the")); ++iter; - ASSERT_EQ(*iter, StringPiece16(u"end")); + ASSERT_EQ(*iter, StringPiece("end")); ++iter; ASSERT_EQ(tokenizer.end(), iter); } TEST(UtilTest, TokenizeEmptyString) { - auto tokenizer = util::tokenize(StringPiece16(u""), u'|'); + auto tokenizer = util::tokenize(StringPiece(""), '|'); auto iter = tokenizer.begin(); ASSERT_NE(tokenizer.end(), iter); - ASSERT_EQ(StringPiece16(), *iter); + ASSERT_EQ(StringPiece(), *iter); ++iter; ASSERT_EQ(tokenizer.end(), iter); } TEST(UtilTest, TokenizeAtEnd) { - auto tokenizer = util::tokenize(StringPiece16(u"one."), u'.'); + auto tokenizer = util::tokenize(StringPiece("one."), '.'); auto iter = tokenizer.begin(); - ASSERT_EQ(*iter, StringPiece16(u"one")); + ASSERT_EQ(*iter, StringPiece("one")); ++iter; ASSERT_NE(iter, tokenizer.end()); - ASSERT_EQ(*iter, StringPiece16()); + ASSERT_EQ(*iter, StringPiece()); } TEST(UtilTest, IsJavaClassName) { - EXPECT_TRUE(util::isJavaClassName(u"android.test.Class")); - EXPECT_TRUE(util::isJavaClassName(u"android.test.Class$Inner")); - EXPECT_TRUE(util::isJavaClassName(u"android_test.test.Class")); - EXPECT_TRUE(util::isJavaClassName(u"_android_.test._Class_")); - EXPECT_FALSE(util::isJavaClassName(u"android.test.$Inner")); - EXPECT_FALSE(util::isJavaClassName(u"android.test.Inner$")); - EXPECT_FALSE(util::isJavaClassName(u".test.Class")); - EXPECT_FALSE(util::isJavaClassName(u"android")); + EXPECT_TRUE(util::isJavaClassName("android.test.Class")); + EXPECT_TRUE(util::isJavaClassName("android.test.Class$Inner")); + EXPECT_TRUE(util::isJavaClassName("android_test.test.Class")); + EXPECT_TRUE(util::isJavaClassName("_android_.test._Class_")); + EXPECT_FALSE(util::isJavaClassName("android.test.$Inner")); + EXPECT_FALSE(util::isJavaClassName("android.test.Inner$")); + EXPECT_FALSE(util::isJavaClassName(".test.Class")); + EXPECT_FALSE(util::isJavaClassName("android")); } TEST(UtilTest, IsJavaPackageName) { - EXPECT_TRUE(util::isJavaPackageName(u"android")); - EXPECT_TRUE(util::isJavaPackageName(u"android.test")); - EXPECT_TRUE(util::isJavaPackageName(u"android.test_thing")); - EXPECT_FALSE(util::isJavaPackageName(u"_android")); - EXPECT_FALSE(util::isJavaPackageName(u"android_")); - EXPECT_FALSE(util::isJavaPackageName(u"android.")); - EXPECT_FALSE(util::isJavaPackageName(u".android")); - EXPECT_FALSE(util::isJavaPackageName(u"android._test")); - EXPECT_FALSE(util::isJavaPackageName(u"..")); + EXPECT_TRUE(util::isJavaPackageName("android")); + EXPECT_TRUE(util::isJavaPackageName("android.test")); + EXPECT_TRUE(util::isJavaPackageName("android.test_thing")); + EXPECT_FALSE(util::isJavaPackageName("_android")); + EXPECT_FALSE(util::isJavaPackageName("android_")); + EXPECT_FALSE(util::isJavaPackageName("android.")); + EXPECT_FALSE(util::isJavaPackageName(".android")); + EXPECT_FALSE(util::isJavaPackageName("android._test")); + EXPECT_FALSE(util::isJavaPackageName("..")); } TEST(UtilTest, FullyQualifiedClassName) { - Maybe<std::u16string> res = util::getFullyQualifiedClassName(u"android", u"asdf"); - AAPT_ASSERT_FALSE(res); + Maybe<std::string> res = util::getFullyQualifiedClassName("android", ".asdf"); + AAPT_ASSERT_TRUE(res); + EXPECT_EQ(res.value(), "android.asdf"); - res = util::getFullyQualifiedClassName(u"android", u".asdf"); + res = util::getFullyQualifiedClassName("android", ".a.b"); AAPT_ASSERT_TRUE(res); - EXPECT_EQ(res.value(), u"android.asdf"); + EXPECT_EQ(res.value(), "android.a.b"); - res = util::getFullyQualifiedClassName(u"android", u".a.b"); + res = util::getFullyQualifiedClassName("android", "a.b"); AAPT_ASSERT_TRUE(res); - EXPECT_EQ(res.value(), u"android.a.b"); + EXPECT_EQ(res.value(), "a.b"); - res = util::getFullyQualifiedClassName(u"android", u"a.b"); + res = util::getFullyQualifiedClassName("", "a.b"); AAPT_ASSERT_TRUE(res); - EXPECT_EQ(res.value(), u"a.b"); + EXPECT_EQ(res.value(), "a.b"); - res = util::getFullyQualifiedClassName(u"", u"a.b"); + res = util::getFullyQualifiedClassName("android", "Class"); AAPT_ASSERT_TRUE(res); - EXPECT_EQ(res.value(), u"a.b"); + EXPECT_EQ(res.value(), "android.Class"); - res = util::getFullyQualifiedClassName(u"", u""); + res = util::getFullyQualifiedClassName("", ""); AAPT_ASSERT_FALSE(res); - res = util::getFullyQualifiedClassName(u"android", u"./Apple"); + res = util::getFullyQualifiedClassName("android", "./Apple"); AAPT_ASSERT_FALSE(res); } TEST(UtilTest, ExtractResourcePathComponents) { - StringPiece16 prefix, entry, suffix; - ASSERT_TRUE(util::extractResFilePathParts(u"res/xml-sw600dp/entry.xml", &prefix, &entry, + StringPiece prefix, entry, suffix; + ASSERT_TRUE(util::extractResFilePathParts("res/xml-sw600dp/entry.xml", &prefix, &entry, &suffix)); - EXPECT_EQ(prefix, u"res/xml-sw600dp/"); - EXPECT_EQ(entry, u"entry"); - EXPECT_EQ(suffix, u".xml"); + EXPECT_EQ(prefix, "res/xml-sw600dp/"); + EXPECT_EQ(entry, "entry"); + EXPECT_EQ(suffix, ".xml"); - ASSERT_TRUE(util::extractResFilePathParts(u"res/xml-sw600dp/entry.9.png", &prefix, &entry, + ASSERT_TRUE(util::extractResFilePathParts("res/xml-sw600dp/entry.9.png", &prefix, &entry, &suffix)); - EXPECT_EQ(prefix, u"res/xml-sw600dp/"); - EXPECT_EQ(entry, u"entry"); - EXPECT_EQ(suffix, u".9.png"); + EXPECT_EQ(prefix, "res/xml-sw600dp/"); + EXPECT_EQ(entry, "entry"); + EXPECT_EQ(suffix, ".9.png"); - EXPECT_FALSE(util::extractResFilePathParts(u"AndroidManifest.xml", &prefix, &entry, &suffix)); - EXPECT_FALSE(util::extractResFilePathParts(u"res/.xml", &prefix, &entry, &suffix)); + EXPECT_FALSE(util::extractResFilePathParts("AndroidManifest.xml", &prefix, &entry, &suffix)); + EXPECT_FALSE(util::extractResFilePathParts("res/.xml", &prefix, &entry, &suffix)); - ASSERT_TRUE(util::extractResFilePathParts(u"res//.", &prefix, &entry, &suffix)); - EXPECT_EQ(prefix, u"res//"); - EXPECT_EQ(entry, u""); - EXPECT_EQ(suffix, u"."); + ASSERT_TRUE(util::extractResFilePathParts("res//.", &prefix, &entry, &suffix)); + EXPECT_EQ(prefix, "res//"); + EXPECT_EQ(entry, ""); + EXPECT_EQ(suffix, "."); } TEST(UtilTest, VerifyJavaStringFormat) { - ASSERT_TRUE(util::verifyJavaStringFormat(u"%09.34f")); - ASSERT_TRUE(util::verifyJavaStringFormat(u"%9$.34f %8$")); - ASSERT_TRUE(util::verifyJavaStringFormat(u"%% %%")); - ASSERT_FALSE(util::verifyJavaStringFormat(u"%09$f %f")); - ASSERT_FALSE(util::verifyJavaStringFormat(u"%09f %08s")); + ASSERT_TRUE(util::verifyJavaStringFormat("%09.34f")); + ASSERT_TRUE(util::verifyJavaStringFormat("%9$.34f %8$")); + ASSERT_TRUE(util::verifyJavaStringFormat("%% %%")); + ASSERT_FALSE(util::verifyJavaStringFormat("%09$f %f")); + ASSERT_FALSE(util::verifyJavaStringFormat("%09f %08s")); } } // namespace aapt diff --git a/tools/aapt2/xml/XmlActionExecutor.cpp b/tools/aapt2/xml/XmlActionExecutor.cpp index 0ef67eaf3dc5..745079c43c7c 100644 --- a/tools/aapt2/xml/XmlActionExecutor.cpp +++ b/tools/aapt2/xml/XmlActionExecutor.cpp @@ -57,8 +57,7 @@ bool XmlNodeAction::execute(XmlActionExecutorPolicy policy, SourcePathDiagnostic for (Element* childEl : el->getChildElements()) { if (childEl->namespaceUri.empty()) { - std::map<std::u16string, XmlNodeAction>::const_iterator iter = - mMap.find(childEl->name); + std::map<std::string, XmlNodeAction>::const_iterator iter = mMap.find(childEl->name); if (iter != mMap.end()) { error |= !iter->second.execute(policy, diag, childEl); continue; @@ -91,7 +90,7 @@ bool XmlActionExecutor::execute(XmlActionExecutorPolicy policy, IDiagnostics* di } if (el->namespaceUri.empty()) { - std::map<std::u16string, XmlNodeAction>::const_iterator iter = mMap.find(el->name); + std::map<std::string, XmlNodeAction>::const_iterator iter = mMap.find(el->name); if (iter != mMap.end()) { return iter->second.execute(policy, &sourceDiag, el); } diff --git a/tools/aapt2/xml/XmlActionExecutor.h b/tools/aapt2/xml/XmlActionExecutor.h index 36b94dbfde05..cad508c6df0a 100644 --- a/tools/aapt2/xml/XmlActionExecutor.h +++ b/tools/aapt2/xml/XmlActionExecutor.h @@ -55,7 +55,7 @@ public: * Find or create a child XmlNodeAction that will be performed for the child element * with the name `name`. */ - XmlNodeAction& operator[](const std::u16string& name) { + XmlNodeAction& operator[](const std::string& name) { return mMap[name]; } @@ -70,7 +70,7 @@ private: bool execute(XmlActionExecutorPolicy policy, SourcePathDiagnostics* diag, Element* el) const; - std::map<std::u16string, XmlNodeAction> mMap; + std::map<std::string, XmlNodeAction> mMap; std::vector<ActionFuncWithDiag> mActions; }; @@ -86,7 +86,7 @@ public: * Find or create a root XmlNodeAction that will be performed for the root XML element * with the name `name`. */ - XmlNodeAction& operator[](const std::u16string& name) { + XmlNodeAction& operator[](const std::string& name) { return mMap[name]; } @@ -97,7 +97,7 @@ public: bool execute(XmlActionExecutorPolicy policy, IDiagnostics* diag, XmlResource* doc) const; private: - std::map<std::u16string, XmlNodeAction> mMap; + std::map<std::string, XmlNodeAction> mMap; DISALLOW_COPY_AND_ASSIGN(XmlActionExecutor); }; diff --git a/tools/aapt2/xml/XmlActionExecutor_test.cpp b/tools/aapt2/xml/XmlActionExecutor_test.cpp index ebf287a251f2..106e85601f6e 100644 --- a/tools/aapt2/xml/XmlActionExecutor_test.cpp +++ b/tools/aapt2/xml/XmlActionExecutor_test.cpp @@ -22,8 +22,8 @@ namespace xml { TEST(XmlActionExecutorTest, BuildsAccessibleNestedPattern) { XmlActionExecutor executor; - XmlNodeAction& manifestAction = executor[u"manifest"]; - XmlNodeAction& applicationAction = manifestAction[u"application"]; + XmlNodeAction& manifestAction = executor["manifest"]; + XmlNodeAction& applicationAction = manifestAction["application"]; Element* manifestEl = nullptr; manifestAction.action([&](Element* manifest) -> bool { @@ -42,15 +42,15 @@ TEST(XmlActionExecutorTest, BuildsAccessibleNestedPattern) { StdErrDiagnostics diag; ASSERT_TRUE(executor.execute(XmlActionExecutorPolicy::None, &diag, doc.get())); ASSERT_NE(nullptr, manifestEl); - EXPECT_EQ(std::u16string(u"manifest"), manifestEl->name); + EXPECT_EQ(std::string("manifest"), manifestEl->name); ASSERT_NE(nullptr, applicationEl); - EXPECT_EQ(std::u16string(u"application"), applicationEl->name); + EXPECT_EQ(std::string("application"), applicationEl->name); } TEST(XmlActionExecutorTest, FailsWhenUndefinedHierarchyExists) { XmlActionExecutor executor; - executor[u"manifest"][u"application"]; + executor["manifest"]["application"]; std::unique_ptr<XmlResource> doc = test::buildXmlDom( "<manifest><application /><activity /></manifest>"); diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp index 0ce333af3115..39bd5bf44f39 100644 --- a/tools/aapt2/xml/XmlDom.cpp +++ b/tools/aapt2/xml/XmlDom.cpp @@ -33,13 +33,13 @@ constexpr char kXmlNamespaceSep = 1; struct Stack { std::unique_ptr<xml::Node> root; std::stack<xml::Node*> nodeStack; - std::u16string pendingComment; + std::string pendingComment; }; /** * Extracts the namespace and name of an expanded element or attribute name. */ -static void splitName(const char* name, std::u16string* outNs, std::u16string* outName) { +static void splitName(const char* name, std::string* outNs, std::string* outName) { const char* p = name; while (*p != 0 && *p != kXmlNamespaceSep) { p++; @@ -47,10 +47,10 @@ static void splitName(const char* name, std::u16string* outNs, std::u16string* o if (*p == 0) { outNs->clear(); - *outName = util::utf8ToUtf16(name); + *outName = StringPiece(name).toString(); } else { - *outNs = util::utf8ToUtf16(StringPiece(name, (p - name))); - *outName = util::utf8ToUtf16(p + 1); + *outNs = StringPiece(name, (p - name)).toString(); + *outName = StringPiece(p + 1).toString(); } } @@ -76,11 +76,11 @@ static void XMLCALL startNamespaceHandler(void* userData, const char* prefix, co std::unique_ptr<Namespace> ns = util::make_unique<Namespace>(); if (prefix) { - ns->namespacePrefix = util::utf8ToUtf16(prefix); + ns->namespacePrefix = StringPiece(prefix).toString(); } if (uri) { - ns->namespaceUri = util::utf8ToUtf16(uri); + ns->namespaceUri = StringPiece(uri).toString(); } addToStack(stack, parser, std::move(ns)); @@ -109,7 +109,7 @@ static void XMLCALL startElementHandler(void* userData, const char* name, const while (*attrs) { Attribute attribute; splitName(*attrs++, &attribute.namespaceUri, &attribute.name); - attribute.value = util::utf8ToUtf16(*attrs++); + attribute.value = StringPiece(*attrs++).toString(); // Insert in sorted order. auto iter = std::lower_bound(el->attributes.begin(), el->attributes.end(), attribute, @@ -144,14 +144,14 @@ static void XMLCALL characterDataHandler(void* userData, const char* s, int len) if (!currentParent->children.empty()) { Node* lastChild = currentParent->children.back().get(); if (Text* text = nodeCast<Text>(lastChild)) { - text->text += util::utf8ToUtf16(StringPiece(s, len)); + text->text += StringPiece(s, len).toString(); return; } } } std::unique_ptr<Text> text = util::make_unique<Text>(); - text->text = util::utf8ToUtf16(StringPiece(s, len)); + text->text = StringPiece(s, len).toString(); addToStack(stack, parser, std::move(text)); } @@ -162,7 +162,7 @@ static void XMLCALL commentDataHandler(void* userData, const char* comment) { if (!stack->pendingComment.empty()) { stack->pendingComment += '\n'; } - stack->pendingComment += util::utf8ToUtf16(comment); + stack->pendingComment += comment; } std::unique_ptr<XmlResource> inflate(std::istream* in, IDiagnostics* diag, const Source& source) { @@ -209,17 +209,17 @@ static void copyAttributes(Element* el, android::ResXMLParser* parser) { size_t len; const char16_t* str16 = parser->getAttributeNamespace(i, &len); if (str16) { - attr.namespaceUri.assign(str16, len); + attr.namespaceUri = util::utf16ToUtf8(StringPiece16(str16, len)); } str16 = parser->getAttributeName(i, &len); if (str16) { - attr.name.assign(str16, len); + attr.name = util::utf16ToUtf8(StringPiece16(str16, len)); } str16 = parser->getAttributeStringValue(i, &len); if (str16) { - attr.value.assign(str16, len); + attr.value = util::utf16ToUtf8(StringPiece16(str16, len)); } el->attributes.push_back(std::move(attr)); } @@ -250,12 +250,12 @@ std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen, IDiagnost size_t len; const char16_t* str16 = tree.getNamespacePrefix(&len); if (str16) { - node->namespacePrefix.assign(str16, len); + node->namespacePrefix = util::utf16ToUtf8(StringPiece16(str16, len)); } str16 = tree.getNamespaceUri(&len); if (str16) { - node->namespaceUri.assign(str16, len); + node->namespaceUri = util::utf16ToUtf8(StringPiece16(str16, len)); } newNode = std::move(node); break; @@ -266,12 +266,12 @@ std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen, IDiagnost size_t len; const char16_t* str16 = tree.getElementNamespace(&len); if (str16) { - node->namespaceUri.assign(str16, len); + node->namespaceUri = util::utf16ToUtf8(StringPiece16(str16, len)); } str16 = tree.getElementName(&len); if (str16) { - node->name.assign(str16, len); + node->name = util::utf16ToUtf8(StringPiece16(str16, len)); } copyAttributes(node.get(), &tree); @@ -285,7 +285,7 @@ std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen, IDiagnost size_t len; const char16_t* str16 = tree.getText(&len); if (str16) { - node->text.assign(str16, len); + node->text = util::utf16ToUtf8(StringPiece16(str16, len)); } newNode = std::move(node); break; @@ -347,7 +347,7 @@ void Node::addChild(std::unique_ptr<Node> child) { children.push_back(std::move(child)); } -Attribute* Element::findAttribute(const StringPiece16& ns, const StringPiece16& name) { +Attribute* Element::findAttribute(const StringPiece& ns, const StringPiece& name) { for (auto& attr : attributes) { if (ns == attr.namespaceUri && name == attr.name) { return &attr; @@ -356,13 +356,13 @@ Attribute* Element::findAttribute(const StringPiece16& ns, const StringPiece16& return nullptr; } -Element* Element::findChild(const StringPiece16& ns, const StringPiece16& name) { +Element* Element::findChild(const StringPiece& ns, const StringPiece& name) { return findChildWithAttribute(ns, name, {}, {}, {}); } -Element* Element::findChildWithAttribute(const StringPiece16& ns, const StringPiece16& name, - const StringPiece16& attrNs, const StringPiece16& attrName, - const StringPiece16& attrValue) { +Element* Element::findChildWithAttribute(const StringPiece& ns, const StringPiece& name, + const StringPiece& attrNs, const StringPiece& attrName, + const StringPiece& attrValue) { for (auto& childNode : children) { Node* child = childNode.get(); while (nodeCast<Namespace>(child)) { @@ -422,7 +422,7 @@ void PackageAwareVisitor::visit(Namespace* ns) { } Maybe<ExtractedPackage> PackageAwareVisitor::transformPackageAlias( - const StringPiece16& alias, const StringPiece16& localPackage) const { + const StringPiece& alias, const StringPiece& localPackage) const { if (alias.empty()) { return ExtractedPackage{ localPackage.toString(), false /* private */ }; } diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h index b374d20039a5..d083d82d711e 100644 --- a/tools/aapt2/xml/XmlDom.h +++ b/tools/aapt2/xml/XmlDom.h @@ -41,7 +41,7 @@ struct Node { Node* parent = nullptr; size_t lineNumber = 0; size_t columnNumber = 0; - std::u16string comment; + std::string comment; std::vector<std::unique_ptr<Node>> children; virtual ~Node() = default; @@ -63,8 +63,8 @@ struct BaseNode : public Node { * A Namespace XML node. Can only have one child. */ struct Namespace : public BaseNode<Namespace> { - std::u16string namespacePrefix; - std::u16string namespaceUri; + std::string namespacePrefix; + std::string namespaceUri; }; struct AaptAttribute { @@ -76,9 +76,9 @@ struct AaptAttribute { * An XML attribute. */ struct Attribute { - std::u16string namespaceUri; - std::u16string name; - std::u16string value; + std::string namespaceUri; + std::string name; + std::string value; Maybe<AaptAttribute> compiledAttribute; std::unique_ptr<Item> compiledValue; @@ -88,16 +88,16 @@ struct Attribute { * An Element XML node. */ struct Element : public BaseNode<Element> { - std::u16string namespaceUri; - std::u16string name; + std::string namespaceUri; + std::string name; std::vector<Attribute> attributes; - Attribute* findAttribute(const StringPiece16& ns, const StringPiece16& name); - xml::Element* findChild(const StringPiece16& ns, const StringPiece16& name); - xml::Element* findChildWithAttribute(const StringPiece16& ns, const StringPiece16& name, - const StringPiece16& attrNs, - const StringPiece16& attrName, - const StringPiece16& attrValue); + Attribute* findAttribute(const StringPiece& ns, const StringPiece& name); + xml::Element* findChild(const StringPiece& ns, const StringPiece& name); + xml::Element* findChildWithAttribute(const StringPiece& ns, const StringPiece& name, + const StringPiece& attrNs, + const StringPiece& attrName, + const StringPiece& attrValue); std::vector<xml::Element*> getChildElements(); }; @@ -105,7 +105,7 @@ struct Element : public BaseNode<Element> { * A Text (CDATA) XML node. Can not have any children. */ struct Text : public BaseNode<Text> { - std::u16string text; + std::string text; }; /** @@ -175,7 +175,7 @@ struct Visitor : public RawVisitor { class PackageAwareVisitor : public Visitor, public IPackageDeclStack { private: struct PackageDecl { - std::u16string prefix; + std::string prefix; ExtractedPackage package; }; @@ -186,7 +186,7 @@ public: void visit(Namespace* ns) override; Maybe<ExtractedPackage> transformPackageAlias( - const StringPiece16& alias, const StringPiece16& localPackage) const override; + const StringPiece& alias, const StringPiece& localPackage) const override; }; // Implementations diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp index 431ee2c8fb46..1909f75ad0c3 100644 --- a/tools/aapt2/xml/XmlDom_test.cpp +++ b/tools/aapt2/xml/XmlDom_test.cpp @@ -43,8 +43,8 @@ TEST(XmlDomTest, Inflate) { xml::Namespace* ns = xml::nodeCast<xml::Namespace>(doc->root.get()); ASSERT_NE(ns, nullptr); - EXPECT_EQ(ns->namespaceUri, u"http://schemas.android.com/apk/res/android"); - EXPECT_EQ(ns->namespacePrefix, u"android"); + EXPECT_EQ(ns->namespaceUri, xml::kSchemaAndroid); + EXPECT_EQ(ns->namespacePrefix, "android"); } } // namespace aapt diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp index 323ec05b5f2c..4a944f1b1e48 100644 --- a/tools/aapt2/xml/XmlPullParser.cpp +++ b/tools/aapt2/xml/XmlPullParser.cpp @@ -98,7 +98,7 @@ const std::string& XmlPullParser::getLastError() const { return mLastError; } -const std::u16string& XmlPullParser::getComment() const { +const std::string& XmlPullParser::getComment() const { return mEventQueue.front().data1; } @@ -110,14 +110,14 @@ size_t XmlPullParser::getDepth() const { return mEventQueue.front().depth; } -const std::u16string& XmlPullParser::getText() const { +const std::string& XmlPullParser::getText() const { if (getEvent() != Event::kText) { return mEmpty; } return mEventQueue.front().data1; } -const std::u16string& XmlPullParser::getNamespacePrefix() const { +const std::string& XmlPullParser::getNamespacePrefix() const { const Event currentEvent = getEvent(); if (currentEvent != Event::kStartNamespace && currentEvent != Event::kEndNamespace) { return mEmpty; @@ -125,7 +125,7 @@ const std::u16string& XmlPullParser::getNamespacePrefix() const { return mEventQueue.front().data1; } -const std::u16string& XmlPullParser::getNamespaceUri() const { +const std::string& XmlPullParser::getNamespaceUri() const { const Event currentEvent = getEvent(); if (currentEvent != Event::kStartNamespace && currentEvent != Event::kEndNamespace) { return mEmpty; @@ -134,7 +134,7 @@ const std::u16string& XmlPullParser::getNamespaceUri() const { } Maybe<ExtractedPackage> XmlPullParser::transformPackageAlias( - const StringPiece16& alias, const StringPiece16& localPackage) const { + const StringPiece& alias, const StringPiece& localPackage) const { if (alias.empty()) { return ExtractedPackage{ localPackage.toString(), false /* private */ }; } @@ -152,7 +152,7 @@ Maybe<ExtractedPackage> XmlPullParser::transformPackageAlias( return {}; } -const std::u16string& XmlPullParser::getElementNamespace() const { +const std::string& XmlPullParser::getElementNamespace() const { const Event currentEvent = getEvent(); if (currentEvent != Event::kStartElement && currentEvent != Event::kEndElement) { return mEmpty; @@ -160,7 +160,7 @@ const std::u16string& XmlPullParser::getElementNamespace() const { return mEventQueue.front().data1; } -const std::u16string& XmlPullParser::getElementName() const { +const std::string& XmlPullParser::getElementName() const { const Event currentEvent = getEvent(); if (currentEvent != Event::kStartElement && currentEvent != Event::kEndElement) { return mEmpty; @@ -186,31 +186,31 @@ size_t XmlPullParser::getAttributeCount() const { /** * Extracts the namespace and name of an expanded element or attribute name. */ -static void splitName(const char* name, std::u16string& outNs, std::u16string& outName) { +static void splitName(const char* name, std::string& outNs, std::string& outName) { const char* p = name; while (*p != 0 && *p != kXmlNamespaceSep) { p++; } if (*p == 0) { - outNs = std::u16string(); - outName = util::utf8ToUtf16(name); + outNs = std::string(); + outName = name; } else { - outNs = util::utf8ToUtf16(StringPiece(name, (p - name))); - outName = util::utf8ToUtf16(p + 1); + outNs = StringPiece(name, (p - name)).toString(); + outName = p + 1; } } void XMLCALL XmlPullParser::startNamespaceHandler(void* userData, const char* prefix, const char* uri) { XmlPullParser* parser = reinterpret_cast<XmlPullParser*>(userData); - std::u16string namespaceUri = uri != nullptr ? util::utf8ToUtf16(uri) : std::u16string(); + std::string namespaceUri = uri != nullptr ? uri : std::string(); parser->mNamespaceUris.push(namespaceUri); parser->mEventQueue.push(EventData{ Event::kStartNamespace, XML_GetCurrentLineNumber(parser->mParser), parser->mDepth++, - prefix != nullptr ? util::utf8ToUtf16(prefix) : std::u16string(), + prefix != nullptr ? prefix : std::string(), namespaceUri }); } @@ -227,7 +227,7 @@ void XMLCALL XmlPullParser::startElementHandler(void* userData, const char* name while (*attrs) { Attribute attribute; splitName(*attrs++, attribute.namespaceUri, attribute.name); - attribute.value = util::utf8ToUtf16(*attrs++); + attribute.value = *attrs++; // Insert in sorted order. auto iter = std::lower_bound(data.attributes.begin(), data.attributes.end(), attribute); @@ -245,7 +245,7 @@ void XMLCALL XmlPullParser::characterDataHandler(void* userData, const char* s, Event::kText, XML_GetCurrentLineNumber(parser->mParser), parser->mDepth, - util::utf8ToUtf16(StringPiece(s, len)) + StringPiece(s, len).toString() }); } @@ -268,7 +268,7 @@ void XMLCALL XmlPullParser::endNamespaceHandler(void* userData, const char* pref Event::kEndNamespace, XML_GetCurrentLineNumber(parser->mParser), --(parser->mDepth), - prefix != nullptr ? util::utf8ToUtf16(prefix) : std::u16string(), + prefix != nullptr ? prefix : std::string(), parser->mNamespaceUris.top() }); parser->mNamespaceUris.pop(); @@ -281,22 +281,22 @@ void XMLCALL XmlPullParser::commentDataHandler(void* userData, const char* comme Event::kComment, XML_GetCurrentLineNumber(parser->mParser), parser->mDepth, - util::utf8ToUtf16(comment) + comment }); } -Maybe<StringPiece16> findAttribute(const XmlPullParser* parser, const StringPiece16& name) { - auto iter = parser->findAttribute(u"", name); +Maybe<StringPiece> findAttribute(const XmlPullParser* parser, const StringPiece& name) { + auto iter = parser->findAttribute("", name); if (iter != parser->endAttributes()) { - return StringPiece16(util::trimWhitespace(iter->value)); + return StringPiece(util::trimWhitespace(iter->value)); } return {}; } -Maybe<StringPiece16> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece16& name) { - auto iter = parser->findAttribute(u"", name); +Maybe<StringPiece> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece& name) { + auto iter = parser->findAttribute("", name); if (iter != parser->endAttributes()) { - StringPiece16 trimmed = util::trimWhitespace(iter->value); + StringPiece trimmed = util::trimWhitespace(iter->value); if (!trimmed.empty()) { return trimmed; } diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h index 7e7070e5e5ea..ee51b360feb4 100644 --- a/tools/aapt2/xml/XmlPullParser.h +++ b/tools/aapt2/xml/XmlPullParser.h @@ -80,28 +80,28 @@ public: // These are available for all nodes. // - const std::u16string& getComment() const; + const std::string& getComment() const; size_t getLineNumber() const; size_t getDepth() const; /** * Returns the character data for a Text event. */ - const std::u16string& getText() const; + const std::string& getText() const; // // Namespace prefix and URI are available for StartNamespace and EndNamespace. // - const std::u16string& getNamespacePrefix() const; - const std::u16string& getNamespaceUri() const; + const std::string& getNamespacePrefix() const; + const std::string& getNamespaceUri() const; // // These are available for StartElement and EndElement. // - const std::u16string& getElementNamespace() const; - const std::u16string& getElementName() const; + const std::string& getElementNamespace() const; + const std::string& getElementName() const; /* * Uses the current stack of namespaces to resolve the package. Eg: @@ -115,7 +115,7 @@ public: * 'package' will be set to 'defaultPackage'. */ Maybe<ExtractedPackage> transformPackageAlias( - const StringPiece16& alias, const StringPiece16& localPackage) const override; + const StringPiece& alias, const StringPiece& localPackage) const override; // // Remaining methods are for retrieving information about attributes @@ -126,9 +126,9 @@ public: // struct Attribute { - std::u16string namespaceUri; - std::u16string name; - std::u16string value; + std::string namespaceUri; + std::string name; + std::string value; int compare(const Attribute& rhs) const; bool operator<(const Attribute& rhs) const; @@ -141,7 +141,7 @@ public: const_iterator beginAttributes() const; const_iterator endAttributes() const; size_t getAttributeCount() const; - const_iterator findAttribute(StringPiece16 namespaceUri, StringPiece16 name) const; + const_iterator findAttribute(StringPiece namespaceUri, StringPiece name) const; private: static void XMLCALL startNamespaceHandler(void* userData, const char* prefix, const char* uri); @@ -155,8 +155,8 @@ private: Event event; size_t lineNumber; size_t depth; - std::u16string data1; - std::u16string data2; + std::string data1; + std::string data2; std::vector<Attribute> attributes; }; @@ -165,12 +165,12 @@ private: char mBuffer[16384]; std::queue<EventData> mEventQueue; std::string mLastError; - const std::u16string mEmpty; + const std::string mEmpty; size_t mDepth; - std::stack<std::u16string> mNamespaceUris; + std::stack<std::string> mNamespaceUris; struct PackageDecl { - std::u16string prefix; + std::string prefix; ExtractedPackage package; }; std::vector<PackageDecl> mPackageAliases; @@ -179,13 +179,13 @@ private: /** * Finds the attribute in the current element within the global namespace. */ -Maybe<StringPiece16> findAttribute(const XmlPullParser* parser, const StringPiece16& name); +Maybe<StringPiece> findAttribute(const XmlPullParser* parser, const StringPiece& name); /** * Finds the attribute in the current element within the global namespace. The attribute's value * must not be the empty string. */ -Maybe<StringPiece16> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece16& name); +Maybe<StringPiece> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece& name); // // Implementation @@ -270,12 +270,12 @@ inline bool XmlPullParser::Attribute::operator!=(const Attribute& rhs) const { return compare(rhs) != 0; } -inline XmlPullParser::const_iterator XmlPullParser::findAttribute(StringPiece16 namespaceUri, - StringPiece16 name) const { +inline XmlPullParser::const_iterator XmlPullParser::findAttribute(StringPiece namespaceUri, + StringPiece name) const { const auto endIter = endAttributes(); const auto iter = std::lower_bound(beginAttributes(), endIter, - std::pair<StringPiece16, StringPiece16>(namespaceUri, name), - [](const Attribute& attr, const std::pair<StringPiece16, StringPiece16>& rhs) -> bool { + std::pair<StringPiece, StringPiece>(namespaceUri, name), + [](const Attribute& attr, const std::pair<StringPiece, StringPiece>& rhs) -> bool { int cmp = attr.namespaceUri.compare(0, attr.namespaceUri.size(), rhs.first.data(), rhs.first.size()); if (cmp < 0) return true; diff --git a/tools/aapt2/xml/XmlPullParser_test.cpp b/tools/aapt2/xml/XmlPullParser_test.cpp index 8fa2c6d274c8..2c1fdc76e9ad 100644 --- a/tools/aapt2/xml/XmlPullParser_test.cpp +++ b/tools/aapt2/xml/XmlPullParser_test.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ +#include "test/Test.h" #include "util/StringPiece.h" #include "xml/XmlPullParser.h" -#include <gtest/gtest.h> #include <sstream> namespace aapt { @@ -32,21 +32,21 @@ TEST(XmlPullParserTest, NextChildNodeTraversesCorrectly) { ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthOuter)); EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent()); - EXPECT_EQ(StringPiece16(u"a"), StringPiece16(parser.getElementName())); + EXPECT_EQ(StringPiece("a"), StringPiece(parser.getElementName())); const size_t depthA = parser.getDepth(); ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthA)); EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent()); - EXPECT_EQ(StringPiece16(u"b"), StringPiece16(parser.getElementName())); + EXPECT_EQ(StringPiece("b"), StringPiece(parser.getElementName())); const size_t depthB = parser.getDepth(); ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthB)); EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent()); - EXPECT_EQ(StringPiece16(u"c"), StringPiece16(parser.getElementName())); + EXPECT_EQ(StringPiece("c"), StringPiece(parser.getElementName())); ASSERT_TRUE(xml::XmlPullParser::nextChildNode(&parser, depthB)); EXPECT_EQ(xml::XmlPullParser::Event::kStartElement, parser.getEvent()); - EXPECT_EQ(StringPiece16(u"e"), StringPiece16(parser.getElementName())); + EXPECT_EQ(StringPiece("e"), StringPiece(parser.getElementName())); ASSERT_FALSE(xml::XmlPullParser::nextChildNode(&parser, depthOuter)); EXPECT_EQ(xml::XmlPullParser::Event::kEndDocument, parser.getEvent()); diff --git a/tools/aapt2/xml/XmlUtil.cpp b/tools/aapt2/xml/XmlUtil.cpp index ab9f544d67ea..0e9d005dd57c 100644 --- a/tools/aapt2/xml/XmlUtil.cpp +++ b/tools/aapt2/xml/XmlUtil.cpp @@ -23,19 +23,25 @@ namespace aapt { namespace xml { -Maybe<ExtractedPackage> extractPackageFromNamespace(const std::u16string& namespaceUri) { - if (util::stringStartsWith<char16_t>(namespaceUri, kSchemaPublicPrefix)) { - StringPiece16 schemaPrefix = kSchemaPublicPrefix; - StringPiece16 package = namespaceUri; +std::string buildPackageNamespace(const StringPiece& package) { + std::string result = kSchemaPublicPrefix; + result.append(package.data(), package.size()); + return result; +} + +Maybe<ExtractedPackage> extractPackageFromNamespace(const std::string& namespaceUri) { + if (util::stringStartsWith(namespaceUri, kSchemaPublicPrefix)) { + StringPiece schemaPrefix = kSchemaPublicPrefix; + StringPiece package = namespaceUri; package = package.substr(schemaPrefix.size(), package.size() - schemaPrefix.size()); if (package.empty()) { return {}; } return ExtractedPackage{ package.toString(), false /* isPrivate */ }; - } else if (util::stringStartsWith<char16_t>(namespaceUri, kSchemaPrivatePrefix)) { - StringPiece16 schemaPrefix = kSchemaPrivatePrefix; - StringPiece16 package = namespaceUri; + } else if (util::stringStartsWith(namespaceUri, kSchemaPrivatePrefix)) { + StringPiece schemaPrefix = kSchemaPrivatePrefix; + StringPiece package = namespaceUri; package = package.substr(schemaPrefix.size(), package.size() - schemaPrefix.size()); if (package.empty()) { return {}; @@ -43,13 +49,13 @@ Maybe<ExtractedPackage> extractPackageFromNamespace(const std::u16string& namesp return ExtractedPackage{ package.toString(), true /* isPrivate */ }; } else if (namespaceUri == kSchemaAuto) { - return ExtractedPackage{ std::u16string(), true /* isPrivate */ }; + return ExtractedPackage{ std::string(), true /* isPrivate */ }; } return {}; } void transformReferenceFromNamespace(IPackageDeclStack* declStack, - const StringPiece16& localPackage, Reference* inRef) { + const StringPiece& localPackage, Reference* inRef) { if (inRef->name) { if (Maybe<ExtractedPackage> transformedPackage = declStack->transformPackageAlias(inRef->name.value().package, localPackage)) { diff --git a/tools/aapt2/xml/XmlUtil.h b/tools/aapt2/xml/XmlUtil.h index 98e5520a6ea2..f0d59b7346b2 100644 --- a/tools/aapt2/xml/XmlUtil.h +++ b/tools/aapt2/xml/XmlUtil.h @@ -25,10 +25,10 @@ namespace aapt { namespace xml { -constexpr const char16_t* kSchemaAuto = u"http://schemas.android.com/apk/res-auto"; -constexpr const char16_t* kSchemaPublicPrefix = u"http://schemas.android.com/apk/res/"; -constexpr const char16_t* kSchemaPrivatePrefix = u"http://schemas.android.com/apk/prv/res/"; -constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android"; +constexpr const char* kSchemaAuto = "http://schemas.android.com/apk/res-auto"; +constexpr const char* kSchemaPublicPrefix = "http://schemas.android.com/apk/res/"; +constexpr const char* kSchemaPrivatePrefix = "http://schemas.android.com/apk/prv/res/"; +constexpr const char* kSchemaAndroid = "http://schemas.android.com/apk/res/android"; /** * Result of extracting a package name from a namespace URI declaration. @@ -38,7 +38,7 @@ struct ExtractedPackage { * The name of the package. This can be the empty string, which means that the package * should be assumed to be the package being compiled. */ - std::u16string package; + std::string package; /** * True if the package's private namespace was declared. This means that private resources @@ -55,7 +55,14 @@ struct ExtractedPackage { * Special case: if namespaceUri is http://schemas.android.com/apk/res-auto, * returns an empty package name. */ -Maybe<ExtractedPackage> extractPackageFromNamespace(const std::u16string& namespaceUri); +Maybe<ExtractedPackage> extractPackageFromNamespace(const std::string& namespaceUri); + +/** + * Returns an XML Android namespace for the given package of the form: + * + * http://schemas.android.com/apk/res/<package> + */ +std::string buildPackageNamespace(const StringPiece& package); /** * Interface representing a stack of XML namespace declarations. When looking up the package @@ -68,7 +75,7 @@ struct IPackageDeclStack { * Returns an ExtractedPackage struct if the alias given corresponds with a package declaration. */ virtual Maybe<ExtractedPackage> transformPackageAlias( - const StringPiece16& alias, const StringPiece16& localPackage) const = 0; + const StringPiece& alias, const StringPiece& localPackage) const = 0; }; /** @@ -77,7 +84,7 @@ struct IPackageDeclStack { * the package declaration was private. */ void transformReferenceFromNamespace(IPackageDeclStack* declStack, - const StringPiece16& localPackage, Reference* inRef); + const StringPiece& localPackage, Reference* inRef); } // namespace xml } // namespace aapt diff --git a/tools/aapt2/xml/XmlUtil_test.cpp b/tools/aapt2/xml/XmlUtil_test.cpp index 319e7707d874..cbeb8bcda9e0 100644 --- a/tools/aapt2/xml/XmlUtil_test.cpp +++ b/tools/aapt2/xml/XmlUtil_test.cpp @@ -14,40 +14,37 @@ * limitations under the License. */ -#include "test/Common.h" +#include "test/Test.h" #include "xml/XmlUtil.h" -#include <gtest/gtest.h> - namespace aapt { TEST(XmlUtilTest, ExtractPackageFromNamespace) { - AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"com.android")); - AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"http://schemas.android.com/apk")); - AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res")); - AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res/")); - AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace( - u"http://schemas.android.com/apk/prv/res/")); + AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace("com.android")); + AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace("http://schemas.android.com/apk")); + AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace("http://schemas.android.com/apk/res")); + AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace("http://schemas.android.com/apk/res/")); + AAPT_ASSERT_FALSE(xml::extractPackageFromNamespace("http://schemas.android.com/apk/prv/res/")); Maybe<xml::ExtractedPackage> p = - xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res/a"); + xml::extractPackageFromNamespace("http://schemas.android.com/apk/res/a"); AAPT_ASSERT_TRUE(p); - EXPECT_EQ(std::u16string(u"a"), p.value().package); + EXPECT_EQ(std::string("a"), p.value().package); EXPECT_FALSE(p.value().privateNamespace); - p = xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/prv/res/android"); + p = xml::extractPackageFromNamespace("http://schemas.android.com/apk/prv/res/android"); AAPT_ASSERT_TRUE(p); - EXPECT_EQ(std::u16string(u"android"), p.value().package); + EXPECT_EQ(std::string("android"), p.value().package); EXPECT_TRUE(p.value().privateNamespace); - p = xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/prv/res/com.test"); + p = xml::extractPackageFromNamespace("http://schemas.android.com/apk/prv/res/com.test"); AAPT_ASSERT_TRUE(p); - EXPECT_EQ(std::u16string(u"com.test"), p.value().package); + EXPECT_EQ(std::string("com.test"), p.value().package); EXPECT_TRUE(p.value().privateNamespace); - p = xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res-auto"); + p = xml::extractPackageFromNamespace("http://schemas.android.com/apk/res-auto"); AAPT_ASSERT_TRUE(p); - EXPECT_EQ(std::u16string(), p.value().package); + EXPECT_EQ(std::string(), p.value().package); EXPECT_TRUE(p.value().privateNamespace); } diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py index 2956d87f247c..1dd53a908036 100755 --- a/tools/fonts/fontchain_lint.py +++ b/tools/fonts/fontchain_lint.py @@ -423,21 +423,11 @@ def parse_ucd(ucd_path): _emoji_zwj_sequences = parse_unicode_datafile( path.join(ucd_path, 'emoji-zwj-sequences.txt')) - # filter modern pentathlon, as it seems likely to be removed from final spec - # also filter rifle - def is_excluded(n): - return n in [0x1f93b, 0x1f946] - - def contains_excluded(t): - if type(t) == int: - return is_excluded(t) - return any(is_excluded(cp) for cp in t) - - # filter modern pentathlon, as it seems likely to be removed from final spec - _emoji_properties['Emoji'] = set( - t for t in _emoji_properties['Emoji'] if not contains_excluded(t)) - _emoji_sequences = dict( - (t, v) for (t, v) in _emoji_sequences.items() if not contains_excluded(t)) + + # add in UN flag + UN_seq = flag_sequence('UN') + _emoji_sequences[UN_seq] = 'Emoji_Flag_Sequence' + # add in UN flag UN_seq = flag_sequence('UN') diff --git a/tools/layoutlib/.idea/inspectionProfiles/Project_Default.xml b/tools/layoutlib/.idea/inspectionProfiles/Project_Default.xml index 3681f2aaf3f1..74fa549f66d4 100644 --- a/tools/layoutlib/.idea/inspectionProfiles/Project_Default.xml +++ b/tools/layoutlib/.idea/inspectionProfiles/Project_Default.xml @@ -11,7 +11,6 @@ <option name="loggerClassName" value="org.apache.log4j.Logger,org.slf4j.LoggerFactory,org.apache.commons.logging.LogFactory,java.util.logging.Logger" /> <option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" /> </inspection_tool> - <inspection_tool class="ToArrayCallWithZeroLengthArrayArgument" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="WeakerAccess" enabled="true" level="WARNING" enabled_by_default="true"> <option name="SUGGEST_PACKAGE_LOCAL_FOR_MEMBERS" value="false" /> <option name="SUGGEST_PACKAGE_LOCAL_FOR_TOP_CLASSES" value="false" /> diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java index d0e431acadff..1d5ac0ca7346 100644 --- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java @@ -584,6 +584,7 @@ public final class BridgeTypedArray extends TypedArray { if (value == null) { return defValue; } + value = value.trim(); // if the value is just an integer, return it. try { @@ -595,6 +596,11 @@ public final class BridgeTypedArray extends TypedArray { // pass } + if (value.startsWith("#")) { + // this looks like a color, do not try to parse it + return defValue; + } + // Handle the @id/<name>, @+id/<name> and @android:id/<name> // We need to return the exact value that was compiled (from the various R classes), // as these values can be reused internally with calls to findViewById(). diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java index 265ebd1755e3..219c487cac53 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java @@ -86,6 +86,8 @@ public final class Path_Delegate { public void reset() { mPath.reset(); + mLastX = 0; + mLastY = 0; } public void setPathIterator(PathIterator iterator) { @@ -124,7 +126,7 @@ public final class Path_Delegate { return; } - pathDelegate.mPath.reset(); + pathDelegate.reset(); } @LayoutlibDelegate diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java index 49f8691986be..1dfd305a6094 100644 --- a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java @@ -96,6 +96,14 @@ public class VectorDrawable_Delegate { } @LayoutlibDelegate + static long nCreateTreeFromCopy(long rendererToCopyPtr, long rootGroupPtr) { + VGroup_Delegate rootGroup = VNativeObject.getDelegate(rootGroupPtr); + VPathRenderer_Delegate rendererToCopy = VNativeObject.getDelegate(rendererToCopyPtr); + return sPathManager.addNewDelegate(new VPathRenderer_Delegate(rendererToCopy, + rootGroup)); + } + + @LayoutlibDelegate static void nSetRendererViewportSize(long rendererPtr, float viewportWidth, float viewportHeight) { VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr); @@ -973,7 +981,7 @@ public class VectorDrawable_Delegate { public VPath_Delegate(VPath_Delegate copy) { mPathName = copy.mPathName; mChangingConfigurations = copy.mChangingConfigurations; - mNodes = PathParser_Delegate.deepCopyNodes(copy.mNodes); + mNodes = copy.mNodes != null ? PathParser_Delegate.deepCopyNodes(copy.mNodes) : null; } public void toPath(Path path) { @@ -1035,6 +1043,14 @@ public class VectorDrawable_Delegate { mRenderPath = new Path(); } + private VPathRenderer_Delegate(VPathRenderer_Delegate rendererToCopy, + VGroup_Delegate rootGroup) { + this(rootGroup); + mViewportWidth = rendererToCopy.mViewportWidth; + mViewportHeight = rendererToCopy.mViewportHeight; + mRootAlpha = rendererToCopy.mRootAlpha; + } + private float getRootAlpha() { return mRootAlpha; } @@ -1133,7 +1149,8 @@ public class VectorDrawable_Delegate { } final Paint fillPaint = mFillPaint; - fillPaint.setColor(applyAlpha(fullPath.mFillColor, fullPath.mFillAlpha)); + fillPaint.setColor(applyAlpha(applyAlpha(fullPath.mFillColor, fullPath + .mFillAlpha), getRootAlpha())); Paint_Delegate fillPaintDelegate = Paint_Delegate.getDelegate(fillPaint .getNativeInstance()); // mFillPaint can not be null at this point so we will have a delegate @@ -1162,7 +1179,8 @@ public class VectorDrawable_Delegate { } strokePaint.setStrokeMiter(fullPath.mStrokeMiterlimit); - strokePaint.setColor(applyAlpha(fullPath.mStrokeColor, fullPath.mStrokeAlpha)); + strokePaint.setColor(applyAlpha(applyAlpha(fullPath.mStrokeColor, fullPath + .mStrokeAlpha), getRootAlpha())); Paint_Delegate strokePaintDelegate = Paint_Delegate.getDelegate(strokePaint .getNativeInstance()); // mStrokePaint can not be null at this point so we will have a delegate diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java index bdddfd8d8ba3..3667f584fc66 100644 --- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java @@ -39,11 +39,27 @@ import android.annotation.NonNull; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; +import android.widget.NumberPicker; import java.io.File; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; - +import java.util.Set; + +import static com.android.SdkConstants.AUTO_COMPLETE_TEXT_VIEW; +import static com.android.SdkConstants.BUTTON; +import static com.android.SdkConstants.CHECKED_TEXT_VIEW; +import static com.android.SdkConstants.CHECK_BOX; +import static com.android.SdkConstants.EDIT_TEXT; +import static com.android.SdkConstants.IMAGE_BUTTON; +import static com.android.SdkConstants.MULTI_AUTO_COMPLETE_TEXT_VIEW; +import static com.android.SdkConstants.RADIO_BUTTON; +import static com.android.SdkConstants.SEEK_BAR; +import static com.android.SdkConstants.SPINNER; +import static com.android.SdkConstants.TEXT_VIEW; import static com.android.layoutlib.bridge.android.BridgeContext.getBaseContext; /** @@ -52,6 +68,13 @@ import static com.android.layoutlib.bridge.android.BridgeContext.getBaseContext; public final class BridgeInflater extends LayoutInflater { private final LayoutlibCallback mLayoutlibCallback; + /** + * If true, the inflater will try to replace the framework widgets with the AppCompat versions. + * Ideally, this should be based on the activity being an AppCompat activity but since that is + * not trivial to check from layoutlib, we currently base the decision on the current theme + * being an AppCompat theme. + */ + private boolean mLoadAppCompatViews; private boolean mIsInMerge = false; private ResourceReference mResourceReference; private Map<View, String> mOpenDrawerLayouts; @@ -59,6 +82,15 @@ public final class BridgeInflater extends LayoutInflater { // Keep in sync with the same value in LayoutInflater. private static final int[] ATTRS_THEME = new int[] {com.android.internal.R.attr.theme }; + private static final String APPCOMPAT_WIDGET_PREFIX = "android.support.v7.widget.AppCompat"; + /** List of platform widgets that have an AppCompat version */ + private static final Set<String> APPCOMPAT_VIEWS = Collections.unmodifiableSet( + new HashSet<>( + Arrays.asList(TEXT_VIEW, "ImageSwitcher", BUTTON, EDIT_TEXT, SPINNER, + IMAGE_BUTTON, CHECK_BOX, RADIO_BUTTON, CHECKED_TEXT_VIEW, + AUTO_COMPLETE_TEXT_VIEW, MULTI_AUTO_COMPLETE_TEXT_VIEW, "RatingBar", + SEEK_BAR))); + /** * List of class prefixes which are tried first by default. * <p/> @@ -74,13 +106,15 @@ public final class BridgeInflater extends LayoutInflater { return sClassPrefixList; } - protected BridgeInflater(LayoutInflater original, Context newContext) { + private BridgeInflater(LayoutInflater original, Context newContext) { super(original, newContext); newContext = getBaseContext(newContext); if (newContext instanceof BridgeContext) { mLayoutlibCallback = ((BridgeContext) newContext).getLayoutlibCallback(); + mLoadAppCompatViews = ((BridgeContext) newContext).isAppCompatTheme(); } else { mLayoutlibCallback = null; + mLoadAppCompatViews = false; } } @@ -90,10 +124,11 @@ public final class BridgeInflater extends LayoutInflater { * @param context The Android application context. * @param layoutlibCallback the {@link LayoutlibCallback} object. */ - public BridgeInflater(Context context, LayoutlibCallback layoutlibCallback) { + public BridgeInflater(BridgeContext context, LayoutlibCallback layoutlibCallback) { super(context); mLayoutlibCallback = layoutlibCallback; mConstructorArgs[0] = context; + mLoadAppCompatViews = context.isAppCompatTheme(); } @Override @@ -101,26 +136,36 @@ public final class BridgeInflater extends LayoutInflater { View view = null; try { - // First try to find a class using the default Android prefixes - for (String prefix : sClassPrefixList) { - try { - view = createView(name, prefix, attrs); - if (view != null) { - break; + if (mLoadAppCompatViews && APPCOMPAT_VIEWS.contains(name)) { + // We are using an AppCompat theme so try to load the appcompat views + view = loadCustomView(APPCOMPAT_WIDGET_PREFIX + name, attrs); + + if (view == null) { + mLoadAppCompatViews = false; // Do not try anymore + } + } else { + + // First try to find a class using the default Android prefixes + for (String prefix : sClassPrefixList) { + try { + view = createView(name, prefix, attrs); + if (view != null) { + break; + } + } catch (ClassNotFoundException e) { + // Ignore. We'll try again using the base class below. } - } catch (ClassNotFoundException e) { - // Ignore. We'll try again using the base class below. } - } - // Next try using the parent loader. This will most likely only work for - // fully-qualified class names. - try { - if (view == null) { - view = super.onCreateView(name, attrs); + // Next try using the parent loader. This will most likely only work for + // fully-qualified class names. + try { + if (view == null) { + view = super.onCreateView(name, attrs); + } + } catch (ClassNotFoundException e) { + // Ignore. We'll try again using the custom view loader below. } - } catch (ClassNotFoundException e) { - // Ignore. We'll try again using the custom view loader below. } // Finally try again using the custom view loader @@ -144,9 +189,26 @@ public final class BridgeInflater extends LayoutInflater { @Override public View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr) { - View view; + View view = null; + if (name.equals("view")) { + // This is usually done by the superclass but this allows us catching the error and + // reporting something useful. + name = attrs.getAttributeValue(null, "class"); + + if (name == null) { + Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Unable to inflate view tag without " + + "class attribute", null); + // We weren't able to resolve the view so we just pass a mock View to be able to + // continue rendering. + view = new MockView(context, attrs); + ((MockView) view).setText("view"); + } + } + try { - view = super.createViewFromTag(parent, name, context, attrs, ignoreThemeAttr); + if (view == null) { + view = super.createViewFromTag(parent, name, context, attrs, ignoreThemeAttr); + } } catch (InflateException e) { // Creation of ContextThemeWrapper code is same as in the super method. // Apply a theme wrapper, if allowed and one is specified. @@ -240,6 +302,9 @@ public final class BridgeInflater extends LayoutInflater { // first get the classname in case it's not the node name if (name.equals("view")) { name = attrs.getAttributeValue(null, "class"); + if (name == null) { + return null; + } } mConstructorArgs[1] = attrs; @@ -300,6 +365,17 @@ public final class BridgeInflater extends LayoutInflater { getDrawerLayoutMap().put(view, attrVal); } } + else if (view instanceof NumberPicker) { + NumberPicker numberPicker = (NumberPicker) view; + String minValue = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI, "minValue"); + if (minValue != null) { + numberPicker.setMinValue(Integer.parseInt(minValue)); + } + String maxValue = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI, "maxValue"); + if (maxValue != null) { + numberPicker.setMaxValue(Integer.parseInt(maxValue)); + } + } } } diff --git a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java index ea9a255e8561..d15cb4865544 100644 --- a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java +++ b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java @@ -54,12 +54,18 @@ public class RectShadowPainter { if (saved == -1) { return; } + + float radius = viewOutline.getRadius(); + if (radius <= 0) { + // We can not paint a shadow with radius 0 + return; + } + try { Paint cornerPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); cornerPaint.setStyle(Style.FILL); Paint edgePaint = new Paint(cornerPaint); edgePaint.setAntiAlias(false); - float radius = viewOutline.getRadius(); float outerArcRadius = radius + shadowSize; int[] colors = {START_COLOR, START_COLOR, END_COLOR}; cornerPaint.setShader(new RadialGradient(0, 0, outerArcRadius, colors, diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index 616cb5761402..72ac4c3f9069 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -110,6 +110,7 @@ import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_AP */ @SuppressWarnings("deprecation") // For use of Pair. public final class BridgeContext extends Context { + private static final String PREFIX_THEME_APPCOMPAT = "Theme.AppCompat"; /** The map adds cookies to each view so that IDE can link xml tags to views. */ private final HashMap<View, Object> mViewKeyMap = new HashMap<>(); @@ -153,6 +154,7 @@ public final class BridgeContext extends Context { private ClassLoader mClassLoader; private IBinder mBinder; private PackageManager mPackageManager; + private Boolean mIsThemeAppCompat; /** * Some applications that target both pre API 17 and post API 17, set the newer attrs to @@ -479,6 +481,36 @@ public final class BridgeContext extends Context { return Pair.of(null, Boolean.FALSE); } + /** + * Returns whether the current selected theme is based on AppCompat + */ + public boolean isAppCompatTheme() { + // If a cached value exists, return it. + if (mIsThemeAppCompat != null) { + return mIsThemeAppCompat; + } + // Ideally, we should check if the corresponding activity extends + // android.support.v7.app.ActionBarActivity, and not care about the theme name at all. + StyleResourceValue defaultTheme = mRenderResources.getDefaultTheme(); + // We can't simply check for parent using resources.themeIsParentOf() since the + // inheritance structure isn't really what one would expect. The first common parent + // between Theme.AppCompat.Light and Theme.AppCompat is Theme.Material (for v21). + boolean isThemeAppCompat = false; + for (int i = 0; i < 50; i++) { + if (defaultTheme == null) { + break; + } + // for loop ensures that we don't run into cyclic theme inheritance. + if (defaultTheme.getName().startsWith(PREFIX_THEME_APPCOMPAT)) { + isThemeAppCompat = true; + break; + } + defaultTheme = mRenderResources.getParent(defaultTheme); + } + mIsThemeAppCompat = isThemeAppCompat; + return isThemeAppCompat; + } + @SuppressWarnings("deprecation") private ILayoutPullParser getParser(ResourceReference resource) { ILayoutPullParser parser; @@ -831,45 +863,55 @@ public final class BridgeContext extends Context { } } - // if there's no direct value for this attribute in the XML, we look for default - // values in the widget defStyle, and then in the theme. - if (value == null) { - ResourceValue resValue = null; - + // Calculate the default value from the Theme in two cases: + // - If defaultPropMap is not null, get the default value to add it to the list + // of default values of properties. + // - If value is null, it means that the attribute is not directly set as an + // attribute in the XML so try to get the default value. + ResourceValue defaultValue = null; + if (defaultPropMap != null || value == null) { // look for the value in the custom style first (and its parent if needed) if (customStyleValues != null) { - resValue = mRenderResources.findItemInStyle(customStyleValues, - attrName, frameworkAttr); + defaultValue = mRenderResources.findItemInStyle(customStyleValues, attrName, + frameworkAttr); } // then look for the value in the default Style (and its parent if needed) - if (resValue == null && defStyleValues != null) { - resValue = mRenderResources.findItemInStyle(defStyleValues, - attrName, frameworkAttr); + if (defaultValue == null && defStyleValues != null) { + defaultValue = mRenderResources.findItemInStyle(defStyleValues, attrName, + frameworkAttr); } // if the item is not present in the defStyle, we look in the main theme (and // its parent themes) - if (resValue == null) { - resValue = mRenderResources.findItemInTheme(attrName, frameworkAttr); + if (defaultValue == null) { + defaultValue = mRenderResources.findItemInTheme(attrName, frameworkAttr); } // if we found a value, we make sure this doesn't reference another value. // So we resolve it. - if (resValue != null) { - String preResolve = resValue.getValue(); - resValue = mRenderResources.resolveResValue(resValue); + if (defaultValue != null) { + String preResolve = defaultValue.getValue(); + defaultValue = mRenderResources.resolveResValue(defaultValue); if (defaultPropMap != null) { defaultPropMap.put( frameworkAttr ? SdkConstants.PREFIX_ANDROID + attrName : - attrName, - new Property(preResolve, resValue.getValue())); + attrName, new Property(preResolve, defaultValue.getValue())); } + } + } + // Done calculating the defaultValue + // if there's no direct value for this attribute in the XML, we look for default + // values in the widget defStyle, and then in the theme. + if (value == null) { + // if we found a value, we make sure this doesn't reference another value. + // So we resolve it. + if (defaultValue != null) { // If the value is a reference to another theme attribute that doesn't // exist, we should log a warning and omit it. - String val = resValue.getValue(); + String val = defaultValue.getValue(); if (val != null && val.startsWith(SdkConstants.PREFIX_THEME_REF)) { if (!attrName.equals(RTL_ATTRS.get(val)) || getApplicationInfo().targetSdkVersion < @@ -880,11 +922,11 @@ public final class BridgeContext extends Context { String.format("Failed to find '%s' in current theme.", val), val); } - resValue = null; + defaultValue = null; } } - ta.bridgeSetValue(index, attrName, frameworkAttr, resValue); + ta.bridgeSetValue(index, attrName, frameworkAttr, defaultValue); } else { // there is a value in the XML, but we need to resolve it in case it's // referencing another resource or a theme value. diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java index c59b1a66bb02..0c39026a15d8 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java @@ -24,8 +24,8 @@ import android.util.SparseArray; import java.io.PrintStream; import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.Set; import java.util.concurrent.atomic.AtomicLong; /** @@ -33,7 +33,7 @@ import java.util.concurrent.atomic.AtomicLong; * * This is used in conjunction with layoublib_create: certain Android java classes are mere * wrappers around a heavily native based implementation, and we need a way to run these classes - * in our Eclipse rendering framework without bringing all the native code from the Android + * in our Android Studio rendering framework without bringing all the native code from the Android * platform. * * Thus we instruct layoutlib_create to modify the bytecode of these classes to replace their @@ -61,7 +61,7 @@ import java.util.concurrent.atomic.AtomicLong; * following mechanism: * * - {@link #addNewDelegate(Object)} and {@link #removeJavaReferenceFor(long)} adds and removes - * the delegate to/from a list. This list hold the reference and prevents the GC from reclaiming + * the delegate to/from a set. This set holds the reference and prevents the GC from reclaiming * the delegate. * * - {@link #addNewDelegate(Object)} also adds the delegate to a {@link SparseArray} that holds a @@ -76,12 +76,12 @@ public final class DelegateManager<T> { @SuppressWarnings("FieldCanBeLocal") private final Class<T> mClass; private static final SparseWeakArray<Object> sDelegates = new SparseWeakArray<>(); - /** list used to store delegates when their main object holds a reference to them. + /** Set used to store delegates when their main object holds a reference to them. * This is to ensure that the WeakReference in the SparseWeakArray doesn't get GC'ed * @see #addNewDelegate(Object) * @see #removeJavaReferenceFor(long) */ - private static final List<Object> sJavaReferences = new ArrayList<>(); + private static final Set<Object> sJavaReferences = new HashSet<>(); private static final AtomicLong sDelegateCounter = new AtomicLong(1); public DelegateManager(Class<T> theClass) { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java index 1afd90d39f31..537fa7744c2a 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java @@ -20,7 +20,6 @@ import com.android.ide.common.rendering.api.HardwareConfig; 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.android.RenderParamsFlags; @@ -94,7 +93,6 @@ class Layout extends RelativeLayout { private static final String ATTR_WINDOW_TITLE_SIZE = "windowTitleSize"; private static final String ATTR_WINDOW_TRANSLUCENT_STATUS = StatusBar.ATTR_TRANSLUCENT; private static final String ATTR_WINDOW_TRANSLUCENT_NAV = NavigationBar.ATTR_TRANSLUCENT; - private static final String PREFIX_THEME_APPCOMPAT = "Theme.AppCompat"; // Default sizes private static final int DEFAULT_STATUS_BAR_HEIGHT = 25; @@ -236,7 +234,7 @@ class Layout extends RelativeLayout { boolean isMenu = "menu".equals(params.getFlag(RenderParamsFlags.FLAG_KEY_ROOT_TAG)); BridgeActionBar actionBar; - if (mBuilder.isThemeAppCompat() && !isMenu) { + if (context.isAppCompatTheme() && !isMenu) { actionBar = new AppCompatActionBar(context, params); } else { actionBar = new FrameworkActionBar(context, params); @@ -324,8 +322,6 @@ class Layout extends RelativeLayout { private boolean mTranslucentStatus; private boolean mTranslucentNav; - private Boolean mIsThemeAppCompat; - public Builder(@NonNull SessionParams params, @NonNull BridgeContext context) { mParams = params; mContext = context; @@ -365,7 +361,7 @@ class Layout extends RelativeLayout { } // Check if an actionbar is needed boolean windowActionBar = getBooleanThemeValue(mResources, ATTR_WINDOW_ACTION_BAR, - !isThemeAppCompat(), true); + !mContext.isAppCompatTheme(), true); if (windowActionBar) { mActionBarSize = getDimension(ATTR_ACTION_BAR_SIZE, true, DEFAULT_TITLE_BAR_HEIGHT); } else { @@ -420,33 +416,6 @@ class Layout extends RelativeLayout { return mParams.getHardwareConfig().hasSoftwareButtons(); } - private boolean isThemeAppCompat() { - // If a cached value exists, return it. - if (mIsThemeAppCompat != null) { - return mIsThemeAppCompat; - } - // Ideally, we should check if the corresponding activity extends - // android.support.v7.app.ActionBarActivity, and not care about the theme name at all. - StyleResourceValue defaultTheme = mResources.getDefaultTheme(); - // We can't simply check for parent using resources.themeIsParentOf() since the - // inheritance structure isn't really what one would expect. The first common parent - // between Theme.AppCompat.Light and Theme.AppCompat is Theme.Material (for v21). - boolean isThemeAppCompat = false; - for (int i = 0; i < 50; i++) { - if (defaultTheme == null) { - break; - } - // for loop ensures that we don't run into cyclic theme inheritance. - if (defaultTheme.getName().startsWith(PREFIX_THEME_APPCOMPAT)) { - isThemeAppCompat = true; - break; - } - defaultTheme = mResources.getParent(defaultTheme); - } - mIsThemeAppCompat = isThemeAppCompat; - return isThemeAppCompat; - } - /** * Return true if the status bar or nav bar are present, they are not translucent (i.e * content doesn't overlap with them). diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java index e273b2cd75cc..1ae9cb646cf3 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java @@ -39,8 +39,6 @@ public class ParserFactory { public final static boolean LOG_PARSER = false; - private final static String ENCODING = "UTF-8"; //$NON-NLS-1$ - // Used to get a new XmlPullParser from the client. @Nullable private static com.android.ide.common.rendering.api.ParserFactory sParserFactory; @@ -74,7 +72,7 @@ public class ParserFactory { stream = readAndClose(stream, name, size); - parser.setInput(stream, ENCODING); + parser.setInput(stream, null); if (isLayout) { try { return new LayoutParserWrapper(parser).peekTillLayoutStart(); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index a8077ccae01a..c890793e290f 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -20,6 +20,7 @@ import com.android.ide.common.rendering.api.AdapterBinding; import com.android.ide.common.rendering.api.HardwareConfig; import com.android.ide.common.rendering.api.IAnimationListener; import com.android.ide.common.rendering.api.ILayoutPullParser; +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.RenderSession; @@ -303,6 +304,20 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { SessionParams params = getParams(); BridgeContext context = getContext(); + if (Bridge.isLocaleRtl(params.getLocale())) { + if (!params.isRtlSupported()) { + Bridge.getLog().warning(LayoutLog.TAG_RTL_NOT_ENABLED, + "You are using a right-to-left " + + "(RTL) locale but RTL is not enabled", null); + } else if (params.getSimulatedPlatformVersion() < 17) { + // This will render ok because we are using the latest layoutlib but at least + // warn the user that this might fail in a real device. + Bridge.getLog().warning(LayoutLog.TAG_RTL_NOT_SUPPORTED, "You are using a " + + "right-to-left " + + "(RTL) locale but RTL is not supported for API level < 17", null); + } + } + // Sets the project callback (custom view loader) to the fragment delegate so that // it can instantiate the custom Fragment. Fragment_Delegate.setLayoutlibCallback(params.getLayoutlibCallback()); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java index a21de56066cb..c197e40eb4cf 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java @@ -77,6 +77,7 @@ public final class ResourceHelper { */ public static int getColor(String value) { if (value != null) { + value = value.trim(); if (!value.startsWith("#")) { if (value.startsWith(SdkConstants.PREFIX_THEME_REF)) { throw new NumberFormatException(String.format( diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png Binary files differindex bad296bf4a66..6eeb82c93735 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png Binary files differindex 9f266278c352..26aed6a7ed7c 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png Binary files differindex 89009be843e7..aaf1514ddc24 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png Binary files differindex 55d6a20949a2..466eca8d1721 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png Binary files differnew file mode 100644 index 000000000000..940fe5bc44ba --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/android.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/android.xml new file mode 100644 index 000000000000..42e3beb50b81 --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/android.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="1102" + android:viewportHeight="642" + android:width="1102px" + android:height="642px"> + + <group + android:translateX="-126.347" + android:translateY="6.7928655e-4"> + + + <path + android:fillColor="#94c147" + android:pathData=" + m 552.777,147.57 + c -14.147,0 -25.622,11.652 -25.622,26.02 + v 101.68 + c 0,14.372 11.475,26.019 25.622,26.019 14.147,0 25.61,-11.646 25.61,-26.019 + V 173.59 + c 0.001,-14.368 -11.462,-26.02 -25.61,-26.02 + z + + m -309.011,0 + c -14.155,0 -25.618,11.652 -25.618,26.02 + v 101.68 + c 0,14.372 11.462,26.019 25.618,26.019 14.153,0 25.623,-11.646 25.623,-26.019 + V 173.59 + c -0.008,-14.368 -11.475,-26.02 -25.623,-26.02 + z" /> + + + <path + android:fillColor="#94c147" + android:pathData="m 284.799,148.364 v 185.768 c 0,11.035 8.948,19.98 19.983,19.98 h 22.815 v 56.567 c 0,14.37 11.47,26.016 25.623,26.016 14.148,0 25.623,-11.646 25.623,-26.016 v -56.567 h 39.878 v 56.567 c 0,14.37 11.463,26.016 25.61,26.016 14.147,0 25.622,-11.646 25.622,-26.016 v -56.567 h 22.828 c 11.022,0 19.971,-8.953 19.971,-19.98 V 148.364 H 284.799 l 0,0 z" /> + + <group + android:name="head" + android:pivotX="400" + android:pivotY="131.105"> + + <path + android:fillColor="#94c147" + android:pathData="m 452.302,52.105 21.057,-30.572 c 1.245,-1.819 0.939,-4.199 -0.695,-5.329 -1.637,-1.123 -3.968,-0.568 -5.225,1.251 l -21.875,31.75 c -14.404,-5.682 -30.418,-8.844 -47.293,-8.844 -16.87,0 -32.893,3.162 -47.297,8.844 l -21.875,-31.75 c -1.257,-1.819 -3.589,-2.375 -5.225,-1.251 -1.636,1.124 -1.946,3.509 -0.696,5.329 l 21.057,30.572 c -33.464,15.57 -56.951,45.166 -59.941,79.706 H 512.25 C 509.259,97.271 485.772,67.676 452.302,52.105 z M 350.187,100.28 c -6.965,0 -12.617,-5.646 -12.617,-12.616 0,-6.958 5.647,-12.61 12.617,-12.61 6.97,0 12.603,5.652 12.603,12.61 0,6.965 -5.64,12.616 -12.603,12.616 z m 97.744,0 c -6.97,0 -12.609,-5.646 -12.609,-12.616 0,-6.958 5.64,-12.61 12.609,-12.61 6.971,0 12.61,5.652 12.61,12.61 0,6.965 -5.64,12.616 -12.61,12.616 z" /> + </group> + + </group> + +</vector>
\ No newline at end of file diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/headset.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/headset.xml new file mode 100644 index 000000000000..897c4113ead6 --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/headset.xml @@ -0,0 +1,25 @@ +<!-- + ~ 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. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="150dp" + android:height="150dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="m12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h4v1h-7v2h6c1.66,0 3,-1.34 3,-3V10c0,-4.97 -4.03,-9 -9,-9z"/> +</vector>
\ No newline at end of file diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_path.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_path.xml index 32e6e732bb5a..0998b25a221e 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_path.xml +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_path.xml @@ -4,7 +4,8 @@ android:height="76dp" android:width="76dp" android:viewportHeight="48" - android:viewportWidth="48"> + android:viewportWidth="48" + android:alpha="0.6"> <group android:name="root" @@ -79,7 +80,7 @@ android:fillType="nonZero" android:strokeWidth="1" android:strokeColor="#AABBCC" - android:fillColor="#AAEFCC" + android:fillColor="#40AAEFCC" android:pathData="M0,-40 l0, 10 l10, 0 l0, -10 l-10,0 m5,0 l0, 10 l10, 0 l0, -10 l-10,0" /> </group> diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/empty.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/empty.xml new file mode 100644 index 000000000000..532241199909 --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/empty.xml @@ -0,0 +1,16 @@ +<!-- + ~ 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. + --> + diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/vector_drawable_android.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/vector_drawable_android.xml new file mode 100644 index 000000000000..3b01ea093122 --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/vector_drawable_android.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:padding="16dp" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + <ImageView + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:src="@drawable/android"/> + <ImageView + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:src="@drawable/headset"/> + +</LinearLayout> + diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java index 8f570aee96b7..ba687fe384e6 100644 --- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java +++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java @@ -116,7 +116,7 @@ public class Main { private static ArrayList<String> sRenderMessages = Lists.newArrayList(); @Rule - public static TestWatcher sRenderMessageWatcher = new TestWatcher() { + public TestWatcher sRenderMessageWatcher = new TestWatcher() { @Override protected void succeeded(Description description) { // We only check error messages if the rest of the test case was successful. @@ -309,7 +309,15 @@ public class Main { /** Test activity.xml */ @Test public void testActivity() throws ClassNotFoundException { - renderAndVerify("activity.xml", "activity.png"); + try { + renderAndVerify("activity.xml", "activity.png"); + } catch (AssertionError e) { + // This is a KI in CalendarWidget and DatePicker rendering. + // Tracker bug: http://b.android.com/214370 + if (!e.getLocalizedMessage().startsWith("Images differ (by 6.5%)")) { + throw e; + } + } } /** Test allwidgets.xml */ @@ -433,6 +441,24 @@ public class Main { renderAndVerify(params, "vector_drawable.png", TimeUnit.SECONDS.toNanos(2)); } + /** + * Regression test for http://b.android.com/91383 and http://b.android.com/203797 + */ + @Test + public void testVectorDrawable91383() throws ClassNotFoundException { + // Create the layout pull parser. + LayoutPullParser parser = createLayoutPullParser("vector_drawable_android.xml"); + // Create LayoutLibCallback. + LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger()); + layoutLibCallback.initResources(); + + SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5, + layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false, + RenderingMode.V_SCROLL, 22); + + renderAndVerify(params, "vector_drawable_91383.png", TimeUnit.SECONDS.toNanos(2)); + } + /** Test activity.xml */ @Test public void testScrolling() throws ClassNotFoundException { @@ -469,7 +495,7 @@ public class Main { @Test public void testGetResourceNameVariants() throws Exception { // Setup - SessionParams params = createSessionParams("", ConfigGenerator.NEXUS_4); + SessionParams params = createSessionParams("empty.xml", ConfigGenerator.NEXUS_4); AssetManager assetManager = AssetManager.getSystem(); DisplayMetrics metrics = new DisplayMetrics(); Configuration configuration = RenderAction.getConfiguration(params); |