Use Google3 style guide with .clang-format
Test: style change only, builds ok
Change-Id: I885180e24cb2e7b58cfb4967c3bcb40058ce4078
diff --git a/tools/aapt2/.clang-format b/tools/aapt2/.clang-format
new file mode 100644
index 0000000..71c5ef2
--- /dev/null
+++ b/tools/aapt2/.clang-format
@@ -0,0 +1,3 @@
+BasedOnStyle: Google
+ColumnLimit: 100
+
diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h
index a9794a4..2cbe117 100644
--- a/tools/aapt2/AppInfo.h
+++ b/tools/aapt2/AppInfo.h
@@ -28,27 +28,27 @@
* will come from the app's AndroidManifest.
*/
struct AppInfo {
- /**
- * App's package name.
- */
- std::string package;
+ /**
+ * App's package name.
+ */
+ std::string package;
- /**
- * The App's minimum SDK version.
- */
- Maybe<std::string> minSdkVersion;
+ /**
+ * The App's minimum SDK version.
+ */
+ Maybe<std::string> minSdkVersion;
- /**
- * The Version code of the app.
- */
- Maybe<uint32_t> versionCode;
+ /**
+ * The Version code of the app.
+ */
+ Maybe<uint32_t> versionCode;
- /**
- * The revision code of the app.
- */
- Maybe<uint32_t> revisionCode;
+ /**
+ * The revision code of the app.
+ */
+ Maybe<uint32_t> revisionCode;
};
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_APP_INFO_H
+#endif // AAPT_APP_INFO_H
diff --git a/tools/aapt2/ConfigDescription.cpp b/tools/aapt2/ConfigDescription.cpp
index 1812d96..6598d63 100644
--- a/tools/aapt2/ConfigDescription.cpp
+++ b/tools/aapt2/ConfigDescription.cpp
@@ -31,854 +31,869 @@
static const char* kWildcardName = "any";
const ConfigDescription& ConfigDescription::defaultConfig() {
- static ConfigDescription config = {};
- return config;
+ static ConfigDescription config = {};
+ return config;
}
static bool parseMcc(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->mcc = 0;
- return true;
- }
- const char* c = name;
- if (tolower(*c) != 'm') return false;
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->mcc = 0;
+ return true;
+ }
+ const char* c = name;
+ if (tolower(*c) != 'm') return false;
+ c++;
+ if (tolower(*c) != 'c') return false;
+ c++;
+ if (tolower(*c) != 'c') return false;
+ c++;
+
+ const char* val = c;
+
+ while (*c >= '0' && *c <= '9') {
c++;
- if (tolower(*c) != 'c') return false;
- c++;
- if (tolower(*c) != 'c') return false;
- c++;
+ }
+ if (*c != 0) return false;
+ if (c - val != 3) return false;
- const char* val = c;
+ int d = atoi(val);
+ if (d != 0) {
+ if (out) out->mcc = d;
+ return true;
+ }
- while (*c >= '0' && *c <= '9') {
- c++;
- }
- if (*c != 0) return false;
- if (c-val != 3) return false;
-
- int d = atoi(val);
- if (d != 0) {
- if (out) out->mcc = d;
- return true;
- }
-
- return false;
+ return false;
}
static bool parseMnc(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->mcc = 0;
- return true;
- }
- const char* c = name;
- if (tolower(*c) != 'm') return false;
- c++;
- if (tolower(*c) != 'n') return false;
- c++;
- if (tolower(*c) != 'c') return false;
- c++;
-
- const char* val = c;
-
- while (*c >= '0' && *c <= '9') {
- c++;
- }
- if (*c != 0) return false;
- if (c-val == 0 || c-val > 3) return false;
-
- if (out) {
- out->mnc = atoi(val);
- if (out->mnc == 0) {
- out->mnc = ACONFIGURATION_MNC_ZERO;
- }
- }
-
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->mcc = 0;
return true;
+ }
+ const char* c = name;
+ if (tolower(*c) != 'm') return false;
+ c++;
+ if (tolower(*c) != 'n') return false;
+ c++;
+ if (tolower(*c) != 'c') return false;
+ c++;
+
+ const char* val = c;
+
+ while (*c >= '0' && *c <= '9') {
+ c++;
+ }
+ if (*c != 0) return false;
+ if (c - val == 0 || c - val > 3) return false;
+
+ if (out) {
+ out->mnc = atoi(val);
+ if (out->mnc == 0) {
+ out->mnc = ACONFIGURATION_MNC_ZERO;
+ }
+ }
+
+ return true;
}
static bool parseLayoutDirection(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
- | ResTable_config::LAYOUTDIR_ANY;
- return true;
- } else if (strcmp(name, "ldltr") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
- | ResTable_config::LAYOUTDIR_LTR;
- return true;
- } else if (strcmp(name, "ldrtl") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
- | ResTable_config::LAYOUTDIR_RTL;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_LAYOUTDIR) |
+ ResTable_config::LAYOUTDIR_ANY;
+ return true;
+ } else if (strcmp(name, "ldltr") == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_LAYOUTDIR) |
+ ResTable_config::LAYOUTDIR_LTR;
+ return true;
+ } else if (strcmp(name, "ldrtl") == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_LAYOUTDIR) |
+ ResTable_config::LAYOUTDIR_RTL;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseScreenLayoutSize(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_ANY;
- return true;
- } else if (strcmp(name, "small") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_SMALL;
- return true;
- } else if (strcmp(name, "normal") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_NORMAL;
- return true;
- } else if (strcmp(name, "large") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_LARGE;
- return true;
- } else if (strcmp(name, "xlarge") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_XLARGE;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) |
+ ResTable_config::SCREENSIZE_ANY;
+ return true;
+ } else if (strcmp(name, "small") == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) |
+ ResTable_config::SCREENSIZE_SMALL;
+ return true;
+ } else if (strcmp(name, "normal") == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) |
+ ResTable_config::SCREENSIZE_NORMAL;
+ return true;
+ } else if (strcmp(name, "large") == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) |
+ ResTable_config::SCREENSIZE_LARGE;
+ return true;
+ } else if (strcmp(name, "xlarge") == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_SCREENSIZE) |
+ ResTable_config::SCREENSIZE_XLARGE;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseScreenLayoutLong(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
- | ResTable_config::SCREENLONG_ANY;
- return true;
- } else if (strcmp(name, "long") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
- | ResTable_config::SCREENLONG_YES;
- return true;
- } else if (strcmp(name, "notlong") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
- | ResTable_config::SCREENLONG_NO;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_SCREENLONG) |
+ ResTable_config::SCREENLONG_ANY;
+ return true;
+ } else if (strcmp(name, "long") == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_SCREENLONG) |
+ ResTable_config::SCREENLONG_YES;
+ return true;
+ } else if (strcmp(name, "notlong") == 0) {
+ if (out)
+ out->screenLayout =
+ (out->screenLayout & ~ResTable_config::MASK_SCREENLONG) |
+ ResTable_config::SCREENLONG_NO;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseScreenRound(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->screenLayout2 =
- (out->screenLayout2&~ResTable_config::MASK_SCREENROUND)
- | ResTable_config::SCREENROUND_ANY;
- return true;
- } else if (strcmp(name, "round") == 0) {
- if (out) out->screenLayout2 =
- (out->screenLayout2&~ResTable_config::MASK_SCREENROUND)
- | ResTable_config::SCREENROUND_YES;
- return true;
- } else if (strcmp(name, "notround") == 0) {
- if (out) out->screenLayout2 =
- (out->screenLayout2&~ResTable_config::MASK_SCREENROUND)
- | ResTable_config::SCREENROUND_NO;
- return true;
- }
- return false;
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out)
+ out->screenLayout2 =
+ (out->screenLayout2 & ~ResTable_config::MASK_SCREENROUND) |
+ ResTable_config::SCREENROUND_ANY;
+ return true;
+ } else if (strcmp(name, "round") == 0) {
+ if (out)
+ out->screenLayout2 =
+ (out->screenLayout2 & ~ResTable_config::MASK_SCREENROUND) |
+ ResTable_config::SCREENROUND_YES;
+ return true;
+ } else if (strcmp(name, "notround") == 0) {
+ if (out)
+ out->screenLayout2 =
+ (out->screenLayout2 & ~ResTable_config::MASK_SCREENROUND) |
+ ResTable_config::SCREENROUND_NO;
+ return true;
+ }
+ return false;
}
static bool parseOrientation(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->orientation = out->ORIENTATION_ANY;
- return true;
- } else if (strcmp(name, "port") == 0) {
- if (out) out->orientation = out->ORIENTATION_PORT;
- return true;
- } else if (strcmp(name, "land") == 0) {
- if (out) out->orientation = out->ORIENTATION_LAND;
- return true;
- } else if (strcmp(name, "square") == 0) {
- if (out) out->orientation = out->ORIENTATION_SQUARE;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->orientation = out->ORIENTATION_ANY;
+ return true;
+ } else if (strcmp(name, "port") == 0) {
+ if (out) out->orientation = out->ORIENTATION_PORT;
+ return true;
+ } else if (strcmp(name, "land") == 0) {
+ if (out) out->orientation = out->ORIENTATION_LAND;
+ return true;
+ } else if (strcmp(name, "square") == 0) {
+ if (out) out->orientation = out->ORIENTATION_SQUARE;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseUiModeType(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_ANY;
- return true;
- } else if (strcmp(name, "desk") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_DESK;
- return true;
- } else if (strcmp(name, "car") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_CAR;
- return true;
- } else if (strcmp(name, "television") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_TELEVISION;
- return true;
- } else if (strcmp(name, "appliance") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_APPLIANCE;
- return true;
- } else if (strcmp(name, "watch") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_WATCH;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out)
+ out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
+ ResTable_config::UI_MODE_TYPE_ANY;
+ return true;
+ } else if (strcmp(name, "desk") == 0) {
+ if (out)
+ out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
+ ResTable_config::UI_MODE_TYPE_DESK;
+ return true;
+ } else if (strcmp(name, "car") == 0) {
+ if (out)
+ out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
+ ResTable_config::UI_MODE_TYPE_CAR;
+ return true;
+ } else if (strcmp(name, "television") == 0) {
+ if (out)
+ out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
+ ResTable_config::UI_MODE_TYPE_TELEVISION;
+ return true;
+ } else if (strcmp(name, "appliance") == 0) {
+ if (out)
+ out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
+ ResTable_config::UI_MODE_TYPE_APPLIANCE;
+ return true;
+ } else if (strcmp(name, "watch") == 0) {
+ if (out)
+ out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_TYPE) |
+ ResTable_config::UI_MODE_TYPE_WATCH;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseUiModeNight(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
- | ResTable_config::UI_MODE_NIGHT_ANY;
- return true;
- } else if (strcmp(name, "night") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
- | ResTable_config::UI_MODE_NIGHT_YES;
- return true;
- } else if (strcmp(name, "notnight") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
- | ResTable_config::UI_MODE_NIGHT_NO;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out)
+ out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_NIGHT) |
+ ResTable_config::UI_MODE_NIGHT_ANY;
+ return true;
+ } else if (strcmp(name, "night") == 0) {
+ if (out)
+ out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_NIGHT) |
+ ResTable_config::UI_MODE_NIGHT_YES;
+ return true;
+ } else if (strcmp(name, "notnight") == 0) {
+ if (out)
+ out->uiMode = (out->uiMode & ~ResTable_config::MASK_UI_MODE_NIGHT) |
+ ResTable_config::UI_MODE_NIGHT_NO;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseDensity(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->density = ResTable_config::DENSITY_DEFAULT;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->density = ResTable_config::DENSITY_DEFAULT;
+ return true;
+ }
- if (strcmp(name, "anydpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_ANY;
- return true;
- }
+ if (strcmp(name, "anydpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_ANY;
+ return true;
+ }
- if (strcmp(name, "nodpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_NONE;
- return true;
- }
+ if (strcmp(name, "nodpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_NONE;
+ return true;
+ }
- if (strcmp(name, "ldpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_LOW;
- return true;
- }
+ if (strcmp(name, "ldpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_LOW;
+ return true;
+ }
- if (strcmp(name, "mdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_MEDIUM;
- return true;
- }
+ if (strcmp(name, "mdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_MEDIUM;
+ return true;
+ }
- if (strcmp(name, "tvdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_TV;
- return true;
- }
+ if (strcmp(name, "tvdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_TV;
+ return true;
+ }
- if (strcmp(name, "hdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_HIGH;
- return true;
- }
+ if (strcmp(name, "hdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_HIGH;
+ return true;
+ }
- if (strcmp(name, "xhdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_XHIGH;
- return true;
- }
+ if (strcmp(name, "xhdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_XHIGH;
+ return true;
+ }
- if (strcmp(name, "xxhdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_XXHIGH;
- return true;
- }
+ if (strcmp(name, "xxhdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_XXHIGH;
+ return true;
+ }
- if (strcmp(name, "xxxhdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
- return true;
- }
+ if (strcmp(name, "xxxhdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
+ return true;
+ }
- char* c = (char*)name;
- while (*c >= '0' && *c <= '9') {
- c++;
- }
+ char* c = (char*)name;
+ while (*c >= '0' && *c <= '9') {
+ c++;
+ }
- // check that we have 'dpi' after the last digit.
- if (toupper(c[0]) != 'D' ||
- toupper(c[1]) != 'P' ||
- toupper(c[2]) != 'I' ||
- c[3] != 0) {
- return false;
- }
-
- // temporarily replace the first letter with \0 to
- // use atoi.
- char tmp = c[0];
- c[0] = '\0';
-
- int d = atoi(name);
- c[0] = tmp;
-
- if (d != 0) {
- if (out) out->density = d;
- return true;
- }
-
+ // check that we have 'dpi' after the last digit.
+ if (toupper(c[0]) != 'D' || toupper(c[1]) != 'P' || toupper(c[2]) != 'I' ||
+ c[3] != 0) {
return false;
+ }
+
+ // temporarily replace the first letter with \0 to
+ // use atoi.
+ char tmp = c[0];
+ c[0] = '\0';
+
+ int d = atoi(name);
+ c[0] = tmp;
+
+ if (d != 0) {
+ if (out) out->density = d;
+ return true;
+ }
+
+ return false;
}
static bool parseTouchscreen(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
- return true;
- } else if (strcmp(name, "notouch") == 0) {
- if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
- return true;
- } else if (strcmp(name, "stylus") == 0) {
- if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
- return true;
- } else if (strcmp(name, "finger") == 0) {
- if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
+ return true;
+ } else if (strcmp(name, "notouch") == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
+ return true;
+ } else if (strcmp(name, "stylus") == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
+ return true;
+ } else if (strcmp(name, "finger") == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseKeysHidden(const char* name, ResTable_config* out) {
- uint8_t mask = 0;
- uint8_t value = 0;
- if (strcmp(name, kWildcardName) == 0) {
- mask = ResTable_config::MASK_KEYSHIDDEN;
- value = ResTable_config::KEYSHIDDEN_ANY;
- } else if (strcmp(name, "keysexposed") == 0) {
- mask = ResTable_config::MASK_KEYSHIDDEN;
- value = ResTable_config::KEYSHIDDEN_NO;
- } else if (strcmp(name, "keyshidden") == 0) {
- mask = ResTable_config::MASK_KEYSHIDDEN;
- value = ResTable_config::KEYSHIDDEN_YES;
- } else if (strcmp(name, "keyssoft") == 0) {
- mask = ResTable_config::MASK_KEYSHIDDEN;
- value = ResTable_config::KEYSHIDDEN_SOFT;
- }
+ uint8_t mask = 0;
+ uint8_t value = 0;
+ if (strcmp(name, kWildcardName) == 0) {
+ mask = ResTable_config::MASK_KEYSHIDDEN;
+ value = ResTable_config::KEYSHIDDEN_ANY;
+ } else if (strcmp(name, "keysexposed") == 0) {
+ mask = ResTable_config::MASK_KEYSHIDDEN;
+ value = ResTable_config::KEYSHIDDEN_NO;
+ } else if (strcmp(name, "keyshidden") == 0) {
+ mask = ResTable_config::MASK_KEYSHIDDEN;
+ value = ResTable_config::KEYSHIDDEN_YES;
+ } else if (strcmp(name, "keyssoft") == 0) {
+ mask = ResTable_config::MASK_KEYSHIDDEN;
+ value = ResTable_config::KEYSHIDDEN_SOFT;
+ }
- if (mask != 0) {
- if (out) out->inputFlags = (out->inputFlags&~mask) | value;
- return true;
- }
+ if (mask != 0) {
+ if (out) out->inputFlags = (out->inputFlags & ~mask) | value;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseKeyboard(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->keyboard = out->KEYBOARD_ANY;
- return true;
- } else if (strcmp(name, "nokeys") == 0) {
- if (out) out->keyboard = out->KEYBOARD_NOKEYS;
- return true;
- } else if (strcmp(name, "qwerty") == 0) {
- if (out) out->keyboard = out->KEYBOARD_QWERTY;
- return true;
- } else if (strcmp(name, "12key") == 0) {
- if (out) out->keyboard = out->KEYBOARD_12KEY;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->keyboard = out->KEYBOARD_ANY;
+ return true;
+ } else if (strcmp(name, "nokeys") == 0) {
+ if (out) out->keyboard = out->KEYBOARD_NOKEYS;
+ return true;
+ } else if (strcmp(name, "qwerty") == 0) {
+ if (out) out->keyboard = out->KEYBOARD_QWERTY;
+ return true;
+ } else if (strcmp(name, "12key") == 0) {
+ if (out) out->keyboard = out->KEYBOARD_12KEY;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseNavHidden(const char* name, ResTable_config* out) {
- uint8_t mask = 0;
- uint8_t value = 0;
- if (strcmp(name, kWildcardName) == 0) {
- mask = ResTable_config::MASK_NAVHIDDEN;
- value = ResTable_config::NAVHIDDEN_ANY;
- } else if (strcmp(name, "navexposed") == 0) {
- mask = ResTable_config::MASK_NAVHIDDEN;
- value = ResTable_config::NAVHIDDEN_NO;
- } else if (strcmp(name, "navhidden") == 0) {
- mask = ResTable_config::MASK_NAVHIDDEN;
- value = ResTable_config::NAVHIDDEN_YES;
- }
+ uint8_t mask = 0;
+ uint8_t value = 0;
+ if (strcmp(name, kWildcardName) == 0) {
+ mask = ResTable_config::MASK_NAVHIDDEN;
+ value = ResTable_config::NAVHIDDEN_ANY;
+ } else if (strcmp(name, "navexposed") == 0) {
+ mask = ResTable_config::MASK_NAVHIDDEN;
+ value = ResTable_config::NAVHIDDEN_NO;
+ } else if (strcmp(name, "navhidden") == 0) {
+ mask = ResTable_config::MASK_NAVHIDDEN;
+ value = ResTable_config::NAVHIDDEN_YES;
+ }
- if (mask != 0) {
- if (out) out->inputFlags = (out->inputFlags&~mask) | value;
- return true;
- }
+ if (mask != 0) {
+ if (out) out->inputFlags = (out->inputFlags & ~mask) | value;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseNavigation(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->navigation = out->NAVIGATION_ANY;
- return true;
- } else if (strcmp(name, "nonav") == 0) {
- if (out) out->navigation = out->NAVIGATION_NONAV;
- return true;
- } else if (strcmp(name, "dpad") == 0) {
- if (out) out->navigation = out->NAVIGATION_DPAD;
- return true;
- } else if (strcmp(name, "trackball") == 0) {
- if (out) out->navigation = out->NAVIGATION_TRACKBALL;
- return true;
- } else if (strcmp(name, "wheel") == 0) {
- if (out) out->navigation = out->NAVIGATION_WHEEL;
- return true;
- }
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->navigation = out->NAVIGATION_ANY;
+ return true;
+ } else if (strcmp(name, "nonav") == 0) {
+ if (out) out->navigation = out->NAVIGATION_NONAV;
+ return true;
+ } else if (strcmp(name, "dpad") == 0) {
+ if (out) out->navigation = out->NAVIGATION_DPAD;
+ return true;
+ } else if (strcmp(name, "trackball") == 0) {
+ if (out) out->navigation = out->NAVIGATION_TRACKBALL;
+ return true;
+ } else if (strcmp(name, "wheel") == 0) {
+ if (out) out->navigation = out->NAVIGATION_WHEEL;
+ return true;
+ }
- return false;
+ return false;
}
static bool parseScreenSize(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->screenWidth = out->SCREENWIDTH_ANY;
- out->screenHeight = out->SCREENHEIGHT_ANY;
- }
- return true;
- }
-
- const char* x = name;
- while (*x >= '0' && *x <= '9') x++;
- if (x == name || *x != 'x') return false;
- std::string xName(name, x-name);
- x++;
-
- const char* y = x;
- while (*y >= '0' && *y <= '9') y++;
- if (y == name || *y != 0) return false;
- std::string yName(x, y-x);
-
- uint16_t w = (uint16_t)atoi(xName.c_str());
- uint16_t h = (uint16_t)atoi(yName.c_str());
- if (w < h) {
- return false;
- }
-
+ if (strcmp(name, kWildcardName) == 0) {
if (out) {
- out->screenWidth = w;
- out->screenHeight = h;
+ out->screenWidth = out->SCREENWIDTH_ANY;
+ out->screenHeight = out->SCREENHEIGHT_ANY;
}
-
return true;
+ }
+
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || *x != 'x') return false;
+ std::string xName(name, x - name);
+ x++;
+
+ const char* y = x;
+ while (*y >= '0' && *y <= '9') y++;
+ if (y == name || *y != 0) return false;
+ std::string yName(x, y - x);
+
+ uint16_t w = (uint16_t)atoi(xName.c_str());
+ uint16_t h = (uint16_t)atoi(yName.c_str());
+ if (w < h) {
+ return false;
+ }
+
+ if (out) {
+ out->screenWidth = w;
+ out->screenHeight = h;
+ }
+
+ return true;
}
static bool parseSmallestScreenWidthDp(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
- }
- return true;
- }
-
- if (*name != 's') return false;
- name++;
- if (*name != 'w') return false;
- name++;
- const char* x = name;
- while (*x >= '0' && *x <= '9') x++;
- if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
- std::string xName(name, x-name);
-
+ if (strcmp(name, kWildcardName) == 0) {
if (out) {
- out->smallestScreenWidthDp = (uint16_t)atoi(xName.c_str());
+ out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
}
-
return true;
+ }
+
+ if (*name != 's') return false;
+ name++;
+ if (*name != 'w') return false;
+ name++;
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+ std::string xName(name, x - name);
+
+ if (out) {
+ out->smallestScreenWidthDp = (uint16_t)atoi(xName.c_str());
+ }
+
+ return true;
}
static bool parseScreenWidthDp(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->screenWidthDp = out->SCREENWIDTH_ANY;
- }
- return true;
- }
-
- if (*name != 'w') return false;
- name++;
- const char* x = name;
- while (*x >= '0' && *x <= '9') x++;
- if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
- std::string xName(name, x-name);
-
+ if (strcmp(name, kWildcardName) == 0) {
if (out) {
- out->screenWidthDp = (uint16_t)atoi(xName.c_str());
+ out->screenWidthDp = out->SCREENWIDTH_ANY;
}
-
return true;
+ }
+
+ if (*name != 'w') return false;
+ name++;
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+ std::string xName(name, x - name);
+
+ if (out) {
+ out->screenWidthDp = (uint16_t)atoi(xName.c_str());
+ }
+
+ return true;
}
static bool parseScreenHeightDp(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->screenHeightDp = out->SCREENWIDTH_ANY;
- }
- return true;
- }
-
- if (*name != 'h') return false;
- name++;
- const char* x = name;
- while (*x >= '0' && *x <= '9') x++;
- if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
- std::string xName(name, x-name);
-
+ if (strcmp(name, kWildcardName) == 0) {
if (out) {
- out->screenHeightDp = (uint16_t)atoi(xName.c_str());
+ out->screenHeightDp = out->SCREENWIDTH_ANY;
}
-
return true;
+ }
+
+ if (*name != 'h') return false;
+ name++;
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+ std::string xName(name, x - name);
+
+ if (out) {
+ out->screenHeightDp = (uint16_t)atoi(xName.c_str());
+ }
+
+ return true;
}
static bool parseVersion(const char* name, ResTable_config* out) {
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->sdkVersion = out->SDKVERSION_ANY;
- out->minorVersion = out->MINORVERSION_ANY;
- }
- return true;
- }
-
- if (*name != 'v') {
- return false;
- }
-
- name++;
- const char* s = name;
- while (*s >= '0' && *s <= '9') s++;
- if (s == name || *s != 0) return false;
- std::string sdkName(name, s-name);
-
+ if (strcmp(name, kWildcardName) == 0) {
if (out) {
- out->sdkVersion = (uint16_t)atoi(sdkName.c_str());
- out->minorVersion = 0;
+ out->sdkVersion = out->SDKVERSION_ANY;
+ out->minorVersion = out->MINORVERSION_ANY;
}
-
return true;
+ }
+
+ if (*name != 'v') {
+ return false;
+ }
+
+ name++;
+ const char* s = name;
+ while (*s >= '0' && *s <= '9') s++;
+ if (s == name || *s != 0) return false;
+ std::string sdkName(name, s - name);
+
+ if (out) {
+ out->sdkVersion = (uint16_t)atoi(sdkName.c_str());
+ out->minorVersion = 0;
+ }
+
+ return true;
}
bool ConfigDescription::parse(const StringPiece& str, ConfigDescription* out) {
- std::vector<std::string> parts = util::splitAndLowercase(str, '-');
+ std::vector<std::string> parts = util::splitAndLowercase(str, '-');
- ConfigDescription config;
- ssize_t partsConsumed = 0;
- LocaleValue locale;
+ ConfigDescription config;
+ ssize_t partsConsumed = 0;
+ LocaleValue locale;
- const auto partsEnd = parts.end();
- auto partIter = parts.begin();
+ const auto partsEnd = parts.end();
+ auto partIter = parts.begin();
- if (str.size() == 0) {
- goto success;
+ if (str.size() == 0) {
+ goto success;
+ }
+
+ if (parseMcc(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
}
+ }
- if (parseMcc(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
+ if (parseMnc(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
}
+ }
- if (parseMnc(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- // Locale spans a few '-' separators, so we let it
- // control the index.
- partsConsumed = locale.initFromParts(partIter, partsEnd);
- if (partsConsumed < 0) {
- return false;
- } else {
- locale.writeTo(&config);
- partIter += partsConsumed;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseLayoutDirection(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseSmallestScreenWidthDp(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseScreenWidthDp(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseScreenHeightDp(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseScreenLayoutSize(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseScreenLayoutLong(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseScreenRound(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseOrientation(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseUiModeType(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseUiModeNight(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseDensity(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseTouchscreen(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseKeysHidden(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseKeyboard(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseNavHidden(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseNavigation(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseScreenSize(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- if (parseVersion(partIter->c_str(), &config)) {
- ++partIter;
- if (partIter == partsEnd) {
- goto success;
- }
- }
-
- // Unrecognized.
+ // Locale spans a few '-' separators, so we let it
+ // control the index.
+ partsConsumed = locale.initFromParts(partIter, partsEnd);
+ if (partsConsumed < 0) {
return false;
+ } else {
+ locale.writeTo(&config);
+ partIter += partsConsumed;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
+ if (parseLayoutDirection(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
+ if (parseSmallestScreenWidthDp(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
+ if (parseScreenWidthDp(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
+ if (parseScreenHeightDp(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
+ if (parseScreenLayoutSize(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
+ if (parseScreenLayoutLong(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
+ if (parseScreenRound(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
+ if (parseOrientation(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
+ if (parseUiModeType(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
+ if (parseUiModeNight(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
+ if (parseDensity(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
+ if (parseTouchscreen(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
+ if (parseKeysHidden(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
+ if (parseKeyboard(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
+ if (parseNavHidden(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
+ if (parseNavigation(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
+ if (parseScreenSize(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
+ if (parseVersion(partIter->c_str(), &config)) {
+ ++partIter;
+ if (partIter == partsEnd) {
+ goto success;
+ }
+ }
+
+ // Unrecognized.
+ return false;
success:
- if (out != NULL) {
- applyVersionForCompatibility(&config);
- *out = config;
- }
- return true;
+ if (out != NULL) {
+ applyVersionForCompatibility(&config);
+ *out = config;
+ }
+ return true;
}
-void ConfigDescription::applyVersionForCompatibility(ConfigDescription* config) {
- uint16_t minSdk = 0;
- if (config->screenLayout2 & ResTable_config::MASK_SCREENROUND) {
- minSdk = SDK_MARSHMALLOW;
- } else if (config->density == ResTable_config::DENSITY_ANY) {
- minSdk = SDK_LOLLIPOP;
- } else if (config->smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
- || config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY
- || config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
- minSdk = SDK_HONEYCOMB_MR2;
- } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE)
- != ResTable_config::UI_MODE_TYPE_ANY
- || (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT)
- != ResTable_config::UI_MODE_NIGHT_ANY) {
- minSdk = SDK_FROYO;
- } else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE)
- != ResTable_config::SCREENSIZE_ANY
- || (config->screenLayout & ResTable_config::MASK_SCREENLONG)
- != ResTable_config::SCREENLONG_ANY
- || config->density != ResTable_config::DENSITY_DEFAULT) {
- minSdk = SDK_DONUT;
- }
+void ConfigDescription::applyVersionForCompatibility(
+ ConfigDescription* config) {
+ uint16_t minSdk = 0;
+ if (config->screenLayout2 & ResTable_config::MASK_SCREENROUND) {
+ minSdk = SDK_MARSHMALLOW;
+ } else if (config->density == ResTable_config::DENSITY_ANY) {
+ minSdk = SDK_LOLLIPOP;
+ } else if (config->smallestScreenWidthDp !=
+ ResTable_config::SCREENWIDTH_ANY ||
+ config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY ||
+ config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
+ minSdk = SDK_HONEYCOMB_MR2;
+ } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE) !=
+ ResTable_config::UI_MODE_TYPE_ANY ||
+ (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT) !=
+ ResTable_config::UI_MODE_NIGHT_ANY) {
+ minSdk = SDK_FROYO;
+ } else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE) !=
+ ResTable_config::SCREENSIZE_ANY ||
+ (config->screenLayout & ResTable_config::MASK_SCREENLONG) !=
+ ResTable_config::SCREENLONG_ANY ||
+ config->density != ResTable_config::DENSITY_DEFAULT) {
+ minSdk = SDK_DONUT;
+ }
- if (minSdk > config->sdkVersion) {
- config->sdkVersion = minSdk;
- }
+ if (minSdk > config->sdkVersion) {
+ config->sdkVersion = minSdk;
+ }
}
ConfigDescription ConfigDescription::copyWithoutSdkVersion() const {
- ConfigDescription copy = *this;
- copy.sdkVersion = 0;
- return copy;
+ ConfigDescription copy = *this;
+ copy.sdkVersion = 0;
+ return copy;
}
bool ConfigDescription::dominates(const ConfigDescription& o) const {
- if (*this == defaultConfig() || *this == o) {
- return true;
- }
- return matchWithDensity(o)
- && !o.matchWithDensity(*this)
- && !isMoreSpecificThan(o)
- && !o.hasHigherPrecedenceThan(*this);
+ if (*this == defaultConfig() || *this == o) {
+ return true;
+ }
+ return matchWithDensity(o) && !o.matchWithDensity(*this) &&
+ !isMoreSpecificThan(o) && !o.hasHigherPrecedenceThan(*this);
}
-bool ConfigDescription::hasHigherPrecedenceThan(const ConfigDescription& o) const {
- // The order of the following tests defines the importance of one
- // configuration parameter over another. Those tests first are more
- // important, trumping any values in those following them.
- // The ordering should be the same as ResTable_config#isBetterThan.
- if (mcc || o.mcc) return (!o.mcc);
- if (mnc || o.mnc) return (!o.mnc);
- if (language[0] || o.language[0]) return (!o.language[0]);
- if (country[0] || o.country[0]) return (!o.country[0]);
- // Script and variant require either a language or country, both of which
- // have higher precedence.
- if ((screenLayout | o.screenLayout) & MASK_LAYOUTDIR) {
- return !(o.screenLayout & MASK_LAYOUTDIR);
- }
- if (smallestScreenWidthDp || o.smallestScreenWidthDp) return (!o.smallestScreenWidthDp);
- if (screenWidthDp || o.screenWidthDp) return (!o.screenWidthDp);
- if (screenHeightDp || o.screenHeightDp) return (!o.screenHeightDp);
- if ((screenLayout | o.screenLayout) & MASK_SCREENSIZE) {
- return !(o.screenLayout & MASK_SCREENSIZE);
- }
- if ((screenLayout | o.screenLayout) & MASK_SCREENLONG) {
- return !(o.screenLayout & MASK_SCREENLONG);
- }
- if ((screenLayout2 | o.screenLayout2) & MASK_SCREENROUND) {
- return !(o.screenLayout2 & MASK_SCREENROUND);
- }
- if (orientation || o.orientation) return (!o.orientation);
- if ((uiMode | o.uiMode) & MASK_UI_MODE_TYPE) {
- return !(o.uiMode & MASK_UI_MODE_TYPE);
- }
- if ((uiMode | o.uiMode) & MASK_UI_MODE_NIGHT) {
- return !(o.uiMode & MASK_UI_MODE_NIGHT);
- }
- if (density || o.density) return (!o.density);
- if (touchscreen || o.touchscreen) return (!o.touchscreen);
- if ((inputFlags | o.inputFlags) & MASK_KEYSHIDDEN) {
- return !(o.inputFlags & MASK_KEYSHIDDEN);
- }
- if ((inputFlags | o.inputFlags) & MASK_NAVHIDDEN) {
- return !(o.inputFlags & MASK_NAVHIDDEN);
- }
- if (keyboard || o.keyboard) return (!o.keyboard);
- if (navigation || o.navigation) return (!o.navigation);
- if (screenWidth || o.screenWidth) return (!o.screenWidth);
- if (screenHeight || o.screenHeight) return (!o.screenHeight);
- if (sdkVersion || o.sdkVersion) return (!o.sdkVersion);
- if (minorVersion || o.minorVersion) return (!o.minorVersion);
- // Both configurations have nothing defined except some possible future
- // value. Returning the comparison of the two configurations is a
- // "best effort" at this point to protect against incorrect dominations.
- return *this != o;
+bool ConfigDescription::hasHigherPrecedenceThan(
+ const ConfigDescription& o) const {
+ // The order of the following tests defines the importance of one
+ // configuration parameter over another. Those tests first are more
+ // important, trumping any values in those following them.
+ // The ordering should be the same as ResTable_config#isBetterThan.
+ if (mcc || o.mcc) return (!o.mcc);
+ if (mnc || o.mnc) return (!o.mnc);
+ if (language[0] || o.language[0]) return (!o.language[0]);
+ if (country[0] || o.country[0]) return (!o.country[0]);
+ // Script and variant require either a language or country, both of which
+ // have higher precedence.
+ if ((screenLayout | o.screenLayout) & MASK_LAYOUTDIR) {
+ return !(o.screenLayout & MASK_LAYOUTDIR);
+ }
+ if (smallestScreenWidthDp || o.smallestScreenWidthDp)
+ return (!o.smallestScreenWidthDp);
+ if (screenWidthDp || o.screenWidthDp) return (!o.screenWidthDp);
+ if (screenHeightDp || o.screenHeightDp) return (!o.screenHeightDp);
+ if ((screenLayout | o.screenLayout) & MASK_SCREENSIZE) {
+ return !(o.screenLayout & MASK_SCREENSIZE);
+ }
+ if ((screenLayout | o.screenLayout) & MASK_SCREENLONG) {
+ return !(o.screenLayout & MASK_SCREENLONG);
+ }
+ if ((screenLayout2 | o.screenLayout2) & MASK_SCREENROUND) {
+ return !(o.screenLayout2 & MASK_SCREENROUND);
+ }
+ if (orientation || o.orientation) return (!o.orientation);
+ if ((uiMode | o.uiMode) & MASK_UI_MODE_TYPE) {
+ return !(o.uiMode & MASK_UI_MODE_TYPE);
+ }
+ if ((uiMode | o.uiMode) & MASK_UI_MODE_NIGHT) {
+ return !(o.uiMode & MASK_UI_MODE_NIGHT);
+ }
+ if (density || o.density) return (!o.density);
+ if (touchscreen || o.touchscreen) return (!o.touchscreen);
+ if ((inputFlags | o.inputFlags) & MASK_KEYSHIDDEN) {
+ return !(o.inputFlags & MASK_KEYSHIDDEN);
+ }
+ if ((inputFlags | o.inputFlags) & MASK_NAVHIDDEN) {
+ return !(o.inputFlags & MASK_NAVHIDDEN);
+ }
+ if (keyboard || o.keyboard) return (!o.keyboard);
+ if (navigation || o.navigation) return (!o.navigation);
+ if (screenWidth || o.screenWidth) return (!o.screenWidth);
+ if (screenHeight || o.screenHeight) return (!o.screenHeight);
+ if (sdkVersion || o.sdkVersion) return (!o.sdkVersion);
+ if (minorVersion || o.minorVersion) return (!o.minorVersion);
+ // Both configurations have nothing defined except some possible future
+ // value. Returning the comparison of the two configurations is a
+ // "best effort" at this point to protect against incorrect dominations.
+ return *this != o;
}
bool ConfigDescription::conflictsWith(const ConfigDescription& o) const {
- // This method should be updated as new configuration parameters are
- // introduced (e.g. screenConfig2).
- auto pred = [](const uint32_t a, const uint32_t b) -> bool {
- return a == 0 || b == 0 || a == b;
- };
- // The values here can be found in ResTable_config#match. Density and range
- // values can't lead to conflicts, and are ignored.
- return !pred(mcc, o.mcc)
- || !pred(mnc, o.mnc)
- || !pred(locale, o.locale)
- || !pred(screenLayout & MASK_LAYOUTDIR, o.screenLayout & MASK_LAYOUTDIR)
- || !pred(screenLayout & MASK_SCREENLONG, o.screenLayout & MASK_SCREENLONG)
- || !pred(screenLayout & MASK_UI_MODE_TYPE, o.screenLayout & MASK_UI_MODE_TYPE)
- || !pred(uiMode & MASK_UI_MODE_TYPE, o.uiMode & MASK_UI_MODE_TYPE)
- || !pred(uiMode & MASK_UI_MODE_NIGHT, o.uiMode & MASK_UI_MODE_NIGHT)
- || !pred(screenLayout2 & MASK_SCREENROUND, o.screenLayout2 & MASK_SCREENROUND)
- || !pred(orientation, o.orientation)
- || !pred(touchscreen, o.touchscreen)
- || !pred(inputFlags & MASK_KEYSHIDDEN, o.inputFlags & MASK_KEYSHIDDEN)
- || !pred(inputFlags & MASK_NAVHIDDEN, o.inputFlags & MASK_NAVHIDDEN)
- || !pred(keyboard, o.keyboard)
- || !pred(navigation, o.navigation);
+ // This method should be updated as new configuration parameters are
+ // introduced (e.g. screenConfig2).
+ auto pred = [](const uint32_t a, const uint32_t b) -> bool {
+ return a == 0 || b == 0 || a == b;
+ };
+ // The values here can be found in ResTable_config#match. Density and range
+ // values can't lead to conflicts, and are ignored.
+ return !pred(mcc, o.mcc) || !pred(mnc, o.mnc) || !pred(locale, o.locale) ||
+ !pred(screenLayout & MASK_LAYOUTDIR,
+ o.screenLayout & MASK_LAYOUTDIR) ||
+ !pred(screenLayout & MASK_SCREENLONG,
+ o.screenLayout & MASK_SCREENLONG) ||
+ !pred(screenLayout & MASK_UI_MODE_TYPE,
+ o.screenLayout & MASK_UI_MODE_TYPE) ||
+ !pred(uiMode & MASK_UI_MODE_TYPE, o.uiMode & MASK_UI_MODE_TYPE) ||
+ !pred(uiMode & MASK_UI_MODE_NIGHT, o.uiMode & MASK_UI_MODE_NIGHT) ||
+ !pred(screenLayout2 & MASK_SCREENROUND,
+ o.screenLayout2 & MASK_SCREENROUND) ||
+ !pred(orientation, o.orientation) ||
+ !pred(touchscreen, o.touchscreen) ||
+ !pred(inputFlags & MASK_KEYSHIDDEN, o.inputFlags & MASK_KEYSHIDDEN) ||
+ !pred(inputFlags & MASK_NAVHIDDEN, o.inputFlags & MASK_NAVHIDDEN) ||
+ !pred(keyboard, o.keyboard) || !pred(navigation, o.navigation);
}
bool ConfigDescription::isCompatibleWith(const ConfigDescription& o) const {
- return !conflictsWith(o) && !dominates(o) && !o.dominates(*this);
+ return !conflictsWith(o) && !dominates(o) && !o.dominates(*this);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h
index d801621..bb54886 100644
--- a/tools/aapt2/ConfigDescription.h
+++ b/tools/aapt2/ConfigDescription.h
@@ -29,148 +29,152 @@
* initialization and comparison methods.
*/
struct ConfigDescription : public android::ResTable_config {
- /**
- * Returns an immutable default config.
- */
- static const ConfigDescription& defaultConfig();
+ /**
+ * Returns an immutable default config.
+ */
+ static const ConfigDescription& defaultConfig();
- /*
- * Parse a string of the form 'fr-sw600dp-land' and fill in the
- * given ResTable_config with resulting configuration parameters.
- *
- * The resulting configuration has the appropriate sdkVersion defined
- * for backwards compatibility.
- */
- static bool parse(const StringPiece& str, ConfigDescription* out = nullptr);
+ /*
+ * Parse a string of the form 'fr-sw600dp-land' and fill in the
+ * given ResTable_config with resulting configuration parameters.
+ *
+ * The resulting configuration has the appropriate sdkVersion defined
+ * for backwards compatibility.
+ */
+ static bool parse(const StringPiece& str, ConfigDescription* out = nullptr);
- /**
- * If the configuration uses an axis that was added after
- * the original Android release, make sure the SDK version
- * is set accordingly.
- */
- static void applyVersionForCompatibility(ConfigDescription* config);
+ /**
+ * If the configuration uses an axis that was added after
+ * the original Android release, make sure the SDK version
+ * is set accordingly.
+ */
+ static void applyVersionForCompatibility(ConfigDescription* config);
- ConfigDescription();
- ConfigDescription(const android::ResTable_config& o); // NOLINT(implicit)
- ConfigDescription(const ConfigDescription& o);
- ConfigDescription(ConfigDescription&& o);
+ ConfigDescription();
+ ConfigDescription(const android::ResTable_config& o); // NOLINT(implicit)
+ ConfigDescription(const ConfigDescription& o);
+ ConfigDescription(ConfigDescription&& o);
- ConfigDescription& operator=(const android::ResTable_config& o);
- ConfigDescription& operator=(const ConfigDescription& o);
- ConfigDescription& operator=(ConfigDescription&& o);
+ ConfigDescription& operator=(const android::ResTable_config& o);
+ ConfigDescription& operator=(const ConfigDescription& o);
+ ConfigDescription& operator=(ConfigDescription&& o);
- ConfigDescription copyWithoutSdkVersion() const;
+ ConfigDescription copyWithoutSdkVersion() const;
- /**
- * A configuration X dominates another configuration Y, if X has at least the
- * precedence of Y and X is strictly more general than Y: for any type defined
- * by X, the same type is defined by Y with a value equal to or, in the case
- * of ranges, more specific than that of X.
- *
- * For example, the configuration 'en-w800dp' dominates 'en-rGB-w1024dp'. It
- * does not dominate 'fr', 'en-w720dp', or 'mcc001-en-w800dp'.
- */
- bool dominates(const ConfigDescription& o) const;
+ /**
+ * A configuration X dominates another configuration Y, if X has at least the
+ * precedence of Y and X is strictly more general than Y: for any type defined
+ * by X, the same type is defined by Y with a value equal to or, in the case
+ * of ranges, more specific than that of X.
+ *
+ * For example, the configuration 'en-w800dp' dominates 'en-rGB-w1024dp'. It
+ * does not dominate 'fr', 'en-w720dp', or 'mcc001-en-w800dp'.
+ */
+ bool dominates(const ConfigDescription& o) const;
- /**
- * Returns true if this configuration defines a more important configuration
- * parameter than o. For example, "en" has higher precedence than "v23",
- * whereas "en" has the same precedence as "en-v23".
- */
- bool hasHigherPrecedenceThan(const ConfigDescription& o) const;
+ /**
+ * Returns true if this configuration defines a more important configuration
+ * parameter than o. For example, "en" has higher precedence than "v23",
+ * whereas "en" has the same precedence as "en-v23".
+ */
+ bool hasHigherPrecedenceThan(const ConfigDescription& o) const;
- /**
- * A configuration conflicts with another configuration if both
- * configurations define an incompatible configuration parameter. An
- * incompatible configuration parameter is a non-range, non-density parameter
- * that is defined in both configurations as a different, non-default value.
- */
- bool conflictsWith(const ConfigDescription& o) const;
+ /**
+ * A configuration conflicts with another configuration if both
+ * configurations define an incompatible configuration parameter. An
+ * incompatible configuration parameter is a non-range, non-density parameter
+ * that is defined in both configurations as a different, non-default value.
+ */
+ bool conflictsWith(const ConfigDescription& o) const;
- /**
- * A configuration is compatible with another configuration if both
- * configurations can match a common concrete device configuration and are
- * unrelated by domination. For example, land-v11 conflicts with port-v21
- * but is compatible with v21 (both land-v11 and v21 would match en-land-v23).
- */
- bool isCompatibleWith(const ConfigDescription& o) const;
+ /**
+ * A configuration is compatible with another configuration if both
+ * configurations can match a common concrete device configuration and are
+ * unrelated by domination. For example, land-v11 conflicts with port-v21
+ * but is compatible with v21 (both land-v11 and v21 would match en-land-v23).
+ */
+ bool isCompatibleWith(const ConfigDescription& o) const;
- bool matchWithDensity(const ConfigDescription& o) const;
+ bool matchWithDensity(const ConfigDescription& o) const;
- bool operator<(const ConfigDescription& o) const;
- bool operator<=(const ConfigDescription& o) const;
- bool operator==(const ConfigDescription& o) const;
- bool operator!=(const ConfigDescription& o) const;
- bool operator>=(const ConfigDescription& o) const;
- bool operator>(const ConfigDescription& o) const;
+ bool operator<(const ConfigDescription& o) const;
+ bool operator<=(const ConfigDescription& o) const;
+ bool operator==(const ConfigDescription& o) const;
+ bool operator!=(const ConfigDescription& o) const;
+ bool operator>=(const ConfigDescription& o) const;
+ bool operator>(const ConfigDescription& o) const;
};
inline ConfigDescription::ConfigDescription() {
- memset(this, 0, sizeof(*this));
- size = sizeof(android::ResTable_config);
+ memset(this, 0, sizeof(*this));
+ size = sizeof(android::ResTable_config);
}
inline ConfigDescription::ConfigDescription(const android::ResTable_config& o) {
- *static_cast<android::ResTable_config*>(this) = o;
- size = sizeof(android::ResTable_config);
+ *static_cast<android::ResTable_config*>(this) = o;
+ size = sizeof(android::ResTable_config);
}
inline ConfigDescription::ConfigDescription(const ConfigDescription& o) {
- *static_cast<android::ResTable_config*>(this) = o;
+ *static_cast<android::ResTable_config*>(this) = o;
}
inline ConfigDescription::ConfigDescription(ConfigDescription&& o) {
- *this = o;
+ *this = o;
}
-inline ConfigDescription& ConfigDescription::operator=(const android::ResTable_config& o) {
- *static_cast<android::ResTable_config*>(this) = o;
- size = sizeof(android::ResTable_config);
- return *this;
+inline ConfigDescription& ConfigDescription::operator=(
+ const android::ResTable_config& o) {
+ *static_cast<android::ResTable_config*>(this) = o;
+ size = sizeof(android::ResTable_config);
+ return *this;
}
-inline ConfigDescription& ConfigDescription::operator=(const ConfigDescription& o) {
- *static_cast<android::ResTable_config*>(this) = o;
- return *this;
+inline ConfigDescription& ConfigDescription::operator=(
+ const ConfigDescription& o) {
+ *static_cast<android::ResTable_config*>(this) = o;
+ return *this;
}
inline ConfigDescription& ConfigDescription::operator=(ConfigDescription&& o) {
- *this = o;
- return *this;
+ *this = o;
+ return *this;
}
-inline bool ConfigDescription::matchWithDensity(const ConfigDescription& o) const {
- return match(o) && (density == 0 || density == o.density);
+inline bool ConfigDescription::matchWithDensity(
+ const ConfigDescription& o) const {
+ return match(o) && (density == 0 || density == o.density);
}
inline bool ConfigDescription::operator<(const ConfigDescription& o) const {
- return compare(o) < 0;
+ return compare(o) < 0;
}
inline bool ConfigDescription::operator<=(const ConfigDescription& o) const {
- return compare(o) <= 0;
+ return compare(o) <= 0;
}
inline bool ConfigDescription::operator==(const ConfigDescription& o) const {
- return compare(o) == 0;
+ return compare(o) == 0;
}
inline bool ConfigDescription::operator!=(const ConfigDescription& o) const {
- return compare(o) != 0;
+ return compare(o) != 0;
}
inline bool ConfigDescription::operator>=(const ConfigDescription& o) const {
- return compare(o) >= 0;
+ return compare(o) >= 0;
}
inline bool ConfigDescription::operator>(const ConfigDescription& o) const {
- return compare(o) > 0;
+ return compare(o) > 0;
}
-inline ::std::ostream& operator<<(::std::ostream& out, const ConfigDescription& o) {
- return out << o.toString().string();
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const ConfigDescription& o) {
+ return out << o.toString().string();
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_CONFIG_DESCRIPTION_H
+#endif // AAPT_CONFIG_DESCRIPTION_H
diff --git a/tools/aapt2/ConfigDescription_test.cpp b/tools/aapt2/ConfigDescription_test.cpp
index 455a57f..66b7d47 100644
--- a/tools/aapt2/ConfigDescription_test.cpp
+++ b/tools/aapt2/ConfigDescription_test.cpp
@@ -23,76 +23,79 @@
namespace aapt {
-static ::testing::AssertionResult TestParse(const StringPiece& input,
- ConfigDescription* config = nullptr) {
- if (ConfigDescription::parse(input, config)) {
- return ::testing::AssertionSuccess() << input << " was successfully parsed";
- }
- return ::testing::AssertionFailure() << input << " could not be parsed";
+static ::testing::AssertionResult TestParse(
+ const StringPiece& input, ConfigDescription* config = nullptr) {
+ if (ConfigDescription::parse(input, config)) {
+ return ::testing::AssertionSuccess() << input << " was successfully parsed";
+ }
+ return ::testing::AssertionFailure() << input << " could not be parsed";
}
TEST(ConfigDescriptionTest, ParseFailWhenQualifiersAreOutOfOrder) {
- EXPECT_FALSE(TestParse("en-sw600dp-ldrtl"));
- EXPECT_FALSE(TestParse("land-en"));
- EXPECT_FALSE(TestParse("hdpi-320dpi"));
+ EXPECT_FALSE(TestParse("en-sw600dp-ldrtl"));
+ EXPECT_FALSE(TestParse("land-en"));
+ EXPECT_FALSE(TestParse("hdpi-320dpi"));
}
TEST(ConfigDescriptionTest, ParseFailWhenQualifiersAreNotMatched) {
- EXPECT_FALSE(TestParse("en-sw600dp-ILLEGAL"));
+ EXPECT_FALSE(TestParse("en-sw600dp-ILLEGAL"));
}
TEST(ConfigDescriptionTest, ParseFailWhenQualifiersHaveTrailingDash) {
- EXPECT_FALSE(TestParse("en-sw600dp-land-"));
+ EXPECT_FALSE(TestParse("en-sw600dp-land-"));
}
TEST(ConfigDescriptionTest, ParseBasicQualifiers) {
- ConfigDescription config;
- EXPECT_TRUE(TestParse("", &config));
- EXPECT_EQ(std::string(""), config.toString().string());
+ ConfigDescription config;
+ EXPECT_TRUE(TestParse("", &config));
+ EXPECT_EQ(std::string(""), config.toString().string());
- EXPECT_TRUE(TestParse("fr-land", &config));
- EXPECT_EQ(std::string("fr-land"), config.toString().string());
+ EXPECT_TRUE(TestParse("fr-land", &config));
+ EXPECT_EQ(std::string("fr-land"), config.toString().string());
- EXPECT_TRUE(TestParse("mcc310-pl-sw720dp-normal-long-port-night-"
- "xhdpi-keyssoft-qwerty-navexposed-nonav", &config));
- EXPECT_EQ(std::string("mcc310-pl-sw720dp-normal-long-port-night-"
- "xhdpi-keyssoft-qwerty-navexposed-nonav-v13"), config.toString().string());
+ EXPECT_TRUE(
+ TestParse("mcc310-pl-sw720dp-normal-long-port-night-"
+ "xhdpi-keyssoft-qwerty-navexposed-nonav",
+ &config));
+ EXPECT_EQ(std::string("mcc310-pl-sw720dp-normal-long-port-night-"
+ "xhdpi-keyssoft-qwerty-navexposed-nonav-v13"),
+ config.toString().string());
}
TEST(ConfigDescriptionTest, ParseLocales) {
- ConfigDescription config;
- EXPECT_TRUE(TestParse("en-rUS", &config));
- EXPECT_EQ(std::string("en-rUS"), config.toString().string());
+ ConfigDescription config;
+ EXPECT_TRUE(TestParse("en-rUS", &config));
+ EXPECT_EQ(std::string("en-rUS"), config.toString().string());
}
TEST(ConfigDescriptionTest, ParseQualifierAddedInApi13) {
- ConfigDescription config;
- EXPECT_TRUE(TestParse("sw600dp", &config));
- EXPECT_EQ(std::string("sw600dp-v13"), config.toString().string());
+ ConfigDescription config;
+ EXPECT_TRUE(TestParse("sw600dp", &config));
+ EXPECT_EQ(std::string("sw600dp-v13"), config.toString().string());
- EXPECT_TRUE(TestParse("sw600dp-v8", &config));
- EXPECT_EQ(std::string("sw600dp-v13"), config.toString().string());
+ EXPECT_TRUE(TestParse("sw600dp-v8", &config));
+ EXPECT_EQ(std::string("sw600dp-v13"), config.toString().string());
}
TEST(ConfigDescriptionTest, ParseCarAttribute) {
- ConfigDescription config;
- EXPECT_TRUE(TestParse("car", &config));
- EXPECT_EQ(android::ResTable_config::UI_MODE_TYPE_CAR, config.uiMode);
+ ConfigDescription config;
+ EXPECT_TRUE(TestParse("car", &config));
+ EXPECT_EQ(android::ResTable_config::UI_MODE_TYPE_CAR, config.uiMode);
}
TEST(ConfigDescriptionTest, TestParsingRoundQualifier) {
- ConfigDescription config;
- EXPECT_TRUE(TestParse("round", &config));
- EXPECT_EQ(android::ResTable_config::SCREENROUND_YES,
- config.screenLayout2 & android::ResTable_config::MASK_SCREENROUND);
- EXPECT_EQ(SDK_MARSHMALLOW, config.sdkVersion);
- EXPECT_EQ(std::string("round-v23"), config.toString().string());
+ ConfigDescription config;
+ EXPECT_TRUE(TestParse("round", &config));
+ EXPECT_EQ(android::ResTable_config::SCREENROUND_YES,
+ config.screenLayout2 & android::ResTable_config::MASK_SCREENROUND);
+ EXPECT_EQ(SDK_MARSHMALLOW, config.sdkVersion);
+ EXPECT_EQ(std::string("round-v23"), config.toString().string());
- EXPECT_TRUE(TestParse("notround", &config));
- EXPECT_EQ(android::ResTable_config::SCREENROUND_NO,
- config.screenLayout2 & android::ResTable_config::MASK_SCREENROUND);
- EXPECT_EQ(SDK_MARSHMALLOW, config.sdkVersion);
- EXPECT_EQ(std::string("notround-v23"), config.toString().string());
+ EXPECT_TRUE(TestParse("notround", &config));
+ EXPECT_EQ(android::ResTable_config::SCREENROUND_NO,
+ config.screenLayout2 & android::ResTable_config::MASK_SCREENROUND);
+ EXPECT_EQ(SDK_MARSHMALLOW, config.sdkVersion);
+ EXPECT_EQ(std::string("notround-v23"), config.toString().string());
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 304e571..965db9e 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -17,8 +17,8 @@
#include "Debug.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
-#include "util/Util.h"
#include "ValueVisitor.h"
+#include "util/Util.h"
#include <algorithm>
#include <iostream>
@@ -31,275 +31,279 @@
namespace aapt {
class PrintVisitor : public ValueVisitor {
-public:
- using ValueVisitor::visit;
+ public:
+ using ValueVisitor::visit;
- void visit(Attribute* attr) override {
- std::cout << "(attr) type=";
- attr->printMask(&std::cout);
- static constexpr uint32_t kMask = android::ResTable_map::TYPE_ENUM |
- android::ResTable_map::TYPE_FLAGS;
- if (attr->typeMask & kMask) {
- for (const auto& symbol : attr->symbols) {
- std::cout << "\n " << symbol.symbol.name.value().entry;
- if (symbol.symbol.id) {
- std::cout << " (" << symbol.symbol.id.value() << ")";
- }
- std::cout << " = " << symbol.value;
- }
+ void visit(Attribute* attr) override {
+ std::cout << "(attr) type=";
+ attr->printMask(&std::cout);
+ static constexpr uint32_t kMask =
+ android::ResTable_map::TYPE_ENUM | android::ResTable_map::TYPE_FLAGS;
+ if (attr->typeMask & kMask) {
+ for (const auto& symbol : attr->symbols) {
+ std::cout << "\n " << symbol.symbol.name.value().entry;
+ if (symbol.symbol.id) {
+ std::cout << " (" << symbol.symbol.id.value() << ")";
}
+ std::cout << " = " << symbol.value;
+ }
}
+ }
- void visit(Style* style) override {
- std::cout << "(style)";
- if (style->parent) {
- const Reference& parentRef = style->parent.value();
- std::cout << " parent=";
- if (parentRef.name) {
- if (parentRef.privateReference) {
- std::cout << "*";
- }
- std::cout << parentRef.name.value() << " ";
- }
-
- if (parentRef.id) {
- std::cout << parentRef.id.value();
- }
+ void visit(Style* style) override {
+ std::cout << "(style)";
+ if (style->parent) {
+ const Reference& parentRef = style->parent.value();
+ std::cout << " parent=";
+ if (parentRef.name) {
+ if (parentRef.privateReference) {
+ std::cout << "*";
}
+ std::cout << parentRef.name.value() << " ";
+ }
- for (const auto& entry : style->entries) {
- std::cout << "\n ";
- if (entry.key.name) {
- const ResourceName& name = entry.key.name.value();
- if (!name.package.empty()) {
- std::cout << name.package << ":";
- }
- std::cout << name.entry;
- }
+ if (parentRef.id) {
+ std::cout << parentRef.id.value();
+ }
+ }
- if (entry.key.id) {
- std::cout << "(" << entry.key.id.value() << ")";
- }
-
- std::cout << "=" << *entry.value;
+ for (const auto& entry : style->entries) {
+ std::cout << "\n ";
+ if (entry.key.name) {
+ const ResourceName& name = entry.key.name.value();
+ if (!name.package.empty()) {
+ std::cout << name.package << ":";
}
+ std::cout << name.entry;
+ }
+
+ if (entry.key.id) {
+ std::cout << "(" << entry.key.id.value() << ")";
+ }
+
+ std::cout << "=" << *entry.value;
}
+ }
- void visit(Array* array) override {
- array->print(&std::cout);
- }
+ void visit(Array* array) override { array->print(&std::cout); }
- void visit(Plural* plural) override {
- plural->print(&std::cout);
- }
+ void visit(Plural* plural) override { plural->print(&std::cout); }
- void visit(Styleable* styleable) override {
- std::cout << "(styleable)";
- for (const auto& attr : styleable->entries) {
- std::cout << "\n ";
- if (attr.name) {
- const ResourceName& name = attr.name.value();
- if (!name.package.empty()) {
- std::cout << name.package << ":";
- }
- std::cout << name.entry;
- }
-
- if (attr.id) {
- std::cout << "(" << attr.id.value() << ")";
- }
+ void visit(Styleable* styleable) override {
+ std::cout << "(styleable)";
+ for (const auto& attr : styleable->entries) {
+ std::cout << "\n ";
+ if (attr.name) {
+ const ResourceName& name = attr.name.value();
+ if (!name.package.empty()) {
+ std::cout << name.package << ":";
}
- }
+ std::cout << name.entry;
+ }
- void visitItem(Item* item) override {
- item->print(&std::cout);
+ if (attr.id) {
+ std::cout << "(" << attr.id.value() << ")";
+ }
}
+ }
+
+ void visitItem(Item* item) override { item->print(&std::cout); }
};
-void Debug::printTable(ResourceTable* table, const DebugPrintTableOptions& options) {
- PrintVisitor visitor;
+void Debug::printTable(ResourceTable* table,
+ const DebugPrintTableOptions& options) {
+ PrintVisitor visitor;
- for (auto& package : table->packages) {
- std::cout << "Package name=" << package->name;
- if (package->id) {
- std::cout << " id=" << std::hex << (int) package->id.value() << std::dec;
+ for (auto& package : table->packages) {
+ std::cout << "Package name=" << package->name;
+ if (package->id) {
+ std::cout << " id=" << std::hex << (int)package->id.value() << std::dec;
+ }
+ std::cout << std::endl;
+
+ for (const auto& type : package->types) {
+ std::cout << "\n type " << type->type;
+ if (type->id) {
+ std::cout << " id=" << std::hex << (int)type->id.value() << std::dec;
+ }
+ std::cout << " entryCount=" << type->entries.size() << std::endl;
+
+ std::vector<const ResourceEntry*> sortedEntries;
+ for (const auto& entry : type->entries) {
+ auto iter = std::lower_bound(
+ sortedEntries.begin(), sortedEntries.end(), entry.get(),
+ [](const ResourceEntry* a, const ResourceEntry* b) -> bool {
+ if (a->id && b->id) {
+ return a->id.value() < b->id.value();
+ } else if (a->id) {
+ return true;
+ } else {
+ return false;
+ }
+ });
+ sortedEntries.insert(iter, entry.get());
+ }
+
+ for (const ResourceEntry* entry : sortedEntries) {
+ ResourceId id(package->id ? package->id.value() : uint8_t(0),
+ type->id ? type->id.value() : uint8_t(0),
+ entry->id ? entry->id.value() : uint16_t(0));
+ ResourceName name(package->name, type->type, entry->name);
+
+ std::cout << " spec resource " << id << " " << name;
+ switch (entry->symbolStatus.state) {
+ case SymbolState::kPublic:
+ std::cout << " PUBLIC";
+ break;
+ case SymbolState::kPrivate:
+ std::cout << " _PRIVATE_";
+ break;
+ default:
+ break;
}
+
std::cout << std::endl;
- for (const auto& type : package->types) {
- std::cout << "\n type " << type->type;
- if (type->id) {
- std::cout << " id=" << std::hex << (int) type->id.value() << std::dec;
- }
- std::cout << " entryCount=" << type->entries.size() << std::endl;
-
- std::vector<const ResourceEntry*> sortedEntries;
- for (const auto& entry : type->entries) {
- auto iter = std::lower_bound(sortedEntries.begin(), sortedEntries.end(), entry.get(),
- [](const ResourceEntry* a, const ResourceEntry* b) -> bool {
- if (a->id && b->id) {
- return a->id.value() < b->id.value();
- } else if (a->id) {
- return true;
- } else {
- return false;
- }
- });
- sortedEntries.insert(iter, entry.get());
- }
-
- for (const ResourceEntry* entry : sortedEntries) {
- ResourceId id(package->id ? package->id.value() : uint8_t(0),
- type->id ? type->id.value() : uint8_t(0),
- entry->id ? entry->id.value() : uint16_t(0));
- ResourceName name(package->name, type->type, entry->name);
-
- std::cout << " spec resource " << id << " " << name;
- switch (entry->symbolStatus.state) {
- case SymbolState::kPublic: std::cout << " PUBLIC"; break;
- case SymbolState::kPrivate: std::cout << " _PRIVATE_"; break;
- default: break;
- }
-
- std::cout << std::endl;
-
- for (const auto& value : entry->values) {
- std::cout << " (" << value->config << ") ";
- value->value->accept(&visitor);
- if (options.showSources && !value->value->getSource().path.empty()) {
- std::cout << " src=" << value->value->getSource();
- }
- std::cout << std::endl;
- }
- }
+ for (const auto& value : entry->values) {
+ std::cout << " (" << value->config << ") ";
+ value->value->accept(&visitor);
+ if (options.showSources && !value->value->getSource().path.empty()) {
+ std::cout << " src=" << value->value->getSource();
+ }
+ std::cout << std::endl;
}
+ }
}
+ }
}
-static size_t getNodeIndex(const std::vector<ResourceName>& names, const ResourceName& name) {
- auto iter = std::lower_bound(names.begin(), names.end(), name);
- assert(iter != names.end() && *iter == name);
- return std::distance(names.begin(), iter);
+static size_t getNodeIndex(const std::vector<ResourceName>& names,
+ const ResourceName& name) {
+ auto iter = std::lower_bound(names.begin(), names.end(), name);
+ assert(iter != names.end() && *iter == name);
+ return std::distance(names.begin(), iter);
}
-void Debug::printStyleGraph(ResourceTable* table, const ResourceName& targetStyle) {
- std::map<ResourceName, std::set<ResourceName>> graph;
+void Debug::printStyleGraph(ResourceTable* table,
+ const ResourceName& targetStyle) {
+ std::map<ResourceName, std::set<ResourceName>> graph;
- std::queue<ResourceName> stylesToVisit;
- stylesToVisit.push(targetStyle);
- for (; !stylesToVisit.empty(); stylesToVisit.pop()) {
- const ResourceName& styleName = stylesToVisit.front();
- std::set<ResourceName>& parents = graph[styleName];
- if (!parents.empty()) {
- // We've already visited this style.
- continue;
+ std::queue<ResourceName> stylesToVisit;
+ stylesToVisit.push(targetStyle);
+ for (; !stylesToVisit.empty(); stylesToVisit.pop()) {
+ const ResourceName& styleName = stylesToVisit.front();
+ std::set<ResourceName>& parents = graph[styleName];
+ if (!parents.empty()) {
+ // We've already visited this style.
+ continue;
+ }
+
+ Maybe<ResourceTable::SearchResult> result = table->findResource(styleName);
+ if (result) {
+ ResourceEntry* entry = result.value().entry;
+ for (const auto& value : entry->values) {
+ if (Style* style = valueCast<Style>(value->value.get())) {
+ if (style->parent && style->parent.value().name) {
+ parents.insert(style->parent.value().name.value());
+ stylesToVisit.push(style->parent.value().name.value());
+ }
}
-
- Maybe<ResourceTable::SearchResult> result = table->findResource(styleName);
- if (result) {
- ResourceEntry* entry = result.value().entry;
- for (const auto& value : entry->values) {
- if (Style* style = valueCast<Style>(value->value.get())) {
- if (style->parent && style->parent.value().name) {
- parents.insert(style->parent.value().name.value());
- stylesToVisit.push(style->parent.value().name.value());
- }
- }
- }
- }
+ }
}
+ }
- std::vector<ResourceName> names;
- for (const auto& entry : graph) {
- names.push_back(entry.first);
+ std::vector<ResourceName> names;
+ for (const auto& entry : graph) {
+ names.push_back(entry.first);
+ }
+
+ std::cout << "digraph styles {\n";
+ for (const auto& name : names) {
+ std::cout << " node_" << getNodeIndex(names, name) << " [label=\"" << name
+ << "\"];\n";
+ }
+
+ for (const auto& entry : graph) {
+ const ResourceName& styleName = entry.first;
+ size_t styleNodeIndex = getNodeIndex(names, styleName);
+
+ for (const auto& parentName : entry.second) {
+ std::cout << " node_" << styleNodeIndex << " -> "
+ << "node_" << getNodeIndex(names, parentName) << ";\n";
}
+ }
- std::cout << "digraph styles {\n";
- for (const auto& name : names) {
- std::cout << " node_" << getNodeIndex(names, name)
- << " [label=\"" << name << "\"];\n";
- }
-
- for (const auto& entry : graph) {
- const ResourceName& styleName = entry.first;
- size_t styleNodeIndex = getNodeIndex(names, styleName);
-
- for (const auto& parentName : entry.second) {
- std::cout << " node_" << styleNodeIndex << " -> "
- << "node_" << getNodeIndex(names, parentName) << ";\n";
- }
- }
-
- std::cout << "}" << std::endl;
+ std::cout << "}" << std::endl;
}
void Debug::dumpHex(const void* data, size_t len) {
- const uint8_t* d = (const uint8_t*) data;
- for (size_t i = 0; i < len; i++) {
- std::cerr << std::hex << std::setfill('0') << std::setw(2) << (uint32_t) d[i] << " ";
- if (i % 8 == 7) {
- std::cerr << "\n";
- }
+ const uint8_t* d = (const uint8_t*)data;
+ for (size_t i = 0; i < len; i++) {
+ std::cerr << std::hex << std::setfill('0') << std::setw(2) << (uint32_t)d[i]
+ << " ";
+ if (i % 8 == 7) {
+ std::cerr << "\n";
}
+ }
- if (len - 1 % 8 != 7) {
- std::cerr << std::endl;
- }
+ if (len - 1 % 8 != 7) {
+ std::cerr << std::endl;
+ }
}
namespace {
class XmlPrinter : public xml::Visitor {
-public:
- using xml::Visitor::visit;
+ public:
+ using xml::Visitor::visit;
- void visit(xml::Element* el) override {
- std::cerr << mPrefix;
- std::cerr << "E: ";
- if (!el->namespaceUri.empty()) {
- std::cerr << el->namespaceUri << ":";
- }
- std::cerr << el->name << " (line=" << el->lineNumber << ")\n";
+ void visit(xml::Element* el) override {
+ std::cerr << mPrefix;
+ std::cerr << "E: ";
+ if (!el->namespaceUri.empty()) {
+ std::cerr << el->namespaceUri << ":";
+ }
+ std::cerr << el->name << " (line=" << el->lineNumber << ")\n";
- for (const xml::Attribute& attr : el->attributes) {
- std::cerr << mPrefix << " A: ";
- if (!attr.namespaceUri.empty()) {
- std::cerr << attr.namespaceUri << ":";
- }
- std::cerr << attr.name << "=" << attr.value << "\n";
- }
-
- const size_t previousSize = mPrefix.size();
- mPrefix += " ";
- xml::Visitor::visit(el);
- mPrefix.resize(previousSize);
+ for (const xml::Attribute& attr : el->attributes) {
+ std::cerr << mPrefix << " A: ";
+ if (!attr.namespaceUri.empty()) {
+ std::cerr << attr.namespaceUri << ":";
+ }
+ std::cerr << attr.name << "=" << attr.value << "\n";
}
- void visit(xml::Namespace* ns) override {
- std::cerr << mPrefix;
- std::cerr << "N: " << ns->namespacePrefix << "=" << ns->namespaceUri
- << " (line=" << ns->lineNumber << ")\n";
+ const size_t previousSize = mPrefix.size();
+ mPrefix += " ";
+ xml::Visitor::visit(el);
+ mPrefix.resize(previousSize);
+ }
- const size_t previousSize = mPrefix.size();
- mPrefix += " ";
- xml::Visitor::visit(ns);
- mPrefix.resize(previousSize);
- }
+ void visit(xml::Namespace* ns) override {
+ std::cerr << mPrefix;
+ std::cerr << "N: " << ns->namespacePrefix << "=" << ns->namespaceUri
+ << " (line=" << ns->lineNumber << ")\n";
- void visit(xml::Text* text) override {
- std::cerr << mPrefix;
- std::cerr << "T: '" << text->text << "'\n";
- }
+ const size_t previousSize = mPrefix.size();
+ mPrefix += " ";
+ xml::Visitor::visit(ns);
+ mPrefix.resize(previousSize);
+ }
-private:
- std::string mPrefix;
+ void visit(xml::Text* text) override {
+ std::cerr << mPrefix;
+ std::cerr << "T: '" << text->text << "'\n";
+ }
+
+ private:
+ std::string mPrefix;
};
-} // namespace
+} // namespace
void Debug::dumpXml(xml::XmlResource* doc) {
- XmlPrinter printer;
- doc->root->accept(&printer);
+ XmlPrinter printer;
+ doc->root->accept(&printer);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h
index c0fcbf1..bd92ec1 100644
--- a/tools/aapt2/Debug.h
+++ b/tools/aapt2/Debug.h
@@ -27,17 +27,18 @@
namespace aapt {
struct DebugPrintTableOptions {
- bool showSources = false;
+ bool showSources = false;
};
struct Debug {
- static void printTable(ResourceTable* table, const DebugPrintTableOptions& options = {});
- static void printStyleGraph(ResourceTable* table,
- const ResourceName& targetStyle);
- static void dumpHex(const void* data, size_t len);
- static void dumpXml(xml::XmlResource* doc);
+ static void printTable(ResourceTable* table,
+ const DebugPrintTableOptions& options = {});
+ static void printStyleGraph(ResourceTable* table,
+ const ResourceName& targetStyle);
+ static void dumpHex(const void* data, size_t len);
+ static void dumpXml(xml::XmlResource* doc);
};
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_DEBUG_H
+#endif // AAPT_DEBUG_H
diff --git a/tools/aapt2/Diagnostics.h b/tools/aapt2/Diagnostics.h
index 725027c..d39cf4c 100644
--- a/tools/aapt2/Diagnostics.h
+++ b/tools/aapt2/Diagnostics.h
@@ -29,119 +29,112 @@
namespace aapt {
struct DiagMessageActual {
- Source source;
- std::string message;
+ Source source;
+ std::string message;
};
struct DiagMessage {
-private:
- Source mSource;
- std::stringstream mMessage;
+ private:
+ Source mSource;
+ std::stringstream mMessage;
-public:
- DiagMessage() = default;
+ public:
+ DiagMessage() = default;
- explicit DiagMessage(const StringPiece& src) : mSource(src) {
- }
+ explicit DiagMessage(const StringPiece& src) : mSource(src) {}
- explicit DiagMessage(const Source& src) : mSource(src) {
- }
+ explicit DiagMessage(const Source& src) : mSource(src) {}
- explicit DiagMessage(size_t line) : mSource(Source().withLine(line)) {
- }
+ explicit DiagMessage(size_t line) : mSource(Source().withLine(line)) {}
- template <typename T>
- DiagMessage& operator<<(const T& value) {
- mMessage << value;
- return *this;
- }
+ template <typename T>
+ DiagMessage& operator<<(const T& value) {
+ mMessage << value;
+ return *this;
+ }
- DiagMessageActual build() const {
- return DiagMessageActual{ mSource, mMessage.str() };
- }
+ DiagMessageActual build() const {
+ return DiagMessageActual{mSource, mMessage.str()};
+ }
};
struct IDiagnostics {
- virtual ~IDiagnostics() = default;
+ virtual ~IDiagnostics() = default;
- enum class Level {
- Note,
- Warn,
- Error
- };
+ enum class Level { Note, Warn, Error };
- virtual void log(Level level, DiagMessageActual& actualMsg) = 0;
+ virtual void log(Level level, DiagMessageActual& actualMsg) = 0;
- virtual void error(const DiagMessage& message) {
- DiagMessageActual actual = message.build();
- log(Level::Error, actual);
- }
+ virtual void error(const DiagMessage& message) {
+ DiagMessageActual actual = message.build();
+ log(Level::Error, actual);
+ }
- virtual void warn(const DiagMessage& message) {
- DiagMessageActual actual = message.build();
- log(Level::Warn, actual);
- }
+ virtual void warn(const DiagMessage& message) {
+ DiagMessageActual actual = message.build();
+ log(Level::Warn, actual);
+ }
- virtual void note(const DiagMessage& message) {
- DiagMessageActual actual = message.build();
- log(Level::Note, actual);
- }
+ virtual void note(const DiagMessage& message) {
+ DiagMessageActual actual = message.build();
+ log(Level::Note, actual);
+ }
};
class StdErrDiagnostics : public IDiagnostics {
-public:
- StdErrDiagnostics() = default;
+ public:
+ StdErrDiagnostics() = default;
- void log(Level level, DiagMessageActual& actualMsg) override {
- const char* tag;
+ void log(Level level, DiagMessageActual& actualMsg) override {
+ const char* tag;
- switch (level) {
- case Level::Error:
- mNumErrors++;
- if (mNumErrors > 20) {
- return;
- }
- tag = "error";
- break;
-
- case Level::Warn:
- tag = "warn";
- break;
-
- case Level::Note:
- tag = "note";
- break;
+ switch (level) {
+ case Level::Error:
+ mNumErrors++;
+ if (mNumErrors > 20) {
+ return;
}
+ tag = "error";
+ break;
- if (!actualMsg.source.path.empty()) {
- std::cerr << actualMsg.source << ": ";
- }
- std::cerr << tag << ": " << actualMsg.message << "." << std::endl;
+ case Level::Warn:
+ tag = "warn";
+ break;
+
+ case Level::Note:
+ tag = "note";
+ break;
}
-private:
- size_t mNumErrors = 0;
+ if (!actualMsg.source.path.empty()) {
+ std::cerr << actualMsg.source << ": ";
+ }
+ std::cerr << tag << ": " << actualMsg.message << "." << std::endl;
+ }
- DISALLOW_COPY_AND_ASSIGN(StdErrDiagnostics);
+ private:
+ size_t mNumErrors = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(StdErrDiagnostics);
};
class SourcePathDiagnostics : public IDiagnostics {
-public:
- SourcePathDiagnostics(const Source& src, IDiagnostics* diag) : mSource(src), mDiag(diag) {
- }
+ public:
+ SourcePathDiagnostics(const Source& src, IDiagnostics* diag)
+ : mSource(src), mDiag(diag) {}
- void log(Level level, DiagMessageActual& actualMsg) override {
- actualMsg.source.path = mSource.path;
- mDiag->log(level, actualMsg);
- }
+ void log(Level level, DiagMessageActual& actualMsg) override {
+ actualMsg.source.path = mSource.path;
+ mDiag->log(level, actualMsg);
+ }
-private:
- Source mSource;
- IDiagnostics* mDiag;
+ private:
+ Source mSource;
+ IDiagnostics* mDiag;
- DISALLOW_COPY_AND_ASSIGN(SourcePathDiagnostics);
+ DISALLOW_COPY_AND_ASSIGN(SourcePathDiagnostics);
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_DIAGNOSTICS_H */
diff --git a/tools/aapt2/DominatorTree.cpp b/tools/aapt2/DominatorTree.cpp
index 29587a8..8f6f783 100644
--- a/tools/aapt2/DominatorTree.cpp
+++ b/tools/aapt2/DominatorTree.cpp
@@ -14,76 +14,78 @@
* limitations under the License.
*/
-#include "ConfigDescription.h"
#include "DominatorTree.h"
+#include "ConfigDescription.h"
#include <algorithm>
namespace aapt {
DominatorTree::DominatorTree(
- const std::vector<std::unique_ptr<ResourceConfigValue>>& configs) {
- for (const auto& config : configs) {
- mProductRoots[config->product].tryAddChild(
- util::make_unique<Node>(config.get(), nullptr));
- }
+ const std::vector<std::unique_ptr<ResourceConfigValue>>& configs) {
+ for (const auto& config : configs) {
+ mProductRoots[config->product].tryAddChild(
+ util::make_unique<Node>(config.get(), nullptr));
+ }
}
void DominatorTree::accept(Visitor* visitor) {
- for (auto& entry : mProductRoots) {
- visitor->visitTree(entry.first, &entry.second);
- }
+ for (auto& entry : mProductRoots) {
+ visitor->visitTree(entry.first, &entry.second);
+ }
}
bool DominatorTree::Node::tryAddChild(std::unique_ptr<Node> newChild) {
- assert(newChild->mValue && "cannot add a root or empty node as a child");
- if (mValue && !dominates(newChild.get())) {
- // This is not the root and the child dominates us.
- return false;
- }
- return addChild(std::move(newChild));
+ assert(newChild->mValue && "cannot add a root or empty node as a child");
+ if (mValue && !dominates(newChild.get())) {
+ // This is not the root and the child dominates us.
+ return false;
+ }
+ return addChild(std::move(newChild));
}
bool DominatorTree::Node::addChild(std::unique_ptr<Node> newChild) {
- bool hasDominatedChildren = false;
- // Demote children dominated by the new config.
- for (auto& child : mChildren) {
- if (newChild->dominates(child.get())) {
- child->mParent = newChild.get();
- newChild->mChildren.push_back(std::move(child));
- child = {};
- hasDominatedChildren = true;
- }
+ bool hasDominatedChildren = false;
+ // Demote children dominated by the new config.
+ for (auto& child : mChildren) {
+ if (newChild->dominates(child.get())) {
+ child->mParent = newChild.get();
+ newChild->mChildren.push_back(std::move(child));
+ child = {};
+ hasDominatedChildren = true;
}
- // Remove dominated children.
- if (hasDominatedChildren) {
- mChildren.erase(std::remove_if(mChildren.begin(), mChildren.end(),
- [](const std::unique_ptr<Node>& child) -> bool {
- return child == nullptr;
- }), mChildren.end());
+ }
+ // Remove dominated children.
+ if (hasDominatedChildren) {
+ mChildren.erase(
+ std::remove_if(mChildren.begin(), mChildren.end(),
+ [](const std::unique_ptr<Node>& child) -> bool {
+ return child == nullptr;
+ }),
+ mChildren.end());
+ }
+ // Add the new config to a child if a child dominates the new config.
+ for (auto& child : mChildren) {
+ if (child->dominates(newChild.get())) {
+ child->addChild(std::move(newChild));
+ return true;
}
- // Add the new config to a child if a child dominates the new config.
- for (auto& child : mChildren) {
- if (child->dominates(newChild.get())) {
- child->addChild(std::move(newChild));
- return true;
- }
- }
- // The new config is not dominated by a child, so add it here.
- newChild->mParent = this;
- mChildren.push_back(std::move(newChild));
- return true;
+ }
+ // The new config is not dominated by a child, so add it here.
+ newChild->mParent = this;
+ mChildren.push_back(std::move(newChild));
+ return true;
}
bool DominatorTree::Node::dominates(const Node* other) const {
- // Check root node dominations.
- if (other->isRootNode()) {
- return isRootNode();
- } else if (isRootNode()) {
- return true;
- }
- // Neither node is a root node; compare the configurations.
- return mValue->config.dominates(other->mValue->config);
+ // Check root node dominations.
+ if (other->isRootNode()) {
+ return isRootNode();
+ } else if (isRootNode()) {
+ return true;
+ }
+ // Neither node is a root node; compare the configurations.
+ return mValue->config.dominates(other->mValue->config);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/DominatorTree.h b/tools/aapt2/DominatorTree.h
index ad2df0e..6d45b5d 100644
--- a/tools/aapt2/DominatorTree.h
+++ b/tools/aapt2/DominatorTree.h
@@ -46,82 +46,76 @@
* will exhibit undefined behavior.
*/
class DominatorTree {
-public:
- explicit DominatorTree(const std::vector<std::unique_ptr<ResourceConfigValue>>& configs);
+ public:
+ explicit DominatorTree(
+ const std::vector<std::unique_ptr<ResourceConfigValue>>& configs);
- class Node {
- public:
- explicit Node(ResourceConfigValue* value = nullptr, Node* parent = nullptr) :
- mValue(value), mParent(parent) {
- }
+ class Node {
+ public:
+ explicit Node(ResourceConfigValue* value = nullptr, Node* parent = nullptr)
+ : mValue(value), mParent(parent) {}
- inline ResourceConfigValue* value() const {
- return mValue;
- }
+ inline ResourceConfigValue* value() const { return mValue; }
- inline Node* parent() const {
- return mParent;
- }
+ inline Node* parent() const { return mParent; }
- inline bool isRootNode() const {
- return !mValue;
- }
+ inline bool isRootNode() const { return !mValue; }
- inline const std::vector<std::unique_ptr<Node>>& children() const {
- return mChildren;
- }
-
- bool tryAddChild(std::unique_ptr<Node> newChild);
-
- private:
- bool addChild(std::unique_ptr<Node> newChild);
- bool dominates(const Node* other) const;
-
- ResourceConfigValue* mValue;
- Node* mParent;
- std::vector<std::unique_ptr<Node>> mChildren;
-
- DISALLOW_COPY_AND_ASSIGN(Node);
- };
-
- struct Visitor {
- virtual ~Visitor() = default;
- virtual void visitTree(const std::string& product, Node* root) = 0;
- };
-
- class BottomUpVisitor : public Visitor {
- public:
- virtual ~BottomUpVisitor() = default;
-
- void visitTree(const std::string& product, Node* root) override {
- for (auto& child : root->children()) {
- visitNode(child.get());
- }
- }
-
- virtual void visitConfig(Node* node) = 0;
-
- private:
- void visitNode(Node* node) {
- for (auto& child : node->children()) {
- visitNode(child.get());
- }
- visitConfig(node);
- }
- };
-
- void accept(Visitor* visitor);
-
- inline const std::map<std::string, Node>& getProductRoots() const {
- return mProductRoots;
+ inline const std::vector<std::unique_ptr<Node>>& children() const {
+ return mChildren;
}
-private:
- DISALLOW_COPY_AND_ASSIGN(DominatorTree);
+ bool tryAddChild(std::unique_ptr<Node> newChild);
- std::map<std::string, Node> mProductRoots;
+ private:
+ bool addChild(std::unique_ptr<Node> newChild);
+ bool dominates(const Node* other) const;
+
+ ResourceConfigValue* mValue;
+ Node* mParent;
+ std::vector<std::unique_ptr<Node>> mChildren;
+
+ DISALLOW_COPY_AND_ASSIGN(Node);
+ };
+
+ struct Visitor {
+ virtual ~Visitor() = default;
+ virtual void visitTree(const std::string& product, Node* root) = 0;
+ };
+
+ class BottomUpVisitor : public Visitor {
+ public:
+ virtual ~BottomUpVisitor() = default;
+
+ void visitTree(const std::string& product, Node* root) override {
+ for (auto& child : root->children()) {
+ visitNode(child.get());
+ }
+ }
+
+ virtual void visitConfig(Node* node) = 0;
+
+ private:
+ void visitNode(Node* node) {
+ for (auto& child : node->children()) {
+ visitNode(child.get());
+ }
+ visitConfig(node);
+ }
+ };
+
+ void accept(Visitor* visitor);
+
+ inline const std::map<std::string, Node>& getProductRoots() const {
+ return mProductRoots;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DominatorTree);
+
+ std::map<std::string, Node> mProductRoots;
};
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_DOMINATOR_TREE_H
+#endif // AAPT_DOMINATOR_TREE_H
diff --git a/tools/aapt2/DominatorTree_test.cpp b/tools/aapt2/DominatorTree_test.cpp
index fb850e4..a42d2f7 100644
--- a/tools/aapt2/DominatorTree_test.cpp
+++ b/tools/aapt2/DominatorTree_test.cpp
@@ -27,125 +27,132 @@
namespace {
class PrettyPrinter : public DominatorTree::Visitor {
-public:
- explicit PrettyPrinter(const int indent = 2) : mIndent(indent) {
- }
+ public:
+ explicit PrettyPrinter(const int indent = 2) : mIndent(indent) {}
- void visitTree(const std::string& product, DominatorTree::Node* root) override {
- for (auto& child : root->children()) {
- visitNode(child.get(), 0);
- }
+ void visitTree(const std::string& product,
+ DominatorTree::Node* root) override {
+ for (auto& child : root->children()) {
+ visitNode(child.get(), 0);
}
+ }
- std::string toString(DominatorTree* tree) {
- mBuffer.str("");
- mBuffer.clear();
- tree->accept(this);
- return mBuffer.str();
+ std::string toString(DominatorTree* tree) {
+ mBuffer.str("");
+ mBuffer.clear();
+ tree->accept(this);
+ return mBuffer.str();
+ }
+
+ private:
+ void visitConfig(const DominatorTree::Node* node, const int indent) {
+ auto configString = node->value()->config.toString();
+ mBuffer << std::string(indent, ' ')
+ << (configString.isEmpty() ? "<default>" : configString)
+ << std::endl;
+ }
+
+ void visitNode(const DominatorTree::Node* node, const int indent) {
+ visitConfig(node, indent);
+ for (const auto& child : node->children()) {
+ visitNode(child.get(), indent + mIndent);
}
+ }
-private:
- void visitConfig(const DominatorTree::Node* node, const int indent) {
- auto configString = node->value()->config.toString();
- mBuffer << std::string(indent, ' ')
- << (configString.isEmpty() ? "<default>" : configString)
- << std::endl;
- }
-
- void visitNode(const DominatorTree::Node* node, const int indent) {
- visitConfig(node, indent);
- for (const auto& child : node->children()) {
- visitNode(child.get(), indent + mIndent);
- }
- }
-
- std::stringstream mBuffer;
- const int mIndent = 2;
+ std::stringstream mBuffer;
+ const int mIndent = 2;
};
-} // namespace
+} // namespace
TEST(DominatorTreeTest, DefaultDominatesEverything) {
- const ConfigDescription defaultConfig = {};
- const ConfigDescription landConfig = test::parseConfigOrDie("land");
- const ConfigDescription sw600dpLandConfig = test::parseConfigOrDie("sw600dp-land-v13");
+ const ConfigDescription defaultConfig = {};
+ const ConfigDescription landConfig = test::parseConfigOrDie("land");
+ const ConfigDescription sw600dpLandConfig =
+ test::parseConfigOrDie("sw600dp-land-v13");
- std::vector<std::unique_ptr<ResourceConfigValue>> configs;
- configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(landConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(sw600dpLandConfig, ""));
+ std::vector<std::unique_ptr<ResourceConfigValue>> configs;
+ configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(landConfig, ""));
+ configs.push_back(
+ util::make_unique<ResourceConfigValue>(sw600dpLandConfig, ""));
- DominatorTree tree(configs);
- PrettyPrinter printer;
+ DominatorTree tree(configs);
+ PrettyPrinter printer;
- std::string expected =
- "<default>\n"
- " land\n"
- " sw600dp-land-v13\n";
- EXPECT_EQ(expected, printer.toString(&tree));
+ std::string expected =
+ "<default>\n"
+ " land\n"
+ " sw600dp-land-v13\n";
+ EXPECT_EQ(expected, printer.toString(&tree));
}
TEST(DominatorTreeTest, ProductsAreDominatedSeparately) {
- const ConfigDescription defaultConfig = {};
- const ConfigDescription landConfig = test::parseConfigOrDie("land");
- const ConfigDescription sw600dpLandConfig = test::parseConfigOrDie("sw600dp-land-v13");
+ const ConfigDescription defaultConfig = {};
+ const ConfigDescription landConfig = test::parseConfigOrDie("land");
+ const ConfigDescription sw600dpLandConfig =
+ test::parseConfigOrDie("sw600dp-land-v13");
- std::vector<std::unique_ptr<ResourceConfigValue>> configs;
- configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(landConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, "phablet"));
- configs.push_back(util::make_unique<ResourceConfigValue>(sw600dpLandConfig, "phablet"));
+ std::vector<std::unique_ptr<ResourceConfigValue>> configs;
+ configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(landConfig, ""));
+ configs.push_back(
+ util::make_unique<ResourceConfigValue>(defaultConfig, "phablet"));
+ configs.push_back(
+ util::make_unique<ResourceConfigValue>(sw600dpLandConfig, "phablet"));
- DominatorTree tree(configs);
- PrettyPrinter printer;
+ DominatorTree tree(configs);
+ PrettyPrinter printer;
- std::string expected =
- "<default>\n"
- " land\n"
- "<default>\n"
- " sw600dp-land-v13\n";
- EXPECT_EQ(expected, printer.toString(&tree));
+ std::string expected =
+ "<default>\n"
+ " land\n"
+ "<default>\n"
+ " sw600dp-land-v13\n";
+ EXPECT_EQ(expected, printer.toString(&tree));
}
TEST(DominatorTreeTest, MoreSpecificConfigurationsAreDominated) {
- const ConfigDescription defaultConfig = {};
- const ConfigDescription enConfig = test::parseConfigOrDie("en");
- const ConfigDescription enV21Config = test::parseConfigOrDie("en-v21");
- const ConfigDescription ldrtlConfig = test::parseConfigOrDie("ldrtl-v4");
- const ConfigDescription ldrtlXhdpiConfig = test::parseConfigOrDie("ldrtl-xhdpi-v4");
- const ConfigDescription sw300dpConfig = test::parseConfigOrDie("sw300dp-v13");
- const ConfigDescription sw540dpConfig = test::parseConfigOrDie("sw540dp-v14");
- const ConfigDescription sw600dpConfig = test::parseConfigOrDie("sw600dp-v14");
- const ConfigDescription sw720dpConfig = test::parseConfigOrDie("sw720dp-v13");
- const ConfigDescription v20Config = test::parseConfigOrDie("v20");
+ const ConfigDescription defaultConfig = {};
+ const ConfigDescription enConfig = test::parseConfigOrDie("en");
+ const ConfigDescription enV21Config = test::parseConfigOrDie("en-v21");
+ const ConfigDescription ldrtlConfig = test::parseConfigOrDie("ldrtl-v4");
+ const ConfigDescription ldrtlXhdpiConfig =
+ test::parseConfigOrDie("ldrtl-xhdpi-v4");
+ const ConfigDescription sw300dpConfig = test::parseConfigOrDie("sw300dp-v13");
+ const ConfigDescription sw540dpConfig = test::parseConfigOrDie("sw540dp-v14");
+ const ConfigDescription sw600dpConfig = test::parseConfigOrDie("sw600dp-v14");
+ const ConfigDescription sw720dpConfig = test::parseConfigOrDie("sw720dp-v13");
+ const ConfigDescription v20Config = test::parseConfigOrDie("v20");
- std::vector<std::unique_ptr<ResourceConfigValue>> configs;
- configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(enConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(enV21Config, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(ldrtlConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(ldrtlXhdpiConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(sw300dpConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(sw540dpConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(sw600dpConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(sw720dpConfig, ""));
- configs.push_back(util::make_unique<ResourceConfigValue>(v20Config, ""));
+ std::vector<std::unique_ptr<ResourceConfigValue>> configs;
+ configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(enConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(enV21Config, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(ldrtlConfig, ""));
+ configs.push_back(
+ util::make_unique<ResourceConfigValue>(ldrtlXhdpiConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(sw300dpConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(sw540dpConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(sw600dpConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(sw720dpConfig, ""));
+ configs.push_back(util::make_unique<ResourceConfigValue>(v20Config, ""));
- DominatorTree tree(configs);
- PrettyPrinter printer;
+ DominatorTree tree(configs);
+ PrettyPrinter printer;
- std::string expected =
- "<default>\n"
- " en\n"
- " en-v21\n"
- " ldrtl-v4\n"
- " ldrtl-xhdpi-v4\n"
- " sw300dp-v13\n"
- " sw540dp-v14\n"
- " sw600dp-v14\n"
- " sw720dp-v13\n"
- " v20\n";
- EXPECT_EQ(expected, printer.toString(&tree));
+ std::string expected =
+ "<default>\n"
+ " en\n"
+ " en-v21\n"
+ " ldrtl-v4\n"
+ " ldrtl-xhdpi-v4\n"
+ " sw300dp-v13\n"
+ " sw540dp-v14\n"
+ " sw600dp-v14\n"
+ " sw720dp-v13\n"
+ " v20\n";
+ EXPECT_EQ(expected, printer.toString(&tree));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/Flags.cpp b/tools/aapt2/Flags.cpp
index 3731ac7..cb16196 100644
--- a/tools/aapt2/Flags.cpp
+++ b/tools/aapt2/Flags.cpp
@@ -25,155 +25,167 @@
namespace aapt {
-Flags& Flags::requiredFlag(const StringPiece& name, const StringPiece& description,
- std::string* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- *value = arg.toString();
- return true;
- };
+Flags& Flags::requiredFlag(const StringPiece& name,
+ const StringPiece& description, std::string* value) {
+ auto func = [value](const StringPiece& arg) -> bool {
+ *value = arg.toString();
+ return true;
+ };
- mFlags.push_back(Flag{ name.toString(), description.toString(), func, true, 1, false});
- return *this;
+ mFlags.push_back(
+ Flag{name.toString(), description.toString(), func, true, 1, false});
+ return *this;
}
-Flags& Flags::requiredFlagList(const StringPiece& name, const StringPiece& description,
+Flags& Flags::requiredFlagList(const StringPiece& name,
+ const StringPiece& description,
std::vector<std::string>* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- value->push_back(arg.toString());
- return true;
- };
+ auto func = [value](const StringPiece& arg) -> bool {
+ value->push_back(arg.toString());
+ return true;
+ };
- mFlags.push_back(Flag{ name.toString(), description.toString(), func, true, 1, false });
- return *this;
+ mFlags.push_back(
+ Flag{name.toString(), description.toString(), func, true, 1, false});
+ return *this;
}
-Flags& Flags::optionalFlag(const StringPiece& name, const StringPiece& description,
+Flags& Flags::optionalFlag(const StringPiece& name,
+ const StringPiece& description,
Maybe<std::string>* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- *value = arg.toString();
- return true;
- };
+ auto func = [value](const StringPiece& arg) -> bool {
+ *value = arg.toString();
+ return true;
+ };
- mFlags.push_back(Flag{ name.toString(), description.toString(), func, false, 1, false });
- return *this;
+ mFlags.push_back(
+ Flag{name.toString(), description.toString(), func, false, 1, false});
+ return *this;
}
-Flags& Flags::optionalFlagList(const StringPiece& name, const StringPiece& description,
+Flags& Flags::optionalFlagList(const StringPiece& name,
+ const StringPiece& description,
std::vector<std::string>* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- value->push_back(arg.toString());
- return true;
- };
+ auto func = [value](const StringPiece& arg) -> bool {
+ value->push_back(arg.toString());
+ return true;
+ };
- mFlags.push_back(Flag{ name.toString(), description.toString(), func, false, 1, false });
- return *this;
+ mFlags.push_back(
+ Flag{name.toString(), description.toString(), func, false, 1, false});
+ return *this;
}
-Flags& Flags::optionalFlagList(const StringPiece& name, const StringPiece& description,
+Flags& Flags::optionalFlagList(const StringPiece& name,
+ const StringPiece& description,
std::unordered_set<std::string>* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- value->insert(arg.toString());
- return true;
- };
+ auto func = [value](const StringPiece& arg) -> bool {
+ value->insert(arg.toString());
+ return true;
+ };
- mFlags.push_back(Flag{ name.toString(), description.toString(), func, false, 1, false });
- return *this;
+ mFlags.push_back(
+ Flag{name.toString(), description.toString(), func, false, 1, false});
+ return *this;
}
-Flags& Flags::optionalSwitch(const StringPiece& name, const StringPiece& description,
- bool* value) {
- auto func = [value](const StringPiece& arg) -> bool {
- *value = true;
- return true;
- };
+Flags& Flags::optionalSwitch(const StringPiece& name,
+ const StringPiece& description, bool* value) {
+ auto func = [value](const StringPiece& arg) -> bool {
+ *value = true;
+ return true;
+ };
- mFlags.push_back(Flag{ name.toString(), description.toString(), func, false, 0, false });
- return *this;
+ mFlags.push_back(
+ Flag{name.toString(), description.toString(), func, false, 0, false});
+ return *this;
}
void Flags::usage(const StringPiece& command, std::ostream* out) {
- constexpr size_t kWidth = 50;
+ constexpr size_t kWidth = 50;
- *out << command << " [options]";
- for (const Flag& flag : mFlags) {
- if (flag.required) {
- *out << " " << flag.name << " arg";
- }
+ *out << command << " [options]";
+ for (const Flag& flag : mFlags) {
+ if (flag.required) {
+ *out << " " << flag.name << " arg";
+ }
+ }
+
+ *out << " files...\n\nOptions:\n";
+
+ for (const Flag& flag : mFlags) {
+ std::string argLine = flag.name;
+ if (flag.numArgs > 0) {
+ argLine += " arg";
}
- *out << " files...\n\nOptions:\n";
-
- for (const Flag& flag : mFlags) {
- std::string argLine = flag.name;
- if (flag.numArgs > 0) {
- argLine += " arg";
- }
-
- // 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(flag.description, '\n')) {
- *out << " " << std::setw(kWidth) << std::left << argLine << line << "\n";
- argLine = " ";
- }
+ // 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(flag.description, '\n')) {
+ *out << " " << std::setw(kWidth) << std::left << argLine << line << "\n";
+ argLine = " ";
}
- *out << " " << std::setw(kWidth) << std::left << "-h" << "Displays this help menu\n";
- out->flush();
+ }
+ *out << " " << std::setw(kWidth) << std::left << "-h"
+ << "Displays this help menu\n";
+ out->flush();
}
-bool Flags::parse(const StringPiece& command, const std::vector<StringPiece>& args,
+bool Flags::parse(const StringPiece& command,
+ const std::vector<StringPiece>& args,
std::ostream* outError) {
- for (size_t i = 0; i < args.size(); i++) {
- StringPiece arg = args[i];
- if (*(arg.data()) != '-') {
- mArgs.push_back(arg.toString());
- continue;
- }
-
- if (arg == "-h" || arg == "--help") {
- usage(command, outError);
- return false;
- }
-
- bool match = false;
- for (Flag& flag : mFlags) {
- if (arg == flag.name) {
- if (flag.numArgs > 0) {
- i++;
- if (i >= args.size()) {
- *outError << flag.name << " missing argument.\n\n";
- usage(command, outError);
- return false;
- }
- flag.action(args[i]);
- } else {
- flag.action({});
- }
- flag.parsed = true;
- match = true;
- break;
- }
- }
-
- if (!match) {
- *outError << "unknown option '" << arg << "'.\n\n";
- usage(command, outError);
- return false;
- }
+ for (size_t i = 0; i < args.size(); i++) {
+ StringPiece arg = args[i];
+ if (*(arg.data()) != '-') {
+ mArgs.push_back(arg.toString());
+ continue;
}
- for (const Flag& flag : mFlags) {
- if (flag.required && !flag.parsed) {
- *outError << "missing required flag " << flag.name << "\n\n";
+ if (arg == "-h" || arg == "--help") {
+ usage(command, outError);
+ return false;
+ }
+
+ bool match = false;
+ for (Flag& flag : mFlags) {
+ if (arg == flag.name) {
+ if (flag.numArgs > 0) {
+ i++;
+ if (i >= args.size()) {
+ *outError << flag.name << " missing argument.\n\n";
usage(command, outError);
return false;
+ }
+ flag.action(args[i]);
+ } else {
+ flag.action({});
}
+ flag.parsed = true;
+ match = true;
+ break;
+ }
}
- return true;
+
+ if (!match) {
+ *outError << "unknown option '" << arg << "'.\n\n";
+ usage(command, outError);
+ return false;
+ }
+ }
+
+ for (const Flag& flag : mFlags) {
+ if (flag.required && !flag.parsed) {
+ *outError << "missing required flag " << flag.name << "\n\n";
+ usage(command, outError);
+ return false;
+ }
+ }
+ return true;
}
-const std::vector<std::string>& Flags::getArgs() {
- return mArgs;
-}
+const std::vector<std::string>& Flags::getArgs() { return mArgs; }
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/Flags.h b/tools/aapt2/Flags.h
index b092855..4a0efdb 100644
--- a/tools/aapt2/Flags.h
+++ b/tools/aapt2/Flags.h
@@ -29,42 +29,45 @@
namespace aapt {
class Flags {
-public:
- Flags& requiredFlag(const StringPiece& name, const StringPiece& description,
- std::string* value);
- Flags& requiredFlagList(const StringPiece& name, const StringPiece& description,
- std::vector<std::string>* value);
- Flags& optionalFlag(const StringPiece& name, const StringPiece& description,
- Maybe<std::string>* value);
- Flags& optionalFlagList(const StringPiece& name, const StringPiece& description,
- std::vector<std::string>* value);
- Flags& optionalFlagList(const StringPiece& name, const StringPiece& description,
- std::unordered_set<std::string>* value);
- Flags& optionalSwitch(const StringPiece& name, const StringPiece& description,
- bool* value);
+ public:
+ Flags& requiredFlag(const StringPiece& name, const StringPiece& description,
+ std::string* value);
+ Flags& requiredFlagList(const StringPiece& name,
+ const StringPiece& description,
+ std::vector<std::string>* value);
+ Flags& optionalFlag(const StringPiece& name, const StringPiece& description,
+ Maybe<std::string>* value);
+ Flags& optionalFlagList(const StringPiece& name,
+ const StringPiece& description,
+ std::vector<std::string>* value);
+ Flags& optionalFlagList(const StringPiece& name,
+ const StringPiece& description,
+ std::unordered_set<std::string>* value);
+ Flags& optionalSwitch(const StringPiece& name, const StringPiece& description,
+ bool* value);
- void usage(const StringPiece& command, std::ostream* out);
+ void usage(const StringPiece& command, std::ostream* out);
- bool parse(const StringPiece& command, const std::vector<StringPiece>& args,
- std::ostream* outError);
+ bool parse(const StringPiece& command, const std::vector<StringPiece>& args,
+ std::ostream* outError);
- const std::vector<std::string>& getArgs();
+ const std::vector<std::string>& getArgs();
-private:
- struct Flag {
- std::string name;
- std::string description;
- std::function<bool(const StringPiece& value)> action;
- bool required;
- size_t numArgs;
+ private:
+ struct Flag {
+ std::string name;
+ std::string description;
+ std::function<bool(const StringPiece& value)> action;
+ bool required;
+ size_t numArgs;
- bool parsed;
- };
+ bool parsed;
+ };
- std::vector<Flag> mFlags;
- std::vector<std::string> mArgs;
+ std::vector<Flag> mFlags;
+ std::vector<std::string> mArgs;
};
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_FLAGS_H
+#endif // AAPT_FLAGS_H
diff --git a/tools/aapt2/Locale.cpp b/tools/aapt2/Locale.cpp
index f7956c0..a0f7641 100644
--- a/tools/aapt2/Locale.cpp
+++ b/tools/aapt2/Locale.cpp
@@ -17,8 +17,8 @@
#include "Locale.h"
#include "util/Util.h"
-#include <algorithm>
#include <ctype.h>
+#include <algorithm>
#include <string>
#include <vector>
@@ -27,225 +27,226 @@
using android::ResTable_config;
void LocaleValue::setLanguage(const char* languageChars) {
- size_t i = 0;
- while ((*languageChars) != '\0') {
- language[i++] = ::tolower(*languageChars);
- languageChars++;
- }
+ size_t i = 0;
+ while ((*languageChars) != '\0') {
+ language[i++] = ::tolower(*languageChars);
+ languageChars++;
+ }
}
void LocaleValue::setRegion(const char* regionChars) {
- size_t i = 0;
- while ((*regionChars) != '\0') {
- region[i++] = ::toupper(*regionChars);
- regionChars++;
- }
+ size_t i = 0;
+ while ((*regionChars) != '\0') {
+ region[i++] = ::toupper(*regionChars);
+ regionChars++;
+ }
}
void LocaleValue::setScript(const char* scriptChars) {
- size_t i = 0;
- while ((*scriptChars) != '\0') {
- if (i == 0) {
- script[i++] = ::toupper(*scriptChars);
- } else {
- script[i++] = ::tolower(*scriptChars);
- }
- scriptChars++;
+ size_t i = 0;
+ while ((*scriptChars) != '\0') {
+ if (i == 0) {
+ script[i++] = ::toupper(*scriptChars);
+ } else {
+ script[i++] = ::tolower(*scriptChars);
}
+ scriptChars++;
+ }
}
void LocaleValue::setVariant(const char* variantChars) {
- size_t i = 0;
- while ((*variantChars) != '\0') {
- variant[i++] = *variantChars;
- variantChars++;
- }
+ size_t i = 0;
+ while ((*variantChars) != '\0') {
+ variant[i++] = *variantChars;
+ variantChars++;
+ }
}
static inline bool isAlpha(const std::string& str) {
- return std::all_of(std::begin(str), std::end(str), ::isalpha);
+ return std::all_of(std::begin(str), std::end(str), ::isalpha);
}
static inline bool isNumber(const std::string& str) {
- return std::all_of(std::begin(str), std::end(str), ::isdigit);
+ return std::all_of(std::begin(str), std::end(str), ::isdigit);
}
bool LocaleValue::initFromFilterString(const StringPiece& str) {
- // A locale (as specified in the filter) is an underscore separated name such
- // as "en_US", "en_Latn_US", or "en_US_POSIX".
- std::vector<std::string> parts = util::splitAndLowercase(str, '_');
+ // A locale (as specified in the filter) is an underscore separated name such
+ // as "en_US", "en_Latn_US", or "en_US_POSIX".
+ std::vector<std::string> parts = util::splitAndLowercase(str, '_');
- const int numTags = parts.size();
- bool valid = false;
- if (numTags >= 1) {
- const std::string& lang = parts[0];
- if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
- setLanguage(lang.c_str());
- valid = true;
- }
- }
+ const int numTags = parts.size();
+ bool valid = false;
+ if (numTags >= 1) {
+ const std::string& lang = parts[0];
+ if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
+ setLanguage(lang.c_str());
+ valid = true;
+ }
+ }
- if (!valid || numTags == 1) {
- return valid;
- }
+ if (!valid || numTags == 1) {
+ return valid;
+ }
- // At this point, valid == true && numTags > 1.
- const std::string& part2 = parts[1];
- if ((part2.length() == 2 && isAlpha(part2)) ||
- (part2.length() == 3 && isNumber(part2))) {
- setRegion(part2.c_str());
- } else if (part2.length() == 4 && isAlpha(part2)) {
- setScript(part2.c_str());
- } else if (part2.length() >= 4 && part2.length() <= 8) {
- setVariant(part2.c_str());
- } else {
- valid = false;
- }
+ // At this point, valid == true && numTags > 1.
+ const std::string& part2 = parts[1];
+ if ((part2.length() == 2 && isAlpha(part2)) ||
+ (part2.length() == 3 && isNumber(part2))) {
+ setRegion(part2.c_str());
+ } else if (part2.length() == 4 && isAlpha(part2)) {
+ setScript(part2.c_str());
+ } else if (part2.length() >= 4 && part2.length() <= 8) {
+ setVariant(part2.c_str());
+ } else {
+ valid = false;
+ }
- if (!valid || numTags == 2) {
- return valid;
- }
+ if (!valid || numTags == 2) {
+ return valid;
+ }
- // At this point, valid == true && numTags > 1.
- const std::string& part3 = parts[2];
- if (((part3.length() == 2 && isAlpha(part3)) ||
- (part3.length() == 3 && isNumber(part3))) && script[0]) {
- setRegion(part3.c_str());
- } else if (part3.length() >= 4 && part3.length() <= 8) {
- setVariant(part3.c_str());
- } else {
- valid = false;
- }
+ // At this point, valid == true && numTags > 1.
+ const std::string& part3 = parts[2];
+ if (((part3.length() == 2 && isAlpha(part3)) ||
+ (part3.length() == 3 && isNumber(part3))) &&
+ script[0]) {
+ setRegion(part3.c_str());
+ } else if (part3.length() >= 4 && part3.length() <= 8) {
+ setVariant(part3.c_str());
+ } else {
+ valid = false;
+ }
- if (!valid || numTags == 3) {
- return valid;
- }
+ if (!valid || numTags == 3) {
+ return valid;
+ }
- const std::string& part4 = parts[3];
- if (part4.length() >= 4 && part4.length() <= 8) {
- setVariant(part4.c_str());
- } else {
- valid = false;
- }
+ const std::string& part4 = parts[3];
+ if (part4.length() >= 4 && part4.length() <= 8) {
+ setVariant(part4.c_str());
+ } else {
+ valid = false;
+ }
- if (!valid || numTags > 4) {
- return false;
- }
+ if (!valid || numTags > 4) {
+ return false;
+ }
- return true;
+ return true;
}
ssize_t LocaleValue::initFromParts(std::vector<std::string>::iterator iter,
- std::vector<std::string>::iterator end) {
- const std::vector<std::string>::iterator startIter = iter;
+ std::vector<std::string>::iterator end) {
+ const std::vector<std::string>::iterator startIter = iter;
- std::string& part = *iter;
- if (part[0] == 'b' && part[1] == '+') {
- // This is a "modified" BCP 47 language tag. Same semantics as BCP 47 tags,
- // except that the separator is "+" and not "-".
- std::vector<std::string> subtags = util::splitAndLowercase(part, '+');
- subtags.erase(subtags.begin());
- if (subtags.size() == 1) {
- setLanguage(subtags[0].c_str());
- } else if (subtags.size() == 2) {
- setLanguage(subtags[0].c_str());
+ std::string& part = *iter;
+ if (part[0] == 'b' && part[1] == '+') {
+ // This is a "modified" BCP 47 language tag. Same semantics as BCP 47 tags,
+ // except that the separator is "+" and not "-".
+ std::vector<std::string> subtags = util::splitAndLowercase(part, '+');
+ subtags.erase(subtags.begin());
+ if (subtags.size() == 1) {
+ setLanguage(subtags[0].c_str());
+ } else if (subtags.size() == 2) {
+ setLanguage(subtags[0].c_str());
- // The second tag can either be a region, a variant or a script.
- switch (subtags[1].size()) {
- case 2:
- case 3:
- setRegion(subtags[1].c_str());
- break;
- case 4:
- if ('0' <= subtags[1][0] && subtags[1][0] <= '9') {
- // This is a variant: fall through
- } else {
- setScript(subtags[1].c_str());
- break;
- }
- case 5:
- case 6:
- case 7:
- case 8:
- setVariant(subtags[1].c_str());
- break;
- default:
- return -1;
- }
- } else if (subtags.size() == 3) {
- // The language is always the first subtag.
- setLanguage(subtags[0].c_str());
-
- // The second subtag can either be a script or a region code.
- // If its size is 4, it's a script code, else it's a region code.
- if (subtags[1].size() == 4) {
- setScript(subtags[1].c_str());
- } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
- setRegion(subtags[1].c_str());
- } else {
- return -1;
- }
-
- // The third tag can either be a region code (if the second tag was
- // a script), else a variant code.
- if (subtags[2].size() >= 4) {
- setVariant(subtags[2].c_str());
- } else {
- setRegion(subtags[2].c_str());
- }
- } else if (subtags.size() == 4) {
- setLanguage(subtags[0].c_str());
+ // The second tag can either be a region, a variant or a script.
+ switch (subtags[1].size()) {
+ case 2:
+ case 3:
+ setRegion(subtags[1].c_str());
+ break;
+ case 4:
+ if ('0' <= subtags[1][0] && subtags[1][0] <= '9') {
+ // This is a variant: fall through
+ } else {
setScript(subtags[1].c_str());
- setRegion(subtags[2].c_str());
- setVariant(subtags[3].c_str());
- } else {
- return -1;
- }
+ break;
+ }
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ setVariant(subtags[1].c_str());
+ break;
+ default:
+ return -1;
+ }
+ } else if (subtags.size() == 3) {
+ // The language is always the first subtag.
+ setLanguage(subtags[0].c_str());
- ++iter;
+ // The second subtag can either be a script or a region code.
+ // If its size is 4, it's a script code, else it's a region code.
+ if (subtags[1].size() == 4) {
+ setScript(subtags[1].c_str());
+ } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
+ setRegion(subtags[1].c_str());
+ } else {
+ return -1;
+ }
+ // The third tag can either be a region code (if the second tag was
+ // a script), else a variant code.
+ if (subtags[2].size() >= 4) {
+ setVariant(subtags[2].c_str());
+ } else {
+ setRegion(subtags[2].c_str());
+ }
+ } else if (subtags.size() == 4) {
+ setLanguage(subtags[0].c_str());
+ setScript(subtags[1].c_str());
+ setRegion(subtags[2].c_str());
+ setVariant(subtags[3].c_str());
} else {
- if ((part.length() == 2 || part.length() == 3)
- && isAlpha(part) && part != "car") {
- setLanguage(part.c_str());
- ++iter;
-
- if (iter != end) {
- const std::string& regionPart = *iter;
- if (regionPart.c_str()[0] == 'r' && regionPart.length() == 3) {
- setRegion(regionPart.c_str() + 1);
- ++iter;
- }
- }
- }
+ return -1;
}
- return static_cast<ssize_t>(iter - startIter);
+ ++iter;
+
+ } else {
+ if ((part.length() == 2 || part.length() == 3) && isAlpha(part) &&
+ part != "car") {
+ setLanguage(part.c_str());
+ ++iter;
+
+ if (iter != end) {
+ const std::string& regionPart = *iter;
+ if (regionPart.c_str()[0] == 'r' && regionPart.length() == 3) {
+ setRegion(regionPart.c_str() + 1);
+ ++iter;
+ }
+ }
+ }
+ }
+
+ return static_cast<ssize_t>(iter - startIter);
}
void LocaleValue::initFromResTable(const ResTable_config& config) {
- config.unpackLanguage(language);
- config.unpackRegion(region);
- if (config.localeScript[0] && !config.localeScriptWasComputed) {
- memcpy(script, config.localeScript, sizeof(config.localeScript));
- }
+ config.unpackLanguage(language);
+ config.unpackRegion(region);
+ if (config.localeScript[0] && !config.localeScriptWasComputed) {
+ memcpy(script, config.localeScript, sizeof(config.localeScript));
+ }
- if (config.localeVariant[0]) {
- memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
- }
+ if (config.localeVariant[0]) {
+ memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
+ }
}
void LocaleValue::writeTo(ResTable_config* out) const {
- out->packLanguage(language);
- out->packRegion(region);
+ out->packLanguage(language);
+ out->packRegion(region);
- if (script[0]) {
- memcpy(out->localeScript, script, sizeof(out->localeScript));
- }
+ if (script[0]) {
+ memcpy(out->localeScript, script, sizeof(out->localeScript));
+ }
- if (variant[0]) {
- memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
- }
+ if (variant[0]) {
+ memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
+ }
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/Locale.h b/tools/aapt2/Locale.h
index 33f80ad..0f75850 100644
--- a/tools/aapt2/Locale.h
+++ b/tools/aapt2/Locale.h
@@ -29,86 +29,84 @@
* A convenience class to build and parse locales.
*/
struct LocaleValue {
- char language[4];
- char region[4];
- char script[4];
- char variant[8];
+ char language[4];
+ char region[4];
+ char script[4];
+ char variant[8];
- inline LocaleValue();
+ inline LocaleValue();
- /**
- * Initialize this LocaleValue from a config string.
- */
- bool initFromFilterString(const StringPiece& config);
+ /**
+ * Initialize this LocaleValue from a config string.
+ */
+ bool initFromFilterString(const StringPiece& config);
- /**
- * Initialize this LocaleValue from parts of a vector.
- */
- ssize_t initFromParts(std::vector<std::string>::iterator iter,
- std::vector<std::string>::iterator end);
+ /**
+ * Initialize this LocaleValue from parts of a vector.
+ */
+ ssize_t initFromParts(std::vector<std::string>::iterator iter,
+ std::vector<std::string>::iterator end);
- /**
- * Initialize this LocaleValue from a ResTable_config.
- */
- void initFromResTable(const android::ResTable_config& config);
+ /**
+ * Initialize this LocaleValue from a ResTable_config.
+ */
+ void initFromResTable(const android::ResTable_config& config);
- /**
- * Set the locale in a ResTable_config from this LocaleValue.
- */
- void writeTo(android::ResTable_config* out) const;
+ /**
+ * Set the locale in a ResTable_config from this LocaleValue.
+ */
+ void writeTo(android::ResTable_config* out) const;
- inline int compare(const LocaleValue& other) const;
+ inline int compare(const LocaleValue& other) const;
- inline bool operator<(const LocaleValue& o) const;
- inline bool operator<=(const LocaleValue& o) const;
- inline bool operator==(const LocaleValue& o) const;
- inline bool operator!=(const LocaleValue& o) const;
- inline bool operator>=(const LocaleValue& o) const;
- inline bool operator>(const LocaleValue& o) const;
+ inline bool operator<(const LocaleValue& o) const;
+ inline bool operator<=(const LocaleValue& o) const;
+ inline bool operator==(const LocaleValue& o) const;
+ inline bool operator!=(const LocaleValue& o) const;
+ inline bool operator>=(const LocaleValue& o) const;
+ inline bool operator>(const LocaleValue& o) const;
-private:
- void setLanguage(const char* language);
- void setRegion(const char* language);
- void setScript(const char* script);
- void setVariant(const char* variant);
+ private:
+ void setLanguage(const char* language);
+ void setRegion(const char* language);
+ void setScript(const char* script);
+ void setVariant(const char* variant);
};
//
// Implementation
//
-LocaleValue::LocaleValue() {
- memset(this, 0, sizeof(LocaleValue));
-}
+LocaleValue::LocaleValue() { memset(this, 0, sizeof(LocaleValue)); }
int LocaleValue::compare(const LocaleValue& other) const {
- return memcmp(this, &other, sizeof(LocaleValue));
+ return memcmp(this, &other, sizeof(LocaleValue));
}
bool LocaleValue::operator<(const LocaleValue& o) const {
- return compare(o) < 0;
+ return compare(o) < 0;
}
bool LocaleValue::operator<=(const LocaleValue& o) const {
- return compare(o) <= 0;
+ return compare(o) <= 0;
}
bool LocaleValue::operator==(const LocaleValue& o) const {
- return compare(o) == 0;
+ return compare(o) == 0;
}
bool LocaleValue::operator!=(const LocaleValue& o) const {
- return compare(o) != 0;
+ return compare(o) != 0;
}
bool LocaleValue::operator>=(const LocaleValue& o) const {
- return compare(o) >= 0;
+ return compare(o) >= 0;
}
bool LocaleValue::operator>(const LocaleValue& o) const {
- return compare(o) > 0;
+ return compare(o) > 0;
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_LOCALE_VALUE_H
+#endif // AAPT_LOCALE_VALUE_H
diff --git a/tools/aapt2/Locale_test.cpp b/tools/aapt2/Locale_test.cpp
index e4b8ce7..b82d2b9 100644
--- a/tools/aapt2/Locale_test.cpp
+++ b/tools/aapt2/Locale_test.cpp
@@ -22,61 +22,73 @@
namespace aapt {
-static ::testing::AssertionResult TestLanguage(const char* input, const char* lang) {
- std::vector<std::string> parts = util::splitAndLowercase(input, '-');
- LocaleValue lv;
- ssize_t count = lv.initFromParts(std::begin(parts), std::end(parts));
- if (count < 0) {
- return ::testing::AssertionFailure() << " failed to parse '" << input << "'.";
- }
+static ::testing::AssertionResult TestLanguage(const char* input,
+ const char* lang) {
+ std::vector<std::string> parts = util::splitAndLowercase(input, '-');
+ LocaleValue lv;
+ ssize_t count = lv.initFromParts(std::begin(parts), std::end(parts));
+ if (count < 0) {
+ return ::testing::AssertionFailure() << " failed to parse '" << input
+ << "'.";
+ }
- if (count != 1) {
- return ::testing::AssertionFailure() << count
- << " parts were consumed parsing '" << input << "' but expected 1.";
- }
+ if (count != 1) {
+ return ::testing::AssertionFailure()
+ << count << " parts were consumed parsing '" << input
+ << "' but expected 1.";
+ }
- if (memcmp(lv.language, lang, std::min(strlen(lang), sizeof(lv.language))) != 0) {
- return ::testing::AssertionFailure() << "expected " << lang << " but got "
- << std::string(lv.language, sizeof(lv.language)) << ".";
- }
+ if (memcmp(lv.language, lang, std::min(strlen(lang), sizeof(lv.language))) !=
+ 0) {
+ return ::testing::AssertionFailure()
+ << "expected " << lang << " but got "
+ << std::string(lv.language, sizeof(lv.language)) << ".";
+ }
- return ::testing::AssertionSuccess();
+ return ::testing::AssertionSuccess();
}
-static ::testing::AssertionResult TestLanguageRegion(const char* input, const char* lang,
+static ::testing::AssertionResult TestLanguageRegion(const char* input,
+ const char* lang,
const char* region) {
- std::vector<std::string> parts = util::splitAndLowercase(input, '-');
- LocaleValue lv;
- ssize_t count = lv.initFromParts(std::begin(parts), std::end(parts));
- if (count < 0) {
- return ::testing::AssertionFailure() << " failed to parse '" << 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) {
+ return ::testing::AssertionFailure() << " failed to parse '" << input
+ << "'.";
+ }
- if (count != 2) {
- return ::testing::AssertionFailure() << count
- << " parts were consumed parsing '" << input << "' but expected 2.";
- }
+ if (count != 2) {
+ return ::testing::AssertionFailure()
+ << count << " parts were consumed parsing '" << input
+ << "' but expected 2.";
+ }
- if (memcmp(lv.language, lang, std::min(strlen(lang), sizeof(lv.language))) != 0) {
- return ::testing::AssertionFailure() << "expected " << input << " but got "
- << std::string(lv.language, sizeof(lv.language)) << ".";
- }
+ if (memcmp(lv.language, lang, std::min(strlen(lang), sizeof(lv.language))) !=
+ 0) {
+ return ::testing::AssertionFailure()
+ << "expected " << input << " but got "
+ << std::string(lv.language, sizeof(lv.language)) << ".";
+ }
- if (memcmp(lv.region, region, std::min(strlen(region), sizeof(lv.region))) != 0) {
- return ::testing::AssertionFailure() << "expected " << region << " but got "
- << std::string(lv.region, sizeof(lv.region)) << ".";
- }
+ if (memcmp(lv.region, region, std::min(strlen(region), sizeof(lv.region))) !=
+ 0) {
+ return ::testing::AssertionFailure()
+ << "expected " << region << " but got "
+ << std::string(lv.region, sizeof(lv.region)) << ".";
+ }
- return ::testing::AssertionSuccess();
+ return ::testing::AssertionSuccess();
}
TEST(ConfigDescriptionTest, ParseLanguage) {
- EXPECT_TRUE(TestLanguage("en", "en"));
- EXPECT_TRUE(TestLanguage("fr", "fr"));
- EXPECT_FALSE(TestLanguage("land", ""));
- EXPECT_TRUE(TestLanguage("fr-land", "fr"));
+ EXPECT_TRUE(TestLanguage("en", "en"));
+ EXPECT_TRUE(TestLanguage("fr", "fr"));
+ EXPECT_FALSE(TestLanguage("land", ""));
+ EXPECT_TRUE(TestLanguage("fr-land", "fr"));
- EXPECT_TRUE(TestLanguageRegion("fr-rCA", "fr", "CA"));
+ EXPECT_TRUE(TestLanguageRegion("fr-rCA", "fr", "CA"));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index f3f70d6..8aedd44 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -28,9 +28,9 @@
static const char* sMinorVersion = "2";
int printVersion() {
- std::cerr << "Android Asset Packaging Tool (aapt) "
- << sMajorVersion << "." << sMinorVersion << std::endl;
- return 0;
+ std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "."
+ << sMinorVersion << std::endl;
+ return 0;
}
extern int compile(const std::vector<StringPiece>& args);
@@ -38,35 +38,36 @@
extern int dump(const std::vector<StringPiece>& args);
extern int diff(const std::vector<StringPiece>& args);
-} // namespace aapt
+} // namespace aapt
int main(int argc, char** argv) {
- if (argc >= 2) {
- argv += 1;
- argc -= 1;
+ if (argc >= 2) {
+ argv += 1;
+ argc -= 1;
- std::vector<aapt::StringPiece> args;
- for (int i = 1; i < argc; i++) {
- args.push_back(argv[i]);
- }
-
- aapt::StringPiece command(argv[0]);
- if (command == "compile" || command == "c") {
- return aapt::compile(args);
- } else if (command == "link" || command == "l") {
- return aapt::link(args);
- } else if (command == "dump" || command == "d") {
- return aapt::dump(args);
- } else if (command == "diff") {
- return aapt::diff(args);
- } else if (command == "version") {
- return aapt::printVersion();
- }
- std::cerr << "unknown command '" << command << "'\n";
- } else {
- std::cerr << "no command specified\n";
+ std::vector<aapt::StringPiece> args;
+ for (int i = 1; i < argc; i++) {
+ args.push_back(argv[i]);
}
- std::cerr << "\nusage: aapt2 [compile|link|dump|diff|version] ..." << std::endl;
- return 1;
+ aapt::StringPiece command(argv[0]);
+ if (command == "compile" || command == "c") {
+ return aapt::compile(args);
+ } else if (command == "link" || command == "l") {
+ return aapt::link(args);
+ } else if (command == "dump" || command == "d") {
+ return aapt::dump(args);
+ } else if (command == "diff") {
+ return aapt::diff(args);
+ } else if (command == "version") {
+ return aapt::printVersion();
+ }
+ std::cerr << "unknown command '" << command << "'\n";
+ } else {
+ std::cerr << "no command specified\n";
+ }
+
+ std::cerr << "\nusage: aapt2 [compile|link|dump|diff|version] ..."
+ << std::endl;
+ return 1;
}
diff --git a/tools/aapt2/NameMangler.h b/tools/aapt2/NameMangler.h
index b6aaa4d..6d244aa 100644
--- a/tools/aapt2/NameMangler.h
+++ b/tools/aapt2/NameMangler.h
@@ -26,69 +26,71 @@
namespace aapt {
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::string targetPackageName;
+ /**
+ * Represents the package we are trying to build. References pointing
+ * to this package are not mangled, and mangled references inherit this
+ * package name.
+ */
+ std::string targetPackageName;
- /**
- * We must know which references to mangle, and which to keep (android vs. com.android.support).
- */
- std::set<std::string> packagesToMangle;
+ /**
+ * We must know which references to mangle, and which to keep (android vs.
+ * com.android.support).
+ */
+ std::set<std::string> packagesToMangle;
};
class NameMangler {
-private:
- NameManglerPolicy mPolicy;
+ private:
+ NameManglerPolicy mPolicy;
-public:
- explicit NameMangler(NameManglerPolicy policy) : mPolicy(policy) {
+ public:
+ explicit NameMangler(NameManglerPolicy policy) : mPolicy(policy) {}
+
+ Maybe<ResourceName> mangleName(const ResourceName& name) {
+ if (mPolicy.targetPackageName == name.package ||
+ mPolicy.packagesToMangle.count(name.package) == 0) {
+ return {};
}
- Maybe<ResourceName> mangleName(const ResourceName& name) {
- if (mPolicy.targetPackageName == name.package ||
- mPolicy.packagesToMangle.count(name.package) == 0) {
- return {};
- }
+ std::string mangledEntryName = mangleEntry(name.package, name.entry);
+ return ResourceName(mPolicy.targetPackageName, name.type, mangledEntryName);
+ }
- std::string mangledEntryName = mangleEntry(name.package, name.entry);
- return ResourceName(mPolicy.targetPackageName, name.type, mangledEntryName);
+ bool shouldMangle(const std::string& package) const {
+ if (package.empty() || mPolicy.targetPackageName == package) {
+ return false;
+ }
+ return mPolicy.packagesToMangle.count(package) != 0;
+ }
+
+ /**
+ * Returns a mangled name that is a combination of `name` and `package`.
+ * The mangled name should contain symbols that are illegal to define in XML,
+ * so that there will never be name mangling collisions.
+ */
+ static std::string mangleEntry(const std::string& package,
+ const std::string& name) {
+ return package + "$" + name;
+ }
+
+ /**
+ * Unmangles the name in `outName`, storing the correct name back in `outName`
+ * 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::string* outName, std::string* outPackage) {
+ size_t pivot = outName->find('$');
+ if (pivot == std::string::npos) {
+ return false;
}
- bool shouldMangle(const std::string& package) const {
- if (package.empty() || mPolicy.targetPackageName == package) {
- return false;
- }
- return mPolicy.packagesToMangle.count(package) != 0;
- }
-
- /**
- * Returns a mangled name that is a combination of `name` and `package`.
- * The mangled name should contain symbols that are illegal to define in XML,
- * so that there will never be name mangling collisions.
- */
- static std::string mangleEntry(const std::string& package, const std::string& name) {
- return package + "$" + name;
- }
-
- /**
- * Unmangles the name in `outName`, storing the correct name back in `outName`
- * 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::string* outName, std::string* outPackage) {
- size_t pivot = outName->find('$');
- if (pivot == std::string::npos) {
- return false;
- }
-
- outPackage->assign(outName->data(), pivot);
- outName->assign(outName->data() + pivot + 1, outName->size() - (pivot + 1));
- return true;
- }
+ outPackage->assign(outName->data(), pivot);
+ outName->assign(outName->data() + pivot + 1, outName->size() - (pivot + 1));
+ return true;
+ }
};
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_NAME_MANGLER_H
+#endif // AAPT_NAME_MANGLER_H
diff --git a/tools/aapt2/NameMangler_test.cpp b/tools/aapt2/NameMangler_test.cpp
index f624df2..b02986d 100644
--- a/tools/aapt2/NameMangler_test.cpp
+++ b/tools/aapt2/NameMangler_test.cpp
@@ -22,25 +22,25 @@
namespace aapt {
TEST(NameManglerTest, MangleName) {
- std::string package = "android.appcompat";
- std::string name = "Platform.AppCompat";
+ std::string package = "android.appcompat";
+ std::string name = "Platform.AppCompat";
- std::string mangledName = NameMangler::mangleEntry(package, name);
- EXPECT_EQ(mangledName, "android.appcompat$Platform.AppCompat");
+ std::string mangledName = NameMangler::mangleEntry(package, name);
+ EXPECT_EQ(mangledName, "android.appcompat$Platform.AppCompat");
- std::string unmangledPackage;
- std::string unmangledName = mangledName;
- ASSERT_TRUE(NameMangler::unmangle(&unmangledName, &unmangledPackage));
- EXPECT_EQ(unmangledName, "Platform.AppCompat");
- EXPECT_EQ(unmangledPackage, "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::string package;
- std::string name = "foo_bar";
+ std::string package;
+ std::string name = "foo_bar";
- EXPECT_FALSE(NameMangler::unmangle(&name, &package));
- EXPECT_EQ(name, "foo_bar");
+ EXPECT_FALSE(NameMangler::unmangle(&name, &package));
+ EXPECT_EQ(name, "foo_bar");
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index b7a091e..6805631 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -23,74 +23,97 @@
namespace aapt {
StringPiece toString(ResourceType type) {
- switch (type) {
- 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 {};
+ switch (type) {
+ 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<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 },
+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 StringPiece& str) {
- auto iter = sResourceTypeMap.find(str);
- if (iter == std::end(sResourceTypeMap)) {
- return nullptr;
- }
- return &iter->second;
+ auto iter = sResourceTypeMap.find(str);
+ if (iter == std::end(sResourceTypeMap)) {
+ return nullptr;
+ }
+ return &iter->second;
}
bool operator<(const ResourceKey& a, const ResourceKey& b) {
- return std::tie(a.name, a.config) < std::tie(b.name, b.config);
+ return std::tie(a.name, a.config) < std::tie(b.name, b.config);
}
bool operator<(const ResourceKeyRef& a, const ResourceKeyRef& b) {
- return std::tie(a.name, a.config) < std::tie(b.name, b.config);
+ return std::tie(a.name, a.config) < std::tie(b.name, b.config);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 2969b8c..30739db 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -37,29 +37,29 @@
* to the 'type' in package:type/entry.
*/
enum class ResourceType {
- kAnim,
- kAnimator,
- kArray,
- kAttr,
- kAttrPrivate,
- kBool,
- kColor,
- kDimen,
- kDrawable,
- kFraction,
- kId,
- kInteger,
- kInterpolator,
- kLayout,
- kMenu,
- kMipmap,
- kPlurals,
- kRaw,
- kString,
- kStyle,
- kStyleable,
- kTransition,
- kXml,
+ kAnim,
+ kAnimator,
+ kArray,
+ kAttr,
+ kAttrPrivate,
+ kBool,
+ kColor,
+ kDimen,
+ kDrawable,
+ kFraction,
+ kId,
+ kInteger,
+ kInterpolator,
+ kLayout,
+ kMenu,
+ kMipmap,
+ kPlurals,
+ kRaw,
+ kString,
+ kStyle,
+ kStyleable,
+ kTransition,
+ kXml,
};
StringPiece toString(ResourceType type);
@@ -75,17 +75,17 @@
* a resource in the ResourceTable.
*/
struct ResourceName {
- std::string package;
- ResourceType type;
- std::string entry;
+ std::string package;
+ ResourceType type;
+ std::string entry;
- ResourceName() : type(ResourceType::kRaw) {}
- ResourceName(const StringPiece& p, ResourceType t, const StringPiece& e);
+ ResourceName() : type(ResourceType::kRaw) {}
+ ResourceName(const StringPiece& p, ResourceType t, const StringPiece& e);
- int compare(const ResourceName& other) const;
+ int compare(const ResourceName& other) const;
- bool isValid() const;
- std::string toString() const;
+ bool isValid() const;
+ std::string toString() const;
};
/**
@@ -95,21 +95,21 @@
* of the original string.
*/
struct ResourceNameRef {
- StringPiece package;
- ResourceType type;
- StringPiece entry;
+ StringPiece package;
+ ResourceType type;
+ StringPiece entry;
- ResourceNameRef() = default;
- ResourceNameRef(const ResourceNameRef&) = default;
- ResourceNameRef(ResourceNameRef&&) = default;
- ResourceNameRef(const ResourceName& rhs); // NOLINT(implicit)
- 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);
+ ResourceNameRef() = default;
+ ResourceNameRef(const ResourceNameRef&) = default;
+ ResourceNameRef(ResourceNameRef&&) = default;
+ ResourceNameRef(const ResourceName& rhs); // NOLINT(implicit)
+ 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);
- ResourceName toResourceName() const;
- bool isValid() const;
+ ResourceName toResourceName() const;
+ bool isValid() const;
};
/**
@@ -124,64 +124,66 @@
* EEEE: 16 bit entry identifier.
*/
struct ResourceId {
- uint32_t id;
+ uint32_t id;
- ResourceId();
- ResourceId(const ResourceId& rhs);
- ResourceId(uint32_t resId); // NOLINT(implicit)
- ResourceId(uint8_t p, uint8_t t, uint16_t e);
+ ResourceId();
+ ResourceId(const ResourceId& rhs);
+ ResourceId(uint32_t resId); // NOLINT(implicit)
+ ResourceId(uint8_t p, uint8_t t, uint16_t e);
- bool isValid() const;
- uint8_t packageId() const;
- uint8_t typeId() const;
- uint16_t entryId() const;
+ bool isValid() const;
+ uint8_t packageId() const;
+ uint8_t typeId() const;
+ uint16_t entryId() const;
};
struct SourcedResourceName {
- ResourceName name;
- size_t line;
+ ResourceName name;
+ size_t line;
};
struct ResourceFile {
- // Name
- ResourceName name;
+ // Name
+ ResourceName name;
- // Configuration
- ConfigDescription config;
+ // Configuration
+ ConfigDescription config;
- // Source
- Source source;
+ // Source
+ Source source;
- // Exported symbols
- std::vector<SourcedResourceName> exportedSymbols;
+ // Exported symbols
+ std::vector<SourcedResourceName> exportedSymbols;
};
/**
- * Useful struct used as a key to represent a unique resource in associative containers.
+ * Useful struct used as a key to represent a unique resource in associative
+ * containers.
*/
struct ResourceKey {
- ResourceName name;
- ConfigDescription config;
+ ResourceName name;
+ ConfigDescription config;
};
bool operator<(const ResourceKey& a, const ResourceKey& b);
/**
- * Useful struct used as a key to represent a unique resource in associative containers.
+ * Useful struct used as a key to represent a unique resource in associative
+ * containers.
* Holds a reference to the name, so that name better live longer than this key!
*/
struct ResourceKeyRef {
- ResourceNameRef name;
- ConfigDescription config;
+ ResourceNameRef name;
+ ConfigDescription config;
- ResourceKeyRef() = default;
- ResourceKeyRef(const ResourceNameRef& n, const ConfigDescription& c) : name(n), config(c) {
- }
+ ResourceKeyRef() = default;
+ ResourceKeyRef(const ResourceNameRef& n, const ConfigDescription& c)
+ : name(n), config(c) {}
- /**
- * Prevent taking a reference to a temporary. This is bad.
- */
- ResourceKeyRef(ResourceName&& n, const ConfigDescription& c) = delete;
+ /**
+ * Prevent taking a reference to a temporary. This is bad.
+ */
+ ResourceKeyRef(ResourceName&& n, const ConfigDescription& c) = delete;
};
bool operator<(const ResourceKeyRef& a, const ResourceKeyRef& b);
@@ -190,193 +192,194 @@
// ResourceId implementation.
//
-inline ResourceId::ResourceId() : id(0) {
-}
+inline ResourceId::ResourceId() : id(0) {}
-inline ResourceId::ResourceId(const ResourceId& rhs) : id(rhs.id) {
-}
+inline ResourceId::ResourceId(const ResourceId& rhs) : id(rhs.id) {}
-inline ResourceId::ResourceId(uint32_t resId) : id(resId) {
-}
+inline ResourceId::ResourceId(uint32_t resId) : id(resId) {}
-inline ResourceId::ResourceId(uint8_t p, uint8_t t, uint16_t e) : id((p << 24) | (t << 16) | e) {
-}
+inline ResourceId::ResourceId(uint8_t p, uint8_t t, uint16_t e)
+ : id((p << 24) | (t << 16) | e) {}
inline bool ResourceId::isValid() const {
- return (id & 0xff000000u) != 0 && (id & 0x00ff0000u) != 0;
+ return (id & 0xff000000u) != 0 && (id & 0x00ff0000u) != 0;
}
inline uint8_t ResourceId::packageId() const {
- return static_cast<uint8_t>(id >> 24);
+ return static_cast<uint8_t>(id >> 24);
}
inline uint8_t ResourceId::typeId() const {
- return static_cast<uint8_t>(id >> 16);
+ return static_cast<uint8_t>(id >> 16);
}
inline uint16_t ResourceId::entryId() const {
- return static_cast<uint16_t>(id);
+ return static_cast<uint16_t>(id);
}
inline bool operator<(const ResourceId& lhs, const ResourceId& rhs) {
- return lhs.id < rhs.id;
+ return lhs.id < rhs.id;
}
inline bool operator>(const ResourceId& lhs, const ResourceId& rhs) {
- return lhs.id > rhs.id;
+ return lhs.id > rhs.id;
}
inline bool operator==(const ResourceId& lhs, const ResourceId& rhs) {
- return lhs.id == rhs.id;
+ return lhs.id == rhs.id;
}
inline bool operator!=(const ResourceId& lhs, const ResourceId& rhs) {
- return lhs.id != rhs.id;
+ return lhs.id != rhs.id;
}
-inline ::std::ostream& operator<<(::std::ostream& out, const ResourceId& resId) {
- std::ios_base::fmtflags oldFlags = out.flags();
- char oldFill = out.fill();
- out << "0x" << std::internal << std::setfill('0') << std::setw(8)
- << std::hex << resId.id;
- out.flags(oldFlags);
- out.fill(oldFill);
- return out;
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const ResourceId& resId) {
+ std::ios_base::fmtflags oldFlags = out.flags();
+ char oldFill = out.fill();
+ out << "0x" << std::internal << std::setfill('0') << std::setw(8) << std::hex
+ << resId.id;
+ out.flags(oldFlags);
+ out.fill(oldFill);
+ return out;
}
//
// ResourceType implementation.
//
-inline ::std::ostream& operator<<(::std::ostream& out, const ResourceType& val) {
- return out << toString(val);
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const ResourceType& val) {
+ return out << toString(val);
}
//
// ResourceName implementation.
//
-inline ResourceName::ResourceName(const StringPiece& p, ResourceType t, const StringPiece& e) :
- package(p.toString()), type(t), entry(e.toString()) {
-}
+inline ResourceName::ResourceName(const StringPiece& p, ResourceType t,
+ const StringPiece& e)
+ : package(p.toString()), type(t), entry(e.toString()) {}
inline int ResourceName::compare(const ResourceName& other) const {
- int cmp = package.compare(other.package);
- if (cmp != 0) return cmp;
- cmp = static_cast<int>(type) - static_cast<int>(other.type);
- if (cmp != 0) return cmp;
- cmp = entry.compare(other.entry);
- return cmp;
+ int cmp = package.compare(other.package);
+ if (cmp != 0) return cmp;
+ cmp = static_cast<int>(type) - static_cast<int>(other.type);
+ if (cmp != 0) return cmp;
+ cmp = entry.compare(other.entry);
+ return cmp;
}
inline bool ResourceName::isValid() const {
- return !package.empty() && !entry.empty();
+ return !package.empty() && !entry.empty();
}
inline bool operator<(const ResourceName& lhs, const ResourceName& rhs) {
- return std::tie(lhs.package, lhs.type, lhs.entry)
- < std::tie(rhs.package, rhs.type, rhs.entry);
+ return std::tie(lhs.package, lhs.type, lhs.entry) <
+ std::tie(rhs.package, rhs.type, rhs.entry);
}
inline bool operator==(const ResourceName& lhs, const ResourceName& rhs) {
- return std::tie(lhs.package, lhs.type, lhs.entry)
- == std::tie(rhs.package, rhs.type, rhs.entry);
+ return std::tie(lhs.package, lhs.type, lhs.entry) ==
+ std::tie(rhs.package, rhs.type, rhs.entry);
}
inline bool operator!=(const ResourceName& lhs, const ResourceName& rhs) {
- return std::tie(lhs.package, lhs.type, lhs.entry)
- != std::tie(rhs.package, rhs.type, rhs.entry);
+ return std::tie(lhs.package, lhs.type, lhs.entry) !=
+ std::tie(rhs.package, rhs.type, rhs.entry);
}
-inline ::std::ostream& operator<<(::std::ostream& out, const ResourceName& name) {
- if (!name.package.empty()) {
- out << name.package << ":";
- }
- return out << name.type << "/" << name.entry;
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const ResourceName& name) {
+ if (!name.package.empty()) {
+ out << name.package << ":";
+ }
+ return out << name.type << "/" << name.entry;
}
inline std::string ResourceName::toString() const {
- std::stringstream stream;
- stream << *this;
- return stream.str();
+ std::stringstream stream;
+ stream << *this;
+ return stream.str();
}
//
// ResourceNameRef implementation.
//
-inline ResourceNameRef::ResourceNameRef(const ResourceName& rhs) :
- package(rhs.package), type(rhs.type), entry(rhs.entry) {
-}
+inline ResourceNameRef::ResourceNameRef(const ResourceName& rhs)
+ : package(rhs.package), type(rhs.type), entry(rhs.entry) {}
inline ResourceNameRef::ResourceNameRef(const StringPiece& p, ResourceType t,
- const StringPiece& e) :
- package(p), type(t), entry(e) {
-}
+ const StringPiece& e)
+ : package(p), type(t), entry(e) {}
inline ResourceNameRef& ResourceNameRef::operator=(const ResourceName& rhs) {
- package = rhs.package;
- type = rhs.type;
- entry = rhs.entry;
- return *this;
+ package = rhs.package;
+ type = rhs.type;
+ entry = rhs.entry;
+ return *this;
}
inline ResourceName ResourceNameRef::toResourceName() const {
- return ResourceName(package, type, entry);
+ return ResourceName(package, type, entry);
}
inline bool ResourceNameRef::isValid() const {
- return !package.empty() && !entry.empty();
+ return !package.empty() && !entry.empty();
}
inline bool operator<(const ResourceNameRef& lhs, const ResourceNameRef& rhs) {
- return std::tie(lhs.package, lhs.type, lhs.entry)
- < std::tie(rhs.package, rhs.type, rhs.entry);
+ return std::tie(lhs.package, lhs.type, lhs.entry) <
+ std::tie(rhs.package, rhs.type, rhs.entry);
}
inline bool operator==(const ResourceNameRef& lhs, const ResourceNameRef& rhs) {
- return std::tie(lhs.package, lhs.type, lhs.entry)
- == std::tie(rhs.package, rhs.type, rhs.entry);
+ return std::tie(lhs.package, lhs.type, lhs.entry) ==
+ std::tie(rhs.package, rhs.type, rhs.entry);
}
inline bool operator!=(const ResourceNameRef& lhs, const ResourceNameRef& rhs) {
- return std::tie(lhs.package, lhs.type, lhs.entry)
- != std::tie(rhs.package, rhs.type, rhs.entry);
+ return std::tie(lhs.package, lhs.type, lhs.entry) !=
+ std::tie(rhs.package, rhs.type, rhs.entry);
}
-inline ::std::ostream& operator<<(::std::ostream& out, const ResourceNameRef& name) {
- if (!name.package.empty()) {
- out << name.package << ":";
- }
- return out << name.type << "/" << name.entry;
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const ResourceNameRef& name) {
+ if (!name.package.empty()) {
+ out << name.package << ":";
+ }
+ return out << name.type << "/" << name.entry;
}
inline bool operator<(const ResourceName& lhs, const ResourceNameRef& b) {
- return ResourceNameRef(lhs) < b;
+ return ResourceNameRef(lhs) < b;
}
inline bool operator!=(const ResourceName& lhs, const ResourceNameRef& rhs) {
- return ResourceNameRef(lhs) != rhs;
+ return ResourceNameRef(lhs) != rhs;
}
-inline bool operator==(const SourcedResourceName& lhs, const SourcedResourceName& rhs) {
- return lhs.name == rhs.name && lhs.line == rhs.line;
+inline bool operator==(const SourcedResourceName& lhs,
+ const SourcedResourceName& rhs) {
+ return lhs.name == rhs.name && lhs.line == rhs.line;
}
-} // namespace aapt
+} // namespace aapt
namespace std {
-template <> struct hash<aapt::ResourceName> {
- size_t operator()(const aapt::ResourceName& name) const {
- android::hash_t h = 0;
- h = android::JenkinsHashMix(h, hash<string>()(name.package));
- h = android::JenkinsHashMix(h, static_cast<uint32_t>(name.type));
- h = android::JenkinsHashMix(h, hash<string>()(name.entry));
- return static_cast<size_t>(h);
- }
+template <>
+struct hash<aapt::ResourceName> {
+ size_t operator()(const aapt::ResourceName& name) const {
+ android::hash_t h = 0;
+ h = android::JenkinsHashMix(h, hash<string>()(name.package));
+ h = android::JenkinsHashMix(h, static_cast<uint32_t>(name.type));
+ h = android::JenkinsHashMix(h, hash<string>()(name.entry));
+ return static_cast<size_t>(h);
+ }
};
-} // namespace std
+} // namespace std
-#endif // AAPT_RESOURCE_H
+#endif // AAPT_RESOURCE_H
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index c430c46..51aed13 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -28,445 +28,488 @@
namespace aapt {
-constexpr const char* sXliffNamespaceUri = "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.
+ * Returns true if the element is <skip> or <eat-comment> and can be safely
+ * ignored.
*/
-static bool shouldIgnoreElement(const StringPiece& ns, const StringPiece& name) {
- return ns.empty() && (name == "skip" || name == "eat-comment");
+static bool shouldIgnoreElement(const StringPiece& ns,
+ const StringPiece& name) {
+ return ns.empty() && (name == "skip" || name == "eat-comment");
}
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;
+ 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 StringPiece& str) {
- uint32_t mask = 0;
- for (StringPiece part : util::tokenize(str, '|')) {
- StringPiece trimmedPart = util::trimWhitespace(part);
- uint32_t type = parseFormatType(trimmedPart);
- if (type == 0) {
- return 0;
- }
- mask |= type;
+ uint32_t mask = 0;
+ for (StringPiece part : util::tokenize(str, '|')) {
+ StringPiece trimmedPart = util::trimWhitespace(part);
+ uint32_t type = parseFormatType(trimmedPart);
+ if (type == 0) {
+ return 0;
}
- return mask;
+ mask |= type;
+ }
+ return mask;
}
/**
* A parsed resource ready to be added to the ResourceTable.
*/
struct ParsedResource {
- ResourceName name;
- ConfigDescription config;
- std::string product;
- Source source;
- ResourceId id;
- Maybe<SymbolState> symbolState;
- std::string comment;
- std::unique_ptr<Value> value;
- std::list<ParsedResource> childResources;
+ ResourceName name;
+ ConfigDescription config;
+ std::string product;
+ Source source;
+ ResourceId id;
+ Maybe<SymbolState> symbolState;
+ 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) {
- 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();
- }
+static bool addResourcesToTable(ResourceTable* table, IDiagnostics* diag,
+ ParsedResource* res) {
+ 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();
+ }
- if (res->symbolState) {
- Symbol symbol;
- symbol.state = res->symbolState.value();
- symbol.source = res->source;
- symbol.comment = res->comment;
- if (!table->setSymbolState(res->name, res->id, symbol, diag)) {
- return false;
- }
+ if (res->symbolState) {
+ Symbol symbol;
+ symbol.state = res->symbolState.value();
+ symbol.source = res->source;
+ symbol.comment = res->comment;
+ if (!table->setSymbolState(res->name, res->id, symbol, diag)) {
+ return false;
}
+ }
- if (res->value) {
- // Attach the comment, source and config to the value.
- res->value->setComment(std::move(res->comment));
- res->value->setSource(std::move(res->source));
+ if (res->value) {
+ // Attach the comment, source and config to the value.
+ res->value->setComment(std::move(res->comment));
+ res->value->setSource(std::move(res->source));
- if (!table->addResource(res->name, res->id, res->config, res->product,
- std::move(res->value), diag)) {
- return false;
- }
+ if (!table->addResource(res->name, res->id, res->config, res->product,
+ std::move(res->value), diag)) {
+ return false;
}
+ }
- bool error = false;
- for (ParsedResource& child : res->childResources) {
- error |= !addResourcesToTable(table, diag, &child);
- }
- return !error;
+ bool error = false;
+ for (ParsedResource& child : res->childResources) {
+ error |= !addResourcesToTable(table, diag, &child);
+ }
+ return !error;
}
// Convenient aliases for more readable function calls.
-enum {
- kAllowRawString = true,
- kNoRawString = false
-};
+enum { kAllowRawString = true, kNoRawString = false };
-ResourceParser::ResourceParser(IDiagnostics* diag, ResourceTable* table, const Source& source,
+ResourceParser::ResourceParser(IDiagnostics* diag, ResourceTable* table,
+ const Source& source,
const ConfigDescription& config,
- const ResourceParserOptions& options) :
- mDiag(diag), mTable(table), mSource(source), mConfig(config), mOptions(options) {
-}
+ const ResourceParserOptions& options)
+ : mDiag(diag),
+ mTable(table),
+ mSource(source),
+ mConfig(config),
+ mOptions(options) {}
/**
* Build a string from XML that converts nested elements into Span objects.
*/
-bool ResourceParser::flattenXmlSubtree(xml::XmlPullParser* parser, std::string* outRawString,
+bool ResourceParser::flattenXmlSubtree(xml::XmlPullParser* parser,
+ std::string* outRawString,
StyleString* outStyleString) {
- std::vector<Span> spanStack;
+ std::vector<Span> spanStack;
- bool error = false;
- outRawString->clear();
- outStyleString->spans.clear();
- util::StringBuilder builder;
- size_t depth = 1;
- while (xml::XmlPullParser::isGoodEvent(parser->next())) {
- const xml::XmlPullParser::Event event = parser->getEvent();
- if (event == xml::XmlPullParser::Event::kEndElement) {
- if (!parser->getElementNamespace().empty()) {
- // We already warned and skipped the start element, so just skip here too
- continue;
- }
+ bool error = false;
+ outRawString->clear();
+ outStyleString->spans.clear();
+ util::StringBuilder builder;
+ size_t depth = 1;
+ while (xml::XmlPullParser::isGoodEvent(parser->next())) {
+ const xml::XmlPullParser::Event event = parser->getEvent();
+ if (event == xml::XmlPullParser::Event::kEndElement) {
+ if (!parser->getElementNamespace().empty()) {
+ // We already warned and skipped the start element, so just skip here
+ // too
+ continue;
+ }
- depth--;
- if (depth == 0) {
- break;
- }
+ depth--;
+ if (depth == 0) {
+ break;
+ }
- spanStack.back().lastChar = builder.utf16Len() - 1;
- outStyleString->spans.push_back(spanStack.back());
- spanStack.pop_back();
+ spanStack.back().lastChar = builder.utf16Len() - 1;
+ outStyleString->spans.push_back(spanStack.back());
+ spanStack.pop_back();
- } else if (event == xml::XmlPullParser::Event::kText) {
- outRawString->append(parser->getText());
- builder.append(parser->getText());
+ } else if (event == xml::XmlPullParser::Event::kText) {
+ outRawString->append(parser->getText());
+ builder.append(parser->getText());
- } else if (event == xml::XmlPullParser::Event::kStartElement) {
- if (!parser->getElementNamespace().empty()) {
- if (parser->getElementNamespace() != sXliffNamespaceUri) {
- // Only warn if this isn't an xliff namespace.
- mDiag->warn(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "skipping element '"
- << parser->getElementName()
- << "' with unknown namespace '"
- << parser->getElementNamespace()
- << "'");
- }
- continue;
- }
- depth++;
-
- // Build a span object out of the nested element.
- std::string spanName = parser->getElementName();
- const auto endAttrIter = parser->endAttributes();
- for (auto attrIter = parser->beginAttributes(); attrIter != endAttrIter; ++attrIter) {
- spanName += ";";
- spanName += attrIter->name;
- spanName += "=";
- spanName += attrIter->value;
- }
-
- if (builder.utf16Len() > std::numeric_limits<uint32_t>::max()) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "style string '" << builder.str() << "' is too long");
- error = true;
- } else {
- spanStack.push_back(Span{ spanName, static_cast<uint32_t>(builder.utf16Len()) });
- }
-
- } else if (event == xml::XmlPullParser::Event::kComment) {
- // Skip
- } else {
- assert(false);
+ } else if (event == xml::XmlPullParser::Event::kStartElement) {
+ if (!parser->getElementNamespace().empty()) {
+ if (parser->getElementNamespace() != sXliffNamespaceUri) {
+ // Only warn if this isn't an xliff namespace.
+ mDiag->warn(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "skipping element '" << parser->getElementName()
+ << "' with unknown namespace '"
+ << parser->getElementNamespace() << "'");
}
- }
- assert(spanStack.empty() && "spans haven't been fully processed");
+ continue;
+ }
+ depth++;
- outStyleString->str = builder.str();
- return !error;
+ // Build a span object out of the nested element.
+ std::string spanName = parser->getElementName();
+ const auto endAttrIter = parser->endAttributes();
+ for (auto attrIter = parser->beginAttributes(); attrIter != endAttrIter;
+ ++attrIter) {
+ spanName += ";";
+ spanName += attrIter->name;
+ spanName += "=";
+ spanName += attrIter->value;
+ }
+
+ if (builder.utf16Len() > std::numeric_limits<uint32_t>::max()) {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "style string '" << builder.str() << "' is too long");
+ error = true;
+ } else {
+ spanStack.push_back(
+ Span{spanName, static_cast<uint32_t>(builder.utf16Len())});
+ }
+
+ } else if (event == xml::XmlPullParser::Event::kComment) {
+ // Skip
+ } else {
+ assert(false);
+ }
+ }
+ assert(spanStack.empty() && "spans haven't been fully processed");
+
+ outStyleString->str = builder.str();
+ return !error;
}
bool ResourceParser::parse(xml::XmlPullParser* parser) {
- bool error = false;
- const size_t depth = parser->getDepth();
- while (xml::XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
- // Skip comments and text.
- continue;
- }
-
- if (!parser->getElementNamespace().empty() || parser->getElementName() != "resources") {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "root element must be <resources>");
- return false;
- }
-
- error |= !parseResources(parser);
- break;
- };
-
- if (parser->getEvent() == xml::XmlPullParser::Event::kBadDocument) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "xml parser error: " << parser->getLastError());
- return false;
+ bool error = false;
+ const size_t depth = parser->getDepth();
+ while (xml::XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
+ // Skip comments and text.
+ continue;
}
- return !error;
+
+ if (!parser->getElementNamespace().empty() ||
+ parser->getElementName() != "resources") {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "root element must be <resources>");
+ return false;
+ }
+
+ error |= !parseResources(parser);
+ break;
+ };
+
+ if (parser->getEvent() == xml::XmlPullParser::Event::kBadDocument) {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "xml parser error: " << parser->getLastError());
+ return false;
+ }
+ return !error;
}
bool ResourceParser::parseResources(xml::XmlPullParser* parser) {
- std::set<ResourceName> strippedResources;
+ std::set<ResourceName> strippedResources;
- bool error = false;
- std::string comment;
- const size_t depth = parser->getDepth();
- while (xml::XmlPullParser::nextChildNode(parser, depth)) {
- const xml::XmlPullParser::Event event = parser->getEvent();
- if (event == xml::XmlPullParser::Event::kComment) {
- comment = parser->getComment();
- continue;
- }
-
- if (event == xml::XmlPullParser::Event::kText) {
- if (!util::trimWhitespace(parser->getText()).empty()) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "plain text not allowed here");
- error = true;
- }
- continue;
- }
-
- assert(event == xml::XmlPullParser::Event::kStartElement);
-
- if (!parser->getElementNamespace().empty()) {
- // Skip unknown namespace.
- continue;
- }
-
- std::string elementName = parser->getElementName();
- if (elementName == "skip" || elementName == "eat-comment") {
- comment = "";
- continue;
- }
-
- ParsedResource parsedResource;
- parsedResource.config = mConfig;
- parsedResource.source = mSource.withLine(parser->getLineNumber());
- parsedResource.comment = std::move(comment);
-
- // Extract the product name if it exists.
- if (Maybe<StringPiece> maybeProduct = xml::findNonEmptyAttribute(parser, "product")) {
- parsedResource.product = maybeProduct.value().toString();
- }
-
- // Parse the resource regardless of product.
- if (!parseResource(parser, &parsedResource)) {
- error = true;
- continue;
- }
-
- if (!addResourcesToTable(mTable, mDiag, &parsedResource)) {
- error = true;
- }
+ bool error = false;
+ std::string comment;
+ const size_t depth = parser->getDepth();
+ while (xml::XmlPullParser::nextChildNode(parser, depth)) {
+ const xml::XmlPullParser::Event event = parser->getEvent();
+ if (event == xml::XmlPullParser::Event::kComment) {
+ comment = parser->getComment();
+ continue;
}
- // Check that we included at least one variant of each stripped resource.
- for (const ResourceName& strippedResource : strippedResources) {
- if (!mTable->findResource(strippedResource)) {
- // Failed to find the resource.
- mDiag->error(DiagMessage(mSource) << "resource '" << strippedResource << "' "
- "was filtered out but no product variant remains");
- error = true;
- }
+ if (event == xml::XmlPullParser::Event::kText) {
+ if (!util::trimWhitespace(parser->getText()).empty()) {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "plain text not allowed here");
+ error = true;
+ }
+ continue;
}
- return !error;
+ assert(event == xml::XmlPullParser::Event::kStartElement);
+
+ if (!parser->getElementNamespace().empty()) {
+ // Skip unknown namespace.
+ continue;
+ }
+
+ std::string elementName = parser->getElementName();
+ if (elementName == "skip" || elementName == "eat-comment") {
+ comment = "";
+ continue;
+ }
+
+ ParsedResource parsedResource;
+ parsedResource.config = mConfig;
+ parsedResource.source = mSource.withLine(parser->getLineNumber());
+ parsedResource.comment = std::move(comment);
+
+ // Extract the product name if it exists.
+ if (Maybe<StringPiece> maybeProduct =
+ xml::findNonEmptyAttribute(parser, "product")) {
+ parsedResource.product = maybeProduct.value().toString();
+ }
+
+ // Parse the resource regardless of product.
+ if (!parseResource(parser, &parsedResource)) {
+ error = true;
+ continue;
+ }
+
+ if (!addResourcesToTable(mTable, mDiag, &parsedResource)) {
+ error = true;
+ }
+ }
+
+ // Check that we included at least one variant of each stripped resource.
+ for (const ResourceName& strippedResource : strippedResources) {
+ if (!mTable->findResource(strippedResource)) {
+ // Failed to find the resource.
+ mDiag->error(DiagMessage(mSource)
+ << "resource '" << strippedResource
+ << "' "
+ "was filtered out but no product variant remains");
+ error = true;
+ }
+ }
+
+ return !error;
}
+bool ResourceParser::parseResource(xml::XmlPullParser* parser,
+ ParsedResource* outResource) {
+ struct ItemTypeFormat {
+ ResourceType type;
+ uint32_t format;
+ };
-bool ResourceParser::parseResource(xml::XmlPullParser* parser, ParsedResource* outResource) {
- struct ItemTypeFormat {
- ResourceType type;
- uint32_t format;
- };
+ using BagParseFunc = std::function<bool(ResourceParser*, xml::XmlPullParser*,
+ ParsedResource*)>;
- using BagParseFunc = std::function<bool(ResourceParser*, xml::XmlPullParser*, ParsedResource*)>;
+ 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}},
+ {"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}},
+ {"integer",
+ {ResourceType::kInteger, android::ResTable_map::TYPE_INTEGER}},
+ {"string",
+ {ResourceType::kString, android::ResTable_map::TYPE_STRING}},
+ });
- 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 } },
- { "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 } },
- { "integer", { ResourceType::kInteger, android::ResTable_map::TYPE_INTEGER } },
- { "string", { ResourceType::kString, android::ResTable_map::TYPE_STRING } },
- });
+ 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)},
+ });
- 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::string resourceType = parser->getElementName();
- std::string resourceType = parser->getElementName();
+ // The value format accepted for this resource.
+ uint32_t resourceFormat = 0u;
- // The value format accepted for this resource.
- uint32_t resourceFormat = 0u;
-
- if (resourceType == "item") {
- // Items have their type encoded in the type attribute.
- if (Maybe<StringPiece> maybeType = xml::findNonEmptyAttribute(parser, "type")) {
- resourceType = maybeType.value().toString();
- } else {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "<item> must have a 'type' attribute");
- return false;
- }
-
- 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());
- if (!resourceFormat) {
- mDiag->error(DiagMessage(outResource->source)
- << "'" << maybeFormat.value() << "' is an invalid format");
- return false;
- }
- }
+ if (resourceType == "item") {
+ // Items have their type encoded in the type attribute.
+ if (Maybe<StringPiece> maybeType =
+ xml::findNonEmptyAttribute(parser, "type")) {
+ resourceType = maybeType.value().toString();
+ } else {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "<item> must have a 'type' attribute");
+ return false;
}
- // Get the name of the resource. This will be checked later, because not all
- // XML elements require a name.
- Maybe<StringPiece> maybeName = xml::findNonEmptyAttribute(parser, "name");
-
- if (resourceType == "id") {
- if (!maybeName) {
- mDiag->error(DiagMessage(outResource->source)
- << "<" << parser->getElementName() << "> missing 'name' attribute");
- return false;
- }
-
- outResource->name.type = ResourceType::kId;
- outResource->name.entry = maybeName.value().toString();
- outResource->value = util::make_unique<Id>();
- return true;
- }
-
- const auto itemIter = elToItemMap.find(resourceType);
- if (itemIter != elToItemMap.end()) {
- // This is an item, record its type and format and start parsing.
-
- if (!maybeName) {
- mDiag->error(DiagMessage(outResource->source)
- << "<" << parser->getElementName() << "> missing 'name' attribute");
- return false;
- }
-
- outResource->name.type = itemIter->second.type;
- outResource->name.entry = maybeName.value().toString();
-
- // Only use the implicit format for this type if it wasn't overridden.
- if (!resourceFormat) {
- resourceFormat = itemIter->second.format;
- }
-
- if (!parseItem(parser, outResource, resourceFormat)) {
- return false;
- }
- return true;
- }
-
- // This might be a bag or something.
- const auto bagIter = elToBagMap.find(resourceType);
- if (bagIter != elToBagMap.end()) {
- // Ensure we have a name (unless this is a <public-group>).
- if (resourceType != "public-group") {
- if (!maybeName) {
- mDiag->error(DiagMessage(outResource->source)
- << "<" << parser->getElementName() << "> missing 'name' attribute");
- return false;
- }
-
- outResource->name.entry = maybeName.value().toString();
- }
-
- // Call the associated parse method. The type will be filled in by the
- // parse func.
- if (!bagIter->second(this, parser, outResource)) {
- return false;
- }
- return true;
- }
-
- // Try parsing the elementName (or type) as a resource. These shall only be
- // resources like 'layout' or 'xml' and they can only be references.
- const ResourceType* parsedType = parseResourceType(resourceType);
- if (parsedType) {
- if (!maybeName) {
- mDiag->error(DiagMessage(outResource->source)
- << "<" << parser->getElementName() << "> missing 'name' attribute");
- return false;
- }
-
- outResource->name.type = *parsedType;
- outResource->name.entry = maybeName.value().toString();
- outResource->value = parseXml(parser, android::ResTable_map::TYPE_REFERENCE, kNoRawString);
- if (!outResource->value) {
- mDiag->error(DiagMessage(outResource->source)
- << "invalid value for type '" << *parsedType << "'. Expected a reference");
- return false;
- }
- return true;
- }
-
- mDiag->warn(DiagMessage(outResource->source)
- << "unknown resource type '" << parser->getElementName() << "'");
- return false;
-}
-
-bool ResourceParser::parseItem(xml::XmlPullParser* parser, ParsedResource* outResource,
- const uint32_t format) {
- if (format == android::ResTable_map::TYPE_STRING) {
- return parseString(parser, outResource);
- }
-
- outResource->value = parseXml(parser, format, kNoRawString);
- if (!outResource->value) {
- mDiag->error(DiagMessage(outResource->source) << "invalid " << outResource->name.type);
+ 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());
+ if (!resourceFormat) {
+ mDiag->error(DiagMessage(outResource->source)
+ << "'" << maybeFormat.value() << "' is an invalid format");
return false;
+ }
+ }
+ }
+
+ // Get the name of the resource. This will be checked later, because not all
+ // XML elements require a name.
+ Maybe<StringPiece> maybeName = xml::findNonEmptyAttribute(parser, "name");
+
+ if (resourceType == "id") {
+ if (!maybeName) {
+ mDiag->error(DiagMessage(outResource->source)
+ << "<" << parser->getElementName()
+ << "> missing 'name' attribute");
+ return false;
+ }
+
+ outResource->name.type = ResourceType::kId;
+ outResource->name.entry = maybeName.value().toString();
+ outResource->value = util::make_unique<Id>();
+ return true;
+ }
+
+ const auto itemIter = elToItemMap.find(resourceType);
+ if (itemIter != elToItemMap.end()) {
+ // This is an item, record its type and format and start parsing.
+
+ if (!maybeName) {
+ mDiag->error(DiagMessage(outResource->source)
+ << "<" << parser->getElementName()
+ << "> missing 'name' attribute");
+ return false;
+ }
+
+ outResource->name.type = itemIter->second.type;
+ outResource->name.entry = maybeName.value().toString();
+
+ // Only use the implicit format for this type if it wasn't overridden.
+ if (!resourceFormat) {
+ resourceFormat = itemIter->second.format;
+ }
+
+ if (!parseItem(parser, outResource, resourceFormat)) {
+ return false;
}
return true;
+ }
+
+ // This might be a bag or something.
+ const auto bagIter = elToBagMap.find(resourceType);
+ if (bagIter != elToBagMap.end()) {
+ // Ensure we have a name (unless this is a <public-group>).
+ if (resourceType != "public-group") {
+ if (!maybeName) {
+ mDiag->error(DiagMessage(outResource->source)
+ << "<" << parser->getElementName()
+ << "> missing 'name' attribute");
+ return false;
+ }
+
+ outResource->name.entry = maybeName.value().toString();
+ }
+
+ // Call the associated parse method. The type will be filled in by the
+ // parse func.
+ if (!bagIter->second(this, parser, outResource)) {
+ return false;
+ }
+ return true;
+ }
+
+ // Try parsing the elementName (or type) as a resource. These shall only be
+ // resources like 'layout' or 'xml' and they can only be references.
+ const ResourceType* parsedType = parseResourceType(resourceType);
+ if (parsedType) {
+ if (!maybeName) {
+ mDiag->error(DiagMessage(outResource->source)
+ << "<" << parser->getElementName()
+ << "> missing 'name' attribute");
+ return false;
+ }
+
+ outResource->name.type = *parsedType;
+ outResource->name.entry = maybeName.value().toString();
+ outResource->value =
+ parseXml(parser, android::ResTable_map::TYPE_REFERENCE, kNoRawString);
+ if (!outResource->value) {
+ mDiag->error(DiagMessage(outResource->source)
+ << "invalid value for type '" << *parsedType
+ << "'. Expected a reference");
+ return false;
+ }
+ return true;
+ }
+
+ mDiag->warn(DiagMessage(outResource->source)
+ << "unknown resource type '" << parser->getElementName() << "'");
+ return false;
+}
+
+bool ResourceParser::parseItem(xml::XmlPullParser* parser,
+ ParsedResource* outResource,
+ const uint32_t format) {
+ if (format == android::ResTable_map::TYPE_STRING) {
+ return parseString(parser, outResource);
+ }
+
+ outResource->value = parseXml(parser, format, kNoRawString);
+ if (!outResource->value) {
+ mDiag->error(DiagMessage(outResource->source) << "invalid "
+ << outResource->name.type);
+ return false;
+ }
+ return true;
}
/**
@@ -476,771 +519,834 @@
* an Item. If allowRawValue is false, nullptr is returned in this
* case.
*/
-std::unique_ptr<Item> ResourceParser::parseXml(xml::XmlPullParser* parser, const uint32_t typeMask,
+std::unique_ptr<Item> ResourceParser::parseXml(xml::XmlPullParser* parser,
+ const uint32_t typeMask,
const bool allowRawValue) {
- const size_t beginXmlLine = parser->getLineNumber();
+ const size_t beginXmlLine = parser->getLineNumber();
- std::string rawValue;
- StyleString styleString;
- if (!flattenXmlSubtree(parser, &rawValue, &styleString)) {
- return {};
- }
-
- if (!styleString.spans.empty()) {
- // This can only be a StyledString.
- return util::make_unique<StyledString>(
- mTable->stringPool.makeRef(styleString, StringPool::Context{ 1, mConfig }));
- }
-
- auto onCreateReference = [&](const ResourceName& name) {
- // name.package can be empty here, as it will assume the package name of the table.
- std::unique_ptr<Id> id = util::make_unique<Id>();
- id->setSource(mSource.withLine(beginXmlLine));
- mTable->addResource(name, {}, {}, std::move(id), mDiag);
- };
-
- // Process the raw value.
- std::unique_ptr<Item> processedItem = ResourceUtils::tryParseItemForAttribute(
- rawValue, typeMask, onCreateReference);
- if (processedItem) {
- // Fix up the reference.
- if (Reference* ref = valueCast<Reference>(processedItem.get())) {
- transformReferenceFromNamespace(parser, "", ref);
- }
- return processedItem;
- }
-
- // Try making a regular string.
- if (typeMask & android::ResTable_map::TYPE_STRING) {
- // Use the trimmed, escaped string.
- return util::make_unique<String>(
- mTable->stringPool.makeRef(styleString.str, StringPool::Context{ 1, mConfig }));
- }
-
- if (allowRawValue) {
- // We can't parse this so return a RawString if we are allowed.
- return util::make_unique<RawString>(
- mTable->stringPool.makeRef(rawValue, StringPool::Context{ 1, mConfig }));
- }
+ std::string rawValue;
+ StyleString styleString;
+ if (!flattenXmlSubtree(parser, &rawValue, &styleString)) {
return {};
+ }
+
+ if (!styleString.spans.empty()) {
+ // This can only be a StyledString.
+ return util::make_unique<StyledString>(mTable->stringPool.makeRef(
+ styleString, StringPool::Context{1, mConfig}));
+ }
+
+ auto onCreateReference = [&](const ResourceName& name) {
+ // name.package can be empty here, as it will assume the package name of the
+ // table.
+ std::unique_ptr<Id> id = util::make_unique<Id>();
+ id->setSource(mSource.withLine(beginXmlLine));
+ mTable->addResource(name, {}, {}, std::move(id), mDiag);
+ };
+
+ // Process the raw value.
+ std::unique_ptr<Item> processedItem = ResourceUtils::tryParseItemForAttribute(
+ rawValue, typeMask, onCreateReference);
+ if (processedItem) {
+ // Fix up the reference.
+ if (Reference* ref = valueCast<Reference>(processedItem.get())) {
+ transformReferenceFromNamespace(parser, "", ref);
+ }
+ return processedItem;
+ }
+
+ // Try making a regular string.
+ if (typeMask & android::ResTable_map::TYPE_STRING) {
+ // Use the trimmed, escaped string.
+ return util::make_unique<String>(mTable->stringPool.makeRef(
+ styleString.str, StringPool::Context{1, mConfig}));
+ }
+
+ if (allowRawValue) {
+ // We can't parse this so return a RawString if we are allowed.
+ return util::make_unique<RawString>(
+ mTable->stringPool.makeRef(rawValue, StringPool::Context{1, mConfig}));
+ }
+ return {};
}
-bool ResourceParser::parseString(xml::XmlPullParser* parser, ParsedResource* outResource) {
- bool formatted = true;
- if (Maybe<StringPiece> formattedAttr = xml::findAttribute(parser, "formatted")) {
- Maybe<bool> maybeFormatted = ResourceUtils::parseBool(formattedAttr.value());
- if (!maybeFormatted) {
- mDiag->error(DiagMessage(outResource->source)
- << "invalid value for 'formatted'. Must be a boolean");
- return false;
- }
- formatted = maybeFormatted.value();
+bool ResourceParser::parseString(xml::XmlPullParser* parser,
+ ParsedResource* outResource) {
+ bool formatted = true;
+ if (Maybe<StringPiece> formattedAttr =
+ xml::findAttribute(parser, "formatted")) {
+ Maybe<bool> maybeFormatted =
+ ResourceUtils::parseBool(formattedAttr.value());
+ if (!maybeFormatted) {
+ mDiag->error(DiagMessage(outResource->source)
+ << "invalid value for 'formatted'. Must be a boolean");
+ return false;
}
+ formatted = maybeFormatted.value();
+ }
- bool translateable = mOptions.translatable;
- if (Maybe<StringPiece> translateableAttr = xml::findAttribute(parser, "translatable")) {
- Maybe<bool> maybeTranslateable = ResourceUtils::parseBool(translateableAttr.value());
- if (!maybeTranslateable) {
- mDiag->error(DiagMessage(outResource->source)
- << "invalid value for 'translatable'. Must be a boolean");
- return false;
- }
- translateable = maybeTranslateable.value();
+ bool translateable = mOptions.translatable;
+ if (Maybe<StringPiece> translateableAttr =
+ xml::findAttribute(parser, "translatable")) {
+ Maybe<bool> maybeTranslateable =
+ ResourceUtils::parseBool(translateableAttr.value());
+ if (!maybeTranslateable) {
+ mDiag->error(DiagMessage(outResource->source)
+ << "invalid value for 'translatable'. Must be a boolean");
+ return false;
}
+ translateable = maybeTranslateable.value();
+ }
- outResource->value = parseXml(parser, android::ResTable_map::TYPE_STRING, kNoRawString);
- if (!outResource->value) {
- mDiag->error(DiagMessage(outResource->source) << "not a valid string");
- return false;
- }
+ outResource->value =
+ parseXml(parser, android::ResTable_map::TYPE_STRING, kNoRawString);
+ if (!outResource->value) {
+ mDiag->error(DiagMessage(outResource->source) << "not a valid string");
+ return false;
+ }
- if (String* stringValue = valueCast<String>(outResource->value.get())) {
- stringValue->setTranslateable(translateable);
+ if (String* stringValue = valueCast<String>(outResource->value.get())) {
+ stringValue->setTranslateable(translateable);
- if (formatted && translateable) {
- if (!util::verifyJavaStringFormat(*stringValue->value)) {
- DiagMessage msg(outResource->source);
- msg << "multiple substitutions specified in non-positional format; "
- "did you mean to add the formatted=\"false\" attribute?";
- if (mOptions.errorOnPositionalArguments) {
- mDiag->error(msg);
- return false;
- }
-
- mDiag->warn(msg);
- }
+ if (formatted && translateable) {
+ if (!util::verifyJavaStringFormat(*stringValue->value)) {
+ DiagMessage msg(outResource->source);
+ msg << "multiple substitutions specified in non-positional format; "
+ "did you mean to add the formatted=\"false\" attribute?";
+ if (mOptions.errorOnPositionalArguments) {
+ mDiag->error(msg);
+ return false;
}
- } else if (StyledString* stringValue = valueCast<StyledString>(outResource->value.get())) {
- stringValue->setTranslateable(translateable);
+ mDiag->warn(msg);
+ }
}
- return true;
+
+ } else if (StyledString* stringValue =
+ valueCast<StyledString>(outResource->value.get())) {
+ stringValue->setTranslateable(translateable);
+ }
+ return true;
}
-bool ResourceParser::parsePublic(xml::XmlPullParser* parser, ParsedResource* outResource) {
- Maybe<StringPiece> maybeType = xml::findNonEmptyAttribute(parser, "type");
- if (!maybeType) {
- mDiag->error(DiagMessage(outResource->source) << "<public> must have a 'type' attribute");
- return false;
- }
+bool ResourceParser::parsePublic(xml::XmlPullParser* parser,
+ ParsedResource* outResource) {
+ Maybe<StringPiece> maybeType = xml::findNonEmptyAttribute(parser, "type");
+ if (!maybeType) {
+ mDiag->error(DiagMessage(outResource->source)
+ << "<public> must have a 'type' attribute");
+ return false;
+ }
- const ResourceType* parsedType = parseResourceType(maybeType.value());
- if (!parsedType) {
- mDiag->error(DiagMessage(outResource->source)
- << "invalid resource type '" << maybeType.value() << "' in <public>");
- return false;
- }
+ const ResourceType* parsedType = parseResourceType(maybeType.value());
+ if (!parsedType) {
+ mDiag->error(DiagMessage(outResource->source) << "invalid resource type '"
+ << maybeType.value()
+ << "' in <public>");
+ return false;
+ }
- outResource->name.type = *parsedType;
+ outResource->name.type = *parsedType;
- if (Maybe<StringPiece> maybeIdStr = xml::findNonEmptyAttribute(parser, "id")) {
- Maybe<ResourceId> maybeId = ResourceUtils::parseResourceId(maybeIdStr.value());
- if (!maybeId) {
- mDiag->error(DiagMessage(outResource->source)
- << "invalid resource ID '" << maybeId.value() << "' in <public>");
- return false;
- }
- outResource->id = maybeId.value();
- }
-
- if (*parsedType == ResourceType::kId) {
- // An ID marked as public is also the definition of an ID.
- outResource->value = util::make_unique<Id>();
- }
-
- outResource->symbolState = SymbolState::kPublic;
- return true;
-}
-
-bool ResourceParser::parsePublicGroup(xml::XmlPullParser* parser, ParsedResource* outResource) {
- Maybe<StringPiece> maybeType = xml::findNonEmptyAttribute(parser, "type");
- if (!maybeType) {
- mDiag->error(DiagMessage(outResource->source)
- << "<public-group> must have a 'type' attribute");
- return false;
- }
-
- const ResourceType* parsedType = parseResourceType(maybeType.value());
- if (!parsedType) {
- mDiag->error(DiagMessage(outResource->source)
- << "invalid resource type '" << maybeType.value() << "' in <public-group>");
- return false;
- }
-
- Maybe<StringPiece> maybeIdStr = xml::findNonEmptyAttribute(parser, "first-id");
- if (!maybeIdStr) {
- mDiag->error(DiagMessage(outResource->source)
- << "<public-group> must have a 'first-id' attribute");
- return false;
- }
-
- Maybe<ResourceId> maybeId = ResourceUtils::parseResourceId(maybeIdStr.value());
+ if (Maybe<StringPiece> maybeIdStr =
+ xml::findNonEmptyAttribute(parser, "id")) {
+ Maybe<ResourceId> maybeId =
+ ResourceUtils::parseResourceId(maybeIdStr.value());
if (!maybeId) {
- mDiag->error(DiagMessage(outResource->source)
- << "invalid resource ID '" << maybeIdStr.value() << "' in <public-group>");
- return false;
+ mDiag->error(DiagMessage(outResource->source) << "invalid resource ID '"
+ << maybeId.value()
+ << "' in <public>");
+ return false;
}
+ outResource->id = maybeId.value();
+ }
- ResourceId nextId = maybeId.value();
+ if (*parsedType == ResourceType::kId) {
+ // An ID marked as public is also the definition of an ID.
+ outResource->value = util::make_unique<Id>();
+ }
- std::string comment;
- bool error = false;
- const size_t depth = parser->getDepth();
- while (xml::XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() == xml::XmlPullParser::Event::kComment) {
- comment = util::trimWhitespace(parser->getComment()).toString();
- continue;
- } else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
- // Skip text.
- continue;
- }
-
- const Source itemSource = mSource.withLine(parser->getLineNumber());
- 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, "id")) {
- mDiag->error(DiagMessage(itemSource) << "'id' is ignored within <public-group>");
- error = true;
- continue;
- }
-
- if (xml::findNonEmptyAttribute(parser, "type")) {
- mDiag->error(DiagMessage(itemSource) << "'type' is ignored within <public-group>");
- error = true;
- continue;
- }
-
- ParsedResource childResource;
- childResource.name.type = *parsedType;
- childResource.name.entry = maybeName.value().toString();
- childResource.id = nextId;
- childResource.comment = std::move(comment);
- childResource.source = itemSource;
- childResource.symbolState = SymbolState::kPublic;
- outResource->childResources.push_back(std::move(childResource));
-
- nextId.id += 1;
-
- } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
- mDiag->error(DiagMessage(itemSource) << ":" << elementName << ">");
- error = true;
- }
- }
- return !error;
+ outResource->symbolState = SymbolState::kPublic;
+ return true;
}
-bool ResourceParser::parseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* outResource) {
- Maybe<StringPiece> maybeType = xml::findNonEmptyAttribute(parser, "type");
- if (!maybeType) {
- mDiag->error(DiagMessage(outResource->source)
- << "<" << parser->getElementName() << "> must have a 'type' attribute");
- return false;
- }
-
- const ResourceType* parsedType = parseResourceType(maybeType.value());
- if (!parsedType) {
- mDiag->error(DiagMessage(outResource->source)
- << "invalid resource type '" << maybeType.value()
- << "' in <" << parser->getElementName() << ">");
- return false;
- }
-
- outResource->name.type = *parsedType;
- return true;
-}
-
-bool ResourceParser::parseSymbol(xml::XmlPullParser* parser, ParsedResource* outResource) {
- if (parseSymbolImpl(parser, outResource)) {
- outResource->symbolState = SymbolState::kPrivate;
- return true;
- }
+bool ResourceParser::parsePublicGroup(xml::XmlPullParser* parser,
+ ParsedResource* outResource) {
+ Maybe<StringPiece> maybeType = xml::findNonEmptyAttribute(parser, "type");
+ if (!maybeType) {
+ mDiag->error(DiagMessage(outResource->source)
+ << "<public-group> must have a 'type' attribute");
return false;
-}
+ }
-bool ResourceParser::parseAddResource(xml::XmlPullParser* parser, ParsedResource* outResource) {
- if (parseSymbolImpl(parser, outResource)) {
- outResource->symbolState = SymbolState::kUndefined;
- return true;
- }
+ const ResourceType* parsedType = parseResourceType(maybeType.value());
+ if (!parsedType) {
+ mDiag->error(DiagMessage(outResource->source) << "invalid resource type '"
+ << maybeType.value()
+ << "' in <public-group>");
return false;
+ }
+
+ Maybe<StringPiece> maybeIdStr =
+ xml::findNonEmptyAttribute(parser, "first-id");
+ if (!maybeIdStr) {
+ mDiag->error(DiagMessage(outResource->source)
+ << "<public-group> must have a 'first-id' attribute");
+ return false;
+ }
+
+ Maybe<ResourceId> maybeId =
+ ResourceUtils::parseResourceId(maybeIdStr.value());
+ if (!maybeId) {
+ mDiag->error(DiagMessage(outResource->source) << "invalid resource ID '"
+ << maybeIdStr.value()
+ << "' in <public-group>");
+ return false;
+ }
+
+ ResourceId nextId = maybeId.value();
+
+ std::string comment;
+ bool error = false;
+ const size_t depth = parser->getDepth();
+ while (xml::XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() == xml::XmlPullParser::Event::kComment) {
+ comment = util::trimWhitespace(parser->getComment()).toString();
+ continue;
+ } else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
+ // Skip text.
+ continue;
+ }
+
+ const Source itemSource = mSource.withLine(parser->getLineNumber());
+ 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, "id")) {
+ mDiag->error(DiagMessage(itemSource)
+ << "'id' is ignored within <public-group>");
+ error = true;
+ continue;
+ }
+
+ if (xml::findNonEmptyAttribute(parser, "type")) {
+ mDiag->error(DiagMessage(itemSource)
+ << "'type' is ignored within <public-group>");
+ error = true;
+ continue;
+ }
+
+ ParsedResource childResource;
+ childResource.name.type = *parsedType;
+ childResource.name.entry = maybeName.value().toString();
+ childResource.id = nextId;
+ childResource.comment = std::move(comment);
+ childResource.source = itemSource;
+ childResource.symbolState = SymbolState::kPublic;
+ outResource->childResources.push_back(std::move(childResource));
+
+ nextId.id += 1;
+
+ } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+ mDiag->error(DiagMessage(itemSource) << ":" << elementName << ">");
+ error = true;
+ }
+ }
+ return !error;
}
+bool ResourceParser::parseSymbolImpl(xml::XmlPullParser* parser,
+ ParsedResource* outResource) {
+ Maybe<StringPiece> maybeType = xml::findNonEmptyAttribute(parser, "type");
+ if (!maybeType) {
+ mDiag->error(DiagMessage(outResource->source)
+ << "<" << parser->getElementName()
+ << "> must have a 'type' attribute");
+ return false;
+ }
-bool ResourceParser::parseAttr(xml::XmlPullParser* parser, ParsedResource* outResource) {
- return parseAttrImpl(parser, outResource, false);
+ const ResourceType* parsedType = parseResourceType(maybeType.value());
+ if (!parsedType) {
+ mDiag->error(DiagMessage(outResource->source)
+ << "invalid resource type '" << maybeType.value() << "' in <"
+ << parser->getElementName() << ">");
+ return false;
+ }
+
+ outResource->name.type = *parsedType;
+ return true;
}
-bool ResourceParser::parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* outResource,
- bool weak) {
- outResource->name.type = ResourceType::kAttr;
-
- // Attributes only end up in default configuration.
- if (outResource->config != ConfigDescription::defaultConfig()) {
- mDiag->warn(DiagMessage(outResource->source) << "ignoring configuration '"
- << outResource->config << "' for attribute " << outResource->name);
- outResource->config = ConfigDescription::defaultConfig();
- }
-
- uint32_t typeMask = 0;
-
- Maybe<StringPiece> maybeFormat = xml::findAttribute(parser, "format");
- if (maybeFormat) {
- typeMask = parseFormatAttribute(maybeFormat.value());
- if (typeMask == 0) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "invalid attribute format '" << maybeFormat.value() << "'");
- return false;
- }
- }
-
- Maybe<int32_t> maybeMin, maybeMax;
-
- 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(minStr16.data(), minStr16.size(), &value)) {
- maybeMin = static_cast<int32_t>(value.data);
- }
- }
-
- if (!maybeMin) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "invalid 'min' value '" << minStr << "'");
- return false;
- }
- }
-
- 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(maxStr16.data(), maxStr16.size(), &value)) {
- maybeMax = static_cast<int32_t>(value.data);
- }
- }
-
- if (!maybeMax) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "invalid 'max' value '" << maxStr << "'");
- return false;
- }
- }
-
- if ((maybeMin || maybeMax) && (typeMask & android::ResTable_map::TYPE_INTEGER) == 0) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "'min' and 'max' can only be used when format='integer'");
- return false;
- }
-
- struct SymbolComparator {
- bool operator()(const Attribute::Symbol& a, const Attribute::Symbol& b) {
- return a.symbol.name.value() < b.symbol.name.value();
- }
- };
-
- std::set<Attribute::Symbol, SymbolComparator> items;
-
- std::string comment;
- bool error = false;
- const size_t depth = parser->getDepth();
- while (xml::XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() == xml::XmlPullParser::Event::kComment) {
- comment = util::trimWhitespace(parser->getComment()).toString();
- continue;
- } else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
- // Skip text.
- continue;
- }
-
- const Source itemSource = mSource.withLine(parser->getLineNumber());
- 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>");
- error = true;
- continue;
- }
- typeMask |= android::ResTable_map::TYPE_ENUM;
-
- } else if (elementName == "flag") {
- if (typeMask & android::ResTable_map::TYPE_ENUM) {
- mDiag->error(DiagMessage(itemSource)
- << "can not define a <flag>; already defined an <enum>");
- error = true;
- continue;
- }
- typeMask |= android::ResTable_map::TYPE_FLAGS;
- }
-
- if (Maybe<Attribute::Symbol> s = parseEnumOrFlagItem(parser, elementName)) {
- Attribute::Symbol& symbol = s.value();
- ParsedResource childResource;
- childResource.name = symbol.symbol.name.value();
- childResource.source = itemSource;
- childResource.value = util::make_unique<Id>();
- outResource->childResources.push_back(std::move(childResource));
-
- symbol.symbol.setComment(std::move(comment));
- symbol.symbol.setSource(itemSource);
-
- auto insertResult = items.insert(std::move(symbol));
- if (!insertResult.second) {
- const Attribute::Symbol& existingSymbol = *insertResult.first;
- mDiag->error(DiagMessage(itemSource)
- << "duplicate symbol '" << existingSymbol.symbol.name.value().entry
- << "'");
-
- mDiag->note(DiagMessage(existingSymbol.symbol.getSource())
- << "first defined here");
- error = true;
- }
- } else {
- error = true;
- }
- } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
- mDiag->error(DiagMessage(itemSource) << ":" << elementName << ">");
- error = true;
- }
-
- comment = {};
- }
-
- if (error) {
- return false;
- }
-
- std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(weak);
- attr->symbols = std::vector<Attribute::Symbol>(items.begin(), items.end());
- attr->typeMask = typeMask ? typeMask : uint32_t(android::ResTable_map::TYPE_ANY);
- if (maybeMin) {
- attr->minInt = maybeMin.value();
- }
-
- if (maybeMax) {
- attr->maxInt = maybeMax.value();
- }
- outResource->value = std::move(attr);
+bool ResourceParser::parseSymbol(xml::XmlPullParser* parser,
+ ParsedResource* outResource) {
+ if (parseSymbolImpl(parser, outResource)) {
+ outResource->symbolState = SymbolState::kPrivate;
return true;
+ }
+ return false;
}
-Maybe<Attribute::Symbol> ResourceParser::parseEnumOrFlagItem(xml::XmlPullParser* parser,
- const StringPiece& tag) {
- const Source source = mSource.withLine(parser->getLineNumber());
+bool ResourceParser::parseAddResource(xml::XmlPullParser* parser,
+ ParsedResource* outResource) {
+ if (parseSymbolImpl(parser, outResource)) {
+ outResource->symbolState = SymbolState::kUndefined;
+ return true;
+ }
+ return false;
+}
- Maybe<StringPiece> maybeName = xml::findNonEmptyAttribute(parser, "name");
- if (!maybeName) {
- mDiag->error(DiagMessage(source) << "no attribute 'name' found for tag <" << tag << ">");
- return {};
+bool ResourceParser::parseAttr(xml::XmlPullParser* parser,
+ ParsedResource* outResource) {
+ return parseAttrImpl(parser, outResource, false);
+}
+
+bool ResourceParser::parseAttrImpl(xml::XmlPullParser* parser,
+ ParsedResource* outResource, bool weak) {
+ outResource->name.type = ResourceType::kAttr;
+
+ // Attributes only end up in default configuration.
+ if (outResource->config != ConfigDescription::defaultConfig()) {
+ mDiag->warn(DiagMessage(outResource->source)
+ << "ignoring configuration '" << outResource->config
+ << "' for attribute " << outResource->name);
+ outResource->config = ConfigDescription::defaultConfig();
+ }
+
+ uint32_t typeMask = 0;
+
+ Maybe<StringPiece> maybeFormat = xml::findAttribute(parser, "format");
+ if (maybeFormat) {
+ typeMask = parseFormatAttribute(maybeFormat.value());
+ if (typeMask == 0) {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "invalid attribute format '" << maybeFormat.value()
+ << "'");
+ return false;
+ }
+ }
+
+ Maybe<int32_t> maybeMin, maybeMax;
+
+ 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(minStr16.data(), minStr16.size(),
+ &value)) {
+ maybeMin = static_cast<int32_t>(value.data);
+ }
}
- Maybe<StringPiece> maybeValue = xml::findNonEmptyAttribute(parser, "value");
- if (!maybeValue) {
- mDiag->error(DiagMessage(source) << "no attribute 'value' found for tag <" << tag << ">");
- return {};
+ if (!maybeMin) {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "invalid 'min' value '" << minStr << "'");
+ return false;
+ }
+ }
+
+ 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(maxStr16.data(), maxStr16.size(),
+ &value)) {
+ maybeMax = static_cast<int32_t>(value.data);
+ }
}
- std::u16string value16 = util::utf8ToUtf16(maybeValue.value());
- android::Res_value 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 {};
+ if (!maybeMax) {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "invalid 'max' value '" << maxStr << "'");
+ return false;
+ }
+ }
+
+ if ((maybeMin || maybeMax) &&
+ (typeMask & android::ResTable_map::TYPE_INTEGER) == 0) {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "'min' and 'max' can only be used when format='integer'");
+ return false;
+ }
+
+ struct SymbolComparator {
+ bool operator()(const Attribute::Symbol& a, const Attribute::Symbol& b) {
+ return a.symbol.name.value() < b.symbol.name.value();
+ }
+ };
+
+ std::set<Attribute::Symbol, SymbolComparator> items;
+
+ std::string comment;
+ bool error = false;
+ const size_t depth = parser->getDepth();
+ while (xml::XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() == xml::XmlPullParser::Event::kComment) {
+ comment = util::trimWhitespace(parser->getComment()).toString();
+ continue;
+ } else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
+ // Skip text.
+ continue;
}
- return Attribute::Symbol{
- Reference(ResourceNameRef({}, ResourceType::kId, maybeName.value())), val.data };
+ const Source itemSource = mSource.withLine(parser->getLineNumber());
+ 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>");
+ error = true;
+ continue;
+ }
+ typeMask |= android::ResTable_map::TYPE_ENUM;
+
+ } else if (elementName == "flag") {
+ if (typeMask & android::ResTable_map::TYPE_ENUM) {
+ mDiag->error(DiagMessage(itemSource)
+ << "can not define a <flag>; already defined an <enum>");
+ error = true;
+ continue;
+ }
+ typeMask |= android::ResTable_map::TYPE_FLAGS;
+ }
+
+ if (Maybe<Attribute::Symbol> s =
+ parseEnumOrFlagItem(parser, elementName)) {
+ Attribute::Symbol& symbol = s.value();
+ ParsedResource childResource;
+ childResource.name = symbol.symbol.name.value();
+ childResource.source = itemSource;
+ childResource.value = util::make_unique<Id>();
+ outResource->childResources.push_back(std::move(childResource));
+
+ symbol.symbol.setComment(std::move(comment));
+ symbol.symbol.setSource(itemSource);
+
+ auto insertResult = items.insert(std::move(symbol));
+ if (!insertResult.second) {
+ const Attribute::Symbol& existingSymbol = *insertResult.first;
+ mDiag->error(DiagMessage(itemSource)
+ << "duplicate symbol '"
+ << existingSymbol.symbol.name.value().entry << "'");
+
+ mDiag->note(DiagMessage(existingSymbol.symbol.getSource())
+ << "first defined here");
+ error = true;
+ }
+ } else {
+ error = true;
+ }
+ } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+ mDiag->error(DiagMessage(itemSource) << ":" << elementName << ">");
+ error = true;
+ }
+
+ comment = {};
+ }
+
+ if (error) {
+ return false;
+ }
+
+ std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(weak);
+ attr->symbols = std::vector<Attribute::Symbol>(items.begin(), items.end());
+ attr->typeMask =
+ typeMask ? typeMask : uint32_t(android::ResTable_map::TYPE_ANY);
+ if (maybeMin) {
+ attr->minInt = maybeMin.value();
+ }
+
+ if (maybeMax) {
+ attr->maxInt = maybeMax.value();
+ }
+ outResource->value = std::move(attr);
+ return true;
+}
+
+Maybe<Attribute::Symbol> ResourceParser::parseEnumOrFlagItem(
+ xml::XmlPullParser* parser, const StringPiece& tag) {
+ const Source source = mSource.withLine(parser->getLineNumber());
+
+ Maybe<StringPiece> maybeName = xml::findNonEmptyAttribute(parser, "name");
+ if (!maybeName) {
+ mDiag->error(DiagMessage(source) << "no attribute 'name' found for tag <"
+ << tag << ">");
+ return {};
+ }
+
+ 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(value16.data(), value16.size(), &val)) {
+ mDiag->error(DiagMessage(source) << "invalid value '" << maybeValue.value()
+ << "' for <" << tag
+ << ">; must be an integer");
+ return {};
+ }
+
+ return Attribute::Symbol{
+ Reference(ResourceNameRef({}, ResourceType::kId, maybeName.value())),
+ val.data};
}
bool ResourceParser::parseStyleItem(xml::XmlPullParser* parser, Style* style) {
- const Source source = mSource.withLine(parser->getLineNumber());
+ const Source source = mSource.withLine(parser->getLineNumber());
- Maybe<StringPiece> maybeName = xml::findNonEmptyAttribute(parser, "name");
- if (!maybeName) {
- mDiag->error(DiagMessage(source) << "<item> must have a 'name' attribute");
+ Maybe<StringPiece> maybeName = xml::findNonEmptyAttribute(parser, "name");
+ if (!maybeName) {
+ mDiag->error(DiagMessage(source) << "<item> must have a 'name' attribute");
+ return false;
+ }
+
+ Maybe<Reference> maybeKey =
+ ResourceUtils::parseXmlAttributeName(maybeName.value());
+ if (!maybeKey) {
+ mDiag->error(DiagMessage(source) << "invalid attribute name '"
+ << maybeName.value() << "'");
+ return false;
+ }
+
+ transformReferenceFromNamespace(parser, "", &maybeKey.value());
+ maybeKey.value().setSource(source);
+
+ std::unique_ptr<Item> value = parseXml(parser, 0, kAllowRawString);
+ if (!value) {
+ mDiag->error(DiagMessage(source) << "could not parse style item");
+ return false;
+ }
+
+ style->entries.push_back(
+ Style::Entry{std::move(maybeKey.value()), std::move(value)});
+ return true;
+}
+
+bool ResourceParser::parseStyle(xml::XmlPullParser* parser,
+ ParsedResource* outResource) {
+ outResource->name.type = ResourceType::kStyle;
+
+ std::unique_ptr<Style> style = util::make_unique<Style>();
+
+ 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()) {
+ std::string errStr;
+ style->parent = ResourceUtils::parseStyleParentReference(
+ maybeParent.value(), &errStr);
+ if (!style->parent) {
+ mDiag->error(DiagMessage(outResource->source) << errStr);
return false;
+ }
+
+ // Transform the namespace prefix to the actual package name, and mark the
+ // reference as
+ // private if appropriate.
+ transformReferenceFromNamespace(parser, "", &style->parent.value());
}
- Maybe<Reference> maybeKey = ResourceUtils::parseXmlAttributeName(maybeName.value());
- if (!maybeKey) {
- mDiag->error(DiagMessage(source) << "invalid attribute name '" << maybeName.value() << "'");
- return false;
+ } else {
+ // No parent was specified, so try inferring it from the style name.
+ std::string styleName = outResource->name.entry;
+ size_t pos = styleName.find_last_of(u'.');
+ if (pos != std::string::npos) {
+ style->parentInferred = true;
+ style->parent = Reference(
+ ResourceName({}, ResourceType::kStyle, styleName.substr(0, pos)));
+ }
+ }
+
+ bool error = false;
+ const size_t depth = parser->getDepth();
+ while (xml::XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
+ // Skip text and comments.
+ continue;
}
- transformReferenceFromNamespace(parser, "", &maybeKey.value());
- maybeKey.value().setSource(source);
+ const std::string& elementNamespace = parser->getElementNamespace();
+ const std::string& elementName = parser->getElementName();
+ if (elementNamespace == "" && elementName == "item") {
+ error |= !parseStyleItem(parser, style.get());
- std::unique_ptr<Item> value = parseXml(parser, 0, kAllowRawString);
- if (!value) {
- mDiag->error(DiagMessage(source) << "could not parse style item");
- return false;
+ } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << ":" << elementName << ">");
+ error = true;
}
+ }
- style->entries.push_back(Style::Entry{ std::move(maybeKey.value()), std::move(value) });
- return true;
+ if (error) {
+ return false;
+ }
+
+ outResource->value = std::move(style);
+ return true;
}
-bool ResourceParser::parseStyle(xml::XmlPullParser* parser, ParsedResource* outResource) {
- outResource->name.type = ResourceType::kStyle;
-
- std::unique_ptr<Style> style = util::make_unique<Style>();
-
- 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()) {
- std::string errStr;
- style->parent = ResourceUtils::parseStyleParentReference(maybeParent.value(), &errStr);
- if (!style->parent) {
- mDiag->error(DiagMessage(outResource->source) << errStr);
- return false;
- }
-
- // Transform the namespace prefix to the actual package name, and mark the reference as
- // private if appropriate.
- transformReferenceFromNamespace(parser, "", &style->parent.value());
- }
-
- } else {
- // No parent was specified, so try inferring it from the style name.
- std::string styleName = outResource->name.entry;
- size_t pos = styleName.find_last_of(u'.');
- if (pos != std::string::npos) {
- style->parentInferred = true;
- style->parent = Reference(ResourceName({}, ResourceType::kStyle,
- styleName.substr(0, pos)));
- }
- }
-
- bool error = false;
- const size_t depth = parser->getDepth();
- while (xml::XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
- // Skip text and comments.
- continue;
- }
-
- 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)) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << ":" << elementName << ">");
- error = true;
- }
- }
-
- if (error) {
- return false;
- }
-
- outResource->value = std::move(style);
- return true;
+bool ResourceParser::parseArray(xml::XmlPullParser* parser,
+ ParsedResource* outResource) {
+ return parseArrayImpl(parser, outResource, android::ResTable_map::TYPE_ANY);
}
-bool ResourceParser::parseArray(xml::XmlPullParser* parser, ParsedResource* outResource) {
- return parseArrayImpl(parser, outResource, android::ResTable_map::TYPE_ANY);
+bool ResourceParser::parseIntegerArray(xml::XmlPullParser* parser,
+ ParsedResource* outResource) {
+ return parseArrayImpl(parser, outResource,
+ android::ResTable_map::TYPE_INTEGER);
}
-bool ResourceParser::parseIntegerArray(xml::XmlPullParser* parser, ParsedResource* outResource) {
- return parseArrayImpl(parser, outResource, android::ResTable_map::TYPE_INTEGER);
+bool ResourceParser::parseStringArray(xml::XmlPullParser* parser,
+ ParsedResource* outResource) {
+ return parseArrayImpl(parser, outResource,
+ android::ResTable_map::TYPE_STRING);
}
-bool ResourceParser::parseStringArray(xml::XmlPullParser* parser, ParsedResource* outResource) {
- return parseArrayImpl(parser, outResource, android::ResTable_map::TYPE_STRING);
-}
-
-bool ResourceParser::parseArrayImpl(xml::XmlPullParser* parser, ParsedResource* outResource,
+bool ResourceParser::parseArrayImpl(xml::XmlPullParser* parser,
+ ParsedResource* outResource,
const uint32_t typeMask) {
- outResource->name.type = ResourceType::kArray;
+ outResource->name.type = ResourceType::kArray;
- std::unique_ptr<Array> array = util::make_unique<Array>();
+ std::unique_ptr<Array> array = util::make_unique<Array>();
- bool translateable = mOptions.translatable;
- if (Maybe<StringPiece> translateableAttr = xml::findAttribute(parser, "translatable")) {
- Maybe<bool> maybeTranslateable = ResourceUtils::parseBool(translateableAttr.value());
- if (!maybeTranslateable) {
- mDiag->error(DiagMessage(outResource->source)
- << "invalid value for 'translatable'. Must be a boolean");
- return false;
- }
- translateable = maybeTranslateable.value();
+ bool translateable = mOptions.translatable;
+ if (Maybe<StringPiece> translateableAttr =
+ xml::findAttribute(parser, "translatable")) {
+ Maybe<bool> maybeTranslateable =
+ ResourceUtils::parseBool(translateableAttr.value());
+ if (!maybeTranslateable) {
+ mDiag->error(DiagMessage(outResource->source)
+ << "invalid value for 'translatable'. Must be a boolean");
+ return false;
}
- array->setTranslateable(translateable);
+ translateable = maybeTranslateable.value();
+ }
+ array->setTranslateable(translateable);
-
- bool error = false;
- const size_t depth = parser->getDepth();
- while (xml::XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
- // Skip text and comments.
- continue;
- }
-
- const Source itemSource = mSource.withLine(parser->getLineNumber());
- 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");
- error = true;
- continue;
- }
- item->setSource(itemSource);
- array->items.emplace_back(std::move(item));
-
- } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
- mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
- << "unknown tag <" << elementNamespace << ":" << elementName << ">");
- error = true;
- }
+ bool error = false;
+ const size_t depth = parser->getDepth();
+ while (xml::XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
+ // Skip text and comments.
+ continue;
}
- if (error) {
- return false;
- }
+ const Source itemSource = mSource.withLine(parser->getLineNumber());
+ 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");
+ error = true;
+ continue;
+ }
+ item->setSource(itemSource);
+ array->items.emplace_back(std::move(item));
- outResource->value = std::move(array);
- return true;
+ } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+ mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber()))
+ << "unknown tag <" << elementNamespace << ":" << elementName
+ << ">");
+ error = true;
+ }
+ }
+
+ if (error) {
+ return false;
+ }
+
+ outResource->value = std::move(array);
+ return true;
}
-bool ResourceParser::parsePlural(xml::XmlPullParser* parser, ParsedResource* outResource) {
- outResource->name.type = ResourceType::kPlurals;
+bool ResourceParser::parsePlural(xml::XmlPullParser* parser,
+ ParsedResource* outResource) {
+ outResource->name.type = ResourceType::kPlurals;
- std::unique_ptr<Plural> plural = util::make_unique<Plural>();
+ std::unique_ptr<Plural> plural = util::make_unique<Plural>();
- bool error = false;
- const size_t depth = parser->getDepth();
- while (xml::XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
- // Skip text and comments.
- continue;
- }
-
- const Source itemSource = mSource.withLine(parser->getLineNumber());
- 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'");
- error = true;
- continue;
- }
-
- StringPiece trimmedQuantity = util::trimWhitespace(maybeQuantity.value());
- size_t index = 0;
- if (trimmedQuantity == "zero") {
- index = Plural::Zero;
- } else if (trimmedQuantity == "one") {
- index = Plural::One;
- } else if (trimmedQuantity == "two") {
- index = Plural::Two;
- } else if (trimmedQuantity == "few") {
- index = Plural::Few;
- } else if (trimmedQuantity == "many") {
- index = Plural::Many;
- } else if (trimmedQuantity == "other") {
- index = Plural::Other;
- } else {
- mDiag->error(DiagMessage(itemSource)
- << "<item> in <plural> has invalid value '" << trimmedQuantity
- << "' for attribute 'quantity'");
- error = true;
- continue;
- }
-
- if (plural->values[index]) {
- mDiag->error(DiagMessage(itemSource)
- << "duplicate quantity '" << trimmedQuantity << "'");
- error = true;
- continue;
- }
-
- if (!(plural->values[index] = parseXml(parser, android::ResTable_map::TYPE_STRING,
- kNoRawString))) {
- error = true;
- }
- plural->values[index]->setSource(itemSource);
-
- } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
- mDiag->error(DiagMessage(itemSource) << "unknown tag <" << elementNamespace << ":"
- << elementName << ">");
- error = true;
- }
+ bool error = false;
+ const size_t depth = parser->getDepth();
+ while (xml::XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
+ // Skip text and comments.
+ continue;
}
- if (error) {
- return false;
- }
+ const Source itemSource = mSource.withLine(parser->getLineNumber());
+ 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'");
+ error = true;
+ continue;
+ }
- outResource->value = std::move(plural);
- return true;
+ StringPiece trimmedQuantity = util::trimWhitespace(maybeQuantity.value());
+ size_t index = 0;
+ if (trimmedQuantity == "zero") {
+ index = Plural::Zero;
+ } else if (trimmedQuantity == "one") {
+ index = Plural::One;
+ } else if (trimmedQuantity == "two") {
+ index = Plural::Two;
+ } else if (trimmedQuantity == "few") {
+ index = Plural::Few;
+ } else if (trimmedQuantity == "many") {
+ index = Plural::Many;
+ } else if (trimmedQuantity == "other") {
+ index = Plural::Other;
+ } else {
+ mDiag->error(DiagMessage(itemSource)
+ << "<item> in <plural> has invalid value '"
+ << trimmedQuantity << "' for attribute 'quantity'");
+ error = true;
+ continue;
+ }
+
+ if (plural->values[index]) {
+ mDiag->error(DiagMessage(itemSource) << "duplicate quantity '"
+ << trimmedQuantity << "'");
+ error = true;
+ continue;
+ }
+
+ if (!(plural->values[index] = parseXml(
+ parser, android::ResTable_map::TYPE_STRING, kNoRawString))) {
+ error = true;
+ }
+ plural->values[index]->setSource(itemSource);
+
+ } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+ mDiag->error(DiagMessage(itemSource)
+ << "unknown tag <" << elementNamespace << ":" << elementName
+ << ">");
+ error = true;
+ }
+ }
+
+ if (error) {
+ return false;
+ }
+
+ outResource->value = std::move(plural);
+ return true;
}
bool ResourceParser::parseDeclareStyleable(xml::XmlPullParser* parser,
ParsedResource* outResource) {
- outResource->name.type = ResourceType::kStyleable;
+ outResource->name.type = ResourceType::kStyleable;
- // Declare-styleable is kPrivate by default, because it technically only exists in R.java.
- outResource->symbolState = SymbolState::kPublic;
+ // Declare-styleable is kPrivate by default, because it technically only
+ // exists in R.java.
+ outResource->symbolState = SymbolState::kPublic;
- // Declare-styleable only ends up in default config;
- if (outResource->config != ConfigDescription::defaultConfig()) {
- mDiag->warn(DiagMessage(outResource->source) << "ignoring configuration '"
- << outResource->config << "' for styleable "
- << outResource->name.entry);
- outResource->config = ConfigDescription::defaultConfig();
+ // Declare-styleable only ends up in default config;
+ if (outResource->config != ConfigDescription::defaultConfig()) {
+ mDiag->warn(DiagMessage(outResource->source)
+ << "ignoring configuration '" << outResource->config
+ << "' for styleable " << outResource->name.entry);
+ outResource->config = ConfigDescription::defaultConfig();
+ }
+
+ std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
+
+ std::string comment;
+ bool error = false;
+ const size_t depth = parser->getDepth();
+ while (xml::XmlPullParser::nextChildNode(parser, depth)) {
+ if (parser->getEvent() == xml::XmlPullParser::Event::kComment) {
+ comment = util::trimWhitespace(parser->getComment()).toString();
+ continue;
+ } else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
+ // Ignore text.
+ continue;
}
- std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
+ const Source itemSource = mSource.withLine(parser->getLineNumber());
+ 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;
+ continue;
+ }
- std::string comment;
- bool error = false;
- const size_t depth = parser->getDepth();
- while (xml::XmlPullParser::nextChildNode(parser, depth)) {
- if (parser->getEvent() == xml::XmlPullParser::Event::kComment) {
- comment = util::trimWhitespace(parser->getComment()).toString();
- continue;
- } else if (parser->getEvent() != xml::XmlPullParser::Event::kStartElement) {
- // Ignore text.
- continue;
- }
+ // If this is a declaration, the package name may be in the name. Separate
+ // these out.
+ // Eg. <attr name="android:text" />
+ Maybe<Reference> maybeRef =
+ ResourceUtils::parseXmlAttributeName(maybeName.value());
+ if (!maybeRef) {
+ mDiag->error(DiagMessage(itemSource) << "<attr> tag has invalid name '"
+ << maybeName.value() << "'");
+ error = true;
+ continue;
+ }
- const Source itemSource = mSource.withLine(parser->getLineNumber());
- 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;
- continue;
- }
+ Reference& childRef = maybeRef.value();
+ xml::transformReferenceFromNamespace(parser, "", &childRef);
- // If this is a declaration, the package name may be in the name. Separate these out.
- // Eg. <attr name="android:text" />
- Maybe<Reference> maybeRef = ResourceUtils::parseXmlAttributeName(maybeName.value());
- if (!maybeRef) {
- mDiag->error(DiagMessage(itemSource) << "<attr> tag has invalid name '"
- << maybeName.value() << "'");
- error = true;
- continue;
- }
+ // Create the ParsedResource that will add the attribute to the table.
+ ParsedResource childResource;
+ childResource.name = childRef.name.value();
+ childResource.source = itemSource;
+ childResource.comment = std::move(comment);
- Reference& childRef = maybeRef.value();
- xml::transformReferenceFromNamespace(parser, "", &childRef);
+ if (!parseAttrImpl(parser, &childResource, true)) {
+ error = true;
+ continue;
+ }
- // Create the ParsedResource that will add the attribute to the table.
- ParsedResource childResource;
- childResource.name = childRef.name.value();
- childResource.source = itemSource;
- childResource.comment = std::move(comment);
+ // Create the reference to this attribute.
+ childRef.setComment(childResource.comment);
+ childRef.setSource(itemSource);
+ styleable->entries.push_back(std::move(childRef));
- if (!parseAttrImpl(parser, &childResource, true)) {
- error = true;
- continue;
- }
+ outResource->childResources.push_back(std::move(childResource));
- // Create the reference to this attribute.
- childRef.setComment(childResource.comment);
- childRef.setSource(itemSource);
- styleable->entries.push_back(std::move(childRef));
-
- outResource->childResources.push_back(std::move(childResource));
-
- } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
- mDiag->error(DiagMessage(itemSource) << "unknown tag <" << elementNamespace << ":"
- << elementName << ">");
- error = true;
- }
-
- comment = {};
+ } else if (!shouldIgnoreElement(elementNamespace, elementName)) {
+ mDiag->error(DiagMessage(itemSource)
+ << "unknown tag <" << elementNamespace << ":" << elementName
+ << ">");
+ error = true;
}
- if (error) {
- return false;
- }
+ comment = {};
+ }
- outResource->value = std::move(styleable);
- return true;
+ if (error) {
+ return false;
+ }
+
+ outResource->value = std::move(styleable);
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index ece3090..644ed49 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -33,79 +33,92 @@
struct ParsedResource;
struct ResourceParserOptions {
- /**
- * Whether the default setting for this parser is to allow translation.
- */
- bool translatable = true;
+ /**
+ * Whether the default setting for this parser is to allow translation.
+ */
+ bool translatable = true;
- /**
- * Whether positional arguments in formatted strings are treated as errors or warnings.
- */
- bool errorOnPositionalArguments = true;
+ /**
+ * Whether positional arguments in formatted strings are treated as errors or
+ * warnings.
+ */
+ bool errorOnPositionalArguments = true;
};
/*
* Parses an XML file for resources and adds them to a ResourceTable.
*/
class ResourceParser {
-public:
- ResourceParser(IDiagnostics* diag, ResourceTable* table, const Source& source,
- const ConfigDescription& config, const ResourceParserOptions& options = {});
+ public:
+ ResourceParser(IDiagnostics* diag, ResourceTable* table, const Source& source,
+ const ConfigDescription& config,
+ const ResourceParserOptions& options = {});
- ResourceParser(const ResourceParser&) = delete; // No copy.
+ ResourceParser(const ResourceParser&) = delete; // No copy.
- bool parse(xml::XmlPullParser* parser);
+ bool parse(xml::XmlPullParser* parser);
-private:
- /*
- * Parses the XML subtree as a StyleString (flattened XML representation for strings
- * with formatting). If successful, `outStyleString`
- * contains the escaped and whitespace trimmed text, while `outRawString`
- * contains the unescaped text. Returns true on success.
- */
- bool flattenXmlSubtree(xml::XmlPullParser* parser, std::string* outRawString,
- StyleString* outStyleString);
+ private:
+ /*
+ * Parses the XML subtree as a StyleString (flattened XML representation for
+ * strings
+ * with formatting). If successful, `outStyleString`
+ * contains the escaped and whitespace trimmed text, while `outRawString`
+ * contains the unescaped text. Returns true on success.
+ */
+ bool flattenXmlSubtree(xml::XmlPullParser* parser, std::string* outRawString,
+ StyleString* outStyleString);
- /*
- * Parses the XML subtree and returns an Item.
- * The type of Item that can be parsed is denoted by the `typeMask`.
- * If `allowRawValue` is true and the subtree can not be parsed as a regular Item, then a
- * RawString is returned. Otherwise this returns false;
- */
- std::unique_ptr<Item> parseXml(xml::XmlPullParser* parser, const uint32_t typeMask,
- const bool allowRawValue);
+ /*
+ * Parses the XML subtree and returns an Item.
+ * The type of Item that can be parsed is denoted by the `typeMask`.
+ * If `allowRawValue` is true and the subtree can not be parsed as a regular
+ * Item, then a
+ * RawString is returned. Otherwise this returns false;
+ */
+ std::unique_ptr<Item> parseXml(xml::XmlPullParser* parser,
+ const uint32_t typeMask,
+ const bool allowRawValue);
- bool parseResources(xml::XmlPullParser* parser);
- bool parseResource(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parseResources(xml::XmlPullParser* parser);
+ bool parseResource(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseItem(xml::XmlPullParser* parser, ParsedResource* outResource, uint32_t format);
- bool parseString(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parseItem(xml::XmlPullParser* parser, ParsedResource* outResource,
+ uint32_t format);
+ bool parseString(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parsePublic(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parsePublicGroup(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseSymbol(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseAddResource(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseAttr(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* outResource, bool weak);
- Maybe<Attribute::Symbol> parseEnumOrFlagItem(xml::XmlPullParser* parser,
- const StringPiece& tag);
- bool parseStyle(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseStyleItem(xml::XmlPullParser* parser, Style* style);
- bool parseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseArray(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseIntegerArray(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseStringArray(xml::XmlPullParser* parser, ParsedResource* outResource);
- bool parseArrayImpl(xml::XmlPullParser* parser, ParsedResource* outResource, uint32_t typeMask);
- bool parsePlural(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parsePublic(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parsePublicGroup(xml::XmlPullParser* parser,
+ ParsedResource* outResource);
+ bool parseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parseSymbol(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parseAddResource(xml::XmlPullParser* parser,
+ ParsedResource* outResource);
+ bool parseAttr(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* outResource,
+ bool weak);
+ Maybe<Attribute::Symbol> parseEnumOrFlagItem(xml::XmlPullParser* parser,
+ const StringPiece& tag);
+ bool parseStyle(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parseStyleItem(xml::XmlPullParser* parser, Style* style);
+ bool parseDeclareStyleable(xml::XmlPullParser* parser,
+ ParsedResource* outResource);
+ bool parseArray(xml::XmlPullParser* parser, ParsedResource* outResource);
+ bool parseIntegerArray(xml::XmlPullParser* parser,
+ ParsedResource* outResource);
+ bool parseStringArray(xml::XmlPullParser* parser,
+ ParsedResource* outResource);
+ bool parseArrayImpl(xml::XmlPullParser* parser, ParsedResource* outResource,
+ uint32_t typeMask);
+ bool parsePlural(xml::XmlPullParser* parser, ParsedResource* outResource);
- IDiagnostics* mDiag;
- ResourceTable* mTable;
- Source mSource;
- ConfigDescription mConfig;
- ResourceParserOptions mOptions;
+ IDiagnostics* mDiag;
+ ResourceTable* mTable;
+ Source mSource;
+ ConfigDescription mConfig;
+ ResourceParserOptions mOptions;
};
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_RESOURCE_PARSER_H
+#endif // AAPT_RESOURCE_PARSER_H
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index e097740..b6d57c0 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -26,512 +26,559 @@
namespace aapt {
-constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+constexpr const char* kXmlPreamble =
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
TEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::stringstream input(kXmlPreamble);
- input << "<attr name=\"foo\"/>" << std::endl;
- ResourceTable table;
- ResourceParser parser(context->getDiagnostics(), &table, Source{ "test" }, {});
- xml::XmlPullParser xmlParser(input);
- ASSERT_FALSE(parser.parse(&xmlParser));
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::stringstream input(kXmlPreamble);
+ input << "<attr name=\"foo\"/>" << std::endl;
+ ResourceTable table;
+ ResourceParser parser(context->getDiagnostics(), &table, Source{"test"}, {});
+ xml::XmlPullParser xmlParser(input);
+ ASSERT_FALSE(parser.parse(&xmlParser));
}
struct ResourceParserTest : public ::testing::Test {
- ResourceTable mTable;
- std::unique_ptr<IAaptContext> mContext;
+ ResourceTable mTable;
+ std::unique_ptr<IAaptContext> mContext;
- void SetUp() override {
- mContext = test::ContextBuilder().build();
- }
+ void SetUp() override { mContext = test::ContextBuilder().build(); }
- ::testing::AssertionResult testParse(const StringPiece& str) {
- return testParse(str, ConfigDescription{});
- }
+ ::testing::AssertionResult testParse(const StringPiece& str) {
+ return testParse(str, ConfigDescription{});
+ }
- ::testing::AssertionResult testParse(const StringPiece& str, const ConfigDescription& config) {
- std::stringstream input(kXmlPreamble);
- input << "<resources>\n" << str << "\n</resources>" << std::endl;
- ResourceParserOptions parserOptions;
- ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, config,
- parserOptions);
- xml::XmlPullParser xmlParser(input);
- if (parser.parse(&xmlParser)) {
- return ::testing::AssertionSuccess();
- }
- return ::testing::AssertionFailure();
+ ::testing::AssertionResult testParse(const StringPiece& str,
+ const ConfigDescription& config) {
+ std::stringstream input(kXmlPreamble);
+ input << "<resources>\n" << str << "\n</resources>" << std::endl;
+ ResourceParserOptions parserOptions;
+ ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{"test"},
+ config, parserOptions);
+ xml::XmlPullParser xmlParser(input);
+ if (parser.parse(&xmlParser)) {
+ return ::testing::AssertionSuccess();
}
+ return ::testing::AssertionFailure();
+ }
};
TEST_F(ResourceParserTest, ParseQuotedString) {
- std::string input = "<string name=\"foo\"> \" hey there \" </string>";
- ASSERT_TRUE(testParse(input));
+ std::string input = "<string name=\"foo\"> \" hey there \" </string>";
+ ASSERT_TRUE(testParse(input));
- String* str = test::getValue<String>(&mTable, "string/foo");
- ASSERT_NE(nullptr, str);
- EXPECT_EQ(std::string(" hey there "), *str->value);
+ String* str = test::getValue<String>(&mTable, "string/foo");
+ ASSERT_NE(nullptr, str);
+ EXPECT_EQ(std::string(" hey there "), *str->value);
}
TEST_F(ResourceParserTest, ParseEscapedString) {
- std::string input = "<string name=\"foo\">\\?123</string>";
- ASSERT_TRUE(testParse(input));
+ std::string input = "<string name=\"foo\">\\?123</string>";
+ ASSERT_TRUE(testParse(input));
- String* str = test::getValue<String>(&mTable, "string/foo");
- ASSERT_NE(nullptr, str);
- EXPECT_EQ(std::string("?123"), *str->value);
+ String* str = test::getValue<String>(&mTable, "string/foo");
+ ASSERT_NE(nullptr, str);
+ EXPECT_EQ(std::string("?123"), *str->value);
}
TEST_F(ResourceParserTest, ParseFormattedString) {
- std::string input = "<string name=\"foo\">%d %s</string>";
- ASSERT_FALSE(testParse(input));
+ std::string input = "<string name=\"foo\">%d %s</string>";
+ ASSERT_FALSE(testParse(input));
- input = "<string name=\"foo\">%1$d %2$s</string>";
- ASSERT_TRUE(testParse(input));
+ input = "<string name=\"foo\">%1$d %2$s</string>";
+ ASSERT_TRUE(testParse(input));
}
TEST_F(ResourceParserTest, ParseStyledString) {
- // Use a surrogate pair unicode point so that we can verify that the span indices
- // use UTF-16 length and not UTF-18 length.
- std::string input = "<string name=\"foo\">This is my aunt\u2019s <b>string</b></string>";
- ASSERT_TRUE(testParse(input));
+ // Use a surrogate pair unicode point so that we can verify that the span
+ // indices
+ // use UTF-16 length and not UTF-18 length.
+ std::string input =
+ "<string name=\"foo\">This is my aunt\u2019s <b>string</b></string>";
+ ASSERT_TRUE(testParse(input));
- StyledString* str = test::getValue<StyledString>(&mTable, "string/foo");
- ASSERT_NE(nullptr, str);
+ StyledString* str = test::getValue<StyledString>(&mTable, "string/foo");
+ ASSERT_NE(nullptr, str);
- const std::string expectedStr = "This is my aunt\u2019s string";
- EXPECT_EQ(expectedStr, *str->value->str);
- EXPECT_EQ(1u, str->value->spans.size());
+ const std::string expectedStr = "This is my aunt\u2019s string";
+ EXPECT_EQ(expectedStr, *str->value->str);
+ EXPECT_EQ(1u, str->value->spans.size());
- EXPECT_EQ(std::string("b"), *str->value->spans[0].name);
- EXPECT_EQ(17u, str->value->spans[0].firstChar);
- EXPECT_EQ(23u, str->value->spans[0].lastChar);
+ EXPECT_EQ(std::string("b"), *str->value->spans[0].name);
+ EXPECT_EQ(17u, str->value->spans[0].firstChar);
+ EXPECT_EQ(23u, str->value->spans[0].lastChar);
}
TEST_F(ResourceParserTest, ParseStringWithWhitespace) {
- std::string input = "<string name=\"foo\"> This is what I think </string>";
- ASSERT_TRUE(testParse(input));
+ std::string input = "<string name=\"foo\"> This is what I think </string>";
+ ASSERT_TRUE(testParse(input));
- String* str = test::getValue<String>(&mTable, "string/foo");
- ASSERT_NE(nullptr, str);
- EXPECT_EQ(std::string("This is what I think"), *str->value);
+ String* str = test::getValue<String>(&mTable, "string/foo");
+ ASSERT_NE(nullptr, str);
+ EXPECT_EQ(std::string("This is what I think"), *str->value);
- input = "<string name=\"foo2\">\" This is what I think \"</string>";
- ASSERT_TRUE(testParse(input));
+ input = "<string name=\"foo2\">\" This is what I think \"</string>";
+ ASSERT_TRUE(testParse(input));
- str = test::getValue<String>(&mTable, "string/foo2");
- ASSERT_NE(nullptr, str);
- EXPECT_EQ(std::string(" This is what I think "), *str->value);
+ str = test::getValue<String>(&mTable, "string/foo2");
+ ASSERT_NE(nullptr, str);
+ EXPECT_EQ(std::string(" This is what I think "), *str->value);
}
TEST_F(ResourceParserTest, IgnoreXliffTags) {
- std::string input = "<string name=\"foo\" \n"
- " xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n"
- " There are <xliff:g id=\"count\">%1$d</xliff:g> apples</string>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<string name=\"foo\" \n"
+ " xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n"
+ " There are <xliff:g id=\"count\">%1$d</xliff:g> apples</string>";
+ ASSERT_TRUE(testParse(input));
- String* str = test::getValue<String>(&mTable, "string/foo");
- ASSERT_NE(nullptr, str);
- EXPECT_EQ(StringPiece("There are %1$d apples"), StringPiece(*str->value));
+ String* str = test::getValue<String>(&mTable, "string/foo");
+ ASSERT_NE(nullptr, str);
+ EXPECT_EQ(StringPiece("There are %1$d apples"), StringPiece(*str->value));
}
TEST_F(ResourceParserTest, ParseNull) {
- std::string input = "<integer name=\"foo\">@null</integer>";
- ASSERT_TRUE(testParse(input));
+ std::string input = "<integer name=\"foo\">@null</integer>";
+ ASSERT_TRUE(testParse(input));
- // The Android runtime treats a value of android::Res_value::TYPE_NULL as
- // 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, "integer/foo");
- ASSERT_NE(nullptr, integer);
- EXPECT_EQ(uint16_t(android::Res_value::TYPE_REFERENCE), integer->value.dataType);
- EXPECT_EQ(0u, integer->value.data);
+ // The Android runtime treats a value of android::Res_value::TYPE_NULL as
+ // 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, "integer/foo");
+ ASSERT_NE(nullptr, integer);
+ EXPECT_EQ(uint16_t(android::Res_value::TYPE_REFERENCE),
+ integer->value.dataType);
+ EXPECT_EQ(0u, integer->value.data);
}
TEST_F(ResourceParserTest, ParseEmpty) {
- std::string input = "<integer name=\"foo\">@empty</integer>";
- ASSERT_TRUE(testParse(input));
+ std::string input = "<integer name=\"foo\">@empty</integer>";
+ ASSERT_TRUE(testParse(input));
- 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);
+ 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);
}
TEST_F(ResourceParserTest, ParseAttr) {
- std::string input = "<attr name=\"foo\" format=\"string\"/>\n"
- "<attr name=\"bar\"/>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<attr name=\"foo\" format=\"string\"/>\n"
+ "<attr name=\"bar\"/>";
+ ASSERT_TRUE(testParse(input));
- Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
+ 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, "attr/bar");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->typeMask);
+ attr = test::getValue<Attribute>(&mTable, "attr/bar");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->typeMask);
}
-// Old AAPT allowed attributes to be defined under different configurations, but ultimately
-// stored them with the default configuration. Check that we have the same behavior.
-TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
- const ConfigDescription watchConfig = test::parseConfigOrDie("watch");
- std::string input = R"EOF(
+// Old AAPT allowed attributes to be defined under different configurations, but
+// ultimately
+// stored them with the default configuration. Check that we have the same
+// behavior.
+TEST_F(ResourceParserTest,
+ ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
+ const ConfigDescription watchConfig = test::parseConfigOrDie("watch");
+ std::string input = R"EOF(
<attr name="foo" />
<declare-styleable name="bar">
<attr name="baz" />
</declare-styleable>)EOF";
- ASSERT_TRUE(testParse(input, watchConfig));
+ ASSERT_TRUE(testParse(input, 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_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, "attr/foo"));
- EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, "attr/baz"));
- EXPECT_NE(nullptr, test::getValue<Styleable>(&mTable, "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));
+ std::string input =
+ "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"integer\"/>";
+ ASSERT_TRUE(testParse(input));
- 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);
- EXPECT_EQ(23, attr->maxInt);
+ 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);
+ EXPECT_EQ(23, attr->maxInt);
}
TEST_F(ResourceParserTest, FailParseAttrWithMinMaxButNotInteger) {
- std::string input = "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"string\"/>";
- ASSERT_FALSE(testParse(input));
+ std::string input =
+ "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"string\"/>";
+ ASSERT_FALSE(testParse(input));
}
TEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) {
- std::string input = "<declare-styleable name=\"Styleable\">\n"
- " <attr name=\"foo\" />\n"
- "</declare-styleable>\n"
- "<attr name=\"foo\" format=\"string\"/>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<declare-styleable name=\"Styleable\">\n"
+ " <attr name=\"foo\" />\n"
+ "</declare-styleable>\n"
+ "<attr name=\"foo\" format=\"string\"/>";
+ ASSERT_TRUE(testParse(input));
- Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
+ Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
}
TEST_F(ResourceParserTest, ParseDoubleUseOfAttr) {
- std::string input = "<declare-styleable name=\"Theme\">"
- " <attr name=\"foo\" />\n"
- "</declare-styleable>\n"
- "<declare-styleable name=\"Window\">\n"
- " <attr name=\"foo\" format=\"boolean\"/>\n"
- "</declare-styleable>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<declare-styleable name=\"Theme\">"
+ " <attr name=\"foo\" />\n"
+ "</declare-styleable>\n"
+ "<declare-styleable name=\"Window\">\n"
+ " <attr name=\"foo\" format=\"boolean\"/>\n"
+ "</declare-styleable>";
+ ASSERT_TRUE(testParse(input));
- Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->typeMask);
+ Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->typeMask);
}
TEST_F(ResourceParserTest, ParseEnumAttr) {
- std::string input = "<attr name=\"foo\">\n"
- " <enum name=\"bar\" value=\"0\"/>\n"
- " <enum name=\"bat\" value=\"1\"/>\n"
- " <enum name=\"baz\" value=\"2\"/>\n"
- "</attr>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<attr name=\"foo\">\n"
+ " <enum name=\"bar\" value=\"0\"/>\n"
+ " <enum name=\"bat\" value=\"1\"/>\n"
+ " <enum name=\"baz\" value=\"2\"/>\n"
+ "</attr>";
+ ASSERT_TRUE(testParse(input));
- 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);
+ 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, "bar");
- EXPECT_EQ(enumAttr->symbols[0].value, 0u);
+ AAPT_ASSERT_TRUE(enumAttr->symbols[0].symbol.name);
+ 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, "bat");
- EXPECT_EQ(enumAttr->symbols[1].value, 1u);
+ AAPT_ASSERT_TRUE(enumAttr->symbols[1].symbol.name);
+ 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, "baz");
- EXPECT_EQ(enumAttr->symbols[2].value, 2u);
+ AAPT_ASSERT_TRUE(enumAttr->symbols[2].symbol.name);
+ EXPECT_EQ(enumAttr->symbols[2].symbol.name.value().entry, "baz");
+ EXPECT_EQ(enumAttr->symbols[2].value, 2u);
}
TEST_F(ResourceParserTest, ParseFlagAttr) {
- std::string input = "<attr name=\"foo\">\n"
- " <flag name=\"bar\" value=\"0\"/>\n"
- " <flag name=\"bat\" value=\"1\"/>\n"
- " <flag name=\"baz\" value=\"2\"/>\n"
- "</attr>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<attr name=\"foo\">\n"
+ " <flag name=\"bar\" value=\"0\"/>\n"
+ " <flag name=\"bat\" value=\"1\"/>\n"
+ " <flag name=\"baz\" value=\"2\"/>\n"
+ "</attr>";
+ ASSERT_TRUE(testParse(input));
- 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);
+ 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, "bar");
- EXPECT_EQ(flagAttr->symbols[0].value, 0u);
+ AAPT_ASSERT_TRUE(flagAttr->symbols[0].symbol.name);
+ 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, "bat");
- EXPECT_EQ(flagAttr->symbols[1].value, 1u);
+ AAPT_ASSERT_TRUE(flagAttr->symbols[1].symbol.name);
+ 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, "baz");
- EXPECT_EQ(flagAttr->symbols[2].value, 2u);
+ AAPT_ASSERT_TRUE(flagAttr->symbols[2].symbol.name);
+ 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,
- "baz|bat");
- ASSERT_NE(nullptr, flagValue);
- EXPECT_EQ(flagValue->value.data, 1u | 2u);
+ std::unique_ptr<BinaryPrimitive> flagValue =
+ ResourceUtils::tryParseFlagSymbol(flagAttr, "baz|bat");
+ ASSERT_NE(nullptr, flagValue);
+ EXPECT_EQ(flagValue->value.data, 1u | 2u);
}
TEST_F(ResourceParserTest, FailToParseEnumAttrWithNonUniqueKeys) {
- std::string input = "<attr name=\"foo\">\n"
- " <enum name=\"bar\" value=\"0\"/>\n"
- " <enum name=\"bat\" value=\"1\"/>\n"
- " <enum name=\"bat\" value=\"2\"/>\n"
- "</attr>";
- ASSERT_FALSE(testParse(input));
+ std::string input =
+ "<attr name=\"foo\">\n"
+ " <enum name=\"bar\" value=\"0\"/>\n"
+ " <enum name=\"bat\" value=\"1\"/>\n"
+ " <enum name=\"bat\" value=\"2\"/>\n"
+ "</attr>";
+ ASSERT_FALSE(testParse(input));
}
TEST_F(ResourceParserTest, ParseStyle) {
- std::string input = "<style name=\"foo\" parent=\"@style/fu\">\n"
- " <item name=\"bar\">#ffffffff</item>\n"
- " <item name=\"bat\">@string/hey</item>\n"
- " <item name=\"baz\"><b>hey</b></item>\n"
- "</style>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<style name=\"foo\" parent=\"@style/fu\">\n"
+ " <item name=\"bar\">#ffffffff</item>\n"
+ " <item name=\"bat\">@string/hey</item>\n"
+ " <item name=\"baz\"><b>hey</b></item>\n"
+ "</style>";
+ ASSERT_TRUE(testParse(input));
- 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("style/fu"), style->parent.value().name.value());
- ASSERT_EQ(3u, style->entries.size());
+ 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("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("attr/bar"), style->entries[0].key.name.value());
+ AAPT_ASSERT_TRUE(style->entries[0].key.name);
+ EXPECT_EQ(test::parseNameOrDie("attr/bar"),
+ style->entries[0].key.name.value());
- AAPT_ASSERT_TRUE(style->entries[1].key.name);
- EXPECT_EQ(test::parseNameOrDie("attr/bat"), style->entries[1].key.name.value());
+ AAPT_ASSERT_TRUE(style->entries[1].key.name);
+ EXPECT_EQ(test::parseNameOrDie("attr/bat"),
+ style->entries[1].key.name.value());
- AAPT_ASSERT_TRUE(style->entries[2].key.name);
- EXPECT_EQ(test::parseNameOrDie("attr/baz"), style->entries[2].key.name.value());
+ AAPT_ASSERT_TRUE(style->entries[2].key.name);
+ 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));
+ std::string input = "<style name=\"foo\" parent=\"com.app:Theme\"/>";
+ ASSERT_TRUE(testParse(input));
- 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("com.app:style/Theme"), style->parent.value().name.value());
+ 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("com.app:style/Theme"),
+ style->parent.value().name.value());
}
TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) {
- std::string input = "<style xmlns:app=\"http://schemas.android.com/apk/res/android\"\n"
- " name=\"foo\" parent=\"app:Theme\"/>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<style xmlns:app=\"http://schemas.android.com/apk/res/android\"\n"
+ " name=\"foo\" parent=\"app:Theme\"/>";
+ ASSERT_TRUE(testParse(input));
- 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("android:style/Theme"), style->parent.value().name.value());
+ 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("android:style/Theme"),
+ style->parent.value().name.value());
}
TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) {
- std::string input =
- "<style xmlns:app=\"http://schemas.android.com/apk/res/android\" name=\"foo\">\n"
- " <item name=\"app:bar\">0</item>\n"
- "</style>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<style xmlns:app=\"http://schemas.android.com/apk/res/android\" "
+ "name=\"foo\">\n"
+ " <item name=\"app:bar\">0</item>\n"
+ "</style>";
+ ASSERT_TRUE(testParse(input));
- Style* style = test::getValue<Style>(&mTable, "style/foo");
- ASSERT_NE(nullptr, style);
- ASSERT_EQ(1u, style->entries.size());
- EXPECT_EQ(test::parseNameOrDie("android:attr/bar"), style->entries[0].key.name.value());
+ Style* style = test::getValue<Style>(&mTable, "style/foo");
+ ASSERT_NE(nullptr, style);
+ ASSERT_EQ(1u, style->entries.size());
+ 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));
+ std::string input = "<style name=\"foo.bar\"/>";
+ ASSERT_TRUE(testParse(input));
- 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("style/foo"));
- EXPECT_TRUE(style->parentInferred);
+ 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("style/foo"));
+ EXPECT_TRUE(style->parentInferred);
}
-TEST_F(ResourceParserTest, ParseStyleWithInferredParentOverridenByEmptyParentAttribute) {
- std::string input = "<style name=\"foo.bar\" parent=\"\"/>";
- ASSERT_TRUE(testParse(input));
+TEST_F(ResourceParserTest,
+ ParseStyleWithInferredParentOverridenByEmptyParentAttribute) {
+ std::string input = "<style name=\"foo.bar\" parent=\"\"/>";
+ ASSERT_TRUE(testParse(input));
- Style* style = test::getValue<Style>(&mTable, "style/foo.bar");
- ASSERT_NE(nullptr, style);
- AAPT_EXPECT_FALSE(style->parent);
- EXPECT_FALSE(style->parentInferred);
+ Style* style = test::getValue<Style>(&mTable, "style/foo.bar");
+ ASSERT_NE(nullptr, style);
+ AAPT_EXPECT_FALSE(style->parent);
+ EXPECT_FALSE(style->parentInferred);
}
TEST_F(ResourceParserTest, ParseStyleWithPrivateParentReference) {
- std::string input = R"EOF(<style name="foo" parent="*android:style/bar" />)EOF";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ R"EOF(<style name="foo" parent="*android:style/bar" />)EOF";
+ ASSERT_TRUE(testParse(input));
- Style* style = test::getValue<Style>(&mTable, "style/foo");
- ASSERT_NE(nullptr, style);
- AAPT_ASSERT_TRUE(style->parent);
- EXPECT_TRUE(style->parent.value().privateReference);
+ Style* style = test::getValue<Style>(&mTable, "style/foo");
+ ASSERT_NE(nullptr, style);
+ AAPT_ASSERT_TRUE(style->parent);
+ EXPECT_TRUE(style->parent.value().privateReference);
}
TEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) {
- std::string input = "<string name=\"foo\">@+id/bar</string>";
- ASSERT_TRUE(testParse(input));
+ std::string input = "<string name=\"foo\">@+id/bar</string>";
+ ASSERT_TRUE(testParse(input));
- Id* id = test::getValue<Id>(&mTable, "id/bar");
- ASSERT_NE(id, nullptr);
+ Id* id = test::getValue<Id>(&mTable, "id/bar");
+ ASSERT_NE(id, nullptr);
}
TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) {
- std::string input = "<declare-styleable name=\"foo\">\n"
- " <attr name=\"bar\" />\n"
- " <attr name=\"bat\" format=\"string|reference\"/>\n"
- " <attr name=\"baz\">\n"
- " <enum name=\"foo\" value=\"1\"/>\n"
- " </attr>\n"
- "</declare-styleable>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<declare-styleable name=\"foo\">\n"
+ " <attr name=\"bar\" />\n"
+ " <attr name=\"bat\" format=\"string|reference\"/>\n"
+ " <attr name=\"baz\">\n"
+ " <enum name=\"foo\" value=\"1\"/>\n"
+ " </attr>\n"
+ "</declare-styleable>";
+ ASSERT_TRUE(testParse(input));
- Maybe<ResourceTable::SearchResult> result =
- mTable.findResource(test::parseNameOrDie("styleable/foo"));
- AAPT_ASSERT_TRUE(result);
- EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state);
+ Maybe<ResourceTable::SearchResult> result =
+ 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, "attr/bar");
- ASSERT_NE(attr, nullptr);
- EXPECT_TRUE(attr->isWeak());
+ Attribute* attr = test::getValue<Attribute>(&mTable, "attr/bar");
+ ASSERT_NE(attr, nullptr);
+ EXPECT_TRUE(attr->isWeak());
- attr = test::getValue<Attribute>(&mTable, "attr/bat");
- ASSERT_NE(attr, nullptr);
- EXPECT_TRUE(attr->isWeak());
+ attr = test::getValue<Attribute>(&mTable, "attr/bat");
+ ASSERT_NE(attr, nullptr);
+ EXPECT_TRUE(attr->isWeak());
- attr = test::getValue<Attribute>(&mTable, "attr/baz");
- ASSERT_NE(attr, nullptr);
- EXPECT_TRUE(attr->isWeak());
- EXPECT_EQ(1u, attr->symbols.size());
+ 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, "id/foo"));
+ EXPECT_NE(nullptr, test::getValue<Id>(&mTable, "id/foo"));
- Styleable* styleable = test::getValue<Styleable>(&mTable, "styleable/foo");
- ASSERT_NE(styleable, nullptr);
- ASSERT_EQ(3u, styleable->entries.size());
+ Styleable* styleable = test::getValue<Styleable>(&mTable, "styleable/foo");
+ ASSERT_NE(styleable, nullptr);
+ ASSERT_EQ(3u, styleable->entries.size());
- EXPECT_EQ(test::parseNameOrDie("attr/bar"), styleable->entries[0].name.value());
- EXPECT_EQ(test::parseNameOrDie("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) {
- std::string input = "<declare-styleable name=\"foo\" xmlns:privAndroid=\"http://schemas.android.com/apk/prv/res/android\">\n"
- " <attr name=\"*android:bar\" />\n"
- " <attr name=\"privAndroid:bat\" />\n"
- "</declare-styleable>";
- ASSERT_TRUE(testParse(input));
- Styleable* styleable = test::getValue<Styleable>(&mTable, "styleable/foo");
- ASSERT_NE(nullptr, styleable);
- ASSERT_EQ(2u, styleable->entries.size());
+ std::string input =
+ "<declare-styleable name=\"foo\" "
+ "xmlns:privAndroid=\"http://schemas.android.com/apk/prv/res/android\">\n"
+ " <attr name=\"*android:bar\" />\n"
+ " <attr name=\"privAndroid:bat\" />\n"
+ "</declare-styleable>";
+ ASSERT_TRUE(testParse(input));
+ 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::string("android"), styleable->entries[0].name.value().package);
+ EXPECT_TRUE(styleable->entries[0].privateReference);
+ AAPT_ASSERT_TRUE(styleable->entries[0].name);
+ 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::string("android"), styleable->entries[1].name.value().package);
+ EXPECT_TRUE(styleable->entries[1].privateReference);
+ AAPT_ASSERT_TRUE(styleable->entries[1].name);
+ EXPECT_EQ(std::string("android"), styleable->entries[1].name.value().package);
}
TEST_F(ResourceParserTest, ParseArray) {
- std::string input = "<array name=\"foo\">\n"
- " <item>@string/ref</item>\n"
- " <item>hey</item>\n"
- " <item>23</item>\n"
- "</array>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<array name=\"foo\">\n"
+ " <item>@string/ref</item>\n"
+ " <item>hey</item>\n"
+ " <item>23</item>\n"
+ "</array>";
+ ASSERT_TRUE(testParse(input));
- Array* array = test::getValue<Array>(&mTable, "array/foo");
- ASSERT_NE(array, nullptr);
- ASSERT_EQ(3u, array->items.size());
+ Array* array = test::getValue<Array>(&mTable, "array/foo");
+ ASSERT_NE(array, nullptr);
+ ASSERT_EQ(3u, array->items.size());
- EXPECT_NE(nullptr, valueCast<Reference>(array->items[0].get()));
- EXPECT_NE(nullptr, valueCast<String>(array->items[1].get()));
- EXPECT_NE(nullptr, valueCast<BinaryPrimitive>(array->items[2].get()));
+ EXPECT_NE(nullptr, valueCast<Reference>(array->items[0].get()));
+ EXPECT_NE(nullptr, valueCast<String>(array->items[1].get()));
+ EXPECT_NE(nullptr, valueCast<BinaryPrimitive>(array->items[2].get()));
}
TEST_F(ResourceParserTest, ParseStringArray) {
- std::string input = "<string-array name=\"foo\">\n"
- " <item>\"Werk\"</item>\n"
- "</string-array>\n";
- ASSERT_TRUE(testParse(input));
- EXPECT_NE(nullptr, test::getValue<Array>(&mTable, "array/foo"));
+ std::string input =
+ "<string-array name=\"foo\">\n"
+ " <item>\"Werk\"</item>\n"
+ "</string-array>\n";
+ ASSERT_TRUE(testParse(input));
+ EXPECT_NE(nullptr, test::getValue<Array>(&mTable, "array/foo"));
}
TEST_F(ResourceParserTest, ParsePlural) {
- std::string input = "<plurals name=\"foo\">\n"
- " <item quantity=\"other\">apples</item>\n"
- " <item quantity=\"one\">apple</item>\n"
- "</plurals>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<plurals name=\"foo\">\n"
+ " <item quantity=\"other\">apples</item>\n"
+ " <item quantity=\"one\">apple</item>\n"
+ "</plurals>";
+ ASSERT_TRUE(testParse(input));
}
TEST_F(ResourceParserTest, ParseCommentsWithResource) {
- std::string input = "<!--This is a comment-->\n"
- "<string name=\"foo\">Hi</string>";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ "<!--This is a comment-->\n"
+ "<string name=\"foo\">Hi</string>";
+ ASSERT_TRUE(testParse(input));
- String* value = test::getValue<String>(&mTable, "string/foo");
- ASSERT_NE(nullptr, value);
- EXPECT_EQ(value->getComment(), "This is a comment");
+ String* value = test::getValue<String>(&mTable, "string/foo");
+ ASSERT_NE(nullptr, value);
+ EXPECT_EQ(value->getComment(), "This is a comment");
}
TEST_F(ResourceParserTest, DoNotCombineMultipleComments) {
- std::string input = "<!--One-->\n"
- "<!--Two-->\n"
- "<string name=\"foo\">Hi</string>";
+ std::string input =
+ "<!--One-->\n"
+ "<!--Two-->\n"
+ "<string name=\"foo\">Hi</string>";
- ASSERT_TRUE(testParse(input));
+ ASSERT_TRUE(testParse(input));
- String* value = test::getValue<String>(&mTable, "string/foo");
- ASSERT_NE(nullptr, value);
- EXPECT_EQ(value->getComment(), "Two");
+ String* value = test::getValue<String>(&mTable, "string/foo");
+ ASSERT_NE(nullptr, value);
+ EXPECT_EQ(value->getComment(), "Two");
}
TEST_F(ResourceParserTest, IgnoreCommentBeforeEndTag) {
- std::string input = "<!--One-->\n"
- "<string name=\"foo\">\n"
- " Hi\n"
- "<!--Two-->\n"
- "</string>";
+ std::string input =
+ "<!--One-->\n"
+ "<string name=\"foo\">\n"
+ " Hi\n"
+ "<!--Two-->\n"
+ "</string>";
- ASSERT_TRUE(testParse(input));
+ ASSERT_TRUE(testParse(input));
- String* value = test::getValue<String>(&mTable, "string/foo");
- ASSERT_NE(nullptr, value);
- EXPECT_EQ(value->getComment(), "One");
+ String* value = test::getValue<String>(&mTable, "string/foo");
+ ASSERT_NE(nullptr, value);
+ EXPECT_EQ(value->getComment(), "One");
}
TEST_F(ResourceParserTest, ParseNestedComments) {
- // We only care about declare-styleable and enum/flag attributes because comments
- // from those end up in R.java
- std::string input = R"EOF(
+ // We only care about declare-styleable and enum/flag attributes because
+ // comments
+ // from those end up in R.java
+ std::string input = R"EOF(
<declare-styleable name="foo">
<!-- The name of the bar -->
<attr name="barName" format="string|reference" />
@@ -541,19 +588,21 @@
<!-- The very first -->
<enum name="one" value="1" />
</attr>)EOF";
- ASSERT_TRUE(testParse(input));
+ ASSERT_TRUE(testParse(input));
- Styleable* styleable = test::getValue<Styleable>(&mTable, "styleable/foo");
- ASSERT_NE(nullptr, styleable);
- ASSERT_EQ(1u, styleable->entries.size());
+ Styleable* styleable = test::getValue<Styleable>(&mTable, "styleable/foo");
+ ASSERT_NE(nullptr, styleable);
+ ASSERT_EQ(1u, styleable->entries.size());
- EXPECT_EQ(StringPiece("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, "attr/foo");
- ASSERT_NE(nullptr, attr);
- ASSERT_EQ(1u, attr->symbols.size());
+ Attribute* attr = test::getValue<Attribute>(&mTable, "attr/foo");
+ ASSERT_NE(nullptr, attr);
+ ASSERT_EQ(1u, attr->symbols.size());
- EXPECT_EQ(StringPiece("The very first"), attr->symbols.front().symbol.getComment());
+ EXPECT_EQ(StringPiece("The very first"),
+ attr->symbols.front().symbol.getComment());
}
/*
@@ -561,15 +610,15 @@
* (as an ID has no value).
*/
TEST_F(ResourceParserTest, ParsePublicIdAsDefinition) {
- std::string input = "<public type=\"id\" name=\"foo\"/>";
- ASSERT_TRUE(testParse(input));
+ std::string input = "<public type=\"id\" name=\"foo\"/>";
+ ASSERT_TRUE(testParse(input));
- Id* id = test::getValue<Id>(&mTable, "id/foo");
- ASSERT_NE(nullptr, id);
+ Id* id = test::getValue<Id>(&mTable, "id/foo");
+ ASSERT_NE(nullptr, id);
}
TEST_F(ResourceParserTest, KeepAllProducts) {
- std::string input = R"EOF(
+ std::string input = R"EOF(
<string name="foo" product="phone">hi</string>
<string name="foo" product="no-sdcard">ho</string>
<string name="bar" product="">wee</string>
@@ -577,88 +626,92 @@
<string name="bit" product="phablet">hoot</string>
<string name="bot" product="default">yes</string>
)EOF";
- ASSERT_TRUE(testParse(input));
+ ASSERT_TRUE(testParse(input));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "string/foo",
- ConfigDescription::defaultConfig(),
- "phone"));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "string/foo",
- ConfigDescription::defaultConfig(),
- "no-sdcard"));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "string/bar",
- ConfigDescription::defaultConfig(),
- ""));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "string/baz",
- ConfigDescription::defaultConfig(),
- ""));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "string/bit",
- ConfigDescription::defaultConfig(),
- "phablet"));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(&mTable, "string/bot",
- ConfigDescription::defaultConfig(),
- "default"));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(
+ &mTable, "string/foo",
+ ConfigDescription::defaultConfig(), "phone"));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(
+ &mTable, "string/foo",
+ ConfigDescription::defaultConfig(), "no-sdcard"));
+ EXPECT_NE(nullptr,
+ test::getValueForConfigAndProduct<String>(
+ &mTable, "string/bar", ConfigDescription::defaultConfig(), ""));
+ EXPECT_NE(nullptr,
+ test::getValueForConfigAndProduct<String>(
+ &mTable, "string/baz", ConfigDescription::defaultConfig(), ""));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(
+ &mTable, "string/bit",
+ ConfigDescription::defaultConfig(), "phablet"));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<String>(
+ &mTable, "string/bot",
+ ConfigDescription::defaultConfig(), "default"));
}
TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) {
- std::string input = R"EOF(
+ std::string input = R"EOF(
<public-group type="attr" first-id="0x01010040">
<public name="foo" />
<public name="bar" />
</public-group>)EOF";
- ASSERT_TRUE(testParse(input));
+ ASSERT_TRUE(testParse(input));
- Maybe<ResourceTable::SearchResult> result = mTable.findResource(
- test::parseNameOrDie("attr/foo"));
- AAPT_ASSERT_TRUE(result);
+ Maybe<ResourceTable::SearchResult> result =
+ mTable.findResource(test::parseNameOrDie("attr/foo"));
+ AAPT_ASSERT_TRUE(result);
- AAPT_ASSERT_TRUE(result.value().package->id);
- AAPT_ASSERT_TRUE(result.value().type->id);
- AAPT_ASSERT_TRUE(result.value().entry->id);
- ResourceId actualId(result.value().package->id.value(),
+ AAPT_ASSERT_TRUE(result.value().package->id);
+ AAPT_ASSERT_TRUE(result.value().type->id);
+ AAPT_ASSERT_TRUE(result.value().entry->id);
+ ResourceId actualId(result.value().package->id.value(),
+ result.value().type->id.value(),
+ result.value().entry->id.value());
+ EXPECT_EQ(ResourceId(0x01010040), actualId);
+
+ result = mTable.findResource(test::parseNameOrDie("attr/bar"));
+ AAPT_ASSERT_TRUE(result);
+
+ AAPT_ASSERT_TRUE(result.value().package->id);
+ AAPT_ASSERT_TRUE(result.value().type->id);
+ AAPT_ASSERT_TRUE(result.value().entry->id);
+ actualId = ResourceId(result.value().package->id.value(),
result.value().type->id.value(),
result.value().entry->id.value());
- EXPECT_EQ(ResourceId(0x01010040), actualId);
-
- result = mTable.findResource(test::parseNameOrDie("attr/bar"));
- AAPT_ASSERT_TRUE(result);
-
- AAPT_ASSERT_TRUE(result.value().package->id);
- AAPT_ASSERT_TRUE(result.value().type->id);
- AAPT_ASSERT_TRUE(result.value().entry->id);
- actualId = ResourceId(result.value().package->id.value(),
- result.value().type->id.value(),
- result.value().entry->id.value());
- EXPECT_EQ(ResourceId(0x01010041), actualId);
+ EXPECT_EQ(ResourceId(0x01010041), actualId);
}
TEST_F(ResourceParserTest, ExternalTypesShouldOnlyBeReferences) {
- std::string input = R"EOF(<item type="layout" name="foo">@layout/bar</item>)EOF";
- ASSERT_TRUE(testParse(input));
+ std::string input =
+ R"EOF(<item type="layout" name="foo">@layout/bar</item>)EOF";
+ ASSERT_TRUE(testParse(input));
- input = R"EOF(<item type="layout" name="bar">"this is a string"</item>)EOF";
- ASSERT_FALSE(testParse(input));
+ input = R"EOF(<item type="layout" name="bar">"this is a string"</item>)EOF";
+ ASSERT_FALSE(testParse(input));
}
-TEST_F(ResourceParserTest, AddResourcesElementShouldAddEntryWithUndefinedSymbol) {
- std::string input = R"EOF(<add-resource name="bar" type="string" />)EOF";
- ASSERT_TRUE(testParse(input));
+TEST_F(ResourceParserTest,
+ AddResourcesElementShouldAddEntryWithUndefinedSymbol) {
+ std::string input = R"EOF(<add-resource name="bar" type="string" />)EOF";
+ ASSERT_TRUE(testParse(input));
- Maybe<ResourceTable::SearchResult> result = mTable.findResource(
- test::parseNameOrDie("string/bar"));
- AAPT_ASSERT_TRUE(result);
- const ResourceEntry* entry = result.value().entry;
- ASSERT_NE(nullptr, entry);
- EXPECT_EQ(SymbolState::kUndefined, entry->symbolStatus.state);
+ Maybe<ResourceTable::SearchResult> result =
+ mTable.findResource(test::parseNameOrDie("string/bar"));
+ AAPT_ASSERT_TRUE(result);
+ const ResourceEntry* entry = result.value().entry;
+ ASSERT_NE(nullptr, entry);
+ EXPECT_EQ(SymbolState::kUndefined, entry->symbolStatus.state);
}
TEST_F(ResourceParserTest, ParseItemElementWithFormat) {
- std::string input = R"EOF(<item name="foo" type="integer" format="float">0.3</item>)EOF";
- ASSERT_TRUE(testParse(input));
+ 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, "integer/foo");
- ASSERT_NE(nullptr, val);
+ BinaryPrimitive* val =
+ test::getValue<BinaryPrimitive>(&mTable, "integer/foo");
+ ASSERT_NE(nullptr, val);
- EXPECT_EQ(uint32_t(android::Res_value::TYPE_FLOAT), val->value.dataType);
+ EXPECT_EQ(uint32_t(android::Res_value::TYPE_FLOAT), val->value.dataType);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index bdc6a8c..c52c91c 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -14,266 +14,282 @@
* limitations under the License.
*/
+#include "ResourceTable.h"
#include "ConfigDescription.h"
#include "NameMangler.h"
-#include "ResourceTable.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
#include "util/Util.h"
-#include <algorithm>
#include <androidfw/ResourceTypes.h>
+#include <algorithm>
#include <memory>
#include <string>
#include <tuple>
namespace aapt {
-static bool lessThanType(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) {
- return lhs->type < rhs;
+static bool lessThanType(const std::unique_ptr<ResourceTableType>& lhs,
+ ResourceType rhs) {
+ return lhs->type < rhs;
}
template <typename T>
static bool lessThanStructWithName(const std::unique_ptr<T>& lhs,
const StringPiece& rhs) {
- return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
+ return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
}
ResourceTablePackage* ResourceTable::findPackage(const StringPiece& name) {
- const auto last = packages.end();
- auto iter = std::lower_bound(packages.begin(), last, name,
- lessThanStructWithName<ResourceTablePackage>);
- if (iter != last && name == (*iter)->name) {
- return iter->get();
- }
- return nullptr;
+ const auto last = packages.end();
+ auto iter = std::lower_bound(packages.begin(), last, name,
+ lessThanStructWithName<ResourceTablePackage>);
+ if (iter != last && name == (*iter)->name) {
+ return iter->get();
+ }
+ return nullptr;
}
ResourceTablePackage* ResourceTable::findPackageById(uint8_t id) {
- for (auto& package : packages) {
- if (package->id && package->id.value() == id) {
- return package.get();
- }
+ for (auto& package : packages) {
+ if (package->id && package->id.value() == id) {
+ return package.get();
}
- return nullptr;
+ }
+ return nullptr;
}
-ResourceTablePackage* ResourceTable::createPackage(const StringPiece& name, Maybe<uint8_t> id) {
- ResourceTablePackage* package = findOrCreatePackage(name);
- if (id && !package->id) {
- package->id = id;
- return package;
- }
-
- if (id && package->id && package->id.value() != id.value()) {
- return nullptr;
- }
+ResourceTablePackage* ResourceTable::createPackage(const StringPiece& name,
+ Maybe<uint8_t> id) {
+ ResourceTablePackage* package = findOrCreatePackage(name);
+ if (id && !package->id) {
+ package->id = id;
return package;
+ }
+
+ if (id && package->id && package->id.value() != id.value()) {
+ return nullptr;
+ }
+ return package;
}
-ResourceTablePackage* ResourceTable::findOrCreatePackage(const StringPiece& name) {
- const auto last = packages.end();
- auto iter = std::lower_bound(packages.begin(), last, name,
- lessThanStructWithName<ResourceTablePackage>);
- if (iter != last && name == (*iter)->name) {
- return iter->get();
- }
+ResourceTablePackage* ResourceTable::findOrCreatePackage(
+ const StringPiece& name) {
+ const auto last = packages.end();
+ auto iter = std::lower_bound(packages.begin(), last, name,
+ lessThanStructWithName<ResourceTablePackage>);
+ if (iter != last && name == (*iter)->name) {
+ return iter->get();
+ }
- std::unique_ptr<ResourceTablePackage> newPackage = util::make_unique<ResourceTablePackage>();
- newPackage->name = name.toString();
- return packages.emplace(iter, std::move(newPackage))->get();
+ std::unique_ptr<ResourceTablePackage> newPackage =
+ util::make_unique<ResourceTablePackage>();
+ newPackage->name = name.toString();
+ return packages.emplace(iter, std::move(newPackage))->get();
}
ResourceTableType* ResourceTablePackage::findType(ResourceType type) {
- const auto last = types.end();
- auto iter = std::lower_bound(types.begin(), last, type, lessThanType);
- if (iter != last && (*iter)->type == type) {
- return iter->get();
- }
- return nullptr;
+ const auto last = types.end();
+ auto iter = std::lower_bound(types.begin(), last, type, lessThanType);
+ if (iter != last && (*iter)->type == type) {
+ return iter->get();
+ }
+ return nullptr;
}
ResourceTableType* ResourceTablePackage::findOrCreateType(ResourceType type) {
- const auto last = types.end();
- auto iter = std::lower_bound(types.begin(), last, type, lessThanType);
- if (iter != last && (*iter)->type == type) {
- return iter->get();
- }
- return types.emplace(iter, new ResourceTableType(type))->get();
+ const auto last = types.end();
+ auto iter = std::lower_bound(types.begin(), last, type, lessThanType);
+ if (iter != last && (*iter)->type == type) {
+ return iter->get();
+ }
+ return types.emplace(iter, new ResourceTableType(type))->get();
}
ResourceEntry* ResourceTableType::findEntry(const StringPiece& name) {
- const auto last = entries.end();
- auto iter = std::lower_bound(entries.begin(), last, name,
- lessThanStructWithName<ResourceEntry>);
- if (iter != last && name == (*iter)->name) {
- return iter->get();
- }
- return nullptr;
+ const auto last = entries.end();
+ auto iter = std::lower_bound(entries.begin(), last, name,
+ lessThanStructWithName<ResourceEntry>);
+ if (iter != last && name == (*iter)->name) {
+ return iter->get();
+ }
+ return nullptr;
}
ResourceEntry* ResourceTableType::findOrCreateEntry(const StringPiece& name) {
- auto last = entries.end();
- auto iter = std::lower_bound(entries.begin(), last, name,
- lessThanStructWithName<ResourceEntry>);
- if (iter != last && name == (*iter)->name) {
- return iter->get();
- }
- return entries.emplace(iter, new ResourceEntry(name))->get();
+ auto last = entries.end();
+ auto iter = std::lower_bound(entries.begin(), last, name,
+ lessThanStructWithName<ResourceEntry>);
+ if (iter != last && name == (*iter)->name) {
+ return iter->get();
+ }
+ return entries.emplace(iter, new ResourceEntry(name))->get();
}
ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config) {
- return findValue(config, StringPiece());
+ return findValue(config, StringPiece());
}
struct ConfigKey {
- const ConfigDescription* config;
- const StringPiece& product;
+ const ConfigDescription* config;
+ const StringPiece& product;
};
-bool ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
- int cmp = lhs->config.compare(*rhs.config);
- if (cmp == 0) {
- cmp = StringPiece(lhs->product).compare(rhs.product);
- }
- return cmp < 0;
+bool ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue>& lhs,
+ const ConfigKey& rhs) {
+ int cmp = lhs->config.compare(*rhs.config);
+ if (cmp == 0) {
+ cmp = StringPiece(lhs->product).compare(rhs.product);
+ }
+ return cmp < 0;
}
ResourceConfigValue* ResourceEntry::findValue(const ConfigDescription& config,
const StringPiece& product) {
- auto iter = std::lower_bound(values.begin(), values.end(),
- ConfigKey{ &config, product }, ltConfigKeyRef);
- if (iter != values.end()) {
- ResourceConfigValue* value = iter->get();
- if (value->config == config && StringPiece(value->product) == product) {
- return value;
- }
+ auto iter = std::lower_bound(values.begin(), values.end(),
+ ConfigKey{&config, product}, ltConfigKeyRef);
+ if (iter != values.end()) {
+ ResourceConfigValue* value = iter->get();
+ if (value->config == config && StringPiece(value->product) == product) {
+ return value;
}
- return nullptr;
+ }
+ return nullptr;
}
-ResourceConfigValue* ResourceEntry::findOrCreateValue(const ConfigDescription& config,
- const StringPiece& product) {
- auto iter = std::lower_bound(values.begin(), values.end(),
- ConfigKey{ &config, product }, ltConfigKeyRef);
- if (iter != values.end()) {
- ResourceConfigValue* value = iter->get();
- if (value->config == config && StringPiece(value->product) == product) {
- return value;
- }
+ResourceConfigValue* ResourceEntry::findOrCreateValue(
+ const ConfigDescription& config, const StringPiece& product) {
+ auto iter = std::lower_bound(values.begin(), values.end(),
+ ConfigKey{&config, product}, ltConfigKeyRef);
+ if (iter != values.end()) {
+ ResourceConfigValue* value = iter->get();
+ if (value->config == config && StringPiece(value->product) == product) {
+ return value;
}
- ResourceConfigValue* newValue = values.insert(
- iter, util::make_unique<ResourceConfigValue>(config, product))->get();
- return newValue;
+ }
+ ResourceConfigValue* newValue =
+ values
+ .insert(iter, util::make_unique<ResourceConfigValue>(config, product))
+ ->get();
+ return newValue;
}
-std::vector<ResourceConfigValue*> ResourceEntry::findAllValues(const ConfigDescription& config) {
- std::vector<ResourceConfigValue*> results;
+std::vector<ResourceConfigValue*> ResourceEntry::findAllValues(
+ const ConfigDescription& config) {
+ std::vector<ResourceConfigValue*> results;
- auto iter = values.begin();
- for (; iter != values.end(); ++iter) {
- ResourceConfigValue* value = iter->get();
- if (value->config == config) {
- results.push_back(value);
- ++iter;
- break;
- }
+ auto iter = values.begin();
+ for (; iter != values.end(); ++iter) {
+ ResourceConfigValue* value = iter->get();
+ if (value->config == config) {
+ results.push_back(value);
+ ++iter;
+ break;
}
+ }
- for (; iter != values.end(); ++iter) {
- ResourceConfigValue* value = iter->get();
- if (value->config == config) {
- results.push_back(value);
- }
+ for (; iter != values.end(); ++iter) {
+ ResourceConfigValue* value = iter->get();
+ if (value->config == config) {
+ results.push_back(value);
}
- return results;
+ }
+ return results;
}
std::vector<ResourceConfigValue*> ResourceEntry::findValuesIf(
- const std::function<bool(ResourceConfigValue*)>& f) {
- std::vector<ResourceConfigValue*> results;
- for (auto& configValue : values) {
- if (f(configValue.get())) {
- results.push_back(configValue.get());
- }
+ const std::function<bool(ResourceConfigValue*)>& f) {
+ std::vector<ResourceConfigValue*> results;
+ for (auto& configValue : values) {
+ if (f(configValue.get())) {
+ results.push_back(configValue.get());
}
- return results;
+ }
+ return results;
}
/**
* The default handler for collisions.
*
- * Typically, a weak value will be overridden by a strong value. An existing weak
+ * Typically, a weak value will be overridden by a strong value. An existing
+ * weak
* value will not be overridden by an incoming weak value.
*
* There are some exceptions:
*
* Attributes: There are two types of Attribute values: USE and DECL.
*
- * USE is anywhere an Attribute is declared without a format, and in a place that would
+ * USE is anywhere an Attribute is declared without a format, and in a place
+ * that would
* be legal to declare if the Attribute already existed. This is typically in a
- * <declare-styleable> tag. Attributes defined in a <declare-styleable> are also weak.
+ * <declare-styleable> tag. Attributes defined in a <declare-styleable> are also
+ * weak.
*
- * DECL is an absolute declaration of an Attribute and specifies an explicit format.
+ * DECL is an absolute declaration of an Attribute and specifies an explicit
+ * format.
*
- * A DECL will override a USE without error. Two DECLs must match in their format for there to be
+ * A DECL will override a USE without error. Two DECLs must match in their
+ * format for there to be
* no error.
*/
ResourceTable::CollisionResult ResourceTable::resolveValueCollision(
- Value* existing, Value* incoming) {
- Attribute* existingAttr = valueCast<Attribute>(existing);
- Attribute* incomingAttr = valueCast<Attribute>(incoming);
- if (!incomingAttr) {
- if (incoming->isWeak()) {
- // We're trying to add a weak resource but a resource
- // already exists. Keep the existing.
- return CollisionResult::kKeepOriginal;
- } else if (existing->isWeak()) {
- // Override the weak resource with the new strong resource.
- return CollisionResult::kTakeNew;
- }
- // The existing and incoming values are strong, this is an error
- // if the values are not both attributes.
- return CollisionResult::kConflict;
+ Value* existing, Value* incoming) {
+ Attribute* existingAttr = valueCast<Attribute>(existing);
+ Attribute* incomingAttr = valueCast<Attribute>(incoming);
+ if (!incomingAttr) {
+ if (incoming->isWeak()) {
+ // We're trying to add a weak resource but a resource
+ // already exists. Keep the existing.
+ return CollisionResult::kKeepOriginal;
+ } else if (existing->isWeak()) {
+ // Override the weak resource with the new strong resource.
+ return CollisionResult::kTakeNew;
}
-
- if (!existingAttr) {
- if (existing->isWeak()) {
- // The existing value is not an attribute and it is weak,
- // so take the incoming attribute value.
- return CollisionResult::kTakeNew;
- }
- // The existing value is not an attribute and it is strong,
- // so the incoming attribute value is an error.
- return CollisionResult::kConflict;
- }
-
- assert(incomingAttr && existingAttr);
-
- //
- // Attribute specific handling. At this point we know both
- // values are attributes. Since we can declare and define
- // attributes all-over, we do special handling to see
- // which definition sticks.
- //
- if (existingAttr->typeMask == incomingAttr->typeMask) {
- // The two attributes are both DECLs, but they are plain attributes
- // with the same formats.
- // Keep the strongest one.
- return existingAttr->isWeak() ? CollisionResult::kTakeNew : CollisionResult::kKeepOriginal;
- }
-
- if (existingAttr->isWeak() && existingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
- // Any incoming attribute is better than this.
- return CollisionResult::kTakeNew;
- }
-
- if (incomingAttr->isWeak() && incomingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
- // The incoming attribute may be a USE instead of a DECL.
- // Keep the existing attribute.
- return CollisionResult::kKeepOriginal;
- }
+ // The existing and incoming values are strong, this is an error
+ // if the values are not both attributes.
return CollisionResult::kConflict;
+ }
+
+ if (!existingAttr) {
+ if (existing->isWeak()) {
+ // The existing value is not an attribute and it is weak,
+ // so take the incoming attribute value.
+ return CollisionResult::kTakeNew;
+ }
+ // The existing value is not an attribute and it is strong,
+ // so the incoming attribute value is an error.
+ return CollisionResult::kConflict;
+ }
+
+ assert(incomingAttr && existingAttr);
+
+ //
+ // Attribute specific handling. At this point we know both
+ // values are attributes. Since we can declare and define
+ // attributes all-over, we do special handling to see
+ // which definition sticks.
+ //
+ if (existingAttr->typeMask == incomingAttr->typeMask) {
+ // The two attributes are both DECLs, but they are plain attributes
+ // with the same formats.
+ // Keep the strongest one.
+ return existingAttr->isWeak() ? CollisionResult::kTakeNew
+ : CollisionResult::kKeepOriginal;
+ }
+
+ if (existingAttr->isWeak() &&
+ existingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
+ // Any incoming attribute is better than this.
+ return CollisionResult::kTakeNew;
+ }
+
+ if (incomingAttr->isWeak() &&
+ incomingAttr->typeMask == android::ResTable_map::TYPE_ANY) {
+ // The incoming attribute may be a USE instead of a DECL.
+ // Keep the existing attribute.
+ return CollisionResult::kKeepOriginal;
+ }
+ return CollisionResult::kConflict;
}
static constexpr const char* kValidNameChars = "._-";
@@ -284,8 +300,8 @@
const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return addResourceImpl(name, {}, config, product, std::move(value), kValidNameChars,
- resolveValueCollision, diag);
+ return addResourceImpl(name, {}, config, product, std::move(value),
+ kValidNameChars, resolveValueCollision, diag);
}
bool ResourceTable::addResource(const ResourceNameRef& name,
@@ -294,8 +310,8 @@
const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return addResourceImpl(name, resId, config, product, std::move(value), kValidNameChars,
- resolveValueCollision, diag);
+ return addResourceImpl(name, resId, config, product, std::move(value),
+ kValidNameChars, resolveValueCollision, diag);
}
bool ResourceTable::addFileReference(const ResourceNameRef& name,
@@ -303,31 +319,29 @@
const Source& source,
const StringPiece& path,
IDiagnostics* diag) {
- return addFileReferenceImpl(name, config, source, path, nullptr, kValidNameChars, diag);
+ return addFileReferenceImpl(name, config, source, path, nullptr,
+ kValidNameChars, diag);
}
-bool ResourceTable::addFileReferenceAllowMangled(const ResourceNameRef& name,
- const ConfigDescription& config,
- const Source& source,
- const StringPiece& path,
- io::IFile* file,
- IDiagnostics* diag) {
- return addFileReferenceImpl(name, config, source, path, file, kValidNameMangledChars, diag);
+bool ResourceTable::addFileReferenceAllowMangled(
+ const ResourceNameRef& name, const ConfigDescription& config,
+ const Source& source, const StringPiece& path, io::IFile* file,
+ IDiagnostics* diag) {
+ return addFileReferenceImpl(name, config, source, path, file,
+ kValidNameMangledChars, diag);
}
-bool ResourceTable::addFileReferenceImpl(const ResourceNameRef& name,
- const ConfigDescription& config,
- const Source& source,
- const StringPiece& path,
- io::IFile* file,
- const char* validChars,
- IDiagnostics* diag) {
- std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
- stringPool.makeRef(path));
- fileRef->setSource(source);
- fileRef->file = file;
- return addResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef),
- validChars, resolveValueCollision, diag);
+bool ResourceTable::addFileReferenceImpl(
+ const ResourceNameRef& name, const ConfigDescription& config,
+ const Source& source, const StringPiece& path, io::IFile* file,
+ const char* validChars, IDiagnostics* diag) {
+ std::unique_ptr<FileReference> fileRef =
+ util::make_unique<FileReference>(stringPool.makeRef(path));
+ fileRef->setSource(source);
+ fileRef->file = file;
+ return addResourceImpl(name, ResourceId{}, config, StringPiece{},
+ std::move(fileRef), validChars, resolveValueCollision,
+ diag);
}
bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
@@ -335,8 +349,8 @@
const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return addResourceImpl(name, ResourceId{}, config, product, std::move(value),
- kValidNameMangledChars, resolveValueCollision, diag);
+ return addResourceImpl(name, ResourceId{}, config, product, std::move(value),
+ kValidNameMangledChars, resolveValueCollision, diag);
}
bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name,
@@ -345,220 +359,193 @@
const StringPiece& product,
std::unique_ptr<Value> value,
IDiagnostics* diag) {
- return addResourceImpl(name, id, config, product, std::move(value), kValidNameMangledChars,
- resolveValueCollision, diag);
+ return addResourceImpl(name, id, config, product, std::move(value),
+ kValidNameMangledChars, resolveValueCollision, diag);
}
-bool ResourceTable::addResourceImpl(const ResourceNameRef& name,
- const ResourceId& resId,
- const ConfigDescription& config,
- const StringPiece& product,
- std::unique_ptr<Value> value,
- const char* validChars,
- const CollisionResolverFunc& conflictResolver,
- IDiagnostics* diag) {
- assert(value && "value can't be nullptr");
- assert(diag && "diagnostics can't be nullptr");
+bool ResourceTable::addResourceImpl(
+ const ResourceNameRef& name, const ResourceId& resId,
+ const ConfigDescription& config, const StringPiece& product,
+ std::unique_ptr<Value> value, const char* validChars,
+ const CollisionResolverFunc& conflictResolver, IDiagnostics* diag) {
+ assert(value && "value can't be nullptr");
+ assert(diag && "diagnostics can't be nullptr");
- auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
- if (badCharIter != name.entry.end()) {
- diag->error(DiagMessage(value->getSource())
- << "resource '"
- << name
- << "' has invalid entry name '"
- << name.entry
- << "'. Invalid character '"
- << StringPiece(badCharIter, 1)
- << "'");
- return false;
- }
+ auto badCharIter =
+ util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
+ if (badCharIter != name.entry.end()) {
+ diag->error(DiagMessage(value->getSource())
+ << "resource '" << name << "' has invalid entry name '"
+ << name.entry << "'. Invalid character '"
+ << StringPiece(badCharIter, 1) << "'");
+ return false;
+ }
- ResourceTablePackage* package = findOrCreatePackage(name.package);
- if (resId.isValid() && package->id && package->id.value() != resId.packageId()) {
- diag->error(DiagMessage(value->getSource())
- << "trying to add resource '"
- << name
- << "' with ID "
- << resId
- << " but package '"
- << package->name
- << "' already has ID "
- << std::hex << (int) package->id.value() << std::dec);
- return false;
- }
+ ResourceTablePackage* package = findOrCreatePackage(name.package);
+ if (resId.isValid() && package->id &&
+ package->id.value() != resId.packageId()) {
+ diag->error(DiagMessage(value->getSource())
+ << "trying to add resource '" << name << "' with ID " << resId
+ << " but package '" << package->name << "' already has ID "
+ << std::hex << (int)package->id.value() << std::dec);
+ return false;
+ }
- ResourceTableType* type = package->findOrCreateType(name.type);
- if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
- diag->error(DiagMessage(value->getSource())
- << "trying to add resource '"
- << name
- << "' with ID "
- << resId
- << " but type '"
- << type->type
- << "' already has ID "
- << std::hex << (int) type->id.value() << std::dec);
- return false;
- }
+ ResourceTableType* type = package->findOrCreateType(name.type);
+ if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
+ diag->error(DiagMessage(value->getSource())
+ << "trying to add resource '" << name << "' with ID " << resId
+ << " but type '" << type->type << "' already has ID "
+ << std::hex << (int)type->id.value() << std::dec);
+ return false;
+ }
- ResourceEntry* entry = type->findOrCreateEntry(name.entry);
- if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
- diag->error(DiagMessage(value->getSource())
- << "trying to add resource '"
- << name
- << "' with ID "
- << resId
- << " but resource already has ID "
- << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
- return false;
- }
+ ResourceEntry* entry = type->findOrCreateEntry(name.entry);
+ if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
+ diag->error(DiagMessage(value->getSource())
+ << "trying to add resource '" << name << "' with ID " << resId
+ << " but resource already has ID "
+ << ResourceId(package->id.value(), type->id.value(),
+ entry->id.value()));
+ return false;
+ }
- ResourceConfigValue* configValue = entry->findOrCreateValue(config, product);
- if (!configValue->value) {
- // Resource does not exist, add it now.
+ ResourceConfigValue* configValue = entry->findOrCreateValue(config, product);
+ if (!configValue->value) {
+ // Resource does not exist, add it now.
+ configValue->value = std::move(value);
+
+ } else {
+ switch (conflictResolver(configValue->value.get(), value.get())) {
+ case CollisionResult::kTakeNew:
+ // Take the incoming value.
configValue->value = std::move(value);
+ break;
- } else {
- switch (conflictResolver(configValue->value.get(), value.get())) {
- case CollisionResult::kTakeNew:
- // Take the incoming value.
- configValue->value = std::move(value);
- break;
+ case CollisionResult::kConflict:
+ diag->error(DiagMessage(value->getSource())
+ << "duplicate value for resource '" << name << "' "
+ << "with config '" << config << "'");
+ diag->error(DiagMessage(configValue->value->getSource())
+ << "resource previously defined here");
+ return false;
- case CollisionResult::kConflict:
- diag->error(DiagMessage(value->getSource())
- << "duplicate value for resource '" << name << "' "
- << "with config '" << config << "'");
- diag->error(DiagMessage(configValue->value->getSource())
- << "resource previously defined here");
- return false;
-
- case CollisionResult::kKeepOriginal:
- break;
- }
+ case CollisionResult::kKeepOriginal:
+ break;
}
+ }
- if (resId.isValid()) {
- package->id = resId.packageId();
- type->id = resId.typeId();
- entry->id = resId.entryId();
- }
- return true;
+ if (resId.isValid()) {
+ package->id = resId.packageId();
+ type->id = resId.typeId();
+ entry->id = resId.entryId();
+ }
+ return true;
}
-bool ResourceTable::setSymbolState(const ResourceNameRef& name, const ResourceId& resId,
+bool ResourceTable::setSymbolState(const ResourceNameRef& name,
+ const ResourceId& resId,
const Symbol& symbol, IDiagnostics* diag) {
- return setSymbolStateImpl(name, resId, symbol, kValidNameChars, diag);
+ return setSymbolStateImpl(name, resId, symbol, kValidNameChars, diag);
}
bool ResourceTable::setSymbolStateAllowMangled(const ResourceNameRef& name,
const ResourceId& resId,
- const Symbol& symbol, IDiagnostics* diag) {
- return setSymbolStateImpl(name, resId, symbol, kValidNameMangledChars, diag);
+ const Symbol& symbol,
+ IDiagnostics* diag) {
+ return setSymbolStateImpl(name, resId, symbol, kValidNameMangledChars, diag);
}
-bool ResourceTable::setSymbolStateImpl(const ResourceNameRef& name, const ResourceId& resId,
- const Symbol& symbol, const char* validChars,
+bool ResourceTable::setSymbolStateImpl(const ResourceNameRef& name,
+ const ResourceId& resId,
+ const Symbol& symbol,
+ const char* validChars,
IDiagnostics* diag) {
- assert(diag && "diagnostics can't be nullptr");
+ assert(diag && "diagnostics can't be nullptr");
- auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
- if (badCharIter != name.entry.end()) {
- diag->error(DiagMessage(symbol.source)
- << "resource '"
- << name
- << "' has invalid entry name '"
- << name.entry
- << "'. Invalid character '"
- << StringPiece(badCharIter, 1)
- << "'");
- return false;
- }
+ auto badCharIter =
+ util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
+ if (badCharIter != name.entry.end()) {
+ diag->error(DiagMessage(symbol.source)
+ << "resource '" << name << "' has invalid entry name '"
+ << name.entry << "'. Invalid character '"
+ << StringPiece(badCharIter, 1) << "'");
+ return false;
+ }
- ResourceTablePackage* package = findOrCreatePackage(name.package);
- if (resId.isValid() && package->id && package->id.value() != resId.packageId()) {
- diag->error(DiagMessage(symbol.source)
- << "trying to add resource '"
- << name
- << "' with ID "
- << resId
- << " but package '"
- << package->name
- << "' already has ID "
- << std::hex << (int) package->id.value() << std::dec);
- return false;
- }
+ ResourceTablePackage* package = findOrCreatePackage(name.package);
+ if (resId.isValid() && package->id &&
+ package->id.value() != resId.packageId()) {
+ diag->error(DiagMessage(symbol.source)
+ << "trying to add resource '" << name << "' with ID " << resId
+ << " but package '" << package->name << "' already has ID "
+ << std::hex << (int)package->id.value() << std::dec);
+ return false;
+ }
- ResourceTableType* type = package->findOrCreateType(name.type);
- if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
- diag->error(DiagMessage(symbol.source)
- << "trying to add resource '"
- << name
- << "' with ID "
- << resId
- << " but type '"
- << type->type
- << "' already has ID "
- << std::hex << (int) type->id.value() << std::dec);
- return false;
- }
+ ResourceTableType* type = package->findOrCreateType(name.type);
+ if (resId.isValid() && type->id && type->id.value() != resId.typeId()) {
+ diag->error(DiagMessage(symbol.source)
+ << "trying to add resource '" << name << "' with ID " << resId
+ << " but type '" << type->type << "' already has ID "
+ << std::hex << (int)type->id.value() << std::dec);
+ return false;
+ }
- ResourceEntry* entry = type->findOrCreateEntry(name.entry);
- if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
- diag->error(DiagMessage(symbol.source)
- << "trying to add resource '"
- << name
- << "' with ID "
- << resId
- << " but resource already has ID "
- << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
- return false;
- }
+ ResourceEntry* entry = type->findOrCreateEntry(name.entry);
+ if (resId.isValid() && entry->id && entry->id.value() != resId.entryId()) {
+ diag->error(DiagMessage(symbol.source)
+ << "trying to add resource '" << name << "' with ID " << resId
+ << " but resource already has ID "
+ << ResourceId(package->id.value(), type->id.value(),
+ entry->id.value()));
+ return false;
+ }
- if (resId.isValid()) {
- package->id = resId.packageId();
- type->id = resId.typeId();
- entry->id = resId.entryId();
- }
+ if (resId.isValid()) {
+ package->id = resId.packageId();
+ type->id = resId.typeId();
+ entry->id = resId.entryId();
+ }
- // Only mark the type state as public, it doesn't care about being private.
- if (symbol.state == SymbolState::kPublic) {
- type->symbolStatus.state = SymbolState::kPublic;
- }
+ // Only mark the type state as public, it doesn't care about being private.
+ if (symbol.state == SymbolState::kPublic) {
+ type->symbolStatus.state = SymbolState::kPublic;
+ }
- if (symbol.state == SymbolState::kUndefined &&
- entry->symbolStatus.state != SymbolState::kUndefined) {
- // We can't undefine a symbol (remove its visibility). Ignore.
- return true;
- }
-
- if (symbol.state == SymbolState::kPrivate &&
- entry->symbolStatus.state == SymbolState::kPublic) {
- // We can't downgrade public to private. Ignore.
- return true;
- }
-
- entry->symbolStatus = std::move(symbol);
+ if (symbol.state == SymbolState::kUndefined &&
+ entry->symbolStatus.state != SymbolState::kUndefined) {
+ // We can't undefine a symbol (remove its visibility). Ignore.
return true;
+ }
+
+ if (symbol.state == SymbolState::kPrivate &&
+ entry->symbolStatus.state == SymbolState::kPublic) {
+ // We can't downgrade public to private. Ignore.
+ return true;
+ }
+
+ entry->symbolStatus = std::move(symbol);
+ return true;
}
-Maybe<ResourceTable::SearchResult>
-ResourceTable::findResource(const ResourceNameRef& name) {
- ResourceTablePackage* package = findPackage(name.package);
- if (!package) {
- return {};
- }
+Maybe<ResourceTable::SearchResult> ResourceTable::findResource(
+ const ResourceNameRef& name) {
+ ResourceTablePackage* package = findPackage(name.package);
+ if (!package) {
+ return {};
+ }
- ResourceTableType* type = package->findType(name.type);
- if (!type) {
- return {};
- }
+ ResourceTableType* type = package->findType(name.type);
+ if (!type) {
+ return {};
+ }
- ResourceEntry* entry = type->findEntry(name.entry);
- if (!entry) {
- return {};
- }
- return SearchResult{ package, type, entry };
+ ResourceEntry* entry = type->findEntry(name.entry);
+ if (!entry) {
+ return {};
+ }
+ return SearchResult{package, type, entry};
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 6c246d0..ebaad41 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -37,42 +37,43 @@
namespace aapt {
enum class SymbolState {
- kUndefined,
- kPrivate,
- kPublic,
+ kUndefined,
+ kPrivate,
+ kPublic,
};
/**
* The Public status of a resource.
*/
struct Symbol {
- SymbolState state = SymbolState::kUndefined;
- Source source;
- std::string comment;
+ SymbolState state = SymbolState::kUndefined;
+ Source source;
+ std::string comment;
};
class ResourceConfigValue {
-public:
- /**
- * The configuration for which this value is defined.
- */
- const ConfigDescription config;
+ public:
+ /**
+ * The configuration for which this value is defined.
+ */
+ const ConfigDescription config;
- /**
- * The product for which this value is defined.
- */
- const std::string product;
+ /**
+ * The product for which this value is defined.
+ */
+ const std::string product;
- /**
- * The actual Value.
- */
- std::unique_ptr<Value> value;
+ /**
+ * The actual Value.
+ */
+ std::unique_ptr<Value> value;
- ResourceConfigValue(const ConfigDescription& config, const StringPiece& product) :
- config(config), product(product.toString()) { }
+ ResourceConfigValue(const ConfigDescription& config,
+ const StringPiece& product)
+ : config(config), product(product.toString()) {}
-private:
- DISALLOW_COPY_AND_ASSIGN(ResourceConfigValue);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceConfigValue);
};
/**
@@ -80,42 +81,44 @@
* varying values for each defined configuration.
*/
class ResourceEntry {
-public:
- /**
- * The name of the resource. Immutable, as
- * this determines the order of this resource
- * when doing lookups.
- */
- const std::string name;
+ public:
+ /**
+ * The name of the resource. Immutable, as
+ * this determines the order of this resource
+ * when doing lookups.
+ */
+ const std::string name;
- /**
- * The entry ID for this resource.
- */
- Maybe<uint16_t> id;
+ /**
+ * The entry ID for this resource.
+ */
+ Maybe<uint16_t> id;
- /**
- * Whether this resource is public (and must maintain the same entry ID across builds).
- */
- Symbol symbolStatus;
+ /**
+ * Whether this resource is public (and must maintain the same entry ID across
+ * builds).
+ */
+ Symbol symbolStatus;
- /**
- * The resource's values for each configuration.
- */
- std::vector<std::unique_ptr<ResourceConfigValue>> values;
+ /**
+ * The resource's values for each configuration.
+ */
+ std::vector<std::unique_ptr<ResourceConfigValue>> values;
- explicit ResourceEntry(const StringPiece& name) : name(name.toString()) { }
+ explicit ResourceEntry(const StringPiece& name) : name(name.toString()) {}
- ResourceConfigValue* findValue(const ConfigDescription& config);
- ResourceConfigValue* findValue(const ConfigDescription& config, const StringPiece& product);
- ResourceConfigValue* findOrCreateValue(const ConfigDescription& config,
- const StringPiece& product);
- std::vector<ResourceConfigValue*> findAllValues(const ConfigDescription& config);
- std::vector<ResourceConfigValue*> findValuesIf(
- const std::function<bool(ResourceConfigValue*)>& f);
+ ResourceConfigValue* findValue(const ConfigDescription& config);
+ ResourceConfigValue* findValue(const ConfigDescription& config,
+ const StringPiece& product);
+ ResourceConfigValue* findOrCreateValue(const ConfigDescription& config,
+ const StringPiece& product);
+ std::vector<ResourceConfigValue*> findAllValues(
+ const ConfigDescription& config);
+ std::vector<ResourceConfigValue*> findValuesIf(
+ const std::function<bool(ResourceConfigValue*)>& f);
-
-private:
- DISALLOW_COPY_AND_ASSIGN(ResourceEntry);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceEntry);
};
/**
@@ -123,58 +126,53 @@
* for this type.
*/
class ResourceTableType {
-public:
- /**
- * The logical type of resource (string, drawable, layout, etc.).
- */
- const ResourceType type;
+ public:
+ /**
+ * The logical type of resource (string, drawable, layout, etc.).
+ */
+ const ResourceType type;
- /**
- * The type ID for this resource.
- */
- Maybe<uint8_t> id;
+ /**
+ * The type ID for this resource.
+ */
+ Maybe<uint8_t> id;
- /**
- * Whether this type is public (and must maintain the same
- * type ID across builds).
- */
- Symbol symbolStatus;
+ /**
+ * Whether this type is public (and must maintain the same
+ * type ID across builds).
+ */
+ Symbol symbolStatus;
- /**
- * List of resources for this type.
- */
- std::vector<std::unique_ptr<ResourceEntry>> entries;
+ /**
+ * List of resources for this type.
+ */
+ std::vector<std::unique_ptr<ResourceEntry>> entries;
- explicit ResourceTableType(const ResourceType type) : type(type) { }
+ explicit ResourceTableType(const ResourceType type) : type(type) {}
- ResourceEntry* findEntry(const StringPiece& name);
- ResourceEntry* findOrCreateEntry(const StringPiece& name);
+ ResourceEntry* findEntry(const StringPiece& name);
+ ResourceEntry* findOrCreateEntry(const StringPiece& name);
-private:
- DISALLOW_COPY_AND_ASSIGN(ResourceTableType);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceTableType);
};
-enum class PackageType {
- System,
- Vendor,
- App,
- Dynamic
-};
+enum class PackageType { System, Vendor, App, Dynamic };
class ResourceTablePackage {
-public:
- PackageType type = PackageType::App;
- Maybe<uint8_t> id;
- std::string name;
+ public:
+ PackageType type = PackageType::App;
+ Maybe<uint8_t> id;
+ std::string name;
- std::vector<std::unique_ptr<ResourceTableType>> types;
+ std::vector<std::unique_ptr<ResourceTableType>> types;
- ResourceTablePackage() = default;
- ResourceTableType* findType(ResourceType type);
- ResourceTableType* findOrCreateType(const ResourceType type);
+ ResourceTablePackage() = default;
+ ResourceTableType* findType(ResourceType type);
+ ResourceTableType* findOrCreateType(const ResourceType type);
-private:
- DISALLOW_COPY_AND_ASSIGN(ResourceTablePackage);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceTablePackage);
};
/**
@@ -182,140 +180,129 @@
* flattened into a binary resource table (resources.arsc).
*/
class ResourceTable {
-public:
- ResourceTable() = default;
+ public:
+ ResourceTable() = default;
- enum class CollisionResult {
- kKeepOriginal,
- kConflict,
- kTakeNew
- };
+ enum class CollisionResult { kKeepOriginal, kConflict, kTakeNew };
- using CollisionResolverFunc = std::function<CollisionResult(Value*, Value*)>;
+ using CollisionResolverFunc = std::function<CollisionResult(Value*, Value*)>;
- /**
- * When a collision of resources occurs, this method decides which value to keep.
- */
- static CollisionResult resolveValueCollision(Value* existing, Value* incoming);
+ /**
+ * When a collision of resources occurs, this method decides which value to
+ * keep.
+ */
+ static CollisionResult resolveValueCollision(Value* existing,
+ Value* incoming);
- bool addResource(const ResourceNameRef& name,
- const ConfigDescription& config,
- const StringPiece& product,
- std::unique_ptr<Value> value,
- IDiagnostics* diag);
+ bool addResource(const ResourceNameRef& name, const ConfigDescription& config,
+ const StringPiece& product, std::unique_ptr<Value> value,
+ IDiagnostics* diag);
- bool addResource(const ResourceNameRef& name,
- const ResourceId& resId,
- const ConfigDescription& config,
- const StringPiece& product,
- std::unique_ptr<Value> value,
- IDiagnostics* diag);
+ bool addResource(const ResourceNameRef& name, const ResourceId& resId,
+ const ConfigDescription& config, const StringPiece& product,
+ std::unique_ptr<Value> value, IDiagnostics* diag);
- bool addFileReference(const ResourceNameRef& name,
- const ConfigDescription& config,
- const Source& source,
- const StringPiece& path,
- IDiagnostics* diag);
+ bool addFileReference(const ResourceNameRef& name,
+ const ConfigDescription& config, const Source& source,
+ const StringPiece& path, IDiagnostics* diag);
- bool addFileReferenceAllowMangled(const ResourceNameRef& name,
- const ConfigDescription& config,
- const Source& source,
- const StringPiece& path,
- io::IFile* file,
- IDiagnostics* diag);
-
- /**
- * Same as addResource, but doesn't verify the validity of the name. This is used
- * when loading resources from an existing binary resource table that may have mangled
- * names.
- */
- bool addResourceAllowMangled(const ResourceNameRef& name,
- const ConfigDescription& config,
- const StringPiece& product,
- std::unique_ptr<Value> value,
- IDiagnostics* diag);
-
- bool addResourceAllowMangled(const ResourceNameRef& name,
- const ResourceId& id,
- const ConfigDescription& config,
- const StringPiece& product,
- std::unique_ptr<Value> value,
- IDiagnostics* diag);
-
- bool setSymbolState(const ResourceNameRef& name,
- const ResourceId& resId,
- const Symbol& symbol,
- IDiagnostics* diag);
-
- bool setSymbolStateAllowMangled(const ResourceNameRef& name,
- const ResourceId& resId,
- const Symbol& symbol,
+ bool addFileReferenceAllowMangled(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const Source& source,
+ const StringPiece& path, io::IFile* file,
IDiagnostics* diag);
- struct SearchResult {
- ResourceTablePackage* package;
- ResourceTableType* type;
- ResourceEntry* entry;
- };
+ /**
+ * Same as addResource, but doesn't verify the validity of the name. This is
+ * used
+ * when loading resources from an existing binary resource table that may have
+ * mangled
+ * names.
+ */
+ bool addResourceAllowMangled(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const StringPiece& product,
+ std::unique_ptr<Value> value,
+ IDiagnostics* diag);
- Maybe<SearchResult> findResource(const ResourceNameRef& name);
+ bool addResourceAllowMangled(const ResourceNameRef& name,
+ const ResourceId& id,
+ const ConfigDescription& config,
+ const StringPiece& product,
+ std::unique_ptr<Value> value,
+ IDiagnostics* diag);
- /**
- * The string pool used by this resource table. Values that reference strings must use
- * this pool to create their strings.
- *
- * NOTE: `stringPool` must come before `packages` so that it is destroyed after.
- * When `string pool` references are destroyed (as they will be when `packages`
- * is destroyed), they decrement a refCount, which would cause invalid
- * memory access if the pool was already destroyed.
- */
- StringPool stringPool;
+ bool setSymbolState(const ResourceNameRef& name, const ResourceId& resId,
+ const Symbol& symbol, IDiagnostics* diag);
- /**
- * The list of packages in this table, sorted alphabetically by package name.
- */
- std::vector<std::unique_ptr<ResourceTablePackage>> packages;
+ bool setSymbolStateAllowMangled(const ResourceNameRef& name,
+ const ResourceId& resId, const Symbol& symbol,
+ IDiagnostics* diag);
- /**
- * Returns the package struct with the given name, or nullptr if such a package does not
- * 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 StringPiece& name);
+ struct SearchResult {
+ ResourceTablePackage* package;
+ ResourceTableType* type;
+ ResourceEntry* entry;
+ };
- ResourceTablePackage* findPackageById(uint8_t id);
+ Maybe<SearchResult> findResource(const ResourceNameRef& name);
- ResourceTablePackage* createPackage(const StringPiece& name, Maybe<uint8_t> id = {});
+ /**
+ * The string pool used by this resource table. Values that reference strings
+ * must use
+ * this pool to create their strings.
+ *
+ * NOTE: `stringPool` must come before `packages` so that it is destroyed
+ * after.
+ * When `string pool` references are destroyed (as they will be when
+ * `packages`
+ * is destroyed), they decrement a refCount, which would cause invalid
+ * memory access if the pool was already destroyed.
+ */
+ StringPool stringPool;
-private:
- ResourceTablePackage* findOrCreatePackage(const StringPiece& name);
+ /**
+ * The list of packages in this table, sorted alphabetically by package name.
+ */
+ std::vector<std::unique_ptr<ResourceTablePackage>> packages;
- bool addResourceImpl(const ResourceNameRef& name,
- const ResourceId& resId,
- const ConfigDescription& config,
- const StringPiece& product,
- std::unique_ptr<Value> value,
- const char* validChars,
- const CollisionResolverFunc& conflictResolver,
- IDiagnostics* diag);
+ /**
+ * Returns the package struct with the given name, or nullptr if such a
+ * package does not
+ * 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 StringPiece& name);
- bool addFileReferenceImpl(const ResourceNameRef& name,
- const ConfigDescription& config,
- const Source& source,
- const StringPiece& path,
- io::IFile* file,
- const char* validChars,
- IDiagnostics* diag);
+ ResourceTablePackage* findPackageById(uint8_t id);
- bool setSymbolStateImpl(const ResourceNameRef& name,
- const ResourceId& resId,
- const Symbol& symbol,
- const char* validChars,
+ ResourceTablePackage* createPackage(const StringPiece& name,
+ Maybe<uint8_t> id = {});
+
+ private:
+ ResourceTablePackage* findOrCreatePackage(const StringPiece& name);
+
+ bool addResourceImpl(const ResourceNameRef& name, const ResourceId& resId,
+ const ConfigDescription& config,
+ const StringPiece& product, std::unique_ptr<Value> value,
+ const char* validChars,
+ const CollisionResolverFunc& conflictResolver,
+ IDiagnostics* diag);
+
+ bool addFileReferenceImpl(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const Source& source, const StringPiece& path,
+ io::IFile* file, const char* validChars,
IDiagnostics* diag);
- DISALLOW_COPY_AND_ASSIGN(ResourceTable);
+ bool setSymbolStateImpl(const ResourceNameRef& name, const ResourceId& resId,
+ const Symbol& symbol, const char* validChars,
+ IDiagnostics* diag);
+
+ DISALLOW_COPY_AND_ASSIGN(ResourceTable);
};
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_RESOURCE_TABLE_H
+#endif // AAPT_RESOURCE_TABLE_H
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 4db40a6..a64ad3e 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "Diagnostics.h"
#include "ResourceTable.h"
+#include "Diagnostics.h"
#include "ResourceValues.h"
#include "test/Test.h"
#include "util/Util.h"
@@ -27,124 +27,113 @@
namespace aapt {
TEST(ResourceTableTest, FailToAddResourceWithBadName) {
- ResourceTable table;
+ ResourceTable table;
- EXPECT_FALSE(table.addResource(
- test::parseNameOrDie("android:id/hey,there"),
- ConfigDescription{}, "",
- test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
- test::getDiagnostics()));
+ EXPECT_FALSE(table.addResource(
+ test::parseNameOrDie("android:id/hey,there"), ConfigDescription{}, "",
+ test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
+ test::getDiagnostics()));
- EXPECT_FALSE(table.addResource(
- test::parseNameOrDie("android:id/hey:there"),
- ConfigDescription{}, "",
- test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
- test::getDiagnostics()));
+ EXPECT_FALSE(table.addResource(
+ test::parseNameOrDie("android:id/hey:there"), ConfigDescription{}, "",
+ test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
+ test::getDiagnostics()));
}
TEST(ResourceTableTest, AddOneResource) {
- ResourceTable table;
+ ResourceTable table;
- EXPECT_TRUE(table.addResource(
- test::parseNameOrDie("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, "android:attr/id"));
+ ASSERT_NE(nullptr, test::getValue<Id>(&table, "android:attr/id"));
}
TEST(ResourceTableTest, AddMultipleResources) {
- ResourceTable table;
+ ResourceTable table;
- ConfigDescription config;
- ConfigDescription languageConfig;
- memcpy(languageConfig.language, "pl", sizeof(languageConfig.language));
+ ConfigDescription config;
+ ConfigDescription languageConfig;
+ memcpy(languageConfig.language, "pl", sizeof(languageConfig.language));
- EXPECT_TRUE(table.addResource(
- 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("android:attr/layout_width"), config, "",
+ test::ValueBuilder<Id>().setSource("test/path/file.xml", 10u).build(),
+ test::getDiagnostics()));
- EXPECT_TRUE(table.addResource(
- test::parseNameOrDie("android:attr/id"),
- config, "",
- test::ValueBuilder<Id>().setSource("test/path/file.xml", 12u).build(),
- test::getDiagnostics()));
+ EXPECT_TRUE(table.addResource(
+ test::parseNameOrDie("android:attr/id"), config, "",
+ test::ValueBuilder<Id>().setSource("test/path/file.xml", 12u).build(),
+ test::getDiagnostics()));
- EXPECT_TRUE(table.addResource(
- test::parseNameOrDie("android:string/ok"),
- config, "",
- test::ValueBuilder<Id>().setSource("test/path/file.xml", 14u).build(),
- test::getDiagnostics()));
+ EXPECT_TRUE(table.addResource(
+ test::parseNameOrDie("android:string/ok"), config, "",
+ test::ValueBuilder<Id>().setSource("test/path/file.xml", 14u).build(),
+ test::getDiagnostics()));
- EXPECT_TRUE(table.addResource(
- test::parseNameOrDie("android:string/ok"),
- languageConfig, "",
- test::ValueBuilder<BinaryPrimitive>(android::Res_value{})
- .setSource("test/path/file.xml", 20u)
- .build(),
- test::getDiagnostics()));
+ EXPECT_TRUE(table.addResource(
+ 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, "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));
+ 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;
+ ResourceTable table;
- ASSERT_TRUE(table.addResource(
- test::parseNameOrDie("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, "android:attr/foo");
- ASSERT_NE(nullptr, attr);
- EXPECT_TRUE(attr->isWeak());
+ Attribute* attr = test::getValue<Attribute>(&table, "android:attr/foo");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_TRUE(attr->isWeak());
- ASSERT_TRUE(table.addResource(
- test::parseNameOrDie("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, "android:attr/foo");
- ASSERT_NE(nullptr, attr);
- EXPECT_FALSE(attr->isWeak());
+ attr = test::getValue<Attribute>(&table, "android:attr/foo");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_FALSE(attr->isWeak());
}
TEST(ResourceTableTest, ProductVaryingValues) {
- ResourceTable table;
+ ResourceTable table;
- 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("android:string/foo"),
- test::parseConfigOrDie("land"), "phone",
- util::make_unique<Id>(),
- test::getDiagnostics()));
+ 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("android:string/foo"),
+ test::parseConfigOrDie("land"), "phone",
+ util::make_unique<Id>(),
+ test::getDiagnostics()));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, "android:string/foo",
- test::parseConfigOrDie("land"),
- "tablet"));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, "android:string/foo",
- test::parseConfigOrDie("land"),
- "phone"));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(
+ &table, "android:string/foo",
+ test::parseConfigOrDie("land"), "tablet"));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(
+ &table, "android:string/foo",
+ test::parseConfigOrDie("land"), "phone"));
- Maybe<ResourceTable::SearchResult> sr = table.findResource(
- test::parseNameOrDie("android:string/foo"));
- AAPT_ASSERT_TRUE(sr);
- std::vector<ResourceConfigValue*> values = sr.value().entry->findAllValues(
- test::parseConfigOrDie("land"));
- ASSERT_EQ(2u, values.size());
- EXPECT_EQ(std::string("phone"), values[0]->product);
- EXPECT_EQ(std::string("tablet"), values[1]->product);
+ Maybe<ResourceTable::SearchResult> sr =
+ table.findResource(test::parseNameOrDie("android:string/foo"));
+ AAPT_ASSERT_TRUE(sr);
+ std::vector<ResourceConfigValue*> values =
+ sr.value().entry->findAllValues(test::parseConfigOrDie("land"));
+ ASSERT_EQ(2u, values.size());
+ EXPECT_EQ(std::string("phone"), values[0]->product);
+ EXPECT_EQ(std::string("tablet"), values[1]->product);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 73a194e..b41be4b 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "NameMangler.h"
#include "ResourceUtils.h"
+#include "NameMangler.h"
#include "SdkConstants.h"
#include "flatten/ResourceTypeExtensions.h"
#include "util/Files.h"
@@ -27,188 +27,196 @@
namespace aapt {
namespace ResourceUtils {
-Maybe<ResourceName> toResourceName(const android::ResTable::resource_name& nameIn) {
- ResourceName nameOut;
- if (!nameIn.package) {
- return {};
- }
+Maybe<ResourceName> toResourceName(
+ const android::ResTable::resource_name& nameIn) {
+ ResourceName nameOut;
+ if (!nameIn.package) {
+ return {};
+ }
- nameOut.package = util::utf16ToUtf8(StringPiece16(nameIn.package, nameIn.packageLen));
+ 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 {};
- }
+ 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 {};
- }
+ if (!type) {
+ return {};
+ }
- nameOut.type = *type;
+ 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;
+ 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 char* start = str.data();
- const char* end = start + str.size();
- const char* current = start;
- while (current != end) {
- if (outType->size() == 0 && *current == '/') {
- hasTypeSeparator = true;
- outType->assign(start, current - start);
- start = current + 1;
- } else if (outPackage->size() == 0 && *current == ':') {
- hasPackageSeparator = true;
- outPackage->assign(start, current - start);
- start = current + 1;
- }
- current++;
+ bool hasPackageSeparator = false;
+ bool hasTypeSeparator = false;
+ const char* start = str.data();
+ const char* end = start + str.size();
+ const char* current = start;
+ while (current != end) {
+ if (outType->size() == 0 && *current == '/') {
+ hasTypeSeparator = true;
+ outType->assign(start, current - start);
+ start = current + 1;
+ } else if (outPackage->size() == 0 && *current == ':') {
+ hasPackageSeparator = true;
+ outPackage->assign(start, current - start);
+ start = current + 1;
}
- outEntry->assign(start, end - start);
+ current++;
+ }
+ outEntry->assign(start, end - start);
- return !(hasPackageSeparator && outPackage->empty()) && !(hasTypeSeparator && outType->empty());
+ return !(hasPackageSeparator && outPackage->empty()) &&
+ !(hasTypeSeparator && outType->empty());
}
-bool parseResourceName(const StringPiece& str, ResourceNameRef* outRef, bool* outPrivate) {
- if (str.empty()) {
- return false;
+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] == '*') {
+ priv = true;
+ offset = 1;
+ }
+
+ StringPiece package;
+ StringPiece type;
+ StringPiece entry;
+ if (!extractResourceName(str.substr(offset, str.size() - offset), &package,
+ &type, &entry)) {
+ return false;
+ }
+
+ const ResourceType* parsedType = parseResourceType(type);
+ if (!parsedType) {
+ return false;
+ }
+
+ if (entry.empty()) {
+ return false;
+ }
+
+ if (outRef) {
+ outRef->package = package;
+ outRef->type = *parsedType;
+ outRef->entry = entry;
+ }
+
+ if (outPrivate) {
+ *outPrivate = priv;
+ }
+ return true;
+}
+
+bool parseReference(const StringPiece& str, ResourceNameRef* outRef,
+ bool* outCreate, bool* outPrivate) {
+ StringPiece trimmedStr(util::trimWhitespace(str));
+ if (trimmedStr.empty()) {
+ return false;
+ }
+
+ bool create = false;
+ bool priv = false;
+ if (trimmedStr.data()[0] == '@') {
+ size_t offset = 1;
+ if (trimmedStr.data()[1] == '+') {
+ create = true;
+ offset += 1;
}
- size_t offset = 0;
- bool priv = false;
- if (str.data()[0] == '*') {
- priv = true;
- offset = 1;
+ ResourceNameRef name;
+ if (!parseResourceName(
+ trimmedStr.substr(offset, trimmedStr.size() - offset), &name,
+ &priv)) {
+ return false;
}
- StringPiece package;
- StringPiece type;
- StringPiece entry;
- if (!extractResourceName(str.substr(offset, str.size() - offset), &package, &type, &entry)) {
- return false;
+ if (create && priv) {
+ return false;
}
- const ResourceType* parsedType = parseResourceType(type);
- if (!parsedType) {
- return false;
- }
-
- if (entry.empty()) {
- return false;
+ if (create && name.type != ResourceType::kId) {
+ return false;
}
if (outRef) {
- outRef->package = package;
- outRef->type = *parsedType;
- outRef->entry = entry;
+ *outRef = name;
+ }
+
+ if (outCreate) {
+ *outCreate = create;
}
if (outPrivate) {
- *outPrivate = priv;
+ *outPrivate = priv;
}
return true;
-}
-
-bool parseReference(const StringPiece& str, ResourceNameRef* outRef, bool* outCreate,
- bool* outPrivate) {
- StringPiece trimmedStr(util::trimWhitespace(str));
- if (trimmedStr.empty()) {
- return false;
- }
-
- bool create = false;
- bool priv = false;
- if (trimmedStr.data()[0] == '@') {
- size_t offset = 1;
- if (trimmedStr.data()[1] == '+') {
- create = true;
- offset += 1;
- }
-
- ResourceNameRef name;
- if (!parseResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset),
- &name, &priv)) {
- return false;
- }
-
- if (create && priv) {
- return false;
- }
-
- if (create && name.type != ResourceType::kId) {
- return false;
- }
-
- if (outRef) {
- *outRef = name;
- }
-
- if (outCreate) {
- *outCreate = create;
- }
-
- if (outPrivate) {
- *outPrivate = priv;
- }
- return true;
- }
- return false;
+ }
+ return false;
}
bool isReference(const StringPiece& str) {
- return parseReference(str, nullptr, nullptr, nullptr);
+ return parseReference(str, nullptr, nullptr, nullptr);
}
bool parseAttributeReference(const StringPiece& str, ResourceNameRef* outRef) {
- StringPiece trimmedStr(util::trimWhitespace(str));
- if (trimmedStr.empty()) {
- return false;
- }
-
- 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 != "attr") {
- return false;
- }
-
- if (entry.empty()) {
- return false;
- }
-
- if (outRef) {
- outRef->package = package;
- outRef->type = ResourceType::kAttr;
- outRef->entry = entry;
- }
- return true;
- }
+ StringPiece trimmedStr(util::trimWhitespace(str));
+ if (trimmedStr.empty()) {
return false;
+ }
+
+ 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 != "attr") {
+ return false;
+ }
+
+ if (entry.empty()) {
+ return false;
+ }
+
+ if (outRef) {
+ outRef->package = package;
+ outRef->type = ResourceType::kAttr;
+ outRef->entry = entry;
+ }
+ return true;
+ }
+ return false;
}
bool isAttributeReference(const StringPiece& str) {
- return parseAttributeReference(str, nullptr);
+ return parseAttributeReference(str, nullptr);
}
/*
@@ -219,414 +227,421 @@
* <[*]package>:[style/]<entry>
* [[*]package:style/]<entry>
*/
-Maybe<Reference> parseStyleParentReference(const StringPiece& str, std::string* outError) {
- if (str.empty()) {
- return {};
+Maybe<Reference> parseStyleParentReference(const StringPiece& str,
+ std::string* outError) {
+ if (str.empty()) {
+ return {};
+ }
+
+ 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] == '@' || name.data()[0] == '?') {
+ hasLeadingIdentifiers = true;
+ name = name.substr(1, name.size() - 1);
+ }
+
+ if (name.data()[0] == '*') {
+ privateRef = true;
+ name = name.substr(1, name.size() - 1);
+ }
+
+ ResourceNameRef ref;
+ ref.type = ResourceType::kStyle;
+
+ StringPiece typeStr;
+ extractResourceName(name, &ref.package, &typeStr, &ref.entry);
+ if (!typeStr.empty()) {
+ // If we have a type, make sure it is a Style.
+ const ResourceType* parsedType = parseResourceType(typeStr);
+ if (!parsedType || *parsedType != ResourceType::kStyle) {
+ std::stringstream err;
+ err << "invalid resource type '" << typeStr << "' for parent of style";
+ *outError = err.str();
+ return {};
}
+ }
- StringPiece name = str;
+ if (!hasLeadingIdentifiers && ref.package.empty() && !typeStr.empty()) {
+ std::stringstream err;
+ err << "invalid parent reference '" << str << "'";
+ *outError = err.str();
+ return {};
+ }
- bool hasLeadingIdentifiers = false;
- bool privateRef = false;
-
- // Skip over these identifiers. A style's parent is a normal reference.
- if (name.data()[0] == '@' || name.data()[0] == '?') {
- hasLeadingIdentifiers = true;
- name = name.substr(1, name.size() - 1);
- }
-
- if (name.data()[0] == '*') {
- privateRef = true;
- name = name.substr(1, name.size() - 1);
- }
-
- ResourceNameRef ref;
- ref.type = ResourceType::kStyle;
-
- StringPiece typeStr;
- extractResourceName(name, &ref.package, &typeStr, &ref.entry);
- if (!typeStr.empty()) {
- // If we have a type, make sure it is a Style.
- const ResourceType* parsedType = parseResourceType(typeStr);
- if (!parsedType || *parsedType != ResourceType::kStyle) {
- std::stringstream err;
- err << "invalid resource type '" << typeStr << "' for parent of style";
- *outError = err.str();
- return {};
- }
- }
-
- if (!hasLeadingIdentifiers && ref.package.empty() && !typeStr.empty()) {
- std::stringstream err;
- err << "invalid parent reference '" << str << "'";
- *outError = err.str();
- return {};
- }
-
- Reference result(ref);
- result.privateReference = privateRef;
- return result;
+ Reference result(ref);
+ result.privateReference = privateRef;
+ return result;
}
Maybe<Reference> parseXmlAttributeName(const StringPiece& str) {
- StringPiece trimmedStr = util::trimWhitespace(str);
- const char* start = trimmedStr.data();
- const char* const end = start + trimmedStr.size();
- const char* p = start;
+ StringPiece trimmedStr = util::trimWhitespace(str);
+ const char* start = trimmedStr.data();
+ const char* const end = start + trimmedStr.size();
+ const char* p = start;
- Reference ref;
- if (p != end && *p == '*') {
- ref.privateReference = true;
- start++;
- p++;
+ Reference ref;
+ if (p != end && *p == '*') {
+ ref.privateReference = true;
+ start++;
+ p++;
+ }
+
+ StringPiece package;
+ StringPiece name;
+ while (p != end) {
+ if (*p == ':') {
+ package = StringPiece(start, p - start);
+ name = StringPiece(p + 1, end - (p + 1));
+ break;
}
+ p++;
+ }
- StringPiece package;
- StringPiece name;
- while (p != end) {
- if (*p == ':') {
- package = StringPiece(start, p - start);
- name = StringPiece(p + 1, end - (p + 1));
- break;
- }
- p++;
- }
-
- ref.name = ResourceName(package.toString(), ResourceType::kAttr,
- name.empty() ? trimmedStr.toString() : name.toString());
- return Maybe<Reference>(std::move(ref));
+ ref.name =
+ ResourceName(package.toString(), ResourceType::kAttr,
+ name.empty() ? trimmedStr.toString() : name.toString());
+ return Maybe<Reference>(std::move(ref));
}
-std::unique_ptr<Reference> tryParseReference(const StringPiece& str, bool* outCreate) {
- ResourceNameRef ref;
- bool privateRef = false;
- if (parseReference(str, &ref, outCreate, &privateRef)) {
- std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
- value->privateReference = privateRef;
- return value;
- }
+std::unique_ptr<Reference> tryParseReference(const StringPiece& str,
+ bool* outCreate) {
+ ResourceNameRef ref;
+ bool privateRef = false;
+ if (parseReference(str, &ref, outCreate, &privateRef)) {
+ std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
+ value->privateReference = privateRef;
+ return value;
+ }
- if (parseAttributeReference(str, &ref)) {
- if (outCreate) {
- *outCreate = false;
- }
- return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
+ if (parseAttributeReference(str, &ref)) {
+ if (outCreate) {
+ *outCreate = false;
}
- return {};
+ return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
+ }
+ return {};
}
std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece& str) {
- StringPiece trimmedStr(util::trimWhitespace(str));
- android::Res_value value = { };
- 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 == "@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;
- } else {
- return {};
- }
- return util::make_unique<BinaryPrimitive>(value);
+ StringPiece trimmedStr(util::trimWhitespace(str));
+ android::Res_value value = {};
+ 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 == "@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;
+ } else {
+ return {};
+ }
+ return util::make_unique<BinaryPrimitive>(value);
}
std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute* enumAttr,
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.
- const ResourceName& enumSymbolResourceName = symbol.symbol.name.value();
- if (trimmedStr == enumSymbolResourceName.entry) {
- android::Res_value value = { };
- value.dataType = android::Res_value::TYPE_INT_DEC;
- value.data = symbol.value;
- return util::make_unique<BinaryPrimitive>(value);
- }
+ 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.
+ const ResourceName& enumSymbolResourceName = symbol.symbol.name.value();
+ if (trimmedStr == enumSymbolResourceName.entry) {
+ android::Res_value value = {};
+ value.dataType = android::Res_value::TYPE_INT_DEC;
+ value.data = symbol.value;
+ return util::make_unique<BinaryPrimitive>(value);
}
- return {};
+ }
+ return {};
}
std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* flagAttr,
const StringPiece& str) {
- android::Res_value flags = { };
- flags.dataType = android::Res_value::TYPE_INT_HEX;
- flags.data = 0u;
+ android::Res_value flags = {};
+ flags.dataType = android::Res_value::TYPE_INT_HEX;
+ flags.data = 0u;
- if (util::trimWhitespace(str).empty()) {
- // Empty string is a valid flag (0).
- return util::make_unique<BinaryPrimitive>(flags);
- }
-
- for (StringPiece part : util::tokenize(str, '|')) {
- StringPiece trimmedPart = util::trimWhitespace(part);
-
- bool flagSet = false;
- for (const Attribute::Symbol& symbol : flagAttr->symbols) {
- // Flag symbols are stored as @package:id/symbol resources,
- // so we need to match against the 'entry' part of the identifier.
- const ResourceName& flagSymbolResourceName = symbol.symbol.name.value();
- if (trimmedPart == flagSymbolResourceName.entry) {
- flags.data |= symbol.value;
- flagSet = true;
- break;
- }
- }
-
- if (!flagSet) {
- return {};
- }
- }
+ if (util::trimWhitespace(str).empty()) {
+ // Empty string is a valid flag (0).
return util::make_unique<BinaryPrimitive>(flags);
+ }
+
+ for (StringPiece part : util::tokenize(str, '|')) {
+ StringPiece trimmedPart = util::trimWhitespace(part);
+
+ bool flagSet = false;
+ for (const Attribute::Symbol& symbol : flagAttr->symbols) {
+ // Flag symbols are stored as @package:id/symbol resources,
+ // so we need to match against the 'entry' part of the identifier.
+ const ResourceName& flagSymbolResourceName = symbol.symbol.name.value();
+ if (trimmedPart == flagSymbolResourceName.entry) {
+ flags.data |= symbol.value;
+ flagSet = true;
+ break;
+ }
+ }
+
+ if (!flagSet) {
+ return {};
+ }
+ }
+ return util::make_unique<BinaryPrimitive>(flags);
}
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;
- }
+ 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 StringPiece& str) {
- StringPiece colorStr(util::trimWhitespace(str));
- const char* start = colorStr.data();
- const size_t len = colorStr.size();
- if (len == 0 || start[0] != '#') {
- return {};
- }
+ StringPiece colorStr(util::trimWhitespace(str));
+ const char* start = colorStr.data();
+ const size_t len = colorStr.size();
+ if (len == 0 || start[0] != '#') {
+ return {};
+ }
- android::Res_value value = { };
- bool error = false;
- if (len == 4) {
- value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
- value.data = 0xff000000u;
- value.data |= parseHex(start[1], &error) << 20;
- value.data |= parseHex(start[1], &error) << 16;
- value.data |= parseHex(start[2], &error) << 12;
- value.data |= parseHex(start[2], &error) << 8;
- value.data |= parseHex(start[3], &error) << 4;
- value.data |= parseHex(start[3], &error);
- } else if (len == 5) {
- value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4;
- value.data |= parseHex(start[1], &error) << 28;
- value.data |= parseHex(start[1], &error) << 24;
- value.data |= parseHex(start[2], &error) << 20;
- value.data |= parseHex(start[2], &error) << 16;
- value.data |= parseHex(start[3], &error) << 12;
- value.data |= parseHex(start[3], &error) << 8;
- value.data |= parseHex(start[4], &error) << 4;
- value.data |= parseHex(start[4], &error);
- } else if (len == 7) {
- value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8;
- value.data = 0xff000000u;
- value.data |= parseHex(start[1], &error) << 20;
- value.data |= parseHex(start[2], &error) << 16;
- value.data |= parseHex(start[3], &error) << 12;
- value.data |= parseHex(start[4], &error) << 8;
- value.data |= parseHex(start[5], &error) << 4;
- value.data |= parseHex(start[6], &error);
- } else if (len == 9) {
- value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8;
- value.data |= parseHex(start[1], &error) << 28;
- value.data |= parseHex(start[2], &error) << 24;
- value.data |= parseHex(start[3], &error) << 20;
- value.data |= parseHex(start[4], &error) << 16;
- value.data |= parseHex(start[5], &error) << 12;
- value.data |= parseHex(start[6], &error) << 8;
- value.data |= parseHex(start[7], &error) << 4;
- value.data |= parseHex(start[8], &error);
- } else {
- return {};
- }
- return error ? std::unique_ptr<BinaryPrimitive>() : util::make_unique<BinaryPrimitive>(value);
+ android::Res_value value = {};
+ bool error = false;
+ if (len == 4) {
+ value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
+ value.data = 0xff000000u;
+ value.data |= parseHex(start[1], &error) << 20;
+ value.data |= parseHex(start[1], &error) << 16;
+ value.data |= parseHex(start[2], &error) << 12;
+ value.data |= parseHex(start[2], &error) << 8;
+ value.data |= parseHex(start[3], &error) << 4;
+ value.data |= parseHex(start[3], &error);
+ } else if (len == 5) {
+ value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4;
+ value.data |= parseHex(start[1], &error) << 28;
+ value.data |= parseHex(start[1], &error) << 24;
+ value.data |= parseHex(start[2], &error) << 20;
+ value.data |= parseHex(start[2], &error) << 16;
+ value.data |= parseHex(start[3], &error) << 12;
+ value.data |= parseHex(start[3], &error) << 8;
+ value.data |= parseHex(start[4], &error) << 4;
+ value.data |= parseHex(start[4], &error);
+ } else if (len == 7) {
+ value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8;
+ value.data = 0xff000000u;
+ value.data |= parseHex(start[1], &error) << 20;
+ value.data |= parseHex(start[2], &error) << 16;
+ value.data |= parseHex(start[3], &error) << 12;
+ value.data |= parseHex(start[4], &error) << 8;
+ value.data |= parseHex(start[5], &error) << 4;
+ value.data |= parseHex(start[6], &error);
+ } else if (len == 9) {
+ value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8;
+ value.data |= parseHex(start[1], &error) << 28;
+ value.data |= parseHex(start[2], &error) << 24;
+ value.data |= parseHex(start[3], &error) << 20;
+ value.data |= parseHex(start[4], &error) << 16;
+ value.data |= parseHex(start[5], &error) << 12;
+ value.data |= parseHex(start[6], &error) << 8;
+ value.data |= parseHex(start[7], &error) << 4;
+ value.data |= parseHex(start[8], &error);
+ } else {
+ return {};
+ }
+ return error ? std::unique_ptr<BinaryPrimitive>()
+ : util::make_unique<BinaryPrimitive>(value);
}
Maybe<bool> parseBool(const StringPiece& str) {
- StringPiece trimmedStr(util::trimWhitespace(str));
- if (trimmedStr == "true" || trimmedStr == "TRUE" || trimmedStr == "True") {
- return Maybe<bool>(true);
- } else if (trimmedStr == "false" || trimmedStr == "FALSE" || trimmedStr == "False") {
- return Maybe<bool>(false);
- }
- return {};
+ StringPiece trimmedStr(util::trimWhitespace(str));
+ if (trimmedStr == "true" || trimmedStr == "TRUE" || trimmedStr == "True") {
+ return Maybe<bool>(true);
+ } else if (trimmedStr == "false" || trimmedStr == "FALSE" ||
+ trimmedStr == "False") {
+ return Maybe<bool>(false);
+ }
+ return {};
}
Maybe<uint32_t> parseInt(const StringPiece& str) {
- std::u16string str16 = util::utf8ToUtf16(str);
- android::Res_value value;
- if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
- return value.data;
- }
- return {};
+ std::u16string str16 = util::utf8ToUtf16(str);
+ android::Res_value value;
+ if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
+ return value.data;
+ }
+ return {};
}
Maybe<ResourceId> parseResourceId(const StringPiece& str) {
- StringPiece trimmedStr(util::trimWhitespace(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)) {
- if (value.dataType == android::Res_value::TYPE_INT_HEX) {
- ResourceId id(value.data);
- if (id.isValid()) {
- return id;
- }
- }
+ std::u16string str16 = util::utf8ToUtf16(trimmedStr);
+ android::Res_value value;
+ if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
+ if (value.dataType == android::Res_value::TYPE_INT_HEX) {
+ ResourceId id(value.data);
+ if (id.isValid()) {
+ return id;
+ }
}
- return {};
+ }
+ return {};
}
Maybe<int> parseSdkVersion(const StringPiece& str) {
- StringPiece trimmedStr(util::trimWhitespace(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);
- }
+ 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 {};
+ // 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) {
- if (Maybe<bool> maybeResult = parseBool(str)) {
- android::Res_value value = {};
- value.dataType = android::Res_value::TYPE_INT_BOOLEAN;
+ if (Maybe<bool> maybeResult = parseBool(str)) {
+ android::Res_value value = {};
+ value.dataType = android::Res_value::TYPE_INT_BOOLEAN;
- if (maybeResult.value()) {
- value.data = 0xffffffffu;
- } else {
- value.data = 0;
- }
- return util::make_unique<BinaryPrimitive>(value);
+ if (maybeResult.value()) {
+ value.data = 0xffffffffu;
+ } else {
+ value.data = 0;
}
- return {};
+ return util::make_unique<BinaryPrimitive>(value);
+ }
+ return {};
}
std::unique_ptr<BinaryPrimitive> tryParseInt(const StringPiece& str) {
- std::u16string str16 = util::utf8ToUtf16(str);
- android::Res_value value;
- if (!android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
- return {};
- }
- return util::make_unique<BinaryPrimitive>(value);
+ std::u16string str16 = util::utf8ToUtf16(str);
+ android::Res_value value;
+ if (!android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
+ return {};
+ }
+ return util::make_unique<BinaryPrimitive>(value);
}
std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece& str) {
- std::u16string str16 = util::utf8ToUtf16(str);
- android::Res_value value;
- if (!android::ResTable::stringToFloat(str16.data(), str16.size(), &value)) {
- return {};
- }
- return util::make_unique<BinaryPrimitive>(value);
+ std::u16string str16 = util::utf8ToUtf16(str);
+ android::Res_value value;
+ if (!android::ResTable::stringToFloat(str16.data(), str16.size(), &value)) {
+ return {};
+ }
+ return util::make_unique<BinaryPrimitive>(value);
}
uint32_t androidTypeToAttributeTypeMask(uint16_t type) {
- switch (type) {
+ switch (type) {
case android::Res_value::TYPE_NULL:
case android::Res_value::TYPE_REFERENCE:
case android::Res_value::TYPE_ATTRIBUTE:
case android::Res_value::TYPE_DYNAMIC_REFERENCE:
- return android::ResTable_map::TYPE_REFERENCE;
+ return android::ResTable_map::TYPE_REFERENCE;
case android::Res_value::TYPE_STRING:
- return android::ResTable_map::TYPE_STRING;
+ return android::ResTable_map::TYPE_STRING;
case android::Res_value::TYPE_FLOAT:
- return android::ResTable_map::TYPE_FLOAT;
+ return android::ResTable_map::TYPE_FLOAT;
case android::Res_value::TYPE_DIMENSION:
- return android::ResTable_map::TYPE_DIMENSION;
+ return android::ResTable_map::TYPE_DIMENSION;
case android::Res_value::TYPE_FRACTION:
- return android::ResTable_map::TYPE_FRACTION;
+ return android::ResTable_map::TYPE_FRACTION;
case android::Res_value::TYPE_INT_DEC:
case android::Res_value::TYPE_INT_HEX:
- return android::ResTable_map::TYPE_INTEGER | android::ResTable_map::TYPE_ENUM
- | android::ResTable_map::TYPE_FLAGS;
+ return android::ResTable_map::TYPE_INTEGER |
+ android::ResTable_map::TYPE_ENUM |
+ android::ResTable_map::TYPE_FLAGS;
case android::Res_value::TYPE_INT_BOOLEAN:
- return android::ResTable_map::TYPE_BOOLEAN;
+ return android::ResTable_map::TYPE_BOOLEAN;
case android::Res_value::TYPE_INT_COLOR_ARGB8:
case android::Res_value::TYPE_INT_COLOR_RGB8:
case android::Res_value::TYPE_INT_COLOR_ARGB4:
case android::Res_value::TYPE_INT_COLOR_RGB4:
- return android::ResTable_map::TYPE_COLOR;
+ return android::ResTable_map::TYPE_COLOR;
default:
- return 0;
- };
+ return 0;
+ };
}
std::unique_ptr<Item> tryParseItemForAttribute(
- const StringPiece& value,
- uint32_t typeMask,
- const std::function<void(const ResourceName&)>& onCreateReference) {
- std::unique_ptr<BinaryPrimitive> nullOrEmpty = tryParseNullOrEmpty(value);
- if (nullOrEmpty) {
- return std::move(nullOrEmpty);
- }
+ const StringPiece& value, uint32_t typeMask,
+ const std::function<void(const ResourceName&)>& onCreateReference) {
+ std::unique_ptr<BinaryPrimitive> nullOrEmpty = tryParseNullOrEmpty(value);
+ if (nullOrEmpty) {
+ return std::move(nullOrEmpty);
+ }
- bool create = false;
- std::unique_ptr<Reference> reference = tryParseReference(value, &create);
- if (reference) {
- if (create && onCreateReference) {
- onCreateReference(reference->name.value());
- }
- return std::move(reference);
+ bool create = false;
+ std::unique_ptr<Reference> reference = tryParseReference(value, &create);
+ if (reference) {
+ if (create && onCreateReference) {
+ onCreateReference(reference->name.value());
}
+ return std::move(reference);
+ }
- if (typeMask & android::ResTable_map::TYPE_COLOR) {
- // Try parsing this as a color.
- std::unique_ptr<BinaryPrimitive> color = tryParseColor(value);
- if (color) {
- return std::move(color);
- }
+ if (typeMask & android::ResTable_map::TYPE_COLOR) {
+ // Try parsing this as a color.
+ std::unique_ptr<BinaryPrimitive> color = tryParseColor(value);
+ if (color) {
+ return std::move(color);
}
+ }
- if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
- // Try parsing this as a boolean.
- std::unique_ptr<BinaryPrimitive> boolean = tryParseBool(value);
- if (boolean) {
- return std::move(boolean);
- }
+ if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
+ // Try parsing this as a boolean.
+ std::unique_ptr<BinaryPrimitive> boolean = tryParseBool(value);
+ if (boolean) {
+ return std::move(boolean);
}
+ }
- if (typeMask & android::ResTable_map::TYPE_INTEGER) {
- // Try parsing this as an integer.
- std::unique_ptr<BinaryPrimitive> integer = tryParseInt(value);
- if (integer) {
- return std::move(integer);
- }
+ if (typeMask & android::ResTable_map::TYPE_INTEGER) {
+ // Try parsing this as an integer.
+ std::unique_ptr<BinaryPrimitive> integer = tryParseInt(value);
+ if (integer) {
+ return std::move(integer);
}
+ }
- const uint32_t floatMask = android::ResTable_map::TYPE_FLOAT
- | android::ResTable_map::TYPE_DIMENSION | android::ResTable_map::TYPE_FRACTION;
- if (typeMask & floatMask) {
- // Try parsing this as a float.
- std::unique_ptr<BinaryPrimitive> floatingPoint = tryParseFloat(value);
- if (floatingPoint) {
- if (typeMask & androidTypeToAttributeTypeMask(floatingPoint->value.dataType)) {
- return std::move(floatingPoint);
- }
- }
+ const uint32_t floatMask = android::ResTable_map::TYPE_FLOAT |
+ android::ResTable_map::TYPE_DIMENSION |
+ android::ResTable_map::TYPE_FRACTION;
+ if (typeMask & floatMask) {
+ // Try parsing this as a float.
+ std::unique_ptr<BinaryPrimitive> floatingPoint = tryParseFloat(value);
+ if (floatingPoint) {
+ if (typeMask &
+ androidTypeToAttributeTypeMask(floatingPoint->value.dataType)) {
+ return std::move(floatingPoint);
+ }
}
- return {};
+ }
+ return {};
}
/**
@@ -634,48 +649,50 @@
* allows.
*/
std::unique_ptr<Item> tryParseItemForAttribute(
- const StringPiece& str, const Attribute* attr,
- const std::function<void(const ResourceName&)>& onCreateReference) {
- const uint32_t typeMask = attr->typeMask;
- std::unique_ptr<Item> value = tryParseItemForAttribute(str, typeMask, onCreateReference);
- if (value) {
- return value;
- }
+ const StringPiece& str, const Attribute* attr,
+ const std::function<void(const ResourceName&)>& onCreateReference) {
+ const uint32_t typeMask = attr->typeMask;
+ std::unique_ptr<Item> value =
+ tryParseItemForAttribute(str, typeMask, onCreateReference);
+ if (value) {
+ return value;
+ }
- if (typeMask & android::ResTable_map::TYPE_ENUM) {
- // Try parsing this as an enum.
- std::unique_ptr<BinaryPrimitive> enumValue = tryParseEnumSymbol(attr, str);
- if (enumValue) {
- return std::move(enumValue);
- }
+ if (typeMask & android::ResTable_map::TYPE_ENUM) {
+ // Try parsing this as an enum.
+ std::unique_ptr<BinaryPrimitive> enumValue = tryParseEnumSymbol(attr, str);
+ if (enumValue) {
+ return std::move(enumValue);
}
+ }
- if (typeMask & android::ResTable_map::TYPE_FLAGS) {
- // Try parsing this as a flag.
- std::unique_ptr<BinaryPrimitive> flagValue = tryParseFlagSymbol(attr, str);
- if (flagValue) {
- return std::move(flagValue);
- }
+ if (typeMask & android::ResTable_map::TYPE_FLAGS) {
+ // Try parsing this as a flag.
+ std::unique_ptr<BinaryPrimitive> flagValue = tryParseFlagSymbol(attr, str);
+ if (flagValue) {
+ return std::move(flagValue);
}
- return {};
+ }
+ return {};
}
-std::string buildResourceFileName(const ResourceFile& resFile, const NameMangler* mangler) {
- std::stringstream out;
- out << "res/" << resFile.name.type;
- if (resFile.config != ConfigDescription{}) {
- out << "-" << resFile.config;
- }
- out << "/";
+std::string buildResourceFileName(const ResourceFile& resFile,
+ const NameMangler* mangler) {
+ std::stringstream out;
+ out << "res/" << resFile.name.type;
+ if (resFile.config != ConfigDescription{}) {
+ out << "-" << resFile.config;
+ }
+ out << "/";
- if (mangler && mangler->shouldMangle(resFile.name.package)) {
- out << NameMangler::mangleEntry(resFile.name.package, resFile.name.entry);
- } else {
- out << resFile.name.entry;
- }
- out << file::getExtension(resFile.source.path);
- return out.str();
+ if (mangler && mangler->shouldMangle(resFile.name.package)) {
+ out << NameMangler::mangleEntry(resFile.name.package, resFile.name.entry);
+ } else {
+ out << resFile.name.entry;
+ }
+ out << file::getExtension(resFile.source.path);
+ return out.str();
}
-} // namespace ResourceUtils
-} // namespace aapt
+} // namespace ResourceUtils
+} // namespace aapt
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index 555203b..cebe47c 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -41,15 +41,18 @@
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
+ * 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 StringPiece& str, ResourceNameRef* outResource,
bool* outPrivate = nullptr);
/*
- * Returns true if the string was parsed as a reference (@[+][package:]type/name), with
+ * Returns true if the string was parsed as a reference
+ * (@[+][package:]type/name), with
* `outReference` set to the parsed reference.
*
* If '+' was present in the reference, `outCreate` is set to true.
@@ -59,28 +62,34 @@
bool* outCreate = nullptr, bool* outPrivate = nullptr);
/*
- * Returns true if the string is in the form of a resource reference (@[+][package:]type/name).
+ * Returns true if the string is in the form of a resource reference
+ * (@[+][package:]type/name).
*/
bool isReference(const StringPiece& str);
/*
- * Returns true if the string was parsed as an attribute reference (?[package:][type/]name),
+ * Returns true if the string was parsed as an attribute reference
+ * (?[package:][type/]name),
* with `outReference` set to the parsed reference.
*/
-bool parseAttributeReference(const StringPiece& str, ResourceNameRef* outReference);
+bool parseAttributeReference(const StringPiece& str,
+ ResourceNameRef* outReference);
/**
- * Returns true if the string is in the form of an attribute reference(?[package:][type/]name).
+ * Returns true if the string is in the form of an attribute
+ * reference(?[package:][type/]name).
*/
bool isAttributeReference(const StringPiece& str);
/**
* Convert an android::ResTable::resource_name to an aapt::ResourceName struct.
*/
-Maybe<ResourceName> toResourceName(const android::ResTable::resource_name& name);
+Maybe<ResourceName> toResourceName(
+ const android::ResTable::resource_name& name);
/**
- * Returns a boolean value if the string is equal to TRUE, true, True, FALSE, false, or False.
+ * Returns a boolean value if the string is equal to TRUE, true, True, FALSE,
+ * false, or False.
*/
Maybe<bool> parseBool(const StringPiece& str);
@@ -100,18 +109,22 @@
Maybe<int> parseSdkVersion(const StringPiece& str);
/*
- * Returns a Reference, or None Maybe instance if the string `str` was parsed as a
+ * Returns a Reference, or None Maybe instance if the string `str` was parsed as
+ * a
* valid reference to a style.
- * The format for a style parent is slightly more flexible than a normal reference:
+ * The format for a style parent is slightly more flexible than a normal
+ * reference:
*
* @[package:]style/<entry> or
* ?[package:]style/<entry> or
* <package>:[style/]<entry>
*/
-Maybe<Reference> parseStyleParentReference(const StringPiece& str, std::string* outError);
+Maybe<Reference> parseStyleParentReference(const StringPiece& str,
+ std::string* outError);
/*
- * Returns a Reference if the string `str` was parsed as a valid XML attribute name.
+ * Returns a Reference if the string `str` was parsed as a valid XML attribute
+ * name.
* The valid format for an XML attribute name is:
*
* package:entry
@@ -119,14 +132,18 @@
Maybe<Reference> parseXmlAttributeName(const StringPiece& str);
/*
- * 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
+ * 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 StringPiece& 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
+ * Returns a BinaryPrimitve object representing @null or @empty if the string
+ * was parsed
* as one.
*/
std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece& str);
@@ -138,13 +155,15 @@
std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece& str);
/*
- * Returns a BinaryPrimitve object representing a boolean if the string was parsed
+ * Returns a BinaryPrimitve object representing a boolean if the string was
+ * parsed
* as one.
*/
std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece& str);
/*
- * Returns a BinaryPrimitve object representing an integer if the string was parsed
+ * Returns a BinaryPrimitve object representing an integer if the string was
+ * parsed
* as one.
*/
std::unique_ptr<BinaryPrimitive> tryParseInt(const StringPiece& str);
@@ -156,45 +175,51 @@
std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece& str);
/*
- * Returns a BinaryPrimitve object representing an enum symbol if the string was parsed
+ * Returns a BinaryPrimitve object representing an enum symbol if the string was
+ * parsed
* as one.
*/
std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute* enumAttr,
const StringPiece& str);
/*
- * Returns a BinaryPrimitve object representing a flag symbol if the string was parsed
+ * Returns a BinaryPrimitve object representing a flag symbol if the string was
+ * parsed
* as one.
*/
std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* enumAttr,
const StringPiece& str);
/*
- * Try to convert a string to an Item for the given attribute. The attribute will
+ * Try to convert a string to an Item for the given attribute. The attribute
+ * will
* restrict what values the string can be converted to.
* The callback function onCreateReference is called when the parsed item is a
* reference to an ID that must be created (@+id/foo).
*/
std::unique_ptr<Item> tryParseItemForAttribute(
- const StringPiece& value, const Attribute* attr,
- const std::function<void(const ResourceName&)>& onCreateReference = {});
+ const StringPiece& value, const Attribute* attr,
+ const std::function<void(const ResourceName&)>& onCreateReference = {});
std::unique_ptr<Item> tryParseItemForAttribute(
- const StringPiece& value, uint32_t typeMask,
- const std::function<void(const ResourceName&)>& onCreateReference = {});
+ const StringPiece& value, uint32_t typeMask,
+ const std::function<void(const ResourceName&)>& onCreateReference = {});
uint32_t androidTypeToAttributeTypeMask(uint16_t type);
/**
- * Returns a string path suitable for use within an APK. The path will look like:
+ * Returns a string path suitable for use within an APK. The path will look
+ * like:
*
* res/type[-config]/<name>.<ext>
*
- * Then name may be mangled if a NameMangler is supplied (can be nullptr) and the package
+ * Then name may be mangled if a NameMangler is supplied (can be nullptr) and
+ * the package
* requires mangling.
*/
-std::string buildResourceFileName(const ResourceFile& resFile, const NameMangler* mangler);
+std::string buildResourceFileName(const ResourceFile& resFile,
+ const NameMangler* mangler);
-} // namespace ResourceUtils
-} // namespace aapt
+} // namespace ResourceUtils
+} // namespace aapt
#endif /* AAPT_RESOURCEUTILS_H */
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index 894cfcf..eb62b1b 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -14,179 +14,188 @@
* limitations under the License.
*/
-#include "Resource.h"
#include "ResourceUtils.h"
+#include "Resource.h"
#include "test/Test.h"
namespace aapt {
TEST(ResourceUtilsTest, ParseBool) {
- EXPECT_EQ(Maybe<bool>(true), ResourceUtils::parseBool("true"));
- EXPECT_EQ(Maybe<bool>(true), ResourceUtils::parseBool("TRUE"));
- EXPECT_EQ(Maybe<bool>(true), ResourceUtils::parseBool("True"));
- EXPECT_EQ(Maybe<bool>(false), ResourceUtils::parseBool("false"));
- EXPECT_EQ(Maybe<bool>(false), ResourceUtils::parseBool("FALSE"));
- EXPECT_EQ(Maybe<bool>(false), ResourceUtils::parseBool("False"));
+ EXPECT_EQ(Maybe<bool>(true), ResourceUtils::parseBool("true"));
+ EXPECT_EQ(Maybe<bool>(true), ResourceUtils::parseBool("TRUE"));
+ EXPECT_EQ(Maybe<bool>(true), ResourceUtils::parseBool("True"));
+ EXPECT_EQ(Maybe<bool>(false), ResourceUtils::parseBool("false"));
+ EXPECT_EQ(Maybe<bool>(false), ResourceUtils::parseBool("FALSE"));
+ EXPECT_EQ(Maybe<bool>(false), ResourceUtils::parseBool("False"));
}
TEST(ResourceUtilsTest, ParseResourceName) {
- ResourceNameRef actual;
- bool actualPriv = false;
- EXPECT_TRUE(ResourceUtils::parseResourceName("android:color/foo", &actual, &actualPriv));
- EXPECT_EQ(ResourceNameRef("android", ResourceType::kColor, "foo"), actual);
- EXPECT_FALSE(actualPriv);
+ ResourceNameRef actual;
+ bool actualPriv = false;
+ EXPECT_TRUE(ResourceUtils::parseResourceName("android:color/foo", &actual,
+ &actualPriv));
+ EXPECT_EQ(ResourceNameRef("android", ResourceType::kColor, "foo"), actual);
+ EXPECT_FALSE(actualPriv);
- EXPECT_TRUE(ResourceUtils::parseResourceName("color/foo", &actual, &actualPriv));
- EXPECT_EQ(ResourceNameRef({}, ResourceType::kColor, "foo"), actual);
- EXPECT_FALSE(actualPriv);
+ EXPECT_TRUE(
+ ResourceUtils::parseResourceName("color/foo", &actual, &actualPriv));
+ EXPECT_EQ(ResourceNameRef({}, ResourceType::kColor, "foo"), actual);
+ EXPECT_FALSE(actualPriv);
- EXPECT_TRUE(ResourceUtils::parseResourceName("*android:color/foo", &actual, &actualPriv));
- EXPECT_EQ(ResourceNameRef("android", ResourceType::kColor, "foo"), actual);
- EXPECT_TRUE(actualPriv);
+ EXPECT_TRUE(ResourceUtils::parseResourceName("*android:color/foo", &actual,
+ &actualPriv));
+ EXPECT_EQ(ResourceNameRef("android", ResourceType::kColor, "foo"), actual);
+ EXPECT_TRUE(actualPriv);
- EXPECT_FALSE(ResourceUtils::parseResourceName(StringPiece(), &actual, &actualPriv));
+ EXPECT_FALSE(
+ ResourceUtils::parseResourceName(StringPiece(), &actual, &actualPriv));
}
TEST(ResourceUtilsTest, ParseReferenceWithNoPackage) {
- ResourceNameRef expected({}, ResourceType::kColor, "foo");
- ResourceNameRef actual;
- bool create = false;
- bool privateRef = false;
- EXPECT_TRUE(ResourceUtils::parseReference("@color/foo", &actual, &create, &privateRef));
- EXPECT_EQ(expected, actual);
- EXPECT_FALSE(create);
- EXPECT_FALSE(privateRef);
+ ResourceNameRef expected({}, ResourceType::kColor, "foo");
+ ResourceNameRef actual;
+ bool create = false;
+ bool privateRef = false;
+ EXPECT_TRUE(ResourceUtils::parseReference("@color/foo", &actual, &create,
+ &privateRef));
+ EXPECT_EQ(expected, actual);
+ EXPECT_FALSE(create);
+ EXPECT_FALSE(privateRef);
}
TEST(ResourceUtilsTest, ParseReferenceWithPackage) {
- ResourceNameRef expected("android", ResourceType::kColor, "foo");
- ResourceNameRef actual;
- bool create = false;
- bool privateRef = false;
- EXPECT_TRUE(ResourceUtils::parseReference("@android:color/foo", &actual, &create,
- &privateRef));
- EXPECT_EQ(expected, actual);
- EXPECT_FALSE(create);
- EXPECT_FALSE(privateRef);
+ ResourceNameRef expected("android", ResourceType::kColor, "foo");
+ ResourceNameRef actual;
+ bool create = false;
+ bool privateRef = false;
+ EXPECT_TRUE(ResourceUtils::parseReference("@android:color/foo", &actual,
+ &create, &privateRef));
+ EXPECT_EQ(expected, actual);
+ EXPECT_FALSE(create);
+ EXPECT_FALSE(privateRef);
}
TEST(ResourceUtilsTest, ParseReferenceWithSurroundingWhitespace) {
- ResourceNameRef expected("android", ResourceType::kColor, "foo");
- ResourceNameRef actual;
- bool create = false;
- bool privateRef = false;
- EXPECT_TRUE(ResourceUtils::parseReference("\t @android:color/foo\n \n\t", &actual,
- &create, &privateRef));
- EXPECT_EQ(expected, actual);
- EXPECT_FALSE(create);
- EXPECT_FALSE(privateRef);
+ ResourceNameRef expected("android", ResourceType::kColor, "foo");
+ ResourceNameRef actual;
+ bool create = false;
+ bool privateRef = false;
+ EXPECT_TRUE(ResourceUtils::parseReference("\t @android:color/foo\n \n\t",
+ &actual, &create, &privateRef));
+ EXPECT_EQ(expected, actual);
+ EXPECT_FALSE(create);
+ EXPECT_FALSE(privateRef);
}
TEST(ResourceUtilsTest, ParseAutoCreateIdReference) {
- ResourceNameRef expected("android", ResourceType::kId, "foo");
- ResourceNameRef actual;
- bool create = false;
- bool privateRef = false;
- EXPECT_TRUE(ResourceUtils::parseReference("@+android:id/foo", &actual, &create,
- &privateRef));
- EXPECT_EQ(expected, actual);
- EXPECT_TRUE(create);
- EXPECT_FALSE(privateRef);
+ ResourceNameRef expected("android", ResourceType::kId, "foo");
+ ResourceNameRef actual;
+ bool create = false;
+ bool privateRef = false;
+ EXPECT_TRUE(ResourceUtils::parseReference("@+android:id/foo", &actual,
+ &create, &privateRef));
+ EXPECT_EQ(expected, actual);
+ EXPECT_TRUE(create);
+ EXPECT_FALSE(privateRef);
}
TEST(ResourceUtilsTest, ParsePrivateReference) {
- ResourceNameRef expected("android", ResourceType::kId, "foo");
- ResourceNameRef actual;
- bool create = false;
- bool privateRef = false;
- EXPECT_TRUE(ResourceUtils::parseReference("@*android:id/foo", &actual, &create,
- &privateRef));
- EXPECT_EQ(expected, actual);
- EXPECT_FALSE(create);
- EXPECT_TRUE(privateRef);
+ ResourceNameRef expected("android", ResourceType::kId, "foo");
+ ResourceNameRef actual;
+ bool create = false;
+ bool privateRef = false;
+ EXPECT_TRUE(ResourceUtils::parseReference("@*android:id/foo", &actual,
+ &create, &privateRef));
+ EXPECT_EQ(expected, actual);
+ EXPECT_FALSE(create);
+ EXPECT_TRUE(privateRef);
}
TEST(ResourceUtilsTest, FailToParseAutoCreateNonIdReference) {
- bool create = false;
- bool privateRef = false;
- ResourceNameRef actual;
- EXPECT_FALSE(ResourceUtils::parseReference("@+android:color/foo", &actual, &create,
- &privateRef));
+ bool create = false;
+ bool privateRef = false;
+ ResourceNameRef actual;
+ EXPECT_FALSE(ResourceUtils::parseReference("@+android:color/foo", &actual,
+ &create, &privateRef));
}
TEST(ResourceUtilsTest, ParseAttributeReferences) {
- EXPECT_TRUE(ResourceUtils::isAttributeReference("?android"));
- EXPECT_TRUE(ResourceUtils::isAttributeReference("?android:foo"));
- EXPECT_TRUE(ResourceUtils::isAttributeReference("?attr/foo"));
- EXPECT_TRUE(ResourceUtils::isAttributeReference("?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("?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"));
+ 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("android", ResourceType::kStyle, "foo");
- const ResourceName kStyleFooName({}, ResourceType::kStyle, "foo");
+ const ResourceName kAndroidStyleFooName("android", ResourceType::kStyle,
+ "foo");
+ const ResourceName kStyleFooName({}, ResourceType::kStyle, "foo");
- std::string errStr;
- Maybe<Reference> ref = ResourceUtils::parseStyleParentReference("@android:style/foo", &errStr);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ std::string errStr;
+ Maybe<Reference> ref =
+ ResourceUtils::parseStyleParentReference("@android:style/foo", &errStr);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
- ref = ResourceUtils::parseStyleParentReference("@style/foo", &errStr);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kStyleFooName);
+ ref = ResourceUtils::parseStyleParentReference("@style/foo", &errStr);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kStyleFooName);
- ref = ResourceUtils::parseStyleParentReference("?android:style/foo", &errStr);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ ref = ResourceUtils::parseStyleParentReference("?android:style/foo", &errStr);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
- ref = ResourceUtils::parseStyleParentReference("?style/foo", &errStr);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kStyleFooName);
+ ref = ResourceUtils::parseStyleParentReference("?style/foo", &errStr);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kStyleFooName);
- ref = ResourceUtils::parseStyleParentReference("android:style/foo", &errStr);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ ref = ResourceUtils::parseStyleParentReference("android:style/foo", &errStr);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
- ref = ResourceUtils::parseStyleParentReference("android:foo", &errStr);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ ref = ResourceUtils::parseStyleParentReference("android:foo", &errStr);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
- ref = ResourceUtils::parseStyleParentReference("@android:foo", &errStr);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ ref = ResourceUtils::parseStyleParentReference("@android:foo", &errStr);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
- ref = ResourceUtils::parseStyleParentReference("foo", &errStr);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kStyleFooName);
+ ref = ResourceUtils::parseStyleParentReference("foo", &errStr);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kStyleFooName);
- ref = ResourceUtils::parseStyleParentReference("*android:style/foo", &errStr);
- AAPT_ASSERT_TRUE(ref);
- EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
- EXPECT_TRUE(ref.value().privateReference);
+ ref = ResourceUtils::parseStyleParentReference("*android:style/foo", &errStr);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ EXPECT_TRUE(ref.value().privateReference);
}
TEST(ResourceUtilsTest, ParseEmptyFlag) {
- std::unique_ptr<Attribute> attr = test::AttributeBuilder(false)
- .setTypeMask(android::ResTable_map::TYPE_FLAGS)
- .addItem("one", 0x01)
- .addItem("two", 0x02)
- .build();
+ std::unique_ptr<Attribute> attr =
+ test::AttributeBuilder(false)
+ .setTypeMask(android::ResTable_map::TYPE_FLAGS)
+ .addItem("one", 0x01)
+ .addItem("two", 0x02)
+ .build();
- std::unique_ptr<BinaryPrimitive> result = ResourceUtils::tryParseFlagSymbol(attr.get(), "");
- ASSERT_NE(nullptr, result);
- EXPECT_EQ(0u, result->value.data);
+ std::unique_ptr<BinaryPrimitive> result =
+ ResourceUtils::tryParseFlagSymbol(attr.get(), "");
+ ASSERT_NE(nullptr, result);
+ EXPECT_EQ(0u, result->value.data);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 492155d..60590b6 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -14,14 +14,14 @@
* limitations under the License.
*/
+#include "ResourceValues.h"
#include "Resource.h"
#include "ResourceUtils.h"
-#include "ResourceValues.h"
#include "ValueVisitor.h"
#include "util/Util.h"
-#include <algorithm>
#include <androidfw/ResourceTypes.h>
+#include <algorithm>
#include <limits>
#include <set>
@@ -29,742 +29,745 @@
template <typename Derived>
void BaseValue<Derived>::accept(RawValueVisitor* visitor) {
- visitor->visit(static_cast<Derived*>(this));
+ visitor->visit(static_cast<Derived*>(this));
}
template <typename Derived>
void BaseItem<Derived>::accept(RawValueVisitor* visitor) {
- visitor->visit(static_cast<Derived*>(this));
+ visitor->visit(static_cast<Derived*>(this));
}
-RawString::RawString(const StringPool::Ref& ref) : value(ref) {
-}
+RawString::RawString(const StringPool::Ref& ref) : value(ref) {}
bool RawString::equals(const Value* value) const {
- const RawString* other = valueCast<RawString>(value);
- if (!other) {
- return false;
- }
- return *this->value == *other->value;
+ const RawString* other = valueCast<RawString>(value);
+ if (!other) {
+ return false;
+ }
+ return *this->value == *other->value;
}
RawString* RawString::clone(StringPool* newPool) const {
- RawString* rs = new RawString(newPool->makeRef(*value));
- rs->mComment = mComment;
- rs->mSource = mSource;
- return rs;
+ RawString* rs = new RawString(newPool->makeRef(*value));
+ rs->mComment = mComment;
+ rs->mSource = mSource;
+ return rs;
}
bool RawString::flatten(android::Res_value* outValue) const {
- outValue->dataType = android::Res_value::TYPE_STRING;
- outValue->data = util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
- return true;
+ outValue->dataType = android::Res_value::TYPE_STRING;
+ outValue->data =
+ util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
+ return true;
}
void RawString::print(std::ostream* out) const {
- *out << "(raw string) " << *value;
+ *out << "(raw string) " << *value;
}
-Reference::Reference() : referenceType(Type::kResource) {
-}
+Reference::Reference() : referenceType(Type::kResource) {}
-Reference::Reference(const ResourceNameRef& n, Type t) :
- name(n.toResourceName()), referenceType(t) {
-}
+Reference::Reference(const ResourceNameRef& n, Type t)
+ : name(n.toResourceName()), referenceType(t) {}
-Reference::Reference(const ResourceId& i, Type type) : id(i), referenceType(type) {
-}
+Reference::Reference(const ResourceId& i, Type type)
+ : id(i), referenceType(type) {}
-Reference::Reference(const ResourceNameRef& n, const ResourceId& i) :
- name(n.toResourceName()), id(i), referenceType(Type::kResource) {
-}
+Reference::Reference(const ResourceNameRef& n, const ResourceId& i)
+ : name(n.toResourceName()), id(i), referenceType(Type::kResource) {}
bool Reference::equals(const Value* value) const {
- const Reference* other = valueCast<Reference>(value);
- if (!other) {
- return false;
- }
- return referenceType == other->referenceType && privateReference == other->privateReference &&
- id == other->id && name == other->name;
+ const Reference* other = valueCast<Reference>(value);
+ if (!other) {
+ return false;
+ }
+ return referenceType == other->referenceType &&
+ privateReference == other->privateReference && id == other->id &&
+ name == other->name;
}
bool Reference::flatten(android::Res_value* outValue) const {
- outValue->dataType = (referenceType == Reference::Type::kResource) ?
- android::Res_value::TYPE_REFERENCE : android::Res_value::TYPE_ATTRIBUTE;
- outValue->data = util::hostToDevice32(id ? id.value().id : 0);
- return true;
+ outValue->dataType = (referenceType == Reference::Type::kResource)
+ ? android::Res_value::TYPE_REFERENCE
+ : android::Res_value::TYPE_ATTRIBUTE;
+ outValue->data = util::hostToDevice32(id ? id.value().id : 0);
+ return true;
}
Reference* Reference::clone(StringPool* /*newPool*/) const {
- return new Reference(*this);
+ return new Reference(*this);
}
void Reference::print(std::ostream* out) const {
- *out << "(reference) ";
- if (referenceType == Reference::Type::kResource) {
- *out << "@";
- if (privateReference) {
- *out << "*";
- }
- } else {
- *out << "?";
+ *out << "(reference) ";
+ if (referenceType == Reference::Type::kResource) {
+ *out << "@";
+ if (privateReference) {
+ *out << "*";
}
+ } else {
+ *out << "?";
+ }
- if (name) {
- *out << name.value();
- }
+ if (name) {
+ *out << name.value();
+ }
- if (id && !Res_INTERNALID(id.value().id)) {
- *out << " " << id.value();
- }
+ if (id && !Res_INTERNALID(id.value().id)) {
+ *out << " " << id.value();
+ }
}
bool Id::equals(const Value* value) const {
- return valueCast<Id>(value) != nullptr;
+ return valueCast<Id>(value) != nullptr;
}
bool Id::flatten(android::Res_value* out) const {
- out->dataType = android::Res_value::TYPE_INT_BOOLEAN;
- out->data = util::hostToDevice32(0);
- return true;
+ out->dataType = android::Res_value::TYPE_INT_BOOLEAN;
+ out->data = util::hostToDevice32(0);
+ return true;
}
-Id* Id::clone(StringPool* /*newPool*/) const {
- return new Id(*this);
-}
+Id* Id::clone(StringPool* /*newPool*/) const { return new Id(*this); }
-void Id::print(std::ostream* out) const {
- *out << "(id)";
-}
+void Id::print(std::ostream* out) const { *out << "(id)"; }
-String::String(const StringPool::Ref& ref) : value(ref) {
-}
+String::String(const StringPool::Ref& ref) : value(ref) {}
bool String::equals(const Value* value) const {
- const String* other = valueCast<String>(value);
- if (!other) {
- return false;
- }
- return *this->value == *other->value;
+ const String* other = valueCast<String>(value);
+ if (!other) {
+ return false;
+ }
+ return *this->value == *other->value;
}
bool String::flatten(android::Res_value* outValue) const {
- // Verify that our StringPool index is within encode-able limits.
- if (value.getIndex() > std::numeric_limits<uint32_t>::max()) {
- return false;
- }
+ // Verify that our StringPool index is within encode-able limits.
+ if (value.getIndex() > std::numeric_limits<uint32_t>::max()) {
+ return false;
+ }
- outValue->dataType = android::Res_value::TYPE_STRING;
- outValue->data = util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
- return true;
+ outValue->dataType = android::Res_value::TYPE_STRING;
+ outValue->data =
+ util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
+ return true;
}
String* String::clone(StringPool* newPool) const {
- String* str = new String(newPool->makeRef(*value));
- str->mComment = mComment;
- str->mSource = mSource;
- return str;
+ String* str = new String(newPool->makeRef(*value));
+ str->mComment = mComment;
+ str->mSource = mSource;
+ return str;
}
void String::print(std::ostream* out) const {
- *out << "(string) \"" << *value << "\"";
+ *out << "(string) \"" << *value << "\"";
}
-StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref) {
-}
+StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref) {}
bool StyledString::equals(const Value* value) const {
- const StyledString* other = valueCast<StyledString>(value);
- if (!other) {
- return false;
- }
-
- if (*this->value->str == *other->value->str) {
- const std::vector<StringPool::Span>& spansA = this->value->spans;
- const std::vector<StringPool::Span>& spansB = other->value->spans;
- return std::equal(spansA.begin(), spansA.end(), spansB.begin(),
- [](const StringPool::Span& a, const StringPool::Span& b) -> bool {
- return *a.name == *b.name && a.firstChar == b.firstChar && a.lastChar == b.lastChar;
- });
- }
+ const StyledString* other = valueCast<StyledString>(value);
+ if (!other) {
return false;
+ }
+
+ if (*this->value->str == *other->value->str) {
+ const std::vector<StringPool::Span>& spansA = this->value->spans;
+ const std::vector<StringPool::Span>& spansB = other->value->spans;
+ return std::equal(
+ spansA.begin(), spansA.end(), spansB.begin(),
+ [](const StringPool::Span& a, const StringPool::Span& b) -> bool {
+ return *a.name == *b.name && a.firstChar == b.firstChar &&
+ a.lastChar == b.lastChar;
+ });
+ }
+ return false;
}
bool StyledString::flatten(android::Res_value* outValue) const {
- if (value.getIndex() > std::numeric_limits<uint32_t>::max()) {
- return false;
- }
+ if (value.getIndex() > std::numeric_limits<uint32_t>::max()) {
+ return false;
+ }
- outValue->dataType = android::Res_value::TYPE_STRING;
- outValue->data = util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
- return true;
+ outValue->dataType = android::Res_value::TYPE_STRING;
+ outValue->data =
+ util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
+ return true;
}
StyledString* StyledString::clone(StringPool* newPool) const {
- StyledString* str = new StyledString(newPool->makeRef(value));
- str->mComment = mComment;
- str->mSource = mSource;
- return str;
+ StyledString* str = new StyledString(newPool->makeRef(value));
+ str->mComment = mComment;
+ str->mSource = mSource;
+ return str;
}
void StyledString::print(std::ostream* out) const {
- *out << "(styled string) \"" << *value->str << "\"";
- for (const StringPool::Span& span : value->spans) {
- *out << " "<< *span.name << ":" << span.firstChar << "," << span.lastChar;
- }
+ *out << "(styled string) \"" << *value->str << "\"";
+ for (const StringPool::Span& span : value->spans) {
+ *out << " " << *span.name << ":" << span.firstChar << "," << span.lastChar;
+ }
}
-FileReference::FileReference(const StringPool::Ref& _path) : path(_path) {
-}
+FileReference::FileReference(const StringPool::Ref& _path) : path(_path) {}
bool FileReference::equals(const Value* value) const {
- const FileReference* other = valueCast<FileReference>(value);
- if (!other) {
- return false;
- }
- return *path == *other->path;
+ const FileReference* other = valueCast<FileReference>(value);
+ if (!other) {
+ return false;
+ }
+ return *path == *other->path;
}
bool FileReference::flatten(android::Res_value* outValue) const {
- if (path.getIndex() > std::numeric_limits<uint32_t>::max()) {
- return false;
- }
+ if (path.getIndex() > std::numeric_limits<uint32_t>::max()) {
+ return false;
+ }
- outValue->dataType = android::Res_value::TYPE_STRING;
- outValue->data = util::hostToDevice32(static_cast<uint32_t>(path.getIndex()));
- return true;
+ outValue->dataType = android::Res_value::TYPE_STRING;
+ outValue->data = util::hostToDevice32(static_cast<uint32_t>(path.getIndex()));
+ return true;
}
FileReference* FileReference::clone(StringPool* newPool) const {
- FileReference* fr = new FileReference(newPool->makeRef(*path));
- fr->file = file;
- fr->mComment = mComment;
- fr->mSource = mSource;
- return fr;
+ FileReference* fr = new FileReference(newPool->makeRef(*path));
+ fr->file = file;
+ fr->mComment = mComment;
+ fr->mSource = mSource;
+ return fr;
}
void FileReference::print(std::ostream* out) const {
- *out << "(file) " << *path;
+ *out << "(file) " << *path;
}
-BinaryPrimitive::BinaryPrimitive(const android::Res_value& val) : value(val) {
-}
+BinaryPrimitive::BinaryPrimitive(const android::Res_value& val) : value(val) {}
BinaryPrimitive::BinaryPrimitive(uint8_t dataType, uint32_t data) {
- value.dataType = dataType;
- value.data = data;
+ value.dataType = dataType;
+ value.data = data;
}
bool BinaryPrimitive::equals(const Value* value) const {
- const BinaryPrimitive* other = valueCast<BinaryPrimitive>(value);
- if (!other) {
- return false;
- }
- return this->value.dataType == other->value.dataType && this->value.data == other->value.data;
+ const BinaryPrimitive* other = valueCast<BinaryPrimitive>(value);
+ if (!other) {
+ return false;
+ }
+ return this->value.dataType == other->value.dataType &&
+ this->value.data == other->value.data;
}
bool BinaryPrimitive::flatten(android::Res_value* outValue) const {
- outValue->dataType = value.dataType;
- outValue->data = util::hostToDevice32(value.data);
- return true;
+ outValue->dataType = value.dataType;
+ outValue->data = util::hostToDevice32(value.data);
+ return true;
}
BinaryPrimitive* BinaryPrimitive::clone(StringPool* /*newPool*/) const {
- return new BinaryPrimitive(*this);
+ return new BinaryPrimitive(*this);
}
void BinaryPrimitive::print(std::ostream* out) const {
- switch (value.dataType) {
- case android::Res_value::TYPE_NULL:
- *out << "(null)";
- break;
- case android::Res_value::TYPE_INT_DEC:
- *out << "(integer) " << static_cast<int32_t>(value.data);
- break;
- case android::Res_value::TYPE_INT_HEX:
- *out << "(integer) 0x" << std::hex << value.data << std::dec;
- break;
- case android::Res_value::TYPE_INT_BOOLEAN:
- *out << "(boolean) " << (value.data != 0 ? "true" : "false");
- break;
- case android::Res_value::TYPE_INT_COLOR_ARGB8:
- case android::Res_value::TYPE_INT_COLOR_RGB8:
- case android::Res_value::TYPE_INT_COLOR_ARGB4:
- case android::Res_value::TYPE_INT_COLOR_RGB4:
- *out << "(color) #" << std::hex << value.data << std::dec;
- break;
- default:
- *out << "(unknown 0x" << std::hex << (int) value.dataType << ") 0x"
- << std::hex << value.data << std::dec;
- break;
- }
+ switch (value.dataType) {
+ case android::Res_value::TYPE_NULL:
+ *out << "(null)";
+ break;
+ case android::Res_value::TYPE_INT_DEC:
+ *out << "(integer) " << static_cast<int32_t>(value.data);
+ break;
+ case android::Res_value::TYPE_INT_HEX:
+ *out << "(integer) 0x" << std::hex << value.data << std::dec;
+ break;
+ case android::Res_value::TYPE_INT_BOOLEAN:
+ *out << "(boolean) " << (value.data != 0 ? "true" : "false");
+ break;
+ case android::Res_value::TYPE_INT_COLOR_ARGB8:
+ case android::Res_value::TYPE_INT_COLOR_RGB8:
+ case android::Res_value::TYPE_INT_COLOR_ARGB4:
+ case android::Res_value::TYPE_INT_COLOR_RGB4:
+ *out << "(color) #" << std::hex << value.data << std::dec;
+ break;
+ default:
+ *out << "(unknown 0x" << std::hex << (int)value.dataType << ") 0x"
+ << std::hex << value.data << std::dec;
+ break;
+ }
}
-Attribute::Attribute(bool w, uint32_t t) :
- typeMask(t),
- minInt(std::numeric_limits<int32_t>::min()),
- maxInt(std::numeric_limits<int32_t>::max()) {
- mWeak = w;
+Attribute::Attribute(bool w, uint32_t t)
+ : typeMask(t),
+ minInt(std::numeric_limits<int32_t>::min()),
+ maxInt(std::numeric_limits<int32_t>::max()) {
+ mWeak = w;
}
template <typename T>
T* addPointer(T& val) {
- return &val;
+ return &val;
}
bool Attribute::equals(const Value* value) const {
- const Attribute* other = valueCast<Attribute>(value);
- if (!other) {
- return false;
- }
+ const Attribute* other = valueCast<Attribute>(value);
+ if (!other) {
+ return false;
+ }
- if (symbols.size() != other->symbols.size()) {
- return false;
- }
+ if (symbols.size() != other->symbols.size()) {
+ return false;
+ }
- if (typeMask != other->typeMask || minInt != other->minInt || maxInt != other->maxInt) {
- 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*> 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;
- });
+ 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;
- });
+ 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;
+ });
}
Attribute* Attribute::clone(StringPool* /*newPool*/) const {
- return new Attribute(*this);
+ return new Attribute(*this);
}
void Attribute::printMask(std::ostream* out) const {
- if (typeMask == android::ResTable_map::TYPE_ANY) {
- *out << "any";
- return;
- }
+ if (typeMask == android::ResTable_map::TYPE_ANY) {
+ *out << "any";
+ return;
+ }
- bool set = false;
- if ((typeMask & android::ResTable_map::TYPE_REFERENCE) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "reference";
+ bool set = false;
+ if ((typeMask & android::ResTable_map::TYPE_REFERENCE) != 0) {
+ if (!set) {
+ set = true;
+ } else {
+ *out << "|";
}
+ *out << "reference";
+ }
- if ((typeMask & android::ResTable_map::TYPE_STRING) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "string";
+ if ((typeMask & android::ResTable_map::TYPE_STRING) != 0) {
+ if (!set) {
+ set = true;
+ } else {
+ *out << "|";
}
+ *out << "string";
+ }
- if ((typeMask & android::ResTable_map::TYPE_INTEGER) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "integer";
+ if ((typeMask & android::ResTable_map::TYPE_INTEGER) != 0) {
+ if (!set) {
+ set = true;
+ } else {
+ *out << "|";
}
+ *out << "integer";
+ }
- if ((typeMask & android::ResTable_map::TYPE_BOOLEAN) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "boolean";
+ if ((typeMask & android::ResTable_map::TYPE_BOOLEAN) != 0) {
+ if (!set) {
+ set = true;
+ } else {
+ *out << "|";
}
+ *out << "boolean";
+ }
- if ((typeMask & android::ResTable_map::TYPE_COLOR) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "color";
+ if ((typeMask & android::ResTable_map::TYPE_COLOR) != 0) {
+ if (!set) {
+ set = true;
+ } else {
+ *out << "|";
}
+ *out << "color";
+ }
- if ((typeMask & android::ResTable_map::TYPE_FLOAT) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "float";
+ if ((typeMask & android::ResTable_map::TYPE_FLOAT) != 0) {
+ if (!set) {
+ set = true;
+ } else {
+ *out << "|";
}
+ *out << "float";
+ }
- if ((typeMask & android::ResTable_map::TYPE_DIMENSION) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "dimension";
+ if ((typeMask & android::ResTable_map::TYPE_DIMENSION) != 0) {
+ if (!set) {
+ set = true;
+ } else {
+ *out << "|";
}
+ *out << "dimension";
+ }
- if ((typeMask & android::ResTable_map::TYPE_FRACTION) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "fraction";
+ if ((typeMask & android::ResTable_map::TYPE_FRACTION) != 0) {
+ if (!set) {
+ set = true;
+ } else {
+ *out << "|";
}
+ *out << "fraction";
+ }
- if ((typeMask & android::ResTable_map::TYPE_ENUM) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "enum";
+ if ((typeMask & android::ResTable_map::TYPE_ENUM) != 0) {
+ if (!set) {
+ set = true;
+ } else {
+ *out << "|";
}
+ *out << "enum";
+ }
- if ((typeMask & android::ResTable_map::TYPE_FLAGS) != 0) {
- if (!set) {
- set = true;
- } else {
- *out << "|";
- }
- *out << "flags";
+ if ((typeMask & android::ResTable_map::TYPE_FLAGS) != 0) {
+ if (!set) {
+ set = true;
+ } else {
+ *out << "|";
}
+ *out << "flags";
+ }
}
void Attribute::print(std::ostream* out) const {
- *out << "(attr) ";
- printMask(out);
+ *out << "(attr) ";
+ printMask(out);
- if (!symbols.empty()) {
- *out << " [" << util::joiner(symbols, ", ") << "]";
- }
+ if (!symbols.empty()) {
+ *out << " [" << util::joiner(symbols, ", ") << "]";
+ }
- if (minInt != std::numeric_limits<int32_t>::min()) {
- *out << " min=" << minInt;
- }
+ if (minInt != std::numeric_limits<int32_t>::min()) {
+ *out << " min=" << minInt;
+ }
- if (maxInt != std::numeric_limits<int32_t>::max()) {
- *out << " max=" << maxInt;
- }
+ if (maxInt != std::numeric_limits<int32_t>::max()) {
+ *out << " max=" << maxInt;
+ }
- if (isWeak()) {
- *out << " [weak]";
- }
+ if (isWeak()) {
+ *out << " [weak]";
+ }
}
-static void buildAttributeMismatchMessage(DiagMessage* msg, const Attribute* attr,
+static void buildAttributeMismatchMessage(DiagMessage* msg,
+ const Attribute* attr,
const Item* value) {
- *msg << "expected";
- if (attr->typeMask & android::ResTable_map::TYPE_BOOLEAN) {
- *msg << " boolean";
- }
+ *msg << "expected";
+ if (attr->typeMask & android::ResTable_map::TYPE_BOOLEAN) {
+ *msg << " boolean";
+ }
- if (attr->typeMask & android::ResTable_map::TYPE_COLOR) {
- *msg << " color";
- }
+ if (attr->typeMask & android::ResTable_map::TYPE_COLOR) {
+ *msg << " color";
+ }
- if (attr->typeMask & android::ResTable_map::TYPE_DIMENSION) {
- *msg << " dimension";
- }
+ if (attr->typeMask & android::ResTable_map::TYPE_DIMENSION) {
+ *msg << " dimension";
+ }
- if (attr->typeMask & android::ResTable_map::TYPE_ENUM) {
- *msg << " enum";
- }
+ if (attr->typeMask & android::ResTable_map::TYPE_ENUM) {
+ *msg << " enum";
+ }
- if (attr->typeMask & android::ResTable_map::TYPE_FLAGS) {
- *msg << " flags";
- }
+ if (attr->typeMask & android::ResTable_map::TYPE_FLAGS) {
+ *msg << " flags";
+ }
- if (attr->typeMask & android::ResTable_map::TYPE_FLOAT) {
- *msg << " float";
- }
+ if (attr->typeMask & android::ResTable_map::TYPE_FLOAT) {
+ *msg << " float";
+ }
- if (attr->typeMask & android::ResTable_map::TYPE_FRACTION) {
- *msg << " fraction";
- }
+ if (attr->typeMask & android::ResTable_map::TYPE_FRACTION) {
+ *msg << " fraction";
+ }
- if (attr->typeMask & android::ResTable_map::TYPE_INTEGER) {
- *msg << " integer";
- }
+ if (attr->typeMask & android::ResTable_map::TYPE_INTEGER) {
+ *msg << " integer";
+ }
- if (attr->typeMask & android::ResTable_map::TYPE_REFERENCE) {
- *msg << " reference";
- }
+ if (attr->typeMask & android::ResTable_map::TYPE_REFERENCE) {
+ *msg << " reference";
+ }
- if (attr->typeMask & android::ResTable_map::TYPE_STRING) {
- *msg << " string";
- }
+ if (attr->typeMask & android::ResTable_map::TYPE_STRING) {
+ *msg << " string";
+ }
- *msg << " but got " << *value;
+ *msg << " but got " << *value;
}
bool Attribute::matches(const Item* item, DiagMessage* outMsg) const {
- android::Res_value val = {};
- item->flatten(&val);
+ android::Res_value val = {};
+ item->flatten(&val);
- // Always allow references.
- const uint32_t mask = typeMask | android::ResTable_map::TYPE_REFERENCE;
- if (!(mask & ResourceUtils::androidTypeToAttributeTypeMask(val.dataType))) {
- if (outMsg) {
- buildAttributeMismatchMessage(outMsg, this, item);
- }
- return false;
-
- } else if (ResourceUtils::androidTypeToAttributeTypeMask(val.dataType) &
- android::ResTable_map::TYPE_INTEGER) {
- if (static_cast<int32_t>(util::deviceToHost32(val.data)) < minInt) {
- if (outMsg) {
- *outMsg << *item << " is less than minimum integer " << minInt;
- }
- return false;
- } else if (static_cast<int32_t>(util::deviceToHost32(val.data)) > maxInt) {
- if (outMsg) {
- *outMsg << *item << " is greater than maximum integer " << maxInt;
- }
- return false;
- }
+ // Always allow references.
+ const uint32_t mask = typeMask | android::ResTable_map::TYPE_REFERENCE;
+ if (!(mask & ResourceUtils::androidTypeToAttributeTypeMask(val.dataType))) {
+ if (outMsg) {
+ buildAttributeMismatchMessage(outMsg, this, item);
}
- return true;
+ return false;
+
+ } else if (ResourceUtils::androidTypeToAttributeTypeMask(val.dataType) &
+ android::ResTable_map::TYPE_INTEGER) {
+ if (static_cast<int32_t>(util::deviceToHost32(val.data)) < minInt) {
+ if (outMsg) {
+ *outMsg << *item << " is less than minimum integer " << minInt;
+ }
+ return false;
+ } else if (static_cast<int32_t>(util::deviceToHost32(val.data)) > maxInt) {
+ if (outMsg) {
+ *outMsg << *item << " is greater than maximum integer " << maxInt;
+ }
+ return false;
+ }
+ }
+ return true;
}
bool Style::equals(const Value* value) const {
- const Style* other = valueCast<Style>(value);
- if (!other) {
- return false;
- }
- if (bool(parent) != bool(other->parent) ||
- (parent && other->parent && !parent.value().equals(&other->parent.value()))) {
- return false;
- }
+ const Style* other = valueCast<Style>(value);
+ if (!other) {
+ return false;
+ }
+ if (bool(parent) != bool(other->parent) ||
+ (parent && other->parent &&
+ !parent.value().equals(&other->parent.value()))) {
+ return false;
+ }
- if (entries.size() != other->entries.size()) {
- return false;
- }
+ 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*> 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;
- });
+ 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());
- });
+ 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());
+ });
}
Style* Style::clone(StringPool* newPool) const {
- Style* style = new Style();
- style->parent = parent;
- style->parentInferred = parentInferred;
- style->mComment = mComment;
- style->mSource = mSource;
- for (auto& entry : entries) {
- style->entries.push_back(Entry{
- entry.key,
- std::unique_ptr<Item>(entry.value->clone(newPool))
- });
- }
- return style;
+ Style* style = new Style();
+ style->parent = parent;
+ style->parentInferred = parentInferred;
+ style->mComment = mComment;
+ style->mSource = mSource;
+ for (auto& entry : entries) {
+ style->entries.push_back(
+ Entry{entry.key, std::unique_ptr<Item>(entry.value->clone(newPool))});
+ }
+ return style;
}
void Style::print(std::ostream* out) const {
- *out << "(style) ";
- if (parent && parent.value().name) {
- if (parent.value().privateReference) {
- *out << "*";
- }
- *out << parent.value().name.value();
+ *out << "(style) ";
+ if (parent && parent.value().name) {
+ if (parent.value().privateReference) {
+ *out << "*";
}
- *out << " ["
- << util::joiner(entries, ", ")
- << "]";
+ *out << parent.value().name.value();
+ }
+ *out << " [" << util::joiner(entries, ", ") << "]";
}
-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 << "???";
- }
- out << " = ";
- value.value->print(&out);
- return out;
+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 << "???";
+ }
+ out << " = ";
+ value.value->print(&out);
+ return out;
}
bool Array::equals(const Value* value) const {
- const Array* other = valueCast<Array>(value);
- if (!other) {
- return false;
- }
+ const Array* other = valueCast<Array>(value);
+ if (!other) {
+ return false;
+ }
- if (items.size() != other->items.size()) {
- 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());
- });
+ 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());
+ });
}
Array* Array::clone(StringPool* newPool) const {
- Array* array = new Array();
- array->mComment = mComment;
- array->mSource = mSource;
- for (auto& item : items) {
- array->items.emplace_back(std::unique_ptr<Item>(item->clone(newPool)));
- }
- return array;
+ Array* array = new Array();
+ array->mComment = mComment;
+ array->mSource = mSource;
+ for (auto& item : items) {
+ array->items.emplace_back(std::unique_ptr<Item>(item->clone(newPool)));
+ }
+ return array;
}
void Array::print(std::ostream* out) const {
- *out << "(array) ["
- << util::joiner(items, ", ")
- << "]";
+ *out << "(array) [" << util::joiner(items, ", ") << "]";
}
bool Plural::equals(const Value* value) const {
- const Plural* other = valueCast<Plural>(value);
- if (!other) {
- return false;
- }
+ const Plural* other = valueCast<Plural>(value);
+ if (!other) {
+ return false;
+ }
- if (values.size() != other->values.size()) {
- 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)) {
- return false;
- }
- return bool(a) == bool(b) || a->equals(b.get());
- });
+ 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)) {
+ return false;
+ }
+ return bool(a) == bool(b) || a->equals(b.get());
+ });
}
Plural* Plural::clone(StringPool* newPool) const {
- Plural* p = new Plural();
- p->mComment = mComment;
- p->mSource = mSource;
- const size_t count = values.size();
- for (size_t i = 0; i < count; i++) {
- if (values[i]) {
- p->values[i] = std::unique_ptr<Item>(values[i]->clone(newPool));
- }
+ Plural* p = new Plural();
+ p->mComment = mComment;
+ p->mSource = mSource;
+ const size_t count = values.size();
+ for (size_t i = 0; i < count; i++) {
+ if (values[i]) {
+ p->values[i] = std::unique_ptr<Item>(values[i]->clone(newPool));
}
- return p;
+ }
+ return p;
}
void Plural::print(std::ostream* out) const {
- *out << "(plural)";
- if (values[Zero]) {
- *out << " zero=" << *values[Zero];
- }
+ *out << "(plural)";
+ if (values[Zero]) {
+ *out << " zero=" << *values[Zero];
+ }
- if (values[One]) {
- *out << " one=" << *values[One];
- }
+ if (values[One]) {
+ *out << " one=" << *values[One];
+ }
- if (values[Two]) {
- *out << " two=" << *values[Two];
- }
+ if (values[Two]) {
+ *out << " two=" << *values[Two];
+ }
- if (values[Few]) {
- *out << " few=" << *values[Few];
- }
+ if (values[Few]) {
+ *out << " few=" << *values[Few];
+ }
- if (values[Many]) {
- *out << " many=" << *values[Many];
- }
+ if (values[Many]) {
+ *out << " many=" << *values[Many];
+ }
}
-static ::std::ostream& operator<<(::std::ostream& out, const std::unique_ptr<Item>& item) {
- return out << *item;
+static ::std::ostream& operator<<(::std::ostream& out,
+ const std::unique_ptr<Item>& item) {
+ return out << *item;
}
bool Styleable::equals(const Value* value) const {
- const Styleable* other = valueCast<Styleable>(value);
- if (!other) {
- return false;
- }
+ const Styleable* other = valueCast<Styleable>(value);
+ if (!other) {
+ return false;
+ }
- if (entries.size() != other->entries.size()) {
- 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);
- });
+ return std::equal(entries.begin(), entries.end(), other->entries.begin(),
+ [](const Reference& a, const Reference& b) -> bool {
+ return a.equals(&b);
+ });
}
Styleable* Styleable::clone(StringPool* /*newPool*/) const {
- return new Styleable(*this);
+ return new Styleable(*this);
}
void Styleable::print(std::ostream* out) const {
- *out << "(styleable) " << " ["
- << util::joiner(entries, ", ")
- << "]";
+ *out << "(styleable) "
+ << " [" << util::joiner(entries, ", ") << "]";
}
bool operator<(const Reference& a, const Reference& b) {
- int cmp = a.name.valueOrDefault({}).compare(b.name.valueOrDefault({}));
- if (cmp != 0) return cmp < 0;
- return a.id < b.id;
+ int cmp = a.name.valueOrDefault({}).compare(b.name.valueOrDefault({}));
+ if (cmp != 0) return cmp < 0;
+ return a.id < b.id;
}
bool operator==(const Reference& a, const Reference& b) {
- return a.name == b.name && a.id == b.id;
+ return a.name == b.name && a.id == b.id;
}
bool operator!=(const Reference& a, const Reference& b) {
- return a.name != b.name || a.id != b.id;
+ return a.name != b.name || a.id != b.id;
}
struct NameOnlyComparator {
- bool operator()(const Reference& a, const Reference& b) const {
- return a.name < b.name;
- }
+ bool operator()(const Reference& a, const Reference& b) const {
+ return a.name < b.name;
+ }
};
void Styleable::mergeWith(Styleable* other) {
- // Compare only names, because some References may already have their IDs assigned
- // (framework IDs that don't change).
- std::set<Reference, NameOnlyComparator> references;
- references.insert(entries.begin(), entries.end());
- references.insert(other->entries.begin(), other->entries.end());
- entries.clear();
- entries.reserve(references.size());
- entries.insert(entries.end(), references.begin(), references.end());
+ // Compare only names, because some References may already have their IDs
+ // assigned
+ // (framework IDs that don't change).
+ std::set<Reference, NameOnlyComparator> references;
+ references.insert(entries.begin(), entries.end());
+ references.insert(other->entries.begin(), other->entries.end());
+ entries.clear();
+ entries.reserve(references.size());
+ entries.insert(entries.end(), references.begin(), references.end());
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 5e5d1f3..a28ffe5 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -23,8 +23,8 @@
#include "io/File.h"
#include "util/Maybe.h"
-#include <array>
#include <androidfw/ResourceTypes.h>
+#include <array>
#include <ostream>
#include <vector>
@@ -40,84 +40,64 @@
* but it is the simplest strategy.
*/
struct Value {
- virtual ~Value() = default;
+ virtual ~Value() = default;
- /**
- * Whether this value is weak and can be overridden without
- * warning or error. Default is false.
- */
- bool isWeak() const {
- return mWeak;
- }
+ /**
+ * Whether this value is weak and can be overridden without
+ * warning or error. Default is false.
+ */
+ bool isWeak() const { return mWeak; }
- void setWeak(bool val) {
- mWeak = val;
- }
+ void setWeak(bool val) { mWeak = val; }
- // Whether the value is marked as translateable.
- // This does not persist when flattened.
- // It is only used during compilation phase.
- void setTranslateable(bool val) {
- mTranslateable = val;
- }
+ // Whether the value is marked as translateable.
+ // This does not persist when flattened.
+ // It is only used during compilation phase.
+ void setTranslateable(bool val) { mTranslateable = val; }
- // Default true.
- bool isTranslateable() const {
- return mTranslateable;
- }
+ // Default true.
+ bool isTranslateable() const { return mTranslateable; }
- /**
- * Returns the source where this value was defined.
- */
- const Source& getSource() const {
- return mSource;
- }
+ /**
+ * Returns the source where this value was defined.
+ */
+ const Source& getSource() const { return mSource; }
- void setSource(const Source& source) {
- mSource = source;
- }
+ void setSource(const Source& source) { mSource = source; }
- void setSource(Source&& source) {
- mSource = std::move(source);
- }
+ void setSource(Source&& source) { mSource = std::move(source); }
- /**
- * Returns the comment that was associated with this resource.
- */
- const std::string& getComment() const {
- return mComment;
- }
+ /**
+ * Returns the comment that was associated with this resource.
+ */
+ const std::string& getComment() const { return mComment; }
- void setComment(const StringPiece& str) {
- mComment = str.toString();
- }
+ void setComment(const StringPiece& str) { mComment = str.toString(); }
- void setComment(std::string&& str) {
- mComment = std::move(str);
- }
+ void setComment(std::string&& str) { mComment = std::move(str); }
- virtual bool equals(const Value* value) const = 0;
+ virtual bool equals(const Value* value) const = 0;
- /**
- * Calls the appropriate overload of ValueVisitor.
- */
- virtual void accept(RawValueVisitor* visitor) = 0;
+ /**
+ * Calls the appropriate overload of ValueVisitor.
+ */
+ virtual void accept(RawValueVisitor* visitor) = 0;
- /**
- * Clone the value.
- */
- virtual Value* clone(StringPool* newPool) const = 0;
+ /**
+ * Clone the value.
+ */
+ virtual Value* clone(StringPool* newPool) const = 0;
- /**
- * Human readable printout of this value.
- */
- virtual void print(std::ostream* out) const = 0;
+ /**
+ * Human readable printout of this value.
+ */
+ virtual void print(std::ostream* out) const = 0;
-protected:
- Source mSource;
- std::string mComment;
- bool mWeak = false;
- bool mTranslateable = true;
+ protected:
+ Source mSource;
+ std::string mComment;
+ bool mWeak = false;
+ bool mTranslateable = true;
};
/**
@@ -125,23 +105,24 @@
*/
template <typename Derived>
struct BaseValue : public Value {
- void accept(RawValueVisitor* visitor) override;
+ void accept(RawValueVisitor* visitor) override;
};
/**
* A resource item with a single value. This maps to android::ResTable_entry.
*/
struct Item : public Value {
- /**
- * Clone the Item.
- */
- virtual Item* clone(StringPool* newPool) const override = 0;
+ /**
+ * Clone the Item.
+ */
+ virtual Item* clone(StringPool* newPool) const override = 0;
- /**
- * Fills in an android::Res_value structure with this Item's binary representation.
- * Returns false if an error occurred.
- */
- virtual bool flatten(android::Res_value* outValue) const = 0;
+ /**
+ * Fills in an android::Res_value structure with this Item's binary
+ * representation.
+ * Returns false if an error occurred.
+ */
+ virtual bool flatten(android::Res_value* outValue) const = 0;
};
/**
@@ -149,35 +130,37 @@
*/
template <typename Derived>
struct BaseItem : public Item {
- void accept(RawValueVisitor* visitor) override;
+ void accept(RawValueVisitor* visitor) override;
};
/**
- * A reference to another resource. This maps to android::Res_value::TYPE_REFERENCE.
+ * A reference to another resource. This maps to
+ * android::Res_value::TYPE_REFERENCE.
*
- * A reference can be symbolic (with the name set to a valid resource name) or be
+ * A reference can be symbolic (with the name set to a valid resource name) or
+ * be
* numeric (the id is set to a valid resource ID).
*/
struct Reference : public BaseItem<Reference> {
- enum class Type {
- kResource,
- kAttribute,
- };
+ enum class Type {
+ kResource,
+ kAttribute,
+ };
- Maybe<ResourceName> name;
- Maybe<ResourceId> id;
- Reference::Type referenceType;
- bool privateReference = false;
+ Maybe<ResourceName> name;
+ Maybe<ResourceId> id;
+ Reference::Type referenceType;
+ bool privateReference = false;
- Reference();
- explicit Reference(const ResourceNameRef& n, Type type = Type::kResource);
- explicit Reference(const ResourceId& i, Type type = Type::kResource);
- explicit Reference(const ResourceNameRef& n, const ResourceId& i);
+ Reference();
+ explicit Reference(const ResourceNameRef& n, Type type = Type::kResource);
+ explicit Reference(const ResourceId& i, Type type = Type::kResource);
+ explicit Reference(const ResourceNameRef& n, const ResourceId& i);
- bool equals(const Value* value) const override;
- bool flatten(android::Res_value* outValue) const override;
- Reference* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ bool equals(const Value* value) const override;
+ bool flatten(android::Res_value* outValue) const override;
+ Reference* clone(StringPool* newPool) const override;
+ void print(std::ostream* out) const override;
};
bool operator<(const Reference&, const Reference&);
@@ -187,11 +170,11 @@
* An ID resource. Has no real value, just a place holder.
*/
struct Id : public BaseItem<Id> {
- Id() { mWeak = true; }
- bool equals(const Value* value) const override;
- bool flatten(android::Res_value* out) const override;
- Id* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ Id() { mWeak = true; }
+ bool equals(const Value* value) const override;
+ bool flatten(android::Res_value* out) const override;
+ Id* clone(StringPool* newPool) const override;
+ void print(std::ostream* out) const override;
};
/**
@@ -200,164 +183,157 @@
* end up in the final resource table.
*/
struct RawString : public BaseItem<RawString> {
- StringPool::Ref value;
+ StringPool::Ref value;
- explicit RawString(const StringPool::Ref& ref);
+ explicit RawString(const StringPool::Ref& ref);
- bool equals(const Value* value) const override;
- bool flatten(android::Res_value* outValue) const override;
- RawString* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ bool equals(const Value* value) const override;
+ bool flatten(android::Res_value* outValue) const override;
+ RawString* clone(StringPool* newPool) const override;
+ void print(std::ostream* out) const override;
};
struct String : public BaseItem<String> {
- StringPool::Ref value;
+ StringPool::Ref value;
- explicit String(const StringPool::Ref& ref);
+ explicit String(const StringPool::Ref& ref);
- bool equals(const Value* value) const override;
- bool flatten(android::Res_value* outValue) const override;
- String* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ bool equals(const Value* value) const override;
+ bool flatten(android::Res_value* outValue) const override;
+ String* clone(StringPool* newPool) const override;
+ void print(std::ostream* out) const override;
};
struct StyledString : public BaseItem<StyledString> {
- StringPool::StyleRef value;
+ StringPool::StyleRef value;
- explicit StyledString(const StringPool::StyleRef& ref);
+ explicit StyledString(const StringPool::StyleRef& ref);
- bool equals(const Value* value) const override;
- bool flatten(android::Res_value* outValue) const override;
- StyledString* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ bool equals(const Value* value) const override;
+ bool flatten(android::Res_value* outValue) const override;
+ StyledString* clone(StringPool* newPool) const override;
+ void print(std::ostream* out) const override;
};
struct FileReference : public BaseItem<FileReference> {
- StringPool::Ref path;
+ StringPool::Ref path;
- /**
- * A handle to the file object from which this file can be read.
- */
- io::IFile* file = nullptr;
+ /**
+ * A handle to the file object from which this file can be read.
+ */
+ io::IFile* file = nullptr;
- FileReference() = default;
- explicit FileReference(const StringPool::Ref& path);
+ FileReference() = default;
+ explicit FileReference(const StringPool::Ref& path);
- bool equals(const Value* value) const override;
- bool flatten(android::Res_value* outValue) const override;
- FileReference* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ bool equals(const Value* value) const override;
+ bool flatten(android::Res_value* outValue) const override;
+ FileReference* clone(StringPool* newPool) const override;
+ void print(std::ostream* out) const override;
};
/**
* Represents any other android::Res_value.
*/
struct BinaryPrimitive : public BaseItem<BinaryPrimitive> {
- android::Res_value value;
+ android::Res_value value;
- BinaryPrimitive() = default;
- explicit BinaryPrimitive(const android::Res_value& val);
- BinaryPrimitive(uint8_t dataType, uint32_t data);
+ BinaryPrimitive() = default;
+ explicit BinaryPrimitive(const android::Res_value& val);
+ BinaryPrimitive(uint8_t dataType, uint32_t data);
- bool equals(const Value* value) const override;
- bool flatten(android::Res_value* outValue) const override;
- BinaryPrimitive* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ bool equals(const Value* value) const override;
+ bool flatten(android::Res_value* outValue) const override;
+ BinaryPrimitive* clone(StringPool* newPool) const override;
+ void print(std::ostream* out) const override;
};
struct Attribute : public BaseValue<Attribute> {
- struct Symbol {
- Reference symbol;
- uint32_t value;
- };
+ struct Symbol {
+ Reference symbol;
+ uint32_t value;
+ };
- uint32_t typeMask;
- int32_t minInt;
- int32_t maxInt;
- std::vector<Symbol> symbols;
+ uint32_t typeMask;
+ int32_t minInt;
+ int32_t maxInt;
+ std::vector<Symbol> symbols;
- explicit Attribute(bool w, uint32_t t = 0u);
+ explicit Attribute(bool w, uint32_t t = 0u);
- bool equals(const Value* value) const override;
- Attribute* clone(StringPool* newPool) const override;
- void printMask(std::ostream* out) const;
- void print(std::ostream* out) const override;
- bool matches(const Item* item, DiagMessage* outMsg) const;
+ bool equals(const Value* value) const override;
+ Attribute* clone(StringPool* newPool) const override;
+ void printMask(std::ostream* out) const;
+ void print(std::ostream* out) const override;
+ bool matches(const Item* item, DiagMessage* outMsg) const;
};
struct Style : public BaseValue<Style> {
- struct Entry {
- Reference key;
- std::unique_ptr<Item> value;
- };
+ struct Entry {
+ Reference key;
+ std::unique_ptr<Item> value;
+ };
- Maybe<Reference> parent;
+ Maybe<Reference> parent;
- /**
- * If set to true, the parent was auto inferred from the
- * style's name.
- */
- bool parentInferred = false;
+ /**
+ * If set to true, the parent was auto inferred from the
+ * style's name.
+ */
+ bool parentInferred = false;
- std::vector<Entry> entries;
+ std::vector<Entry> entries;
- bool equals(const Value* value) const override;
- Style* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ bool equals(const Value* value) const override;
+ Style* clone(StringPool* newPool) const override;
+ void print(std::ostream* out) const override;
};
struct Array : public BaseValue<Array> {
- std::vector<std::unique_ptr<Item>> items;
+ std::vector<std::unique_ptr<Item>> items;
- bool equals(const Value* value) const override;
- Array* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ bool equals(const Value* value) const override;
+ Array* clone(StringPool* newPool) const override;
+ void print(std::ostream* out) const override;
};
struct Plural : public BaseValue<Plural> {
- enum {
- Zero = 0,
- One,
- Two,
- Few,
- Many,
- Other,
- Count
- };
+ enum { Zero = 0, One, Two, Few, Many, Other, Count };
- std::array<std::unique_ptr<Item>, Count> values;
+ std::array<std::unique_ptr<Item>, Count> values;
- bool equals(const Value* value) const override;
- Plural* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
+ bool equals(const Value* value) const override;
+ Plural* clone(StringPool* newPool) const override;
+ void print(std::ostream* out) const override;
};
struct Styleable : public BaseValue<Styleable> {
- std::vector<Reference> entries;
+ std::vector<Reference> entries;
- bool equals(const Value* value) const override;
- Styleable* clone(StringPool* newPool) const override;
- void print(std::ostream* out) const override;
- void mergeWith(Styleable* styleable);
+ bool equals(const Value* value) const override;
+ Styleable* clone(StringPool* newPool) const override;
+ void print(std::ostream* out) const override;
+ void mergeWith(Styleable* styleable);
};
/**
* Stream operator for printing Value objects.
*/
inline ::std::ostream& operator<<(::std::ostream& out, const Value& value) {
- value.print(&out);
- return out;
+ value.print(&out);
+ return out;
}
-inline ::std::ostream& operator<<(::std::ostream& out, const Attribute::Symbol& s) {
- if (s.symbol.name) {
- out << s.symbol.name.value().entry;
- } else {
- out << "???";
- }
- return out << "=" << s.value;
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const Attribute::Symbol& s) {
+ if (s.symbol.name) {
+ out << s.symbol.name.value().entry;
+ } else {
+ out << "???";
+ }
+ return out << "=" << s.value;
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_RESOURCE_VALUES_H
+#endif // AAPT_RESOURCE_VALUES_H
diff --git a/tools/aapt2/Resource_test.cpp b/tools/aapt2/Resource_test.cpp
index 06cddc7..4b6b122 100644
--- a/tools/aapt2/Resource_test.cpp
+++ b/tools/aapt2/Resource_test.cpp
@@ -20,96 +20,96 @@
namespace aapt {
TEST(ResourceTypeTest, ParseResourceTypes) {
- const ResourceType* type = parseResourceType("anim");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kAnim);
+ const ResourceType* type = parseResourceType("anim");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kAnim);
- type = parseResourceType("animator");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kAnimator);
+ type = parseResourceType("animator");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kAnimator);
- type = parseResourceType("array");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kArray);
+ type = parseResourceType("array");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kArray);
- type = parseResourceType("attr");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kAttr);
+ type = parseResourceType("attr");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kAttr);
- type = parseResourceType("^attr-private");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kAttrPrivate);
+ type = parseResourceType("^attr-private");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kAttrPrivate);
- type = parseResourceType("bool");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kBool);
+ type = parseResourceType("bool");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kBool);
- type = parseResourceType("color");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kColor);
+ type = parseResourceType("color");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kColor);
- type = parseResourceType("dimen");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kDimen);
+ type = parseResourceType("dimen");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kDimen);
- type = parseResourceType("drawable");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kDrawable);
+ type = parseResourceType("drawable");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kDrawable);
- type = parseResourceType("fraction");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kFraction);
+ type = parseResourceType("fraction");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kFraction);
- type = parseResourceType("id");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kId);
+ type = parseResourceType("id");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kId);
- type = parseResourceType("integer");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kInteger);
+ type = parseResourceType("integer");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kInteger);
- type = parseResourceType("interpolator");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kInterpolator);
+ type = parseResourceType("interpolator");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kInterpolator);
- type = parseResourceType("layout");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kLayout);
+ type = parseResourceType("layout");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kLayout);
- type = parseResourceType("menu");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kMenu);
+ type = parseResourceType("menu");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kMenu);
- type = parseResourceType("mipmap");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kMipmap);
+ type = parseResourceType("mipmap");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kMipmap);
- type = parseResourceType("plurals");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kPlurals);
+ type = parseResourceType("plurals");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kPlurals);
- type = parseResourceType("raw");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kRaw);
+ type = parseResourceType("raw");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kRaw);
- type = parseResourceType("string");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kString);
+ type = parseResourceType("string");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kString);
- type = parseResourceType("style");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kStyle);
+ type = parseResourceType("style");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kStyle);
- type = parseResourceType("transition");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kTransition);
+ type = parseResourceType("transition");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kTransition);
- type = parseResourceType("xml");
- ASSERT_NE(type, nullptr);
- EXPECT_EQ(*type, ResourceType::kXml);
+ type = parseResourceType("xml");
+ ASSERT_NE(type, nullptr);
+ EXPECT_EQ(*type, ResourceType::kXml);
- type = parseResourceType("blahaha");
- EXPECT_EQ(type, nullptr);
+ type = parseResourceType("blahaha");
+ EXPECT_EQ(type, nullptr);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index ccf0383..75375da 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -27,719 +27,721 @@
static int sDevelopmentSdkLevel = 26;
static const std::vector<std::pair<uint16_t, size_t>> sAttrIdMap = {
- { 0x021c, 1 },
- { 0x021d, 2 },
- { 0x0269, SDK_CUPCAKE },
- { 0x028d, SDK_DONUT },
- { 0x02ad, SDK_ECLAIR },
- { 0x02b3, SDK_ECLAIR_0_1 },
- { 0x02b5, SDK_ECLAIR_MR1 },
- { 0x02bd, SDK_FROYO },
- { 0x02cb, SDK_GINGERBREAD },
- { 0x0361, SDK_HONEYCOMB },
- { 0x0363, SDK_HONEYCOMB_MR1 },
- { 0x0366, SDK_HONEYCOMB_MR2 },
- { 0x03a6, SDK_ICE_CREAM_SANDWICH },
- { 0x03ae, SDK_JELLY_BEAN },
- { 0x03cc, SDK_JELLY_BEAN_MR1 },
- { 0x03da, SDK_JELLY_BEAN_MR2 },
- { 0x03f1, SDK_KITKAT },
- { 0x03f6, SDK_KITKAT_WATCH },
- { 0x04ce, SDK_LOLLIPOP },
+ {0x021c, 1},
+ {0x021d, 2},
+ {0x0269, SDK_CUPCAKE},
+ {0x028d, SDK_DONUT},
+ {0x02ad, SDK_ECLAIR},
+ {0x02b3, SDK_ECLAIR_0_1},
+ {0x02b5, SDK_ECLAIR_MR1},
+ {0x02bd, SDK_FROYO},
+ {0x02cb, SDK_GINGERBREAD},
+ {0x0361, SDK_HONEYCOMB},
+ {0x0363, SDK_HONEYCOMB_MR1},
+ {0x0366, SDK_HONEYCOMB_MR2},
+ {0x03a6, SDK_ICE_CREAM_SANDWICH},
+ {0x03ae, SDK_JELLY_BEAN},
+ {0x03cc, SDK_JELLY_BEAN_MR1},
+ {0x03da, SDK_JELLY_BEAN_MR2},
+ {0x03f1, SDK_KITKAT},
+ {0x03f6, SDK_KITKAT_WATCH},
+ {0x04ce, SDK_LOLLIPOP},
};
-static bool lessEntryId(const std::pair<uint16_t, size_t>& p, uint16_t entryId) {
- return p.first < entryId;
+static bool lessEntryId(const std::pair<uint16_t, size_t>& p,
+ uint16_t entryId) {
+ return p.first < entryId;
}
size_t findAttributeSdkLevel(const ResourceId& id) {
- if (id.packageId() != 0x01 && id.typeId() != 0x01) {
- return 0;
- }
- auto iter = std::lower_bound(sAttrIdMap.begin(), sAttrIdMap.end(), id.entryId(), lessEntryId);
- if (iter == sAttrIdMap.end()) {
- return SDK_LOLLIPOP_MR1;
- }
- return iter->second;
+ if (id.packageId() != 0x01 && id.typeId() != 0x01) {
+ return 0;
+ }
+ auto iter = std::lower_bound(sAttrIdMap.begin(), sAttrIdMap.end(),
+ id.entryId(), lessEntryId);
+ if (iter == sAttrIdMap.end()) {
+ return SDK_LOLLIPOP_MR1;
+ }
+ return iter->second;
}
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 }
-};
+ {"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 != "android" && name.type != ResourceType::kAttr) {
- return 0;
- }
+ if (name.package != "android" && name.type != ResourceType::kAttr) {
+ return 0;
+ }
- auto iter = sAttrMap.find(name.entry);
- if (iter != sAttrMap.end()) {
- return iter->second;
- }
- return SDK_LOLLIPOP_MR1;
+ auto iter = sAttrMap.find(name.entry);
+ if (iter != sAttrMap.end()) {
+ return iter->second;
+ }
+ return SDK_LOLLIPOP_MR1;
}
std::pair<StringPiece, int> getDevelopmentSdkCodeNameAndVersion() {
- return std::make_pair(StringPiece(sDevelopmentSdkCodeName), sDevelopmentSdkLevel);
+ return std::make_pair(StringPiece(sDevelopmentSdkCodeName),
+ sDevelopmentSdkLevel);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index c9dbdca..bd17fe4 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -24,33 +24,33 @@
namespace aapt {
enum {
- SDK_CUPCAKE = 3,
- SDK_DONUT = 4,
- SDK_ECLAIR = 5,
- SDK_ECLAIR_0_1 = 6,
- SDK_ECLAIR_MR1 = 7,
- SDK_FROYO = 8,
- SDK_GINGERBREAD = 9,
- SDK_GINGERBREAD_MR1 = 10,
- SDK_HONEYCOMB = 11,
- SDK_HONEYCOMB_MR1 = 12,
- SDK_HONEYCOMB_MR2 = 13,
- SDK_ICE_CREAM_SANDWICH = 14,
- SDK_ICE_CREAM_SANDWICH_MR1 = 15,
- SDK_JELLY_BEAN = 16,
- SDK_JELLY_BEAN_MR1 = 17,
- SDK_JELLY_BEAN_MR2 = 18,
- SDK_KITKAT = 19,
- SDK_KITKAT_WATCH = 20,
- SDK_LOLLIPOP = 21,
- SDK_LOLLIPOP_MR1 = 22,
- SDK_MARSHMALLOW = 23,
+ SDK_CUPCAKE = 3,
+ SDK_DONUT = 4,
+ SDK_ECLAIR = 5,
+ SDK_ECLAIR_0_1 = 6,
+ SDK_ECLAIR_MR1 = 7,
+ SDK_FROYO = 8,
+ SDK_GINGERBREAD = 9,
+ SDK_GINGERBREAD_MR1 = 10,
+ SDK_HONEYCOMB = 11,
+ SDK_HONEYCOMB_MR1 = 12,
+ SDK_HONEYCOMB_MR2 = 13,
+ SDK_ICE_CREAM_SANDWICH = 14,
+ SDK_ICE_CREAM_SANDWICH_MR1 = 15,
+ SDK_JELLY_BEAN = 16,
+ SDK_JELLY_BEAN_MR1 = 17,
+ SDK_JELLY_BEAN_MR2 = 18,
+ SDK_KITKAT = 19,
+ SDK_KITKAT_WATCH = 20,
+ SDK_LOLLIPOP = 21,
+ SDK_LOLLIPOP_MR1 = 22,
+ SDK_MARSHMALLOW = 23,
};
size_t findAttributeSdkLevel(const ResourceId& id);
size_t findAttributeSdkLevel(const ResourceName& name);
std::pair<StringPiece, int> getDevelopmentSdkCodeNameAndVersion();
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_SDK_CONSTANTS_H
+#endif // AAPT_SDK_CONSTANTS_H
diff --git a/tools/aapt2/SdkConstants_test.cpp b/tools/aapt2/SdkConstants_test.cpp
index e81f412..3b70acb 100644
--- a/tools/aapt2/SdkConstants_test.cpp
+++ b/tools/aapt2/SdkConstants_test.cpp
@@ -21,18 +21,18 @@
namespace aapt {
TEST(SdkConstantsTest, FirstAttributeIsSdk1) {
- EXPECT_EQ(1u, findAttributeSdkLevel(ResourceId(0x01010000)));
+ EXPECT_EQ(1u, findAttributeSdkLevel(ResourceId(0x01010000)));
}
TEST(SdkConstantsTest, AllAttributesAfterLollipopAreLollipopMR1) {
- EXPECT_EQ(SDK_LOLLIPOP, findAttributeSdkLevel(ResourceId(0x010103f7)));
- EXPECT_EQ(SDK_LOLLIPOP, findAttributeSdkLevel(ResourceId(0x010104ce)));
+ EXPECT_EQ(SDK_LOLLIPOP, findAttributeSdkLevel(ResourceId(0x010103f7)));
+ EXPECT_EQ(SDK_LOLLIPOP, findAttributeSdkLevel(ResourceId(0x010104ce)));
- EXPECT_EQ(SDK_LOLLIPOP_MR1, findAttributeSdkLevel(ResourceId(0x010104cf)));
- EXPECT_EQ(SDK_LOLLIPOP_MR1, findAttributeSdkLevel(ResourceId(0x010104d8)));
+ EXPECT_EQ(SDK_LOLLIPOP_MR1, findAttributeSdkLevel(ResourceId(0x010104cf)));
+ EXPECT_EQ(SDK_LOLLIPOP_MR1, findAttributeSdkLevel(ResourceId(0x010104d8)));
- EXPECT_EQ(SDK_LOLLIPOP_MR1, findAttributeSdkLevel(ResourceId(0x010104d9)));
- EXPECT_EQ(SDK_LOLLIPOP_MR1, findAttributeSdkLevel(ResourceId(0x0101ffff)));
+ EXPECT_EQ(SDK_LOLLIPOP_MR1, findAttributeSdkLevel(ResourceId(0x010104d9)));
+ EXPECT_EQ(SDK_LOLLIPOP_MR1, findAttributeSdkLevel(ResourceId(0x0101ffff)));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/Source.h b/tools/aapt2/Source.h
index 8a1021d..422b361 100644
--- a/tools/aapt2/Source.h
+++ b/tools/aapt2/Source.h
@@ -30,20 +30,19 @@
* showing errors.
*/
struct Source {
- std::string path;
- Maybe<size_t> line;
+ std::string path;
+ Maybe<size_t> line;
- Source() = default;
+ Source() = default;
- inline Source(const StringPiece& path) : path(path.toString()) { // NOLINT(implicit)
- }
+ inline Source(const StringPiece& path)
+ : path(path.toString()) { // NOLINT(implicit)
+ }
- inline Source(const StringPiece& path, size_t line) : path(path.toString()), line(line) {
- }
+ inline Source(const StringPiece& path, size_t line)
+ : path(path.toString()), line(line) {}
- inline Source withLine(size_t line) const {
- return Source(path, line);
- }
+ inline Source withLine(size_t line) const { return Source(path, line); }
};
//
@@ -51,30 +50,30 @@
//
inline ::std::ostream& operator<<(::std::ostream& out, const Source& source) {
- out << source.path;
- if (source.line) {
- out << ":" << source.line.value();
- }
- return out;
+ out << source.path;
+ if (source.line) {
+ out << ":" << source.line.value();
+ }
+ return out;
}
inline bool operator==(const Source& lhs, const Source& rhs) {
- return lhs.path == rhs.path && lhs.line == rhs.line;
+ return lhs.path == rhs.path && lhs.line == rhs.line;
}
inline bool operator<(const Source& lhs, const Source& rhs) {
- int cmp = lhs.path.compare(rhs.path);
- if (cmp < 0) return true;
- if (cmp > 0) return false;
- if (lhs.line) {
- if (rhs.line) {
- return lhs.line.value() < rhs.line.value();
- }
- return false;
+ int cmp = lhs.path.compare(rhs.path);
+ if (cmp < 0) return true;
+ if (cmp > 0) return false;
+ if (lhs.line) {
+ if (rhs.line) {
+ return lhs.line.value() < rhs.line.value();
}
- return bool(rhs.line);
+ return false;
+ }
+ return bool(rhs.line);
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_SOURCE_H
+#endif // AAPT_SOURCE_H
diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp
index fe4b967..a167a6a 100644
--- a/tools/aapt2/StringPool.cpp
+++ b/tools/aapt2/StringPool.cpp
@@ -19,395 +19,400 @@
#include "util/StringPiece.h"
#include "util/Util.h"
-#include <algorithm>
#include <androidfw/ResourceTypes.h>
+#include <algorithm>
#include <memory>
#include <string>
namespace aapt {
-StringPool::Ref::Ref() : mEntry(nullptr) {
-}
+StringPool::Ref::Ref() : mEntry(nullptr) {}
StringPool::Ref::Ref(const StringPool::Ref& rhs) : mEntry(rhs.mEntry) {
- if (mEntry != nullptr) {
- mEntry->ref++;
- }
+ if (mEntry != nullptr) {
+ mEntry->ref++;
+ }
}
StringPool::Ref::Ref(StringPool::Entry* entry) : mEntry(entry) {
- if (mEntry != nullptr) {
- mEntry->ref++;
- }
+ if (mEntry != nullptr) {
+ mEntry->ref++;
+ }
}
StringPool::Ref::~Ref() {
- if (mEntry != nullptr) {
- mEntry->ref--;
- }
+ if (mEntry != nullptr) {
+ mEntry->ref--;
+ }
}
StringPool::Ref& StringPool::Ref::operator=(const StringPool::Ref& rhs) {
- if (rhs.mEntry != nullptr) {
- rhs.mEntry->ref++;
- }
+ if (rhs.mEntry != nullptr) {
+ rhs.mEntry->ref++;
+ }
- if (mEntry != nullptr) {
- mEntry->ref--;
- }
- mEntry = rhs.mEntry;
- return *this;
+ if (mEntry != nullptr) {
+ mEntry->ref--;
+ }
+ mEntry = rhs.mEntry;
+ return *this;
}
const std::string* StringPool::Ref::operator->() const {
- return &mEntry->value;
+ return &mEntry->value;
}
-const std::string& StringPool::Ref::operator*() const {
- return mEntry->value;
-}
+const std::string& StringPool::Ref::operator*() const { return mEntry->value; }
-size_t StringPool::Ref::getIndex() const {
- return mEntry->index;
-}
+size_t StringPool::Ref::getIndex() const { return mEntry->index; }
const StringPool::Context& StringPool::Ref::getContext() const {
- return mEntry->context;
+ return mEntry->context;
}
-StringPool::StyleRef::StyleRef() : mEntry(nullptr) {
-}
+StringPool::StyleRef::StyleRef() : mEntry(nullptr) {}
-StringPool::StyleRef::StyleRef(const StringPool::StyleRef& rhs) : mEntry(rhs.mEntry) {
- if (mEntry != nullptr) {
- mEntry->ref++;
- }
+StringPool::StyleRef::StyleRef(const StringPool::StyleRef& rhs)
+ : mEntry(rhs.mEntry) {
+ if (mEntry != nullptr) {
+ mEntry->ref++;
+ }
}
StringPool::StyleRef::StyleRef(StringPool::StyleEntry* entry) : mEntry(entry) {
- if (mEntry != nullptr) {
- mEntry->ref++;
- }
+ if (mEntry != nullptr) {
+ mEntry->ref++;
+ }
}
StringPool::StyleRef::~StyleRef() {
- if (mEntry != nullptr) {
- mEntry->ref--;
- }
+ if (mEntry != nullptr) {
+ mEntry->ref--;
+ }
}
-StringPool::StyleRef& StringPool::StyleRef::operator=(const StringPool::StyleRef& rhs) {
- if (rhs.mEntry != nullptr) {
- rhs.mEntry->ref++;
- }
+StringPool::StyleRef& StringPool::StyleRef::operator=(
+ const StringPool::StyleRef& rhs) {
+ if (rhs.mEntry != nullptr) {
+ rhs.mEntry->ref++;
+ }
- if (mEntry != nullptr) {
- mEntry->ref--;
- }
- mEntry = rhs.mEntry;
- return *this;
+ if (mEntry != nullptr) {
+ mEntry->ref--;
+ }
+ mEntry = rhs.mEntry;
+ return *this;
}
const StringPool::StyleEntry* StringPool::StyleRef::operator->() const {
- return mEntry;
+ return mEntry;
}
const StringPool::StyleEntry& StringPool::StyleRef::operator*() const {
- return *mEntry;
+ return *mEntry;
}
-size_t StringPool::StyleRef::getIndex() const {
- return mEntry->str.getIndex();
-}
+size_t StringPool::StyleRef::getIndex() const { return mEntry->str.getIndex(); }
const StringPool::Context& StringPool::StyleRef::getContext() const {
- return mEntry->str.getContext();
+ return mEntry->str.getContext();
}
StringPool::Ref StringPool::makeRef(const StringPiece& str) {
- return makeRefImpl(str, Context{}, true);
+ return makeRefImpl(str, Context{}, true);
}
-StringPool::Ref StringPool::makeRef(const StringPiece& str, const Context& context) {
- return makeRefImpl(str, context, true);
+StringPool::Ref StringPool::makeRef(const StringPiece& str,
+ const Context& context) {
+ return makeRefImpl(str, context, true);
}
-StringPool::Ref StringPool::makeRefImpl(const StringPiece& str, const Context& context,
- bool unique) {
- if (unique) {
- auto iter = mIndexedStrings.find(str);
- if (iter != std::end(mIndexedStrings)) {
- return Ref(iter->second);
- }
+StringPool::Ref StringPool::makeRefImpl(const StringPiece& str,
+ const Context& context, bool unique) {
+ if (unique) {
+ auto iter = mIndexedStrings.find(str);
+ if (iter != std::end(mIndexedStrings)) {
+ return Ref(iter->second);
}
+ }
- Entry* entry = new Entry();
- entry->value = str.toString();
- entry->context = context;
- entry->index = mStrings.size();
- entry->ref = 0;
- mStrings.emplace_back(entry);
- mIndexedStrings.insert(std::make_pair(StringPiece(entry->value), entry));
- return Ref(entry);
+ Entry* entry = new Entry();
+ entry->value = str.toString();
+ entry->context = context;
+ entry->index = mStrings.size();
+ entry->ref = 0;
+ mStrings.emplace_back(entry);
+ mIndexedStrings.insert(std::make_pair(StringPiece(entry->value), entry));
+ return Ref(entry);
}
StringPool::StyleRef StringPool::makeRef(const StyleString& str) {
- return makeRef(str, Context{});
+ return makeRef(str, Context{});
}
-StringPool::StyleRef StringPool::makeRef(const StyleString& str, const Context& context) {
- Entry* entry = new Entry();
- entry->value = str.str;
- entry->context = context;
- entry->index = mStrings.size();
- entry->ref = 0;
- mStrings.emplace_back(entry);
- mIndexedStrings.insert(std::make_pair(StringPiece(entry->value), entry));
+StringPool::StyleRef StringPool::makeRef(const StyleString& str,
+ const Context& context) {
+ Entry* entry = new Entry();
+ entry->value = str.str;
+ entry->context = context;
+ entry->index = mStrings.size();
+ entry->ref = 0;
+ mStrings.emplace_back(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->ref = 0;
- mStyles.emplace_back(styleEntry);
- return StyleRef(styleEntry);
+ 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->ref = 0;
+ mStyles.emplace_back(styleEntry);
+ return StyleRef(styleEntry);
}
StringPool::StyleRef StringPool::makeRef(const StyleRef& ref) {
- Entry* entry = new Entry();
- entry->value = *ref.mEntry->str;
- entry->context = ref.mEntry->str.mEntry->context;
- entry->index = mStrings.size();
- entry->ref = 0;
- mStrings.emplace_back(entry);
- mIndexedStrings.insert(std::make_pair(StringPiece(entry->value), entry));
+ Entry* entry = new Entry();
+ entry->value = *ref.mEntry->str;
+ entry->context = ref.mEntry->str.mEntry->context;
+ entry->index = mStrings.size();
+ entry->ref = 0;
+ mStrings.emplace_back(entry);
+ mIndexedStrings.insert(std::make_pair(StringPiece(entry->value), entry));
- StyleEntry* styleEntry = new StyleEntry();
- styleEntry->str = Ref(entry);
- for (const Span& span : ref.mEntry->spans) {
- styleEntry->spans.emplace_back(Span{ makeRef(*span.name), span.firstChar, span.lastChar });
- }
- styleEntry->ref = 0;
- mStyles.emplace_back(styleEntry);
- return StyleRef(styleEntry);
+ StyleEntry* styleEntry = new StyleEntry();
+ styleEntry->str = Ref(entry);
+ for (const Span& span : ref.mEntry->spans) {
+ styleEntry->spans.emplace_back(
+ Span{makeRef(*span.name), span.firstChar, span.lastChar});
+ }
+ styleEntry->ref = 0;
+ mStyles.emplace_back(styleEntry);
+ return StyleRef(styleEntry);
}
void StringPool::merge(StringPool&& pool) {
- mIndexedStrings.insert(pool.mIndexedStrings.begin(), pool.mIndexedStrings.end());
- pool.mIndexedStrings.clear();
- std::move(pool.mStrings.begin(), pool.mStrings.end(), std::back_inserter(mStrings));
- pool.mStrings.clear();
- std::move(pool.mStyles.begin(), pool.mStyles.end(), std::back_inserter(mStyles));
- pool.mStyles.clear();
+ mIndexedStrings.insert(pool.mIndexedStrings.begin(),
+ pool.mIndexedStrings.end());
+ pool.mIndexedStrings.clear();
+ std::move(pool.mStrings.begin(), pool.mStrings.end(),
+ std::back_inserter(mStrings));
+ pool.mStrings.clear();
+ std::move(pool.mStyles.begin(), pool.mStyles.end(),
+ std::back_inserter(mStyles));
+ pool.mStyles.clear();
- // Assign the indices.
- const size_t len = mStrings.size();
- for (size_t index = 0; index < len; index++) {
- mStrings[index]->index = index;
- }
+ // Assign the indices.
+ const size_t len = mStrings.size();
+ for (size_t index = 0; index < len; index++) {
+ mStrings[index]->index = index;
+ }
}
void StringPool::hintWillAdd(size_t stringCount, size_t styleCount) {
- mStrings.reserve(mStrings.size() + stringCount);
- mStyles.reserve(mStyles.size() + styleCount);
+ mStrings.reserve(mStrings.size() + stringCount);
+ mStyles.reserve(mStyles.size() + styleCount);
}
void StringPool::prune() {
- const auto iterEnd = std::end(mIndexedStrings);
- auto indexIter = std::begin(mIndexedStrings);
- while (indexIter != iterEnd) {
- if (indexIter->second->ref <= 0) {
- indexIter = mIndexedStrings.erase(indexIter);
- } else {
- ++indexIter;
- }
+ const auto iterEnd = std::end(mIndexedStrings);
+ auto indexIter = std::begin(mIndexedStrings);
+ while (indexIter != iterEnd) {
+ if (indexIter->second->ref <= 0) {
+ indexIter = mIndexedStrings.erase(indexIter);
+ } else {
+ ++indexIter;
}
+ }
- auto endIter2 = std::remove_if(std::begin(mStrings), std::end(mStrings),
- [](const std::unique_ptr<Entry>& entry) -> bool {
- return entry->ref <= 0;
- }
- );
+ auto endIter2 =
+ std::remove_if(std::begin(mStrings), std::end(mStrings),
+ [](const std::unique_ptr<Entry>& entry) -> bool {
+ return entry->ref <= 0;
+ });
- auto endIter3 = std::remove_if(std::begin(mStyles), std::end(mStyles),
- [](const std::unique_ptr<StyleEntry>& entry) -> bool {
- return entry->ref <= 0;
- }
- );
+ auto endIter3 =
+ std::remove_if(std::begin(mStyles), std::end(mStyles),
+ [](const std::unique_ptr<StyleEntry>& entry) -> bool {
+ return entry->ref <= 0;
+ });
- // Remove the entries at the end or else we'll be accessing
- // a deleted string from the StyleEntry.
- mStrings.erase(endIter2, std::end(mStrings));
- mStyles.erase(endIter3, std::end(mStyles));
+ // Remove the entries at the end or else we'll be accessing
+ // a deleted string from the StyleEntry.
+ mStrings.erase(endIter2, std::end(mStrings));
+ mStyles.erase(endIter3, std::end(mStyles));
- // Reassign the indices.
- const size_t len = mStrings.size();
- for (size_t index = 0; index < len; index++) {
- mStrings[index]->index = index;
- }
+ // Reassign the indices.
+ const size_t len = mStrings.size();
+ for (size_t index = 0; index < len; index++) {
+ mStrings[index]->index = index;
+ }
}
-void StringPool::sort(const std::function<bool(const Entry&, const Entry&)>& cmp) {
- std::sort(std::begin(mStrings), std::end(mStrings),
- [&cmp](const std::unique_ptr<Entry>& a, const std::unique_ptr<Entry>& b) -> bool {
- return cmp(*a, *b);
- }
- );
+void StringPool::sort(
+ const std::function<bool(const Entry&, const Entry&)>& cmp) {
+ std::sort(
+ std::begin(mStrings), std::end(mStrings),
+ [&cmp](const std::unique_ptr<Entry>& a,
+ const std::unique_ptr<Entry>& b) -> bool { return cmp(*a, *b); });
- // Assign the indices.
- const size_t len = mStrings.size();
- for (size_t index = 0; index < len; index++) {
- mStrings[index]->index = index;
- }
+ // Assign the indices.
+ const size_t len = mStrings.size();
+ for (size_t index = 0; index < len; index++) {
+ mStrings[index]->index = index;
+ }
- // Reorder the styles.
- std::sort(std::begin(mStyles), std::end(mStyles),
+ // Reorder the styles.
+ std::sort(std::begin(mStyles), std::end(mStyles),
[](const std::unique_ptr<StyleEntry>& lhs,
const std::unique_ptr<StyleEntry>& rhs) -> bool {
- return lhs->str.getIndex() < rhs->str.getIndex();
- }
- );
+ return lhs->str.getIndex() < rhs->str.getIndex();
+ });
}
template <typename T>
static T* encodeLength(T* data, size_t length) {
- static_assert(std::is_integral<T>::value, "wat.");
+ static_assert(std::is_integral<T>::value, "wat.");
- constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1);
- constexpr size_t kMaxSize = kMask - 1;
- if (length > kMaxSize) {
- *data++ = kMask | (kMaxSize & (length >> (sizeof(T) * 8)));
- }
- *data++ = length;
- return data;
+ constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1);
+ constexpr size_t kMaxSize = kMask - 1;
+ if (length > kMaxSize) {
+ *data++ = kMask | (kMaxSize & (length >> (sizeof(T) * 8)));
+ }
+ *data++ = length;
+ return data;
}
template <typename T>
static size_t encodedLengthUnits(size_t length) {
- static_assert(std::is_integral<T>::value, "wat.");
+ static_assert(std::is_integral<T>::value, "wat.");
- constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1);
- constexpr size_t kMaxSize = kMask - 1;
- return length > kMaxSize ? 2 : 1;
+ constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1);
+ constexpr size_t kMaxSize = kMask - 1;
+ return length > kMaxSize ? 2 : 1;
}
-
bool StringPool::flatten(BigBuffer* out, const StringPool& pool, bool utf8) {
- const size_t startIndex = out->size();
- android::ResStringPool_header* header = out->nextBlock<android::ResStringPool_header>();
- header->header.type = android::RES_STRING_POOL_TYPE;
- header->header.headerSize = sizeof(*header);
- header->stringCount = pool.size();
+ const size_t startIndex = out->size();
+ android::ResStringPool_header* header =
+ out->nextBlock<android::ResStringPool_header>();
+ header->header.type = android::RES_STRING_POOL_TYPE;
+ header->header.headerSize = sizeof(*header);
+ header->stringCount = pool.size();
+ if (utf8) {
+ header->flags |= android::ResStringPool_header::UTF8_FLAG;
+ }
+
+ uint32_t* indices =
+ pool.size() != 0 ? out->nextBlock<uint32_t>(pool.size()) : nullptr;
+
+ uint32_t* styleIndices = nullptr;
+ if (!pool.mStyles.empty()) {
+ header->styleCount = pool.mStyles.back()->str.getIndex() + 1;
+ styleIndices = out->nextBlock<uint32_t>(header->styleCount);
+ }
+
+ const size_t beforeStringsIndex = out->size();
+ header->stringsStart = beforeStringsIndex - startIndex;
+
+ for (const auto& entry : pool) {
+ *indices = out->size() - beforeStringsIndex;
+ indices++;
+
if (utf8) {
- header->flags |= android::ResStringPool_header::UTF8_FLAG;
+ 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>(utf16Length) +
+ encodedLengthUnits<char>(encoded.length()) +
+ encoded.size() + 1;
+
+ char* data = out->nextBlock<char>(totalSize);
+
+ // First encode the UTF16 string length.
+ data = encodeLength(data, utf16Length);
+
+ // Now encode the size of the real UTF8 string.
+ data = encodeLength(data, encoded.length());
+ strncpy(data, encoded.data(), encoded.size());
+
+ } else {
+ 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, 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, encoded.data(), byteLength);
+
+ // The null-terminating character is already here due to the block of data
+ // being set
+ // to 0s on allocation.
+ }
+ }
+
+ out->align4();
+
+ if (!pool.mStyles.empty()) {
+ const size_t beforeStylesIndex = out->size();
+ header->stylesStart = beforeStylesIndex - startIndex;
+
+ size_t currentIndex = 0;
+ for (const auto& entry : pool.mStyles) {
+ while (entry->str.getIndex() > currentIndex) {
+ styleIndices[currentIndex++] = out->size() - beforeStylesIndex;
+
+ uint32_t* spanOffset = out->nextBlock<uint32_t>();
+ *spanOffset = android::ResStringPool_span::END;
+ }
+ styleIndices[currentIndex++] = out->size() - beforeStylesIndex;
+
+ android::ResStringPool_span* span =
+ out->nextBlock<android::ResStringPool_span>(entry->spans.size());
+ for (const auto& s : entry->spans) {
+ span->name.index = s.name.getIndex();
+ span->firstChar = s.firstChar;
+ span->lastChar = s.lastChar;
+ span++;
+ }
+
+ uint32_t* spanEnd = out->nextBlock<uint32_t>();
+ *spanEnd = android::ResStringPool_span::END;
}
- uint32_t* indices = pool.size() != 0 ? out->nextBlock<uint32_t>(pool.size()) : nullptr;
-
- uint32_t* styleIndices = nullptr;
- if (!pool.mStyles.empty()) {
- header->styleCount = pool.mStyles.back()->str.getIndex() + 1;
- styleIndices = out->nextBlock<uint32_t>(header->styleCount);
- }
-
- const size_t beforeStringsIndex = out->size();
- header->stringsStart = beforeStringsIndex - startIndex;
-
- for (const auto& entry : pool) {
- *indices = out->size() - beforeStringsIndex;
- indices++;
-
- if (utf8) {
- 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>(utf16Length)
- + encodedLengthUnits<char>(encoded.length())
- + encoded.size() + 1;
-
- char* data = out->nextBlock<char>(totalSize);
-
- // First encode the UTF16 string length.
- data = encodeLength(data, utf16Length);
-
- // Now encode the size of the real UTF8 string.
- data = encodeLength(data, encoded.length());
- strncpy(data, encoded.data(), encoded.size());
-
- } else {
- 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, 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, encoded.data(), byteLength);
-
- // The null-terminating character is already here due to the block of data being set
- // to 0s on allocation.
- }
- }
-
+ // The error checking code in the platform looks for an entire
+ // ResStringPool_span structure worth of 0xFFFFFFFF at the end
+ // of the style block, so fill in the remaining 2 32bit words
+ // with 0xFFFFFFFF.
+ const size_t paddingLength = sizeof(android::ResStringPool_span) -
+ sizeof(android::ResStringPool_span::name);
+ uint8_t* padding = out->nextBlock<uint8_t>(paddingLength);
+ memset(padding, 0xff, paddingLength);
out->align4();
-
- if (!pool.mStyles.empty()) {
- const size_t beforeStylesIndex = out->size();
- header->stylesStart = beforeStylesIndex - startIndex;
-
- size_t currentIndex = 0;
- for (const auto& entry : pool.mStyles) {
- while (entry->str.getIndex() > currentIndex) {
- styleIndices[currentIndex++] = out->size() - beforeStylesIndex;
-
- uint32_t* spanOffset = out->nextBlock<uint32_t>();
- *spanOffset = android::ResStringPool_span::END;
- }
- styleIndices[currentIndex++] = out->size() - beforeStylesIndex;
-
- android::ResStringPool_span* span =
- out->nextBlock<android::ResStringPool_span>(entry->spans.size());
- for (const auto& s : entry->spans) {
- span->name.index = s.name.getIndex();
- span->firstChar = s.firstChar;
- span->lastChar = s.lastChar;
- span++;
- }
-
- uint32_t* spanEnd = out->nextBlock<uint32_t>();
- *spanEnd = android::ResStringPool_span::END;
- }
-
- // The error checking code in the platform looks for an entire
- // ResStringPool_span structure worth of 0xFFFFFFFF at the end
- // of the style block, so fill in the remaining 2 32bit words
- // with 0xFFFFFFFF.
- const size_t paddingLength = sizeof(android::ResStringPool_span)
- - sizeof(android::ResStringPool_span::name);
- uint8_t* padding = out->nextBlock<uint8_t>(paddingLength);
- memset(padding, 0xff, paddingLength);
- out->align4();
- }
- header->header.size = out->size() - startIndex;
- return true;
+ }
+ header->header.size = out->size() - startIndex;
+ return true;
}
bool StringPool::flattenUtf8(BigBuffer* out, const StringPool& pool) {
- return flatten(out, pool, true);
+ return flatten(out, pool, true);
}
bool StringPool::flattenUtf16(BigBuffer* out, const StringPool& pool) {
- return flatten(out, pool, false);
+ return flatten(out, pool, false);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
index 13545be..05c26e7 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -17,207 +17,205 @@
#ifndef AAPT_STRING_POOL_H
#define AAPT_STRING_POOL_H
-#include "util/BigBuffer.h"
#include "ConfigDescription.h"
+#include "util/BigBuffer.h"
#include "util/StringPiece.h"
#include <functional>
-#include <unordered_map>
#include <memory>
#include <string>
+#include <unordered_map>
#include <vector>
namespace aapt {
struct Span {
- std::string name;
- uint32_t firstChar;
- uint32_t lastChar;
+ std::string name;
+ uint32_t firstChar;
+ uint32_t lastChar;
};
struct StyleString {
- std::string str;
- std::vector<Span> spans;
+ std::string str;
+ std::vector<Span> spans;
};
class StringPool {
-public:
- struct Context {
- uint32_t priority;
- ConfigDescription config;
- };
+ public:
+ struct Context {
+ uint32_t priority;
+ ConfigDescription config;
+ };
- class Entry;
+ class Entry;
- class Ref {
- public:
- Ref();
- Ref(const Ref&);
- ~Ref();
+ class Ref {
+ public:
+ Ref();
+ Ref(const Ref&);
+ ~Ref();
- Ref& operator=(const Ref& rhs);
- const std::string* operator->() const;
- const std::string& operator*() const;
+ Ref& operator=(const Ref& rhs);
+ const std::string* operator->() const;
+ const std::string& operator*() const;
- size_t getIndex() const;
- const Context& getContext() const;
+ size_t getIndex() const;
+ const Context& getContext() const;
- private:
- friend class StringPool;
+ private:
+ friend class StringPool;
- explicit Ref(Entry* entry);
+ explicit Ref(Entry* entry);
- Entry* mEntry;
- };
+ Entry* mEntry;
+ };
- class StyleEntry;
+ class StyleEntry;
- class StyleRef {
- public:
- StyleRef();
- StyleRef(const StyleRef&);
- ~StyleRef();
+ class StyleRef {
+ public:
+ StyleRef();
+ StyleRef(const StyleRef&);
+ ~StyleRef();
- StyleRef& operator=(const StyleRef& rhs);
- const StyleEntry* operator->() const;
- const StyleEntry& operator*() const;
+ StyleRef& operator=(const StyleRef& rhs);
+ const StyleEntry* operator->() const;
+ const StyleEntry& operator*() const;
- size_t getIndex() const;
- const Context& getContext() const;
+ size_t getIndex() const;
+ const Context& getContext() const;
- private:
- friend class StringPool;
+ private:
+ friend class StringPool;
- explicit StyleRef(StyleEntry* entry);
+ explicit StyleRef(StyleEntry* entry);
- StyleEntry* mEntry;
- };
+ StyleEntry* mEntry;
+ };
- class Entry {
- public:
- std::string value;
- Context context;
- size_t index;
+ class Entry {
+ public:
+ std::string value;
+ Context context;
+ size_t index;
- private:
- friend class StringPool;
- friend class Ref;
+ private:
+ friend class StringPool;
+ friend class Ref;
- int ref;
- };
+ int ref;
+ };
- struct Span {
- Ref name;
- uint32_t firstChar;
- uint32_t lastChar;
- };
+ struct Span {
+ Ref name;
+ uint32_t firstChar;
+ uint32_t lastChar;
+ };
- class StyleEntry {
- public:
- Ref str;
- std::vector<Span> spans;
+ class StyleEntry {
+ public:
+ Ref str;
+ std::vector<Span> spans;
- private:
- friend class StringPool;
- friend class StyleRef;
+ private:
+ friend class StringPool;
+ friend class StyleRef;
- int ref;
- };
+ int ref;
+ };
- using const_iterator = std::vector<std::unique_ptr<Entry>>::const_iterator;
+ using const_iterator = std::vector<std::unique_ptr<Entry>>::const_iterator;
- static bool flattenUtf8(BigBuffer* out, const StringPool& pool);
- static bool flattenUtf16(BigBuffer* out, const StringPool& pool);
+ static bool flattenUtf8(BigBuffer* out, const StringPool& pool);
+ static bool flattenUtf16(BigBuffer* out, const StringPool& pool);
- StringPool() = default;
- StringPool(const StringPool&) = delete;
+ StringPool() = default;
+ StringPool(const StringPool&) = delete;
- /**
- * Adds a string to the pool, unless it already exists. Returns
- * a reference to the string in the pool.
- */
- Ref makeRef(const StringPiece& str);
+ /**
+ * Adds a string to the pool, unless it already exists. Returns
+ * a reference to the string in the pool.
+ */
+ 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 StringPiece& str, const Context& context);
+ /**
+ * 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 StringPiece& str, const Context& context);
- /**
- * Adds a style to the string pool and returns a reference to it.
- */
- StyleRef makeRef(const StyleString& str);
+ /**
+ * Adds a style to the string pool and returns a reference to it.
+ */
+ StyleRef makeRef(const StyleString& str);
- /**
- * Adds a style to the string pool with a context object that
- * can be used when sorting the string pool. Returns a reference
- * to the style in the string pool.
- */
- StyleRef makeRef(const StyleString& str, const Context& context);
+ /**
+ * Adds a style to the string pool with a context object that
+ * can be used when sorting the string pool. Returns a reference
+ * to the style in the string pool.
+ */
+ StyleRef makeRef(const StyleString& str, const Context& context);
- /**
- * Adds a style from another string pool. Returns a reference to the
- * style in the string pool.
- */
- StyleRef makeRef(const StyleRef& ref);
+ /**
+ * Adds a style from another string pool. Returns a reference to the
+ * style in the string pool.
+ */
+ StyleRef makeRef(const StyleRef& ref);
- /**
- * Moves pool into this one without coalescing strings. When this
- * function returns, pool will be empty.
- */
- void merge(StringPool&& pool);
+ /**
+ * Moves pool into this one without coalescing strings. When this
+ * function returns, pool will be empty.
+ */
+ void merge(StringPool&& pool);
- /**
- * Retuns the number of strings in the table.
- */
- inline size_t size() const;
+ /**
+ * Retuns the number of strings in the table.
+ */
+ inline size_t size() const;
- /**
- * Reserves space for strings and styles as an optimization.
- */
- void hintWillAdd(size_t stringCount, size_t styleCount);
+ /**
+ * Reserves space for strings and styles as an optimization.
+ */
+ void hintWillAdd(size_t stringCount, size_t styleCount);
- /**
- * Sorts the strings according to some comparison function.
- */
- void sort(const std::function<bool(const Entry&, const Entry&)>& cmp);
+ /**
+ * Sorts the strings according to some comparison function.
+ */
+ void sort(const std::function<bool(const Entry&, const Entry&)>& cmp);
- /**
- * Removes any strings that have no references.
- */
- void prune();
+ /**
+ * Removes any strings that have no references.
+ */
+ void prune();
-private:
- friend const_iterator begin(const StringPool& pool);
- friend const_iterator end(const StringPool& pool);
+ private:
+ friend const_iterator begin(const StringPool& pool);
+ friend const_iterator end(const StringPool& pool);
- static bool flatten(BigBuffer* out, const StringPool& pool, bool utf8);
+ static bool flatten(BigBuffer* out, const StringPool& pool, bool utf8);
- Ref makeRefImpl(const StringPiece& 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::unordered_multimap<StringPiece, Entry*> mIndexedStrings;
+ std::vector<std::unique_ptr<Entry>> mStrings;
+ std::vector<std::unique_ptr<StyleEntry>> mStyles;
+ std::unordered_multimap<StringPiece, Entry*> mIndexedStrings;
};
//
// Inline implementation
//
-inline size_t StringPool::size() const {
- return mStrings.size();
-}
+inline size_t StringPool::size() const { return mStrings.size(); }
inline StringPool::const_iterator begin(const StringPool& pool) {
- return pool.mStrings.begin();
+ return pool.mStrings.begin();
}
inline StringPool::const_iterator end(const StringPool& pool) {
- return pool.mStrings.end();
+ return pool.mStrings.end();
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_STRING_POOL_H
+#endif // AAPT_STRING_POOL_H
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index 1367af7..2a7e1dd 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -23,246 +23,245 @@
namespace aapt {
TEST(StringPoolTest, InsertOneString) {
- StringPool pool;
+ StringPool pool;
- StringPool::Ref ref = pool.makeRef("wut");
- EXPECT_EQ(*ref, "wut");
+ StringPool::Ref ref = pool.makeRef("wut");
+ EXPECT_EQ(*ref, "wut");
}
TEST(StringPoolTest, InsertTwoUniqueStrings) {
- StringPool pool;
+ StringPool pool;
- StringPool::Ref ref = pool.makeRef("wut");
- StringPool::Ref ref2 = pool.makeRef("hey");
+ StringPool::Ref ref = pool.makeRef("wut");
+ StringPool::Ref ref2 = pool.makeRef("hey");
- EXPECT_EQ(*ref, "wut");
- EXPECT_EQ(*ref2, "hey");
+ EXPECT_EQ(*ref, "wut");
+ EXPECT_EQ(*ref2, "hey");
}
TEST(StringPoolTest, DoNotInsertNewDuplicateString) {
- StringPool pool;
+ StringPool pool;
- StringPool::Ref ref = pool.makeRef("wut");
- StringPool::Ref ref2 = pool.makeRef("wut");
+ StringPool::Ref ref = pool.makeRef("wut");
+ StringPool::Ref ref2 = pool.makeRef("wut");
- EXPECT_EQ(*ref, "wut");
- EXPECT_EQ(*ref2, "wut");
- EXPECT_EQ(1u, pool.size());
+ EXPECT_EQ(*ref, "wut");
+ EXPECT_EQ(*ref2, "wut");
+ EXPECT_EQ(1u, pool.size());
}
TEST(StringPoolTest, MaintainInsertionOrderIndex) {
- StringPool pool;
+ StringPool pool;
- StringPool::Ref ref = pool.makeRef("z");
- StringPool::Ref ref2 = pool.makeRef("a");
- StringPool::Ref ref3 = pool.makeRef("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());
- EXPECT_EQ(2u, ref3.getIndex());
+ EXPECT_EQ(0u, ref.getIndex());
+ EXPECT_EQ(1u, ref2.getIndex());
+ EXPECT_EQ(2u, ref3.getIndex());
}
TEST(StringPoolTest, PruneStringsWithNoReferences) {
- StringPool pool;
+ StringPool pool;
- StringPool::Ref refA = pool.makeRef("foo");
- {
- StringPool::Ref ref = pool.makeRef("wut");
- EXPECT_EQ(*ref, "wut");
- EXPECT_EQ(2u, pool.size());
- }
- StringPool::Ref refB = pool.makeRef("bar");
-
- EXPECT_EQ(3u, pool.size());
- pool.prune();
+ StringPool::Ref refA = pool.makeRef("foo");
+ {
+ StringPool::Ref ref = pool.makeRef("wut");
+ EXPECT_EQ(*ref, "wut");
EXPECT_EQ(2u, pool.size());
- StringPool::const_iterator iter = begin(pool);
- EXPECT_EQ((*iter)->value, "foo");
- EXPECT_LT((*iter)->index, 2u);
- ++iter;
- EXPECT_EQ((*iter)->value, "bar");
- EXPECT_LT((*iter)->index, 2u);
+ }
+ 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, "foo");
+ EXPECT_LT((*iter)->index, 2u);
+ ++iter;
+ EXPECT_EQ((*iter)->value, "bar");
+ EXPECT_LT((*iter)->index, 2u);
}
TEST(StringPoolTest, SortAndMaintainIndexesInReferences) {
- StringPool pool;
+ StringPool pool;
- StringPool::Ref ref = pool.makeRef("z");
- StringPool::StyleRef ref2 = pool.makeRef(StyleString{ {"a"} });
- StringPool::Ref ref3 = pool.makeRef("m");
+ StringPool::Ref ref = pool.makeRef("z");
+ StringPool::StyleRef ref2 = pool.makeRef(StyleString{{"a"}});
+ StringPool::Ref ref3 = pool.makeRef("m");
- EXPECT_EQ(*ref, "z");
- EXPECT_EQ(0u, ref.getIndex());
+ EXPECT_EQ(*ref, "z");
+ EXPECT_EQ(0u, ref.getIndex());
- EXPECT_EQ(*(ref2->str), "a");
- EXPECT_EQ(1u, ref2.getIndex());
+ EXPECT_EQ(*(ref2->str), "a");
+ EXPECT_EQ(1u, ref2.getIndex());
- EXPECT_EQ(*ref3, "m");
- EXPECT_EQ(2u, ref3.getIndex());
+ EXPECT_EQ(*ref3, "m");
+ EXPECT_EQ(2u, ref3.getIndex());
- pool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
- return a.value < b.value;
- });
+ pool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
+ return a.value < b.value;
+ });
+ EXPECT_EQ(*ref, "z");
+ EXPECT_EQ(2u, ref.getIndex());
- EXPECT_EQ(*ref, "z");
- EXPECT_EQ(2u, ref.getIndex());
+ EXPECT_EQ(*(ref2->str), "a");
+ EXPECT_EQ(0u, ref2.getIndex());
- EXPECT_EQ(*(ref2->str), "a");
- EXPECT_EQ(0u, ref2.getIndex());
-
- EXPECT_EQ(*ref3, "m");
- EXPECT_EQ(1u, ref3.getIndex());
+ EXPECT_EQ(*ref3, "m");
+ EXPECT_EQ(1u, ref3.getIndex());
}
TEST(StringPoolTest, SortAndStillDedupe) {
- StringPool pool;
+ StringPool pool;
- StringPool::Ref ref = pool.makeRef("z");
- StringPool::Ref ref2 = pool.makeRef("a");
- StringPool::Ref ref3 = pool.makeRef("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;
- });
+ pool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
+ return a.value < b.value;
+ });
- StringPool::Ref ref4 = pool.makeRef("z");
- StringPool::Ref ref5 = pool.makeRef("a");
- StringPool::Ref ref6 = pool.makeRef("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());
- EXPECT_EQ(ref6.getIndex(), ref3.getIndex());
+ EXPECT_EQ(ref4.getIndex(), ref.getIndex());
+ EXPECT_EQ(ref5.getIndex(), ref2.getIndex());
+ EXPECT_EQ(ref6.getIndex(), ref3.getIndex());
}
TEST(StringPoolTest, AddStyles) {
- StringPool pool;
+ StringPool pool;
- StyleString str {
- { "android" },
- {
- Span{ { "b" }, 2, 6 }
- }
- };
+ StyleString str{{"android"}, {Span{{"b"}, 2, 6}}};
- StringPool::StyleRef ref = pool.makeRef(str);
+ StringPool::StyleRef ref = pool.makeRef(str);
- EXPECT_EQ(0u, ref.getIndex());
- EXPECT_EQ(std::string("android"), *(ref->str));
- ASSERT_EQ(1u, ref->spans.size());
+ EXPECT_EQ(0u, ref.getIndex());
+ EXPECT_EQ(std::string("android"), *(ref->str));
+ ASSERT_EQ(1u, ref->spans.size());
- const StringPool::Span& span = ref->spans.front();
- EXPECT_EQ(*(span.name), "b");
- EXPECT_EQ(2u, span.firstChar);
- EXPECT_EQ(6u, span.lastChar);
+ const StringPool::Span& span = ref->spans.front();
+ EXPECT_EQ(*(span.name), "b");
+ EXPECT_EQ(2u, span.firstChar);
+ EXPECT_EQ(6u, span.lastChar);
}
TEST(StringPoolTest, DoNotDedupeStyleWithSameStringAsNonStyle) {
- StringPool pool;
+ StringPool pool;
- StringPool::Ref ref = pool.makeRef("android");
+ StringPool::Ref ref = pool.makeRef("android");
- StyleString str { { "android" } };
- StringPool::StyleRef styleRef = pool.makeRef(str);
+ StyleString str{{"android"}};
+ StringPool::StyleRef styleRef = pool.makeRef(str);
- EXPECT_NE(ref.getIndex(), styleRef.getIndex());
+ EXPECT_NE(ref.getIndex(), styleRef.getIndex());
}
TEST(StringPoolTest, FlattenEmptyStringPoolUtf8) {
- using namespace android; // For NO_ERROR on Windows.
+ using namespace android; // For NO_ERROR on Windows.
- StringPool pool;
- BigBuffer buffer(1024);
- StringPool::flattenUtf8(&buffer, pool);
+ StringPool pool;
+ BigBuffer buffer(1024);
+ StringPool::flattenUtf8(&buffer, pool);
- std::unique_ptr<uint8_t[]> data = util::copy(buffer);
- ResStringPool test;
- ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
+ std::unique_ptr<uint8_t[]> data = util::copy(buffer);
+ ResStringPool test;
+ ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
}
TEST(StringPoolTest, FlattenOddCharactersUtf16) {
- using namespace android; // For NO_ERROR on Windows.
+ using namespace android; // For NO_ERROR on Windows.
- StringPool pool;
- pool.makeRef("\u093f");
- BigBuffer buffer(1024);
- StringPool::flattenUtf16(&buffer, pool);
+ StringPool pool;
+ pool.makeRef("\u093f");
+ BigBuffer buffer(1024);
+ StringPool::flattenUtf16(&buffer, pool);
- std::unique_ptr<uint8_t[]> data = util::copy(buffer);
- ResStringPool test;
- ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
- size_t len = 0;
- const char16_t* str = test.stringAt(0, &len);
- EXPECT_EQ(1u, len);
- EXPECT_EQ(u'\u093f', *str);
- EXPECT_EQ(0u, str[1]);
+ std::unique_ptr<uint8_t[]> data = util::copy(buffer);
+ ResStringPool test;
+ ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
+ size_t len = 0;
+ const char16_t* str = test.stringAt(0, &len);
+ EXPECT_EQ(1u, len);
+ EXPECT_EQ(u'\u093f', *str);
+ EXPECT_EQ(0u, str[1]);
}
-constexpr const char* sLongString = "バッテリーを長持ちさせるため、バッテリーセーバーは端末のパフォーマンスを抑え、バイブレーション、位置情報サービス、大半のバックグラウンドデータを制限します。メール、SMSや、同期を使 用するその他のアプリは、起動しても更新されないことがあります。バッテリーセーバーは端末の充電中は自動的にOFFになります。";
+constexpr const char* sLongString =
+ "バッテリーを長持ちさせるため、バッテリーセーバーは端末のパフォーマンスを抑"
+ "え、バイブレーション、位置情報サービス、大半のバックグラウンドデータを制限"
+ "します。メール、SMSや、同期を使 "
+ "用するその他のアプリは、起動しても更新されないことがあります。バッテリーセ"
+ "ーバーは端末の充電中は自動的にOFFになります。";
TEST(StringPoolTest, Flatten) {
- using namespace android; // For NO_ERROR on Windows.
+ using namespace android; // For NO_ERROR on Windows.
- StringPool pool;
+ StringPool pool;
- StringPool::Ref ref1 = pool.makeRef("hello");
- StringPool::Ref ref2 = pool.makeRef("goodbye");
- StringPool::Ref ref3 = pool.makeRef(sLongString);
- StringPool::Ref ref4 = pool.makeRef("");
- StringPool::StyleRef ref5 = pool.makeRef(StyleString{
- { "style" },
- { Span{ { "b" }, 0, 1 }, Span{ { "i" }, 2, 3 } }
- });
+ StringPool::Ref ref1 = pool.makeRef("hello");
+ StringPool::Ref ref2 = pool.makeRef("goodbye");
+ StringPool::Ref ref3 = pool.makeRef(sLongString);
+ 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());
+ 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 buffers[2] = { BigBuffer(1024), BigBuffer(1024) };
- StringPool::flattenUtf8(&buffers[0], pool);
- StringPool::flattenUtf16(&buffers[1], 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);
+ // Test both UTF-8 and UTF-16 buffers.
+ for (const BigBuffer& buffer : buffers) {
+ std::unique_ptr<uint8_t[]> data = util::copy(buffer);
- ResStringPool test;
- ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
+ ResStringPool test;
+ ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
- EXPECT_EQ(std::string("hello"), util::getString(test, 0));
- EXPECT_EQ(StringPiece16(u"hello"), util::getString16(test, 0));
+ 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(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());
+ 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);
+ 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));
+ EXPECT_EQ(std::string("style"), util::getString(test, 4));
+ EXPECT_EQ(StringPiece16(u"style"), util::getString16(test, 4));
- const ResStringPool_span* span = test.styleAt(4);
- ASSERT_NE(nullptr, span);
- 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++;
+ const ResStringPool_span* span = test.styleAt(4);
+ ASSERT_NE(nullptr, span);
+ 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(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++;
+ ASSERT_NE(ResStringPool_span::END, span->name.index);
+ 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++;
- EXPECT_EQ(ResStringPool_span::END, span->name.index);
- }
+ EXPECT_EQ(ResStringPool_span::END, span->name.index);
+ }
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/ValueVisitor.h b/tools/aapt2/ValueVisitor.h
index 9dc6a9c..121c337 100644
--- a/tools/aapt2/ValueVisitor.h
+++ b/tools/aapt2/ValueVisitor.h
@@ -17,91 +17,94 @@
#ifndef AAPT_VALUE_VISITOR_H
#define AAPT_VALUE_VISITOR_H
-#include "ResourceValues.h"
#include "ResourceTable.h"
+#include "ResourceValues.h"
namespace aapt {
/**
- * Visits a value and invokes the appropriate method based on its type. Does not traverse
+ * Visits a value and invokes the appropriate method based on its type. Does not
+ * traverse
* into compound types. Use ValueVisitor for that.
*/
struct RawValueVisitor {
- virtual ~RawValueVisitor() = default;
+ virtual ~RawValueVisitor() = default;
- virtual void visitItem(Item* value) {}
- virtual void visit(Reference* value) { visitItem(value); }
- virtual void visit(RawString* value) { visitItem(value); }
- virtual void visit(String* value) { visitItem(value); }
- virtual void visit(StyledString* value) { visitItem(value); }
- virtual void visit(FileReference* value) { visitItem(value); }
- virtual void visit(Id* value) { visitItem(value); }
- virtual void visit(BinaryPrimitive* value) { visitItem(value); }
+ virtual void visitItem(Item* value) {}
+ virtual void visit(Reference* value) { visitItem(value); }
+ virtual void visit(RawString* value) { visitItem(value); }
+ virtual void visit(String* value) { visitItem(value); }
+ virtual void visit(StyledString* value) { visitItem(value); }
+ virtual void visit(FileReference* value) { visitItem(value); }
+ virtual void visit(Id* value) { visitItem(value); }
+ virtual void visit(BinaryPrimitive* value) { visitItem(value); }
- virtual void visit(Attribute* value) {}
- virtual void visit(Style* value) {}
- virtual void visit(Array* value) {}
- virtual void visit(Plural* value) {}
- virtual void visit(Styleable* value) {}
+ virtual void visit(Attribute* value) {}
+ virtual void visit(Style* value) {}
+ virtual void visit(Array* value) {}
+ virtual void visit(Plural* value) {}
+ virtual void visit(Styleable* value) {}
};
// NOLINT, do not add parentheses around T.
-#define DECL_VISIT_COMPOUND_VALUE(T) \
- virtual void visit(T* value) { /* NOLINT */ \
- visitSubValues(value); \
- }
+#define DECL_VISIT_COMPOUND_VALUE(T) \
+ virtual void visit(T* value) { /* NOLINT */ \
+ visitSubValues(value); \
+ }
/**
- * Visits values, and if they are compound values, visits the components as well.
+ * Visits values, and if they are compound values, visits the components as
+ * well.
*/
struct ValueVisitor : public RawValueVisitor {
- // The compiler will think we're hiding an overload, when we actually intend
- // to call into RawValueVisitor. This will expose the visit methods in the super
- // class so the compiler knows we are trying to call them.
- using RawValueVisitor::visit;
+ // The compiler will think we're hiding an overload, when we actually intend
+ // to call into RawValueVisitor. This will expose the visit methods in the
+ // super
+ // class so the compiler knows we are trying to call them.
+ using RawValueVisitor::visit;
- void visitSubValues(Attribute* attribute) {
- for (Attribute::Symbol& symbol : attribute->symbols) {
- visit(&symbol.symbol);
- }
+ void visitSubValues(Attribute* attribute) {
+ for (Attribute::Symbol& symbol : attribute->symbols) {
+ visit(&symbol.symbol);
+ }
+ }
+
+ void visitSubValues(Style* style) {
+ if (style->parent) {
+ visit(&style->parent.value());
}
- void visitSubValues(Style* style) {
- if (style->parent) {
- visit(&style->parent.value());
- }
-
- for (Style::Entry& entry : style->entries) {
- visit(&entry.key);
- entry.value->accept(this);
- }
+ for (Style::Entry& entry : style->entries) {
+ visit(&entry.key);
+ entry.value->accept(this);
}
+ }
- void visitSubValues(Array* array) {
- for (std::unique_ptr<Item>& item : array->items) {
- item->accept(this);
- }
+ void visitSubValues(Array* array) {
+ for (std::unique_ptr<Item>& item : array->items) {
+ item->accept(this);
}
+ }
- void visitSubValues(Plural* plural) {
- for (std::unique_ptr<Item>& item : plural->values) {
- if (item) {
- item->accept(this);
- }
- }
+ void visitSubValues(Plural* plural) {
+ for (std::unique_ptr<Item>& item : plural->values) {
+ if (item) {
+ item->accept(this);
+ }
}
+ }
- void visitSubValues(Styleable* styleable) {
- for (Reference& reference : styleable->entries) {
- visit(&reference);
- }
+ void visitSubValues(Styleable* styleable) {
+ for (Reference& reference : styleable->entries) {
+ visit(&reference);
}
+ }
- DECL_VISIT_COMPOUND_VALUE(Attribute);
- DECL_VISIT_COMPOUND_VALUE(Style);
- DECL_VISIT_COMPOUND_VALUE(Array);
- DECL_VISIT_COMPOUND_VALUE(Plural);
- DECL_VISIT_COMPOUND_VALUE(Styleable);
+ DECL_VISIT_COMPOUND_VALUE(Attribute);
+ DECL_VISIT_COMPOUND_VALUE(Style);
+ DECL_VISIT_COMPOUND_VALUE(Array);
+ DECL_VISIT_COMPOUND_VALUE(Plural);
+ DECL_VISIT_COMPOUND_VALUE(Styleable);
};
/**
@@ -109,11 +112,9 @@
*/
template <typename T>
struct DynCastVisitor : public RawValueVisitor {
- T* value = nullptr;
+ T* value = nullptr;
- void visit(T* v) override {
- value = v;
- }
+ void visit(T* v) override { value = v; }
};
/**
@@ -121,16 +122,14 @@
*/
template <>
struct DynCastVisitor<Item> : public RawValueVisitor {
- Item* value = nullptr;
+ Item* value = nullptr;
- void visitItem(Item* item) override {
- value = item;
- }
+ void visitItem(Item* item) override { value = item; }
};
template <typename T>
const T* valueCast(const Value* value) {
- return valueCast<T>(const_cast<Value*>(value));
+ return valueCast<T>(const_cast<Value*>(value));
}
/**
@@ -139,30 +138,32 @@
*/
template <typename T>
T* valueCast(Value* value) {
- if (!value) {
- return nullptr;
- }
- DynCastVisitor<T> visitor;
- value->accept(&visitor);
- return visitor.value;
+ if (!value) {
+ return nullptr;
+ }
+ DynCastVisitor<T> visitor;
+ value->accept(&visitor);
+ return visitor.value;
}
-inline void visitAllValuesInPackage(ResourceTablePackage* pkg, RawValueVisitor* visitor) {
- for (auto& type : pkg->types) {
- for (auto& entry : type->entries) {
- for (auto& configValue : entry->values) {
- configValue->value->accept(visitor);
- }
- }
+inline void visitAllValuesInPackage(ResourceTablePackage* pkg,
+ RawValueVisitor* visitor) {
+ for (auto& type : pkg->types) {
+ for (auto& entry : type->entries) {
+ for (auto& configValue : entry->values) {
+ configValue->value->accept(visitor);
+ }
}
+ }
}
-inline void visitAllValuesInTable(ResourceTable* table, RawValueVisitor* visitor) {
- for (auto& pkg : table->packages) {
- visitAllValuesInPackage(pkg.get(), visitor);
- }
+inline void visitAllValuesInTable(ResourceTable* table,
+ RawValueVisitor* visitor) {
+ for (auto& pkg : table->packages) {
+ visitAllValuesInPackage(pkg.get(), visitor);
+ }
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_VALUE_VISITOR_H
+#endif // AAPT_VALUE_VISITOR_H
diff --git a/tools/aapt2/ValueVisitor_test.cpp b/tools/aapt2/ValueVisitor_test.cpp
index 54e9fcd..ed9c7f6 100644
--- a/tools/aapt2/ValueVisitor_test.cpp
+++ b/tools/aapt2/ValueVisitor_test.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "ResourceValues.h"
#include "ValueVisitor.h"
+#include "ResourceValues.h"
#include "test/Test.h"
#include "util/Util.h"
@@ -24,63 +24,63 @@
namespace aapt {
struct SingleReferenceVisitor : public ValueVisitor {
- using ValueVisitor::visit;
+ using ValueVisitor::visit;
- Reference* visited = nullptr;
+ Reference* visited = nullptr;
- void visit(Reference* ref) override {
- visited = ref;
- }
+ void visit(Reference* ref) override { visited = ref; }
};
struct StyleVisitor : public ValueVisitor {
- using ValueVisitor::visit;
+ using ValueVisitor::visit;
- std::list<Reference*> visitedRefs;
- Style* visitedStyle = nullptr;
+ std::list<Reference*> visitedRefs;
+ Style* visitedStyle = nullptr;
- void visit(Reference* ref) override {
- visitedRefs.push_back(ref);
- }
+ void visit(Reference* ref) override { visitedRefs.push_back(ref); }
- void visit(Style* style) override {
- visitedStyle = style;
- ValueVisitor::visit(style);
- }
+ void visit(Style* style) override {
+ visitedStyle = style;
+ ValueVisitor::visit(style);
+ }
};
TEST(ValueVisitorTest, VisitsReference) {
- Reference ref(ResourceName{"android", ResourceType::kAttr, "foo"});
- SingleReferenceVisitor visitor;
- ref.accept(&visitor);
+ Reference ref(ResourceName{"android", ResourceType::kAttr, "foo"});
+ SingleReferenceVisitor visitor;
+ ref.accept(&visitor);
- EXPECT_EQ(visitor.visited, &ref);
+ EXPECT_EQ(visitor.visited, &ref);
}
TEST(ValueVisitorTest, VisitsReferencesInStyle) {
- std::unique_ptr<Style> style = test::StyleBuilder()
- .setParent("android:style/foo")
- .addItem("android:attr/one", test::buildReference("android:id/foo"))
- .build();
+ std::unique_ptr<Style> style =
+ test::StyleBuilder()
+ .setParent("android:style/foo")
+ .addItem("android:attr/one", test::buildReference("android:id/foo"))
+ .build();
- StyleVisitor visitor;
- style->accept(&visitor);
+ StyleVisitor visitor;
+ style->accept(&visitor);
- ASSERT_EQ(style.get(), visitor.visitedStyle);
+ ASSERT_EQ(style.get(), visitor.visitedStyle);
- // Entry attribute references, plus the parent reference, plus one value reference.
- ASSERT_EQ(style->entries.size() + 2, visitor.visitedRefs.size());
+ // Entry attribute references, plus the parent reference, plus one value
+ // reference.
+ ASSERT_EQ(style->entries.size() + 2, visitor.visitedRefs.size());
}
TEST(ValueVisitorTest, ValueCast) {
- std::unique_ptr<Reference> ref = test::buildReference("android:color/white");
- EXPECT_NE(valueCast<Reference>(ref.get()), nullptr);
+ 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("android:attr/foo", test::buildReference("android:color/black"))
- .build();
- EXPECT_NE(valueCast<Style>(style.get()), nullptr);
- EXPECT_EQ(valueCast<Reference>(style.get()), nullptr);
+ std::unique_ptr<Style> style =
+ test::StyleBuilder()
+ .addItem("android:attr/foo",
+ test::buildReference("android:color/black"))
+ .build();
+ EXPECT_NE(valueCast<Style>(style.get()), nullptr);
+ EXPECT_EQ(valueCast<Reference>(style.get()), nullptr);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
index dbd8062..d001228 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -33,8 +33,8 @@
#include "xml/XmlDom.h"
#include "xml/XmlPullParser.h"
-#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <android-base/errors.h>
#include <android-base/file.h>
@@ -48,16 +48,18 @@
namespace aapt {
struct ResourcePathData {
- Source source;
- std::string resourceDir;
- std::string name;
- std::string extension;
+ Source source;
+ 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
- // version qualifiers. We want to preserve the original input so the output is easily
- // computed before hand.
- std::string configStr;
- ConfigDescription config;
+ // Original config str. We keep this because when we parse the config, we may
+ // add on
+ // version qualifiers. We want to preserve the original input so the output is
+ // easily
+ // computed before hand.
+ std::string configStr;
+ ConfigDescription config;
};
/**
@@ -66,696 +68,735 @@
*/
static Maybe<ResourcePathData> extractResourcePathData(const std::string& path,
std::string* outError) {
- std::vector<std::string> parts = util::split(path, file::sDirSep);
- if (parts.size() < 2) {
- if (outError) *outError = "bad resource path";
- return {};
+ std::vector<std::string> parts = util::split(path, file::sDirSep);
+ if (parts.size() < 2) {
+ if (outError) *outError = "bad resource path";
+ return {};
+ }
+
+ std::string& dir = parts[parts.size() - 2];
+ StringPiece dirStr = dir;
+
+ StringPiece configStr;
+ ConfigDescription config;
+ size_t dashPos = dir.find('-');
+ if (dashPos != std::string::npos) {
+ configStr = dirStr.substr(dashPos + 1, dir.size() - (dashPos + 1));
+ if (!ConfigDescription::parse(configStr, &config)) {
+ if (outError) {
+ std::stringstream errStr;
+ errStr << "invalid configuration '" << configStr << "'";
+ *outError = errStr.str();
+ }
+ return {};
}
+ dirStr = dirStr.substr(0, dashPos);
+ }
- std::string& dir = parts[parts.size() - 2];
- StringPiece dirStr = dir;
+ std::string& filename = parts[parts.size() - 1];
+ StringPiece name = filename;
+ StringPiece extension;
+ size_t dotPos = filename.find('.');
+ if (dotPos != std::string::npos) {
+ extension = name.substr(dotPos + 1, filename.size() - (dotPos + 1));
+ name = name.substr(0, dotPos);
+ }
- StringPiece configStr;
- ConfigDescription config;
- size_t dashPos = dir.find('-');
- if (dashPos != std::string::npos) {
- configStr = dirStr.substr(dashPos + 1, dir.size() - (dashPos + 1));
- if (!ConfigDescription::parse(configStr, &config)) {
- if (outError) {
- std::stringstream errStr;
- errStr << "invalid configuration '" << configStr << "'";
- *outError = errStr.str();
- }
- return {};
- }
- dirStr = dirStr.substr(0, dashPos);
- }
-
- std::string& filename = parts[parts.size() - 1];
- StringPiece name = filename;
- StringPiece extension;
- size_t dotPos = filename.find('.');
- if (dotPos != std::string::npos) {
- extension = name.substr(dotPos + 1, filename.size() - (dotPos + 1));
- name = name.substr(0, dotPos);
- }
-
- return ResourcePathData{
- Source(path),
- dirStr.toString(),
- name.toString(),
- extension.toString(),
- configStr.toString(),
- config
- };
+ return ResourcePathData{Source(path), dirStr.toString(),
+ name.toString(), extension.toString(),
+ configStr.toString(), config};
}
struct CompileOptions {
- std::string outputPath;
- Maybe<std::string> resDir;
- bool pseudolocalize = false;
- bool legacyMode = false;
- bool verbose = false;
+ std::string outputPath;
+ Maybe<std::string> resDir;
+ bool pseudolocalize = false;
+ bool legacyMode = false;
+ bool verbose = false;
};
static std::string buildIntermediateFilename(const ResourcePathData& data) {
- std::stringstream name;
- name << data.resourceDir;
- if (!data.configStr.empty()) {
- name << "-" << data.configStr;
- }
- name << "_" << data.name;
- if (!data.extension.empty()) {
- name << "." << data.extension;
- }
- name << ".flat";
- return name.str();
+ std::stringstream name;
+ name << data.resourceDir;
+ if (!data.configStr.empty()) {
+ name << "-" << data.configStr;
+ }
+ name << "_" << data.name;
+ if (!data.extension.empty()) {
+ name << "." << data.extension;
+ }
+ name << ".flat";
+ return name.str();
}
static bool isHidden(const StringPiece& filename) {
- return util::stringStartsWith(filename, ".");
+ return util::stringStartsWith(filename, ".");
}
/**
* Walks the res directory structure, looking for resource files.
*/
-static bool loadInputFilesFromDir(IAaptContext* context, const CompileOptions& options,
+static bool loadInputFilesFromDir(IAaptContext* context,
+ const CompileOptions& options,
std::vector<ResourcePathData>* outPathData) {
- const std::string& rootDir = options.resDir.value();
- std::unique_ptr<DIR, decltype(closedir)*> d(opendir(rootDir.data()), closedir);
- if (!d) {
- context->getDiagnostics()->error(DiagMessage() << strerror(errno));
+ const std::string& rootDir = options.resDir.value();
+ std::unique_ptr<DIR, decltype(closedir)*> d(opendir(rootDir.data()),
+ closedir);
+ if (!d) {
+ context->getDiagnostics()->error(DiagMessage() << strerror(errno));
+ return false;
+ }
+
+ while (struct dirent* entry = readdir(d.get())) {
+ if (isHidden(entry->d_name)) {
+ continue;
+ }
+
+ std::string prefixPath = rootDir;
+ file::appendPath(&prefixPath, entry->d_name);
+
+ if (file::getFileType(prefixPath) != file::FileType::kDirectory) {
+ continue;
+ }
+
+ std::unique_ptr<DIR, decltype(closedir)*> subDir(opendir(prefixPath.data()),
+ closedir);
+ if (!subDir) {
+ context->getDiagnostics()->error(DiagMessage() << strerror(errno));
+ return false;
+ }
+
+ while (struct dirent* leafEntry = readdir(subDir.get())) {
+ if (isHidden(leafEntry->d_name)) {
+ continue;
+ }
+
+ std::string fullPath = prefixPath;
+ file::appendPath(&fullPath, leafEntry->d_name);
+
+ std::string errStr;
+ Maybe<ResourcePathData> pathData =
+ extractResourcePathData(fullPath, &errStr);
+ if (!pathData) {
+ context->getDiagnostics()->error(DiagMessage() << errStr);
return false;
+ }
+
+ outPathData->push_back(std::move(pathData.value()));
}
-
- while (struct dirent* entry = readdir(d.get())) {
- if (isHidden(entry->d_name)) {
- continue;
- }
-
- std::string prefixPath = rootDir;
- file::appendPath(&prefixPath, entry->d_name);
-
- if (file::getFileType(prefixPath) != file::FileType::kDirectory) {
- continue;
- }
-
- std::unique_ptr<DIR, decltype(closedir)*> subDir(opendir(prefixPath.data()), closedir);
- if (!subDir) {
- context->getDiagnostics()->error(DiagMessage() << strerror(errno));
- return false;
- }
-
- while (struct dirent* leafEntry = readdir(subDir.get())) {
- if (isHidden(leafEntry->d_name)) {
- continue;
- }
-
- std::string fullPath = prefixPath;
- file::appendPath(&fullPath, leafEntry->d_name);
-
- std::string errStr;
- Maybe<ResourcePathData> pathData = extractResourcePathData(fullPath, &errStr);
- if (!pathData) {
- context->getDiagnostics()->error(DiagMessage() << errStr);
- return false;
- }
-
- outPathData->push_back(std::move(pathData.value()));
- }
- }
- return true;
+ }
+ return true;
}
static bool compileTable(IAaptContext* context, const CompileOptions& options,
- const ResourcePathData& pathData, IArchiveWriter* writer,
+ const ResourcePathData& pathData,
+ IArchiveWriter* writer,
const std::string& outputPath) {
- ResourceTable table;
- {
- std::ifstream fin(pathData.source.path, std::ifstream::binary);
- if (!fin) {
- context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
- return false;
- }
-
-
- // Parse the values file from XML.
- xml::XmlPullParser xmlParser(fin);
-
- ResourceParserOptions parserOptions;
- parserOptions.errorOnPositionalArguments = !options.legacyMode;
-
- // If the filename includes donottranslate, then the default translatable is false.
- parserOptions.translatable = pathData.name.find("donottranslate") == std::string::npos;
-
- ResourceParser resParser(context->getDiagnostics(), &table, pathData.source,
- pathData.config, parserOptions);
- if (!resParser.parse(&xmlParser)) {
- return false;
- }
-
- fin.close();
+ ResourceTable table;
+ {
+ std::ifstream fin(pathData.source.path, std::ifstream::binary);
+ if (!fin) {
+ context->getDiagnostics()->error(DiagMessage(pathData.source)
+ << strerror(errno));
+ return false;
}
- if (options.pseudolocalize) {
- // Generate pseudo-localized strings (en-XA and ar-XB).
- // These are created as weak symbols, and are only generated from default configuration
- // strings and plurals.
- PseudolocaleGenerator pseudolocaleGenerator;
- if (!pseudolocaleGenerator.consume(context, &table)) {
- return false;
- }
+ // Parse the values file from XML.
+ xml::XmlPullParser xmlParser(fin);
+
+ ResourceParserOptions parserOptions;
+ parserOptions.errorOnPositionalArguments = !options.legacyMode;
+
+ // If the filename includes donottranslate, then the default translatable is
+ // false.
+ parserOptions.translatable =
+ pathData.name.find("donottranslate") == std::string::npos;
+
+ ResourceParser resParser(context->getDiagnostics(), &table, pathData.source,
+ pathData.config, parserOptions);
+ if (!resParser.parse(&xmlParser)) {
+ return false;
}
- // Ensure we have the compilation package at least.
- table.createPackage(context->getCompilationPackage());
+ fin.close();
+ }
- // Assign an ID to any package that has resources.
- for (auto& pkg : table.packages) {
- if (!pkg->id) {
- // If no package ID was set while parsing (public identifiers), auto assign an ID.
- pkg->id = context->getPackageId();
- }
+ if (options.pseudolocalize) {
+ // Generate pseudo-localized strings (en-XA and ar-XB).
+ // These are created as weak symbols, and are only generated from default
+ // configuration
+ // strings and plurals.
+ PseudolocaleGenerator pseudolocaleGenerator;
+ if (!pseudolocaleGenerator.consume(context, &table)) {
+ return false;
}
+ }
- // Create the file/zip entry.
- if (!writer->startEntry(outputPath, 0)) {
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open");
- return false;
+ // Ensure we have the compilation package at least.
+ table.createPackage(context->getCompilationPackage());
+
+ // Assign an ID to any package that has resources.
+ for (auto& pkg : table.packages) {
+ if (!pkg->id) {
+ // If no package ID was set while parsing (public identifiers), auto
+ // assign an ID.
+ pkg->id = context->getPackageId();
}
+ }
- // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->finishEntry().
- {
- // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
- // interface.
- CopyingOutputStreamAdaptor copyingAdaptor(writer);
+ // Create the file/zip entry.
+ if (!writer->startEntry(outputPath, 0)) {
+ context->getDiagnostics()->error(DiagMessage(outputPath)
+ << "failed to open");
+ return false;
+ }
- std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(&table);
- if (!pbTable->SerializeToZeroCopyStream(©ingAdaptor)) {
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
- return false;
- }
+ // Make sure CopyingOutputStreamAdaptor is deleted before we call
+ // writer->finishEntry().
+ {
+ // Wrap our IArchiveWriter with an adaptor that implements the
+ // ZeroCopyOutputStream
+ // interface.
+ CopyingOutputStreamAdaptor copyingAdaptor(writer);
+
+ std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(&table);
+ if (!pbTable->SerializeToZeroCopyStream(©ingAdaptor)) {
+ context->getDiagnostics()->error(DiagMessage(outputPath)
+ << "failed to write");
+ return false;
}
+ }
- if (!writer->finishEntry()) {
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to finish entry");
- return false;
- }
- return true;
+ if (!writer->finishEntry()) {
+ context->getDiagnostics()->error(DiagMessage(outputPath)
+ << "failed to finish entry");
+ return false;
+ }
+ return true;
}
-static bool writeHeaderAndBufferToWriter(const StringPiece& outputPath, const ResourceFile& file,
- const BigBuffer& buffer, IArchiveWriter* writer,
+static bool writeHeaderAndBufferToWriter(const StringPiece& outputPath,
+ const ResourceFile& file,
+ const BigBuffer& buffer,
+ IArchiveWriter* writer,
IDiagnostics* diag) {
- // Start the entry so we can write the header.
- if (!writer->startEntry(outputPath, 0)) {
- diag->error(DiagMessage(outputPath) << "failed to open file");
- return false;
+ // Start the entry so we can write the header.
+ if (!writer->startEntry(outputPath, 0)) {
+ diag->error(DiagMessage(outputPath) << "failed to open file");
+ return false;
+ }
+
+ // Make sure CopyingOutputStreamAdaptor is deleted before we call
+ // writer->finishEntry().
+ {
+ // Wrap our IArchiveWriter with an adaptor that implements the
+ // ZeroCopyOutputStream
+ // interface.
+ CopyingOutputStreamAdaptor copyingAdaptor(writer);
+ CompiledFileOutputStream outputStream(©ingAdaptor);
+
+ // Number of CompiledFiles.
+ outputStream.WriteLittleEndian32(1);
+
+ std::unique_ptr<pb::CompiledFile> compiledFile =
+ serializeCompiledFileToPb(file);
+ outputStream.WriteCompiledFile(compiledFile.get());
+ outputStream.WriteData(&buffer);
+
+ if (outputStream.HadError()) {
+ diag->error(DiagMessage(outputPath) << "failed to write data");
+ return false;
}
+ }
- // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->finishEntry().
- {
- // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
- // interface.
- CopyingOutputStreamAdaptor copyingAdaptor(writer);
- CompiledFileOutputStream outputStream(©ingAdaptor);
-
- // Number of CompiledFiles.
- outputStream.WriteLittleEndian32(1);
-
- std::unique_ptr<pb::CompiledFile> compiledFile = serializeCompiledFileToPb(file);
- outputStream.WriteCompiledFile(compiledFile.get());
- outputStream.WriteData(&buffer);
-
- if (outputStream.HadError()) {
- diag->error(DiagMessage(outputPath) << "failed to write data");
- return false;
- }
- }
-
- if (!writer->finishEntry()) {
- diag->error(DiagMessage(outputPath) << "failed to finish writing data");
- return false;
- }
- return true;
+ if (!writer->finishEntry()) {
+ diag->error(DiagMessage(outputPath) << "failed to finish writing data");
+ return false;
+ }
+ return true;
}
-static bool writeHeaderAndMmapToWriter(const StringPiece& outputPath, const ResourceFile& file,
- const android::FileMap& map, IArchiveWriter* writer,
+static bool writeHeaderAndMmapToWriter(const StringPiece& outputPath,
+ const ResourceFile& file,
+ const android::FileMap& map,
+ IArchiveWriter* writer,
IDiagnostics* diag) {
- // Start the entry so we can write the header.
- if (!writer->startEntry(outputPath, 0)) {
- diag->error(DiagMessage(outputPath) << "failed to open file");
- return false;
+ // Start the entry so we can write the header.
+ if (!writer->startEntry(outputPath, 0)) {
+ diag->error(DiagMessage(outputPath) << "failed to open file");
+ return false;
+ }
+
+ // Make sure CopyingOutputStreamAdaptor is deleted before we call
+ // writer->finishEntry().
+ {
+ // Wrap our IArchiveWriter with an adaptor that implements the
+ // ZeroCopyOutputStream
+ // interface.
+ CopyingOutputStreamAdaptor copyingAdaptor(writer);
+ CompiledFileOutputStream outputStream(©ingAdaptor);
+
+ // Number of CompiledFiles.
+ outputStream.WriteLittleEndian32(1);
+
+ std::unique_ptr<pb::CompiledFile> compiledFile =
+ serializeCompiledFileToPb(file);
+ outputStream.WriteCompiledFile(compiledFile.get());
+ outputStream.WriteData(map.getDataPtr(), map.getDataLength());
+
+ if (outputStream.HadError()) {
+ diag->error(DiagMessage(outputPath) << "failed to write data");
+ return false;
}
+ }
- // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->finishEntry().
- {
- // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
- // interface.
- CopyingOutputStreamAdaptor copyingAdaptor(writer);
- CompiledFileOutputStream outputStream(©ingAdaptor);
-
- // Number of CompiledFiles.
- outputStream.WriteLittleEndian32(1);
-
- std::unique_ptr<pb::CompiledFile> compiledFile = serializeCompiledFileToPb(file);
- outputStream.WriteCompiledFile(compiledFile.get());
- outputStream.WriteData(map.getDataPtr(), map.getDataLength());
-
- if (outputStream.HadError()) {
- diag->error(DiagMessage(outputPath) << "failed to write data");
- return false;
- }
- }
-
- if (!writer->finishEntry()) {
- diag->error(DiagMessage(outputPath) << "failed to finish writing data");
- return false;
- }
- return true;
+ if (!writer->finishEntry()) {
+ diag->error(DiagMessage(outputPath) << "failed to finish writing data");
+ return false;
+ }
+ return true;
}
-static bool flattenXmlToOutStream(IAaptContext* context, const StringPiece& outputPath,
+static bool flattenXmlToOutStream(IAaptContext* context,
+ const StringPiece& outputPath,
xml::XmlResource* xmlRes,
CompiledFileOutputStream* out) {
- BigBuffer buffer(1024);
- XmlFlattenerOptions xmlFlattenerOptions;
- xmlFlattenerOptions.keepRawValues = true;
- XmlFlattener flattener(&buffer, xmlFlattenerOptions);
- if (!flattener.consume(context, xmlRes)) {
- return false;
- }
+ BigBuffer buffer(1024);
+ XmlFlattenerOptions xmlFlattenerOptions;
+ xmlFlattenerOptions.keepRawValues = true;
+ XmlFlattener flattener(&buffer, xmlFlattenerOptions);
+ if (!flattener.consume(context, xmlRes)) {
+ return false;
+ }
- std::unique_ptr<pb::CompiledFile> pbCompiledFile = serializeCompiledFileToPb(xmlRes->file);
- out->WriteCompiledFile(pbCompiledFile.get());
- out->WriteData(&buffer);
+ std::unique_ptr<pb::CompiledFile> pbCompiledFile =
+ serializeCompiledFileToPb(xmlRes->file);
+ out->WriteCompiledFile(pbCompiledFile.get());
+ out->WriteData(&buffer);
- if (out->HadError()) {
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write data");
- return false;
- }
- return true;
+ if (out->HadError()) {
+ context->getDiagnostics()->error(DiagMessage(outputPath)
+ << "failed to write data");
+ return false;
+ }
+ return true;
}
static bool compileXml(IAaptContext* context, const CompileOptions& options,
const ResourcePathData& pathData, IArchiveWriter* writer,
const std::string& outputPath) {
- if (context->verbose()) {
- context->getDiagnostics()->note(DiagMessage(pathData.source) << "compiling XML");
+ if (context->verbose()) {
+ context->getDiagnostics()->note(DiagMessage(pathData.source)
+ << "compiling XML");
+ }
+
+ std::unique_ptr<xml::XmlResource> xmlRes;
+ {
+ std::ifstream fin(pathData.source.path, std::ifstream::binary);
+ if (!fin) {
+ context->getDiagnostics()->error(DiagMessage(pathData.source)
+ << strerror(errno));
+ return false;
}
- std::unique_ptr<xml::XmlResource> xmlRes;
- {
- std::ifstream fin(pathData.source.path, std::ifstream::binary);
- if (!fin) {
- context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
- return false;
- }
+ xmlRes = xml::inflate(&fin, context->getDiagnostics(), pathData.source);
- xmlRes = xml::inflate(&fin, context->getDiagnostics(), pathData.source);
+ fin.close();
+ }
- fin.close();
+ if (!xmlRes) {
+ return false;
+ }
+
+ xmlRes->file.name =
+ ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
+ xmlRes->file.config = pathData.config;
+ xmlRes->file.source = pathData.source;
+
+ // Collect IDs that are defined here.
+ XmlIdCollector collector;
+ if (!collector.consume(context, xmlRes.get())) {
+ return false;
+ }
+
+ // Look for and process any <aapt:attr> tags and create sub-documents.
+ InlineXmlFormatParser inlineXmlFormatParser;
+ if (!inlineXmlFormatParser.consume(context, xmlRes.get())) {
+ return false;
+ }
+
+ // Start the entry so we can write the header.
+ if (!writer->startEntry(outputPath, 0)) {
+ context->getDiagnostics()->error(DiagMessage(outputPath)
+ << "failed to open file");
+ return false;
+ }
+
+ // Make sure CopyingOutputStreamAdaptor is deleted before we call
+ // writer->finishEntry().
+ {
+ // Wrap our IArchiveWriter with an adaptor that implements the
+ // ZeroCopyOutputStream
+ // interface.
+ CopyingOutputStreamAdaptor copyingAdaptor(writer);
+ CompiledFileOutputStream outputStream(©ingAdaptor);
+
+ std::vector<std::unique_ptr<xml::XmlResource>>& inlineDocuments =
+ inlineXmlFormatParser.getExtractedInlineXmlDocuments();
+
+ // Number of CompiledFiles.
+ outputStream.WriteLittleEndian32(1 + inlineDocuments.size());
+
+ if (!flattenXmlToOutStream(context, outputPath, xmlRes.get(),
+ &outputStream)) {
+ return false;
}
- if (!xmlRes) {
+ for (auto& inlineXmlDoc : inlineDocuments) {
+ if (!flattenXmlToOutStream(context, outputPath, inlineXmlDoc.get(),
+ &outputStream)) {
return false;
+ }
}
+ }
- xmlRes->file.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
- xmlRes->file.config = pathData.config;
- xmlRes->file.source = pathData.source;
-
- // Collect IDs that are defined here.
- XmlIdCollector collector;
- if (!collector.consume(context, xmlRes.get())) {
- return false;
- }
-
- // Look for and process any <aapt:attr> tags and create sub-documents.
- InlineXmlFormatParser inlineXmlFormatParser;
- if (!inlineXmlFormatParser.consume(context, xmlRes.get())) {
- return false;
- }
-
- // Start the entry so we can write the header.
- if (!writer->startEntry(outputPath, 0)) {
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to open file");
- return false;
- }
-
- // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->finishEntry().
- {
- // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
- // interface.
- CopyingOutputStreamAdaptor copyingAdaptor(writer);
- CompiledFileOutputStream outputStream(©ingAdaptor);
-
- std::vector<std::unique_ptr<xml::XmlResource>>& inlineDocuments =
- inlineXmlFormatParser.getExtractedInlineXmlDocuments();
-
- // Number of CompiledFiles.
- outputStream.WriteLittleEndian32(1 + inlineDocuments.size());
-
- if (!flattenXmlToOutStream(context, outputPath, xmlRes.get(), &outputStream)) {
- return false;
- }
-
- for (auto& inlineXmlDoc : inlineDocuments) {
- if (!flattenXmlToOutStream(context, outputPath, inlineXmlDoc.get(), &outputStream)) {
- return false;
- }
- }
- }
-
- if (!writer->finishEntry()) {
- context->getDiagnostics()->error(DiagMessage(outputPath)
- << "failed to finish writing data");
- return false;
- }
- return true;
+ if (!writer->finishEntry()) {
+ context->getDiagnostics()->error(DiagMessage(outputPath)
+ << "failed to finish writing data");
+ return false;
+ }
+ return true;
}
class BigBufferOutputStream : public io::OutputStream {
-public:
- explicit BigBufferOutputStream(BigBuffer* buffer) : mBuffer(buffer) {
- }
+ public:
+ explicit BigBufferOutputStream(BigBuffer* buffer) : mBuffer(buffer) {}
- bool Next(void** data, int* len) override {
- size_t count;
- *data = mBuffer->nextBlock(&count);
- *len = static_cast<int>(count);
- return true;
- }
+ bool Next(void** data, int* len) override {
+ size_t count;
+ *data = mBuffer->nextBlock(&count);
+ *len = static_cast<int>(count);
+ return true;
+ }
- void BackUp(int count) override {
- mBuffer->backUp(count);
- }
+ void BackUp(int count) override { mBuffer->backUp(count); }
- int64_t ByteCount() const override {
- return mBuffer->size();
- }
+ int64_t ByteCount() const override { return mBuffer->size(); }
- bool HadError() const override {
- return false;
- }
+ bool HadError() const override { return false; }
-private:
- BigBuffer* mBuffer;
+ private:
+ BigBuffer* mBuffer;
- DISALLOW_COPY_AND_ASSIGN(BigBufferOutputStream);
+ DISALLOW_COPY_AND_ASSIGN(BigBufferOutputStream);
};
static bool compilePng(IAaptContext* context, const CompileOptions& options,
const ResourcePathData& pathData, IArchiveWriter* writer,
const std::string& outputPath) {
- if (context->verbose()) {
- context->getDiagnostics()->note(DiagMessage(pathData.source) << "compiling PNG");
+ if (context->verbose()) {
+ context->getDiagnostics()->note(DiagMessage(pathData.source)
+ << "compiling PNG");
+ }
+
+ BigBuffer buffer(4096);
+ ResourceFile resFile;
+ resFile.name =
+ ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
+ resFile.config = pathData.config;
+ resFile.source = pathData.source;
+
+ {
+ std::string content;
+ if (!android::base::ReadFileToString(pathData.source.path, &content)) {
+ context->getDiagnostics()->error(
+ DiagMessage(pathData.source)
+ << android::base::SystemErrorCodeToString(errno));
+ return false;
}
- BigBuffer buffer(4096);
- ResourceFile resFile;
- resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
- resFile.config = pathData.config;
- resFile.source = pathData.source;
+ BigBuffer crunchedPngBuffer(4096);
+ BigBufferOutputStream crunchedPngBufferOut(&crunchedPngBuffer);
- {
- std::string content;
- if (!android::base::ReadFileToString(pathData.source.path, &content)) {
- context->getDiagnostics()->error(DiagMessage(pathData.source)
- << android::base::SystemErrorCodeToString(errno));
- return false;
- }
-
- BigBuffer crunchedPngBuffer(4096);
- BigBufferOutputStream crunchedPngBufferOut(&crunchedPngBuffer);
-
- // Ensure that we only keep the chunks we care about if we end up
- // using the original PNG instead of the crunched one.
- PngChunkFilter pngChunkFilter(content);
- std::unique_ptr<Image> image = readPng(context, &pngChunkFilter);
- if (!image) {
- return false;
- }
-
- std::unique_ptr<NinePatch> ninePatch;
- if (pathData.extension == "9.png") {
- std::string err;
- ninePatch = NinePatch::create(image->rows.get(), image->width, image->height, &err);
- if (!ninePatch) {
- context->getDiagnostics()->error(DiagMessage() << err);
- return false;
- }
-
- // Remove the 1px border around the NinePatch.
- // Basically the row array is shifted up by 1, and the length is treated
- // as height - 2.
- // For each row, shift the array to the left by 1, and treat the length as width - 2.
- image->width -= 2;
- image->height -= 2;
- memmove(image->rows.get(), image->rows.get() + 1, image->height * sizeof(uint8_t**));
- for (int32_t h = 0; h < image->height; h++) {
- memmove(image->rows[h], image->rows[h] + 4, image->width * 4);
- }
-
- if (context->verbose()) {
- context->getDiagnostics()->note(DiagMessage(pathData.source)
- << "9-patch: " << *ninePatch);
- }
- }
-
- // Write the crunched PNG.
- if (!writePng(context, image.get(), ninePatch.get(), &crunchedPngBufferOut, {})) {
- return false;
- }
-
- if (ninePatch != nullptr
- || crunchedPngBufferOut.ByteCount() <= pngChunkFilter.ByteCount()) {
- // No matter what, we must use the re-encoded PNG, even if it is larger.
- // 9-patch images must be re-encoded since their borders are stripped.
- buffer.appendBuffer(std::move(crunchedPngBuffer));
- } else {
- // The re-encoded PNG is larger than the original, and there is
- // no mandatory transformation. Use the original.
- if (context->verbose()) {
- context->getDiagnostics()->note(DiagMessage(pathData.source)
- << "original PNG is smaller than crunched PNG"
- << ", using original");
- }
-
- PngChunkFilter pngChunkFilterAgain(content);
- BigBuffer filteredPngBuffer(4096);
- BigBufferOutputStream filteredPngBufferOut(&filteredPngBuffer);
- io::copy(&filteredPngBufferOut, &pngChunkFilterAgain);
- buffer.appendBuffer(std::move(filteredPngBuffer));
- }
-
- if (context->verbose()) {
- // For debugging only, use the legacy PNG cruncher and compare the resulting file sizes.
- // This will help catch exotic cases where the new code may generate larger PNGs.
- std::stringstream legacyStream(content);
- BigBuffer legacyBuffer(4096);
- Png png(context->getDiagnostics());
- if (!png.process(pathData.source, &legacyStream, &legacyBuffer, {})) {
- return false;
- }
-
- context->getDiagnostics()->note(DiagMessage(pathData.source)
- << "legacy=" << legacyBuffer.size()
- << " new=" << buffer.size());
- }
+ // Ensure that we only keep the chunks we care about if we end up
+ // using the original PNG instead of the crunched one.
+ PngChunkFilter pngChunkFilter(content);
+ std::unique_ptr<Image> image = readPng(context, &pngChunkFilter);
+ if (!image) {
+ return false;
}
- if (!writeHeaderAndBufferToWriter(outputPath, resFile, buffer, writer,
- context->getDiagnostics())) {
+ std::unique_ptr<NinePatch> ninePatch;
+ if (pathData.extension == "9.png") {
+ std::string err;
+ ninePatch = NinePatch::create(image->rows.get(), image->width,
+ image->height, &err);
+ if (!ninePatch) {
+ context->getDiagnostics()->error(DiagMessage() << err);
return false;
+ }
+
+ // Remove the 1px border around the NinePatch.
+ // Basically the row array is shifted up by 1, and the length is treated
+ // as height - 2.
+ // For each row, shift the array to the left by 1, and treat the length as
+ // width - 2.
+ image->width -= 2;
+ image->height -= 2;
+ memmove(image->rows.get(), image->rows.get() + 1,
+ image->height * sizeof(uint8_t**));
+ for (int32_t h = 0; h < image->height; h++) {
+ memmove(image->rows[h], image->rows[h] + 4, image->width * 4);
+ }
+
+ if (context->verbose()) {
+ context->getDiagnostics()->note(DiagMessage(pathData.source)
+ << "9-patch: " << *ninePatch);
+ }
}
- return true;
+
+ // Write the crunched PNG.
+ if (!writePng(context, image.get(), ninePatch.get(), &crunchedPngBufferOut,
+ {})) {
+ return false;
+ }
+
+ if (ninePatch != nullptr ||
+ crunchedPngBufferOut.ByteCount() <= pngChunkFilter.ByteCount()) {
+ // No matter what, we must use the re-encoded PNG, even if it is larger.
+ // 9-patch images must be re-encoded since their borders are stripped.
+ buffer.appendBuffer(std::move(crunchedPngBuffer));
+ } else {
+ // The re-encoded PNG is larger than the original, and there is
+ // no mandatory transformation. Use the original.
+ if (context->verbose()) {
+ context->getDiagnostics()->note(
+ DiagMessage(pathData.source)
+ << "original PNG is smaller than crunched PNG"
+ << ", using original");
+ }
+
+ PngChunkFilter pngChunkFilterAgain(content);
+ BigBuffer filteredPngBuffer(4096);
+ BigBufferOutputStream filteredPngBufferOut(&filteredPngBuffer);
+ io::copy(&filteredPngBufferOut, &pngChunkFilterAgain);
+ buffer.appendBuffer(std::move(filteredPngBuffer));
+ }
+
+ if (context->verbose()) {
+ // For debugging only, use the legacy PNG cruncher and compare the
+ // resulting file sizes.
+ // This will help catch exotic cases where the new code may generate
+ // larger PNGs.
+ std::stringstream legacyStream(content);
+ BigBuffer legacyBuffer(4096);
+ Png png(context->getDiagnostics());
+ if (!png.process(pathData.source, &legacyStream, &legacyBuffer, {})) {
+ return false;
+ }
+
+ context->getDiagnostics()->note(DiagMessage(pathData.source)
+ << "legacy=" << legacyBuffer.size()
+ << " new=" << buffer.size());
+ }
+ }
+
+ if (!writeHeaderAndBufferToWriter(outputPath, resFile, buffer, writer,
+ context->getDiagnostics())) {
+ return false;
+ }
+ return true;
}
static bool compileFile(IAaptContext* context, const CompileOptions& options,
- const ResourcePathData& pathData, IArchiveWriter* writer,
- const std::string& outputPath) {
- if (context->verbose()) {
- context->getDiagnostics()->note(DiagMessage(pathData.source) << "compiling file");
- }
+ const ResourcePathData& pathData,
+ IArchiveWriter* writer, const std::string& outputPath) {
+ if (context->verbose()) {
+ context->getDiagnostics()->note(DiagMessage(pathData.source)
+ << "compiling file");
+ }
- BigBuffer buffer(256);
- ResourceFile resFile;
- resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
- resFile.config = pathData.config;
- resFile.source = pathData.source;
+ BigBuffer buffer(256);
+ ResourceFile resFile;
+ resFile.name =
+ ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
+ resFile.config = pathData.config;
+ resFile.source = pathData.source;
- std::string errorStr;
- Maybe<android::FileMap> f = file::mmapPath(pathData.source.path, &errorStr);
- if (!f) {
- context->getDiagnostics()->error(DiagMessage(pathData.source) << errorStr);
- return false;
- }
+ std::string errorStr;
+ Maybe<android::FileMap> f = file::mmapPath(pathData.source.path, &errorStr);
+ if (!f) {
+ context->getDiagnostics()->error(DiagMessage(pathData.source) << errorStr);
+ return false;
+ }
- if (!writeHeaderAndMmapToWriter(outputPath, resFile, f.value(), writer,
- context->getDiagnostics())) {
- return false;
- }
- return true;
+ if (!writeHeaderAndMmapToWriter(outputPath, resFile, f.value(), writer,
+ context->getDiagnostics())) {
+ return false;
+ }
+ return true;
}
class CompileContext : public IAaptContext {
-public:
- void setVerbose(bool val) {
- mVerbose = val;
- }
+ public:
+ void setVerbose(bool val) { mVerbose = val; }
- bool verbose() override {
- return mVerbose;
- }
+ bool verbose() override { return mVerbose; }
- IDiagnostics* getDiagnostics() override {
- return &mDiagnostics;
- }
+ IDiagnostics* getDiagnostics() override { return &mDiagnostics; }
- NameMangler* getNameMangler() override {
- abort();
- return nullptr;
- }
+ NameMangler* getNameMangler() override {
+ abort();
+ return nullptr;
+ }
- const std::string& getCompilationPackage() override {
- static std::string empty;
- return empty;
- }
+ const std::string& getCompilationPackage() override {
+ static std::string empty;
+ return empty;
+ }
- uint8_t getPackageId() override {
- return 0x0;
- }
+ uint8_t getPackageId() override { return 0x0; }
- SymbolTable* getExternalSymbols() override {
- abort();
- return nullptr;
- }
+ SymbolTable* getExternalSymbols() override {
+ abort();
+ return nullptr;
+ }
- int getMinSdkVersion() override {
- return 0;
- }
+ int getMinSdkVersion() override { return 0; }
-private:
- StdErrDiagnostics mDiagnostics;
- bool mVerbose = false;
-
+ private:
+ StdErrDiagnostics mDiagnostics;
+ bool mVerbose = false;
};
/**
- * Entry point for compilation phase. Parses arguments and dispatches to the correct steps.
+ * Entry point for compilation phase. Parses arguments and dispatches to the
+ * correct steps.
*/
int compile(const std::vector<StringPiece>& args) {
- CompileContext context;
- CompileOptions options;
+ CompileContext context;
+ CompileOptions options;
- bool verbose = false;
- Flags flags = Flags()
- .requiredFlag("-o", "Output path", &options.outputPath)
- .optionalFlag("--dir", "Directory to scan for resources", &options.resDir)
- .optionalSwitch("--pseudo-localize", "Generate resources for pseudo-locales "
- "(en-XA and ar-XB)", &options.pseudolocalize)
- .optionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
- &options.legacyMode)
- .optionalSwitch("-v", "Enables verbose logging", &verbose);
- if (!flags.parse("aapt2 compile", args, &std::cerr)) {
- return 1;
+ bool verbose = false;
+ Flags flags =
+ Flags()
+ .requiredFlag("-o", "Output path", &options.outputPath)
+ .optionalFlag("--dir", "Directory to scan for resources",
+ &options.resDir)
+ .optionalSwitch("--pseudo-localize",
+ "Generate resources for pseudo-locales "
+ "(en-XA and ar-XB)",
+ &options.pseudolocalize)
+ .optionalSwitch(
+ "--legacy",
+ "Treat errors that used to be valid in AAPT as warnings",
+ &options.legacyMode)
+ .optionalSwitch("-v", "Enables verbose logging", &verbose);
+ if (!flags.parse("aapt2 compile", args, &std::cerr)) {
+ return 1;
+ }
+
+ context.setVerbose(verbose);
+
+ std::unique_ptr<IArchiveWriter> archiveWriter;
+
+ std::vector<ResourcePathData> inputData;
+ if (options.resDir) {
+ if (!flags.getArgs().empty()) {
+ // Can't have both files and a resource directory.
+ context.getDiagnostics()->error(DiagMessage()
+ << "files given but --dir specified");
+ flags.usage("aapt2 compile", &std::cerr);
+ return 1;
}
- context.setVerbose(verbose);
+ if (!loadInputFilesFromDir(&context, options, &inputData)) {
+ return 1;
+ }
- std::unique_ptr<IArchiveWriter> archiveWriter;
+ archiveWriter = createZipFileArchiveWriter(context.getDiagnostics(),
+ options.outputPath);
- std::vector<ResourcePathData> inputData;
- if (options.resDir) {
- if (!flags.getArgs().empty()) {
- // Can't have both files and a resource directory.
- context.getDiagnostics()->error(DiagMessage() << "files given but --dir specified");
- flags.usage("aapt2 compile", &std::cerr);
- return 1;
- }
+ } else {
+ inputData.reserve(flags.getArgs().size());
- if (!loadInputFilesFromDir(&context, options, &inputData)) {
- return 1;
- }
+ // Collect data from the path for each input file.
+ for (const std::string& arg : flags.getArgs()) {
+ std::string errorStr;
+ if (Maybe<ResourcePathData> pathData =
+ extractResourcePathData(arg, &errorStr)) {
+ inputData.push_back(std::move(pathData.value()));
+ } else {
+ context.getDiagnostics()->error(DiagMessage() << errorStr << " (" << arg
+ << ")");
+ return 1;
+ }
+ }
- archiveWriter = createZipFileArchiveWriter(context.getDiagnostics(), options.outputPath);
+ archiveWriter = createDirectoryArchiveWriter(context.getDiagnostics(),
+ options.outputPath);
+ }
+
+ if (!archiveWriter) {
+ return false;
+ }
+
+ bool error = false;
+ for (ResourcePathData& pathData : inputData) {
+ if (options.verbose) {
+ context.getDiagnostics()->note(DiagMessage(pathData.source)
+ << "processing");
+ }
+
+ if (pathData.resourceDir == "values") {
+ // Overwrite the extension.
+ pathData.extension = "arsc";
+
+ const std::string outputFilename = buildIntermediateFilename(pathData);
+ if (!compileTable(&context, options, pathData, archiveWriter.get(),
+ outputFilename)) {
+ error = true;
+ }
} else {
- inputData.reserve(flags.getArgs().size());
-
- // Collect data from the path for each input file.
- for (const std::string& arg : flags.getArgs()) {
- std::string errorStr;
- if (Maybe<ResourcePathData> pathData = extractResourcePathData(arg, &errorStr)) {
- inputData.push_back(std::move(pathData.value()));
- } else {
- context.getDiagnostics()->error(DiagMessage() << errorStr << " (" << arg << ")");
- return 1;
+ const std::string outputFilename = buildIntermediateFilename(pathData);
+ if (const ResourceType* type = parseResourceType(pathData.resourceDir)) {
+ if (*type != ResourceType::kRaw) {
+ if (pathData.extension == "xml") {
+ if (!compileXml(&context, options, pathData, archiveWriter.get(),
+ outputFilename)) {
+ error = true;
}
- }
-
- archiveWriter = createDirectoryArchiveWriter(context.getDiagnostics(), options.outputPath);
- }
-
- if (!archiveWriter) {
- return false;
- }
-
- bool error = false;
- for (ResourcePathData& pathData : inputData) {
- if (options.verbose) {
- context.getDiagnostics()->note(DiagMessage(pathData.source) << "processing");
- }
-
- if (pathData.resourceDir == "values") {
- // Overwrite the extension.
- pathData.extension = "arsc";
-
- const std::string outputFilename = buildIntermediateFilename(pathData);
- if (!compileTable(&context, options, pathData, archiveWriter.get(), outputFilename)) {
- error = true;
+ } else if (pathData.extension == "png" ||
+ pathData.extension == "9.png") {
+ if (!compilePng(&context, options, pathData, archiveWriter.get(),
+ outputFilename)) {
+ error = true;
}
-
+ } else {
+ if (!compileFile(&context, options, pathData, archiveWriter.get(),
+ outputFilename)) {
+ error = true;
+ }
+ }
} else {
- const std::string outputFilename = buildIntermediateFilename(pathData);
- if (const ResourceType* type = parseResourceType(pathData.resourceDir)) {
- if (*type != ResourceType::kRaw) {
- if (pathData.extension == "xml") {
- if (!compileXml(&context, options, pathData, archiveWriter.get(),
- outputFilename)) {
- error = true;
- }
- } else if (pathData.extension == "png" || pathData.extension == "9.png") {
- if (!compilePng(&context, options, pathData, archiveWriter.get(),
- outputFilename)) {
- error = true;
- }
- } else {
- if (!compileFile(&context, options, pathData, archiveWriter.get(),
- outputFilename)) {
- error = true;
- }
- }
- } else {
- if (!compileFile(&context, options, pathData, archiveWriter.get(),
- outputFilename)) {
- error = true;
- }
- }
- } else {
- context.getDiagnostics()->error(
- DiagMessage() << "invalid file path '" << pathData.source << "'");
- error = true;
- }
+ if (!compileFile(&context, options, pathData, archiveWriter.get(),
+ outputFilename)) {
+ error = true;
+ }
}
+ } else {
+ context.getDiagnostics()->error(
+ DiagMessage() << "invalid file path '" << pathData.source << "'");
+ error = true;
+ }
}
+ }
- if (error) {
- return 1;
- }
- return 0;
+ if (error) {
+ return 1;
+ }
+ return 0;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp
index 4a3f1e1..73eb066 100644
--- a/tools/aapt2/compile/IdAssigner.cpp
+++ b/tools/aapt2/compile/IdAssigner.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "ResourceTable.h"
#include "compile/IdAssigner.h"
+#include "ResourceTable.h"
#include "process/IResourceTableConsumer.h"
#include "util/Util.h"
@@ -25,178 +25,192 @@
namespace aapt {
/**
- * Assigns the intended ID to the ResourceTablePackage, ResourceTableType, and ResourceEntry,
+ * Assigns the intended ID to the ResourceTablePackage, ResourceTableType, and
+ * ResourceEntry,
* as long as there is no existing ID or the ID is the same.
*/
-static bool assignId(IDiagnostics* diag, const ResourceId& id, const ResourceName& name,
- ResourceTablePackage* pkg, ResourceTableType* type, ResourceEntry* entry) {
- if (pkg->id.value() == id.packageId()) {
- if (!type->id || type->id.value() == id.typeId()) {
- type->id = id.typeId();
+static bool assignId(IDiagnostics* diag, const ResourceId& id,
+ const ResourceName& name, ResourceTablePackage* pkg,
+ ResourceTableType* type, ResourceEntry* entry) {
+ if (pkg->id.value() == id.packageId()) {
+ if (!type->id || type->id.value() == id.typeId()) {
+ type->id = id.typeId();
- if (!entry->id || entry->id.value() == id.entryId()) {
- entry->id = id.entryId();
- return true;
- }
- }
+ if (!entry->id || entry->id.value() == id.entryId()) {
+ entry->id = id.entryId();
+ return true;
+ }
}
+ }
- const ResourceId existingId(pkg->id.value(),
- type->id ? type->id.value() : 0,
- entry->id ? entry->id.value() : 0);
- diag->error(DiagMessage() << "can't assign ID " << id
- << " to resource " << name
- << " with conflicting ID " << existingId);
- return false;
+ const ResourceId existingId(pkg->id.value(), type->id ? type->id.value() : 0,
+ entry->id ? entry->id.value() : 0);
+ diag->error(DiagMessage() << "can't assign ID " << id << " to resource "
+ << name << " with conflicting ID " << existingId);
+ return false;
}
bool IdAssigner::consume(IAaptContext* context, ResourceTable* table) {
- std::map<ResourceId, ResourceName> assignedIds;
+ std::map<ResourceId, ResourceName> assignedIds;
- for (auto& package : table->packages) {
- assert(package->id && "packages must have manually assigned IDs");
+ for (auto& package : table->packages) {
+ assert(package->id && "packages must have manually assigned IDs");
- for (auto& type : package->types) {
- for (auto& entry : type->entries) {
- const ResourceName name(package->name, type->type, entry->name);
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
+ const ResourceName name(package->name, type->type, entry->name);
- if (mAssignedIdMap) {
- // Assign the pre-assigned stable ID meant for this resource.
- const auto iter = mAssignedIdMap->find(name);
- if (iter != mAssignedIdMap->end()) {
- const ResourceId assignedId = iter->second;
- const bool result = assignId(context->getDiagnostics(), assignedId, name,
- package.get(), type.get(), entry.get());
- if (!result) {
- return false;
- }
- }
- }
-
- if (package->id && type->id && entry->id) {
- // If the ID is set for this resource, then reserve it.
- ResourceId resourceId(package->id.value(), type->id.value(), entry->id.value());
- auto result = assignedIds.insert({ resourceId, name });
- const ResourceName& existingName = result.first->second;
- if (!result.second) {
- context->getDiagnostics()->error(DiagMessage() << "resource " << name
- << " has same ID "
- << resourceId
- << " as " << existingName);
- return false;
- }
- }
+ if (mAssignedIdMap) {
+ // Assign the pre-assigned stable ID meant for this resource.
+ const auto iter = mAssignedIdMap->find(name);
+ if (iter != mAssignedIdMap->end()) {
+ const ResourceId assignedId = iter->second;
+ const bool result =
+ assignId(context->getDiagnostics(), assignedId, name,
+ package.get(), type.get(), entry.get());
+ if (!result) {
+ return false;
}
+ }
}
- }
- if (mAssignedIdMap) {
- // Reserve all the IDs mentioned in the stable ID map. That way we won't assign
- // IDs that were listed in the map if they don't exist in the table.
- for (const auto& stableIdEntry : *mAssignedIdMap) {
- const ResourceName& preAssignedName = stableIdEntry.first;
- const ResourceId& preAssignedId = stableIdEntry.second;
- auto result = assignedIds.insert({ preAssignedId, preAssignedName });
- const ResourceName& existingName = result.first->second;
- if (!result.second && existingName != preAssignedName) {
- context->getDiagnostics()->error(DiagMessage() << "stable ID " << preAssignedId
- << " for resource " << preAssignedName
- << " is already taken by resource "
- << existingName);
- return false;
- }
+ if (package->id && type->id && entry->id) {
+ // If the ID is set for this resource, then reserve it.
+ ResourceId resourceId(package->id.value(), type->id.value(),
+ entry->id.value());
+ auto result = assignedIds.insert({resourceId, name});
+ const ResourceName& existingName = result.first->second;
+ if (!result.second) {
+ context->getDiagnostics()->error(
+ DiagMessage() << "resource " << name << " has same ID "
+ << resourceId << " as " << existingName);
+ return false;
+ }
}
+ }
}
+ }
- // Assign any resources without IDs the next available ID. Gaps will be filled if possible,
- // unless those IDs have been reserved.
+ if (mAssignedIdMap) {
+ // Reserve all the IDs mentioned in the stable ID map. That way we won't
+ // assign
+ // IDs that were listed in the map if they don't exist in the table.
+ for (const auto& stableIdEntry : *mAssignedIdMap) {
+ const ResourceName& preAssignedName = stableIdEntry.first;
+ const ResourceId& preAssignedId = stableIdEntry.second;
+ auto result = assignedIds.insert({preAssignedId, preAssignedName});
+ const ResourceName& existingName = result.first->second;
+ if (!result.second && existingName != preAssignedName) {
+ context->getDiagnostics()->error(
+ DiagMessage() << "stable ID " << preAssignedId << " for resource "
+ << preAssignedName << " is already taken by resource "
+ << existingName);
+ return false;
+ }
+ }
+ }
- const auto assignedIdsIterEnd = assignedIds.end();
- for (auto& package : table->packages) {
- assert(package->id && "packages must have manually assigned IDs");
+ // Assign any resources without IDs the next available ID. Gaps will be filled
+ // if possible,
+ // unless those IDs have been reserved.
- // Build a half filled ResourceId object, which will be used to find the closest matching
- // reserved ID in the assignedId map. From that point the next available type ID can be
- // found.
- ResourceId resourceId(package->id.value(), 0, 0);
- uint8_t nextExpectedTypeId = 1;
+ const auto assignedIdsIterEnd = assignedIds.end();
+ for (auto& package : table->packages) {
+ assert(package->id && "packages must have manually assigned IDs");
- // Find the closest matching ResourceId that is <= the one with only the package set.
- auto nextTypeIter = assignedIds.lower_bound(resourceId);
- for (auto& type : package->types) {
- if (!type->id) {
- // We need to assign a type ID. Iterate over the reserved IDs until we find
- // some type ID that is a distance of 2 greater than the last one we've seen.
- // That means there is an available type ID between these reserved IDs.
- while (nextTypeIter != assignedIdsIterEnd) {
- if (nextTypeIter->first.packageId() != package->id.value()) {
- break;
- }
+ // Build a half filled ResourceId object, which will be used to find the
+ // closest matching
+ // reserved ID in the assignedId map. From that point the next available
+ // type ID can be
+ // found.
+ ResourceId resourceId(package->id.value(), 0, 0);
+ uint8_t nextExpectedTypeId = 1;
- const uint8_t typeId = nextTypeIter->first.typeId();
- if (typeId > nextExpectedTypeId) {
- // There is a gap in the type IDs, so use the missing one.
- type->id = nextExpectedTypeId++;
- break;
- }
+ // Find the closest matching ResourceId that is <= the one with only the
+ // package set.
+ auto nextTypeIter = assignedIds.lower_bound(resourceId);
+ for (auto& type : package->types) {
+ if (!type->id) {
+ // We need to assign a type ID. Iterate over the reserved IDs until we
+ // find
+ // some type ID that is a distance of 2 greater than the last one we've
+ // seen.
+ // That means there is an available type ID between these reserved IDs.
+ while (nextTypeIter != assignedIdsIterEnd) {
+ if (nextTypeIter->first.packageId() != package->id.value()) {
+ break;
+ }
- // Set our expectation to be the next type ID after the reserved one we
- // just saw.
- nextExpectedTypeId = typeId + 1;
+ const uint8_t typeId = nextTypeIter->first.typeId();
+ if (typeId > nextExpectedTypeId) {
+ // There is a gap in the type IDs, so use the missing one.
+ type->id = nextExpectedTypeId++;
+ break;
+ }
- // Move to the next reserved ID.
- ++nextTypeIter;
- }
+ // Set our expectation to be the next type ID after the reserved one
+ // we
+ // just saw.
+ nextExpectedTypeId = typeId + 1;
- if (!type->id) {
- // We must have hit the end of the reserved IDs and not found a gap.
- // That means the next ID is available.
- type->id = nextExpectedTypeId++;
- }
- }
-
- resourceId = ResourceId(package->id.value(), type->id.value(), 0);
- uint16_t nextExpectedEntryId = 0;
-
- // Find the closest matching ResourceId that is <= the one with only the package
- // and type set.
- auto nextEntryIter = assignedIds.lower_bound(resourceId);
- for (auto& entry : type->entries) {
- if (!entry->id) {
- // We need to assign an entry ID. Iterate over the reserved IDs until we find
- // some entry ID that is a distance of 2 greater than the last one we've seen.
- // That means there is an available entry ID between these reserved IDs.
- while (nextEntryIter != assignedIdsIterEnd) {
- if (nextEntryIter->first.packageId() != package->id.value() ||
- nextEntryIter->first.typeId() != type->id.value()) {
- break;
- }
-
- const uint16_t entryId = nextEntryIter->first.entryId();
- if (entryId > nextExpectedEntryId) {
- // There is a gap in the entry IDs, so use the missing one.
- entry->id = nextExpectedEntryId++;
- break;
- }
-
- // Set our expectation to be the next type ID after the reserved one we
- // just saw.
- nextExpectedEntryId = entryId + 1;
-
- // Move to the next reserved entry ID.
- ++nextEntryIter;
- }
-
- if (!entry->id) {
- // We must have hit the end of the reserved IDs and not found a gap.
- // That means the next ID is available.
- entry->id = nextExpectedEntryId++;
- }
- }
- }
+ // Move to the next reserved ID.
+ ++nextTypeIter;
}
+
+ if (!type->id) {
+ // We must have hit the end of the reserved IDs and not found a gap.
+ // That means the next ID is available.
+ type->id = nextExpectedTypeId++;
+ }
+ }
+
+ resourceId = ResourceId(package->id.value(), type->id.value(), 0);
+ uint16_t nextExpectedEntryId = 0;
+
+ // Find the closest matching ResourceId that is <= the one with only the
+ // package
+ // and type set.
+ auto nextEntryIter = assignedIds.lower_bound(resourceId);
+ for (auto& entry : type->entries) {
+ if (!entry->id) {
+ // We need to assign an entry ID. Iterate over the reserved IDs until
+ // we find
+ // some entry ID that is a distance of 2 greater than the last one
+ // we've seen.
+ // That means there is an available entry ID between these reserved
+ // IDs.
+ while (nextEntryIter != assignedIdsIterEnd) {
+ if (nextEntryIter->first.packageId() != package->id.value() ||
+ nextEntryIter->first.typeId() != type->id.value()) {
+ break;
+ }
+
+ const uint16_t entryId = nextEntryIter->first.entryId();
+ if (entryId > nextExpectedEntryId) {
+ // There is a gap in the entry IDs, so use the missing one.
+ entry->id = nextExpectedEntryId++;
+ break;
+ }
+
+ // Set our expectation to be the next type ID after the reserved one
+ // we
+ // just saw.
+ nextExpectedEntryId = entryId + 1;
+
+ // Move to the next reserved entry ID.
+ ++nextEntryIter;
+ }
+
+ if (!entry->id) {
+ // We must have hit the end of the reserved IDs and not found a gap.
+ // That means the next ID is available.
+ entry->id = nextExpectedEntryId++;
+ }
+ }
+ }
}
- return true;
+ }
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/IdAssigner.h b/tools/aapt2/compile/IdAssigner.h
index 06cd5e3..d399064 100644
--- a/tools/aapt2/compile/IdAssigner.h
+++ b/tools/aapt2/compile/IdAssigner.h
@@ -26,22 +26,22 @@
namespace aapt {
/**
- * Assigns IDs to each resource in the table, respecting existing IDs and filling in gaps
+ * Assigns IDs to each resource in the table, respecting existing IDs and
+ * filling in gaps
* in between fixed ID assignments.
*/
class IdAssigner : public IResourceTableConsumer {
-public:
- IdAssigner() = default;
- explicit IdAssigner(const std::unordered_map<ResourceName, ResourceId>* map) :
- mAssignedIdMap(map) {
- }
+ public:
+ IdAssigner() = default;
+ explicit IdAssigner(const std::unordered_map<ResourceName, ResourceId>* map)
+ : mAssignedIdMap(map) {}
- bool consume(IAaptContext* context, ResourceTable* table) override;
+ bool consume(IAaptContext* context, ResourceTable* table) override;
-private:
- const std::unordered_map<ResourceName, ResourceId>* mAssignedIdMap = nullptr;
+ private:
+ const std::unordered_map<ResourceName, ResourceId>* mAssignedIdMap = nullptr;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_COMPILE_IDASSIGNER_H */
diff --git a/tools/aapt2/compile/IdAssigner_test.cpp b/tools/aapt2/compile/IdAssigner_test.cpp
index d21fcba..ff7bf5c 100644
--- a/tools/aapt2/compile/IdAssigner_test.cpp
+++ b/tools/aapt2/compile/IdAssigner_test.cpp
@@ -22,154 +22,163 @@
::testing::AssertionResult verifyIds(ResourceTable* table);
TEST(IdAssignerTest, AssignIds) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addSimple("android:attr/foo")
- .addSimple("android:attr/bar")
- .addSimple("android:id/foo")
- .setPackageId("android", 0x01)
- .build();
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .addSimple("android:attr/foo")
+ .addSimple("android:attr/bar")
+ .addSimple("android:id/foo")
+ .setPackageId("android", 0x01)
+ .build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- IdAssigner assigner;
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ IdAssigner assigner;
- ASSERT_TRUE(assigner.consume(context.get(), table.get()));
- ASSERT_TRUE(verifyIds(table.get()));
+ ASSERT_TRUE(assigner.consume(context.get(), table.get()));
+ ASSERT_TRUE(verifyIds(table.get()));
}
TEST(IdAssignerTest, AssignIdsWithReservedIds) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addSimple("android:id/foo", ResourceId(0x01010000))
- .addSimple("android:dimen/two")
- .addSimple("android:integer/three")
- .addSimple("android:string/five")
- .addSimple("android:attr/fun", ResourceId(0x01040000))
- .addSimple("android:attr/foo", ResourceId(0x01040006))
- .addSimple("android:attr/bar")
- .addSimple("android:attr/baz")
- .addSimple("app:id/biz")
- .setPackageId("android", 0x01)
- .setPackageId("app", 0x7f)
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .addSimple("android:id/foo", ResourceId(0x01010000))
+ .addSimple("android:dimen/two")
+ .addSimple("android:integer/three")
+ .addSimple("android:string/five")
+ .addSimple("android:attr/fun", ResourceId(0x01040000))
+ .addSimple("android:attr/foo", ResourceId(0x01040006))
+ .addSimple("android:attr/bar")
+ .addSimple("android:attr/baz")
+ .addSimple("app:id/biz")
+ .setPackageId("android", 0x01)
+ .setPackageId("app", 0x7f)
+ .build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- IdAssigner assigner;
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ IdAssigner assigner;
- ASSERT_TRUE(assigner.consume(context.get(), table.get()));
- ASSERT_TRUE(verifyIds(table.get()));
+ ASSERT_TRUE(assigner.consume(context.get(), table.get()));
+ ASSERT_TRUE(verifyIds(table.get()));
- Maybe<ResourceTable::SearchResult> maybeResult;
+ Maybe<ResourceTable::SearchResult> maybeResult;
- // Expect to fill in the gaps between 0x0101XXXX and 0x0104XXXX.
+ // Expect to fill in the gaps between 0x0101XXXX and 0x0104XXXX.
- maybeResult = table->findResource(test::parseNameOrDie("android:dimen/two"));
- AAPT_ASSERT_TRUE(maybeResult);
- EXPECT_EQ(make_value<uint8_t>(2), maybeResult.value().type->id);
+ maybeResult = table->findResource(test::parseNameOrDie("android:dimen/two"));
+ AAPT_ASSERT_TRUE(maybeResult);
+ EXPECT_EQ(make_value<uint8_t>(2), maybeResult.value().type->id);
- maybeResult = table->findResource(test::parseNameOrDie("android:integer/three"));
- AAPT_ASSERT_TRUE(maybeResult);
- EXPECT_EQ(make_value<uint8_t>(3), maybeResult.value().type->id);
+ maybeResult =
+ table->findResource(test::parseNameOrDie("android:integer/three"));
+ AAPT_ASSERT_TRUE(maybeResult);
+ EXPECT_EQ(make_value<uint8_t>(3), maybeResult.value().type->id);
- // Expect to bypass the reserved 0x0104XXXX IDs and use the next 0x0105XXXX IDs.
+ // Expect to bypass the reserved 0x0104XXXX IDs and use the next 0x0105XXXX
+ // IDs.
- maybeResult = table->findResource(test::parseNameOrDie("android:string/five"));
- AAPT_ASSERT_TRUE(maybeResult);
- EXPECT_EQ(make_value<uint8_t>(5), maybeResult.value().type->id);
+ maybeResult =
+ table->findResource(test::parseNameOrDie("android:string/five"));
+ AAPT_ASSERT_TRUE(maybeResult);
+ EXPECT_EQ(make_value<uint8_t>(5), maybeResult.value().type->id);
- // Expect to fill in the gaps between 0x01040000 and 0x01040006.
+ // Expect to fill in the gaps between 0x01040000 and 0x01040006.
- maybeResult = table->findResource(test::parseNameOrDie("android:attr/bar"));
- AAPT_ASSERT_TRUE(maybeResult);
- EXPECT_EQ(make_value<uint16_t>(1), maybeResult.value().entry->id);
+ maybeResult = table->findResource(test::parseNameOrDie("android:attr/bar"));
+ AAPT_ASSERT_TRUE(maybeResult);
+ EXPECT_EQ(make_value<uint16_t>(1), maybeResult.value().entry->id);
- maybeResult = table->findResource(test::parseNameOrDie("android:attr/baz"));
- AAPT_ASSERT_TRUE(maybeResult);
- EXPECT_EQ(make_value<uint16_t>(2), maybeResult.value().entry->id);
+ maybeResult = table->findResource(test::parseNameOrDie("android:attr/baz"));
+ AAPT_ASSERT_TRUE(maybeResult);
+ EXPECT_EQ(make_value<uint16_t>(2), maybeResult.value().entry->id);
}
TEST(IdAssignerTest, FailWhenNonUniqueIdsAssigned) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addSimple("android:attr/foo", ResourceId(0x01040006))
- .addSimple("android:attr/bar", ResourceId(0x01040006))
- .setPackageId("android", 0x01)
- .setPackageId("app", 0x7f)
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .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();
- IdAssigner assigner;
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ IdAssigner assigner;
- ASSERT_FALSE(assigner.consume(context.get(), table.get()));
+ ASSERT_FALSE(assigner.consume(context.get(), table.get()));
}
TEST(IdAssignerTest, AssignIdsWithIdMap) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addSimple("android:attr/foo")
- .addSimple("android:attr/bar")
- .setPackageId("android", 0x01)
- .build();
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .addSimple("android:attr/foo")
+ .addSimple("android:attr/bar")
+ .setPackageId("android", 0x01)
+ .build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::unordered_map<ResourceName, ResourceId> idMap = {
- { test::parseNameOrDie("android:attr/foo"), ResourceId(0x01010002) } };
- IdAssigner assigner(&idMap);
- ASSERT_TRUE(assigner.consume(context.get(), table.get()));
- ASSERT_TRUE(verifyIds(table.get()));
- Maybe<ResourceTable::SearchResult> result = table->findResource(
- test::parseNameOrDie("android:attr/foo"));
- AAPT_ASSERT_TRUE(result);
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unordered_map<ResourceName, ResourceId> idMap = {
+ {test::parseNameOrDie("android:attr/foo"), ResourceId(0x01010002)}};
+ IdAssigner assigner(&idMap);
+ ASSERT_TRUE(assigner.consume(context.get(), table.get()));
+ ASSERT_TRUE(verifyIds(table.get()));
+ Maybe<ResourceTable::SearchResult> result =
+ table->findResource(test::parseNameOrDie("android:attr/foo"));
+ AAPT_ASSERT_TRUE(result);
- const ResourceTable::SearchResult& searchResult = result.value();
- EXPECT_EQ(make_value<uint8_t>(0x01), searchResult.package->id);
- EXPECT_EQ(make_value<uint8_t>(0x01), searchResult.type->id);
- EXPECT_EQ(make_value<uint16_t>(0x0002), searchResult.entry->id);
+ const ResourceTable::SearchResult& searchResult = result.value();
+ EXPECT_EQ(make_value<uint8_t>(0x01), searchResult.package->id);
+ EXPECT_EQ(make_value<uint8_t>(0x01), searchResult.type->id);
+ EXPECT_EQ(make_value<uint16_t>(0x0002), searchResult.entry->id);
}
::testing::AssertionResult verifyIds(ResourceTable* table) {
- std::set<uint8_t> packageIds;
- for (auto& package : table->packages) {
- if (!package->id) {
- return ::testing::AssertionFailure() << "package " << package->name << " has no ID";
- }
-
- if (!packageIds.insert(package->id.value()).second) {
- return ::testing::AssertionFailure() << "package " << package->name
- << " has non-unique ID " << std::hex << (int) package->id.value() << std::dec;
- }
+ std::set<uint8_t> packageIds;
+ for (auto& package : table->packages) {
+ if (!package->id) {
+ return ::testing::AssertionFailure() << "package " << package->name
+ << " has no ID";
}
- for (auto& package : table->packages) {
- std::set<uint8_t> typeIds;
- for (auto& type : package->types) {
- if (!type->id) {
- return ::testing::AssertionFailure() << "type " << type->type << " of package "
- << package->name << " has no ID";
- }
-
- if (!typeIds.insert(type->id.value()).second) {
- return ::testing::AssertionFailure() << "type " << type->type
- << " of package " << package->name << " has non-unique ID "
- << std::hex << (int) type->id.value() << std::dec;
- }
- }
-
-
- for (auto& type : package->types) {
- std::set<uint16_t> entryIds;
- for (auto& entry : type->entries) {
- if (!entry->id) {
- return ::testing::AssertionFailure() << "entry " << entry->name << " of type "
- << type->type << " of package " << package->name << " has no ID";
- }
-
- if (!entryIds.insert(entry->id.value()).second) {
- return ::testing::AssertionFailure() << "entry " << entry->name
- << " of type " << type->type << " of package " << package->name
- << " has non-unique ID "
- << std::hex << (int) entry->id.value() << std::dec;
- }
- }
- }
+ if (!packageIds.insert(package->id.value()).second) {
+ return ::testing::AssertionFailure()
+ << "package " << package->name << " has non-unique ID " << std::hex
+ << (int)package->id.value() << std::dec;
}
- return ::testing::AssertionSuccess() << "all IDs are unique and assigned";
+ }
+
+ for (auto& package : table->packages) {
+ std::set<uint8_t> typeIds;
+ for (auto& type : package->types) {
+ if (!type->id) {
+ return ::testing::AssertionFailure() << "type " << type->type
+ << " of package " << package->name
+ << " has no ID";
+ }
+
+ if (!typeIds.insert(type->id.value()).second) {
+ return ::testing::AssertionFailure()
+ << "type " << type->type << " of package " << package->name
+ << " has non-unique ID " << std::hex << (int)type->id.value()
+ << std::dec;
+ }
+ }
+
+ for (auto& type : package->types) {
+ std::set<uint16_t> entryIds;
+ for (auto& entry : type->entries) {
+ if (!entry->id) {
+ return ::testing::AssertionFailure()
+ << "entry " << entry->name << " of type " << type->type
+ << " of package " << package->name << " has no ID";
+ }
+
+ if (!entryIds.insert(entry->id.value()).second) {
+ return ::testing::AssertionFailure()
+ << "entry " << entry->name << " of type " << type->type
+ << " of package " << package->name << " has non-unique ID "
+ << std::hex << (int)entry->id.value() << std::dec;
+ }
+ }
+ }
+ }
+ return ::testing::AssertionSuccess() << "all IDs are unique and assigned";
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/Image.h b/tools/aapt2/compile/Image.h
index fda6a3a..4cf2ea7 100644
--- a/tools/aapt2/compile/Image.h
+++ b/tools/aapt2/compile/Image.h
@@ -29,173 +29,180 @@
* An in-memory image, loaded from disk, with pixels in RGBA_8888 format.
*/
class Image {
-public:
- explicit Image() = default;
+ public:
+ explicit Image() = default;
- /**
- * A `height` sized array of pointers, where each element points to a
- * `width` sized row of RGBA_8888 pixels.
- */
- std::unique_ptr<uint8_t*[]> rows;
+ /**
+ * A `height` sized array of pointers, where each element points to a
+ * `width` sized row of RGBA_8888 pixels.
+ */
+ std::unique_ptr<uint8_t* []> rows;
- /**
- * The width of the image in RGBA_8888 pixels. This is int32_t because of 9-patch data
- * format limitations.
- */
- int32_t width = 0;
+ /**
+ * The width of the image in RGBA_8888 pixels. This is int32_t because of
+ * 9-patch data
+ * format limitations.
+ */
+ int32_t width = 0;
- /**
- * The height of the image in RGBA_8888 pixels. This is int32_t because of 9-patch data
- * format limitations.
- */
- int32_t height = 0;
+ /**
+ * The height of the image in RGBA_8888 pixels. This is int32_t because of
+ * 9-patch data
+ * format limitations.
+ */
+ int32_t height = 0;
- /**
- * Buffer to the raw image data stored sequentially.
- * Use `rows` to access the data on a row-by-row basis.
- */
- std::unique_ptr<uint8_t[]> data;
+ /**
+ * Buffer to the raw image data stored sequentially.
+ * Use `rows` to access the data on a row-by-row basis.
+ */
+ std::unique_ptr<uint8_t[]> data;
-private:
- DISALLOW_COPY_AND_ASSIGN(Image);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Image);
};
/**
- * A range of pixel values, starting at 'start' and ending before 'end' exclusive. Or rather [a, b).
+ * A range of pixel values, starting at 'start' and ending before 'end'
+ * exclusive. Or rather [a, b).
*/
struct Range {
- int32_t start = 0;
- int32_t end = 0;
+ int32_t start = 0;
+ int32_t end = 0;
- explicit Range() = default;
- inline explicit Range(int32_t s, int32_t e) : start(s), end(e) {
- }
+ explicit Range() = default;
+ inline explicit Range(int32_t s, int32_t e) : start(s), end(e) {}
};
inline bool operator==(const Range& left, const Range& right) {
- return left.start == right.start && left.end == right.end;
+ return left.start == right.start && left.end == right.end;
}
/**
- * Inset lengths from all edges of a rectangle. `left` and `top` are measured from the left and top
- * edges, while `right` and `bottom` are measured from the right and bottom edges, respectively.
+ * Inset lengths from all edges of a rectangle. `left` and `top` are measured
+ * from the left and top
+ * edges, while `right` and `bottom` are measured from the right and bottom
+ * edges, respectively.
*/
struct Bounds {
- int32_t left = 0;
- int32_t top = 0;
- int32_t right = 0;
- int32_t bottom = 0;
+ int32_t left = 0;
+ int32_t top = 0;
+ int32_t right = 0;
+ int32_t bottom = 0;
- explicit Bounds() = default;
- inline explicit Bounds(int32_t l, int32_t t, int32_t r, int32_t b) :
- left(l), top(t), right(r), bottom(b) {
- }
+ explicit Bounds() = default;
+ inline explicit Bounds(int32_t l, int32_t t, int32_t r, int32_t b)
+ : left(l), top(t), right(r), bottom(b) {}
- bool nonZero() const;
+ bool nonZero() const;
};
inline bool Bounds::nonZero() const {
- return left != 0 || top != 0 || right != 0 || bottom != 0;
+ return left != 0 || top != 0 || right != 0 || bottom != 0;
}
inline bool operator==(const Bounds& left, const Bounds& right) {
- return left.left == right.left && left.top == right.top &&
- left.right == right.right && left.bottom == right.bottom;
+ return left.left == right.left && left.top == right.top &&
+ left.right == right.right && left.bottom == right.bottom;
}
/**
- * Contains 9-patch data from a source image. All measurements exclude the 1px border of the
+ * Contains 9-patch data from a source image. All measurements exclude the 1px
+ * border of the
* source 9-patch image.
*/
class NinePatch {
-public:
- static std::unique_ptr<NinePatch> create(uint8_t** rows,
- const int32_t width, const int32_t height,
- std::string* errOut);
+ public:
+ static std::unique_ptr<NinePatch> create(uint8_t** rows, const int32_t width,
+ const int32_t height,
+ std::string* errOut);
- /**
- * Packs the RGBA_8888 data pointed to by pixel into a uint32_t
- * with format 0xAARRGGBB (the way 9-patch expects it).
- */
- static uint32_t packRGBA(const uint8_t* pixel);
+ /**
+ * Packs the RGBA_8888 data pointed to by pixel into a uint32_t
+ * with format 0xAARRGGBB (the way 9-patch expects it).
+ */
+ static uint32_t packRGBA(const uint8_t* pixel);
- /**
- * 9-patch content padding/insets. All positions are relative to the 9-patch
- * NOT including the 1px thick source border.
- */
- Bounds padding;
+ /**
+ * 9-patch content padding/insets. All positions are relative to the 9-patch
+ * NOT including the 1px thick source border.
+ */
+ Bounds padding;
- /**
- * Optical layout bounds/insets. This overrides the padding for
- * layout purposes. All positions are relative to the 9-patch
- * NOT including the 1px thick source border.
- * See https://developer.android.com/about/versions/android-4.3.html#OpticalBounds
- */
- Bounds layoutBounds;
+ /**
+ * Optical layout bounds/insets. This overrides the padding for
+ * layout purposes. All positions are relative to the 9-patch
+ * NOT including the 1px thick source border.
+ * See
+ * https://developer.android.com/about/versions/android-4.3.html#OpticalBounds
+ */
+ Bounds layoutBounds;
- /**
- * Outline of the image, calculated based on opacity.
- */
- Bounds outline;
+ /**
+ * Outline of the image, calculated based on opacity.
+ */
+ Bounds outline;
- /**
- * The computed radius of the outline. If non-zero, the outline is a rounded-rect.
- */
- float outlineRadius = 0.0f;
+ /**
+ * The computed radius of the outline. If non-zero, the outline is a
+ * rounded-rect.
+ */
+ float outlineRadius = 0.0f;
- /**
- * The largest alpha value within the outline.
- */
- uint32_t outlineAlpha = 0x000000ffu;
+ /**
+ * The largest alpha value within the outline.
+ */
+ uint32_t outlineAlpha = 0x000000ffu;
- /**
- * Horizontal regions of the image that are stretchable.
- * All positions are relative to the 9-patch
- * NOT including the 1px thick source border.
- */
- std::vector<Range> horizontalStretchRegions;
+ /**
+ * Horizontal regions of the image that are stretchable.
+ * All positions are relative to the 9-patch
+ * NOT including the 1px thick source border.
+ */
+ std::vector<Range> horizontalStretchRegions;
- /**
- * Vertical regions of the image that are stretchable.
- * All positions are relative to the 9-patch
- * NOT including the 1px thick source border.
- */
- std::vector<Range> verticalStretchRegions;
+ /**
+ * Vertical regions of the image that are stretchable.
+ * All positions are relative to the 9-patch
+ * NOT including the 1px thick source border.
+ */
+ std::vector<Range> verticalStretchRegions;
- /**
- * The colors within each region, fixed or stretchable.
- * For w*h regions, the color of region (x,y) is addressable
- * via index y*w + x.
- */
- std::vector<uint32_t> regionColors;
+ /**
+ * The colors within each region, fixed or stretchable.
+ * For w*h regions, the color of region (x,y) is addressable
+ * via index y*w + x.
+ */
+ std::vector<uint32_t> regionColors;
- /**
- * Returns serialized data containing the original basic 9-patch meta data.
- * Optical layout bounds and round rect outline data must be serialized
- * separately using serializeOpticalLayoutBounds() and serializeRoundedRectOutline().
- */
- std::unique_ptr<uint8_t[]> serializeBase(size_t* outLen) const;
+ /**
+ * Returns serialized data containing the original basic 9-patch meta data.
+ * Optical layout bounds and round rect outline data must be serialized
+ * separately using serializeOpticalLayoutBounds() and
+ * serializeRoundedRectOutline().
+ */
+ std::unique_ptr<uint8_t[]> serializeBase(size_t* outLen) const;
- /**
- * Serializes the layout bounds.
- */
- std::unique_ptr<uint8_t[]> serializeLayoutBounds(size_t* outLen) const;
+ /**
+ * Serializes the layout bounds.
+ */
+ std::unique_ptr<uint8_t[]> serializeLayoutBounds(size_t* outLen) const;
- /**
- * Serializes the rounded-rect outline.
- */
- std::unique_ptr<uint8_t[]> serializeRoundedRectOutline(size_t* outLen) const;
+ /**
+ * Serializes the rounded-rect outline.
+ */
+ std::unique_ptr<uint8_t[]> serializeRoundedRectOutline(size_t* outLen) const;
-private:
- explicit NinePatch() = default;
+ private:
+ explicit NinePatch() = default;
- DISALLOW_COPY_AND_ASSIGN(NinePatch);
+ DISALLOW_COPY_AND_ASSIGN(NinePatch);
};
::std::ostream& operator<<(::std::ostream& out, const Range& range);
::std::ostream& operator<<(::std::ostream& out, const Bounds& bounds);
::std::ostream& operator<<(::std::ostream& out, const NinePatch& ninePatch);
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_COMPILE_IMAGE_H */
diff --git a/tools/aapt2/compile/InlineXmlFormatParser.cpp b/tools/aapt2/compile/InlineXmlFormatParser.cpp
index f965bff..56f72b5 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser.cpp
+++ b/tools/aapt2/compile/InlineXmlFormatParser.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
+#include "compile/InlineXmlFormatParser.h"
#include "Debug.h"
#include "ResourceUtils.h"
-#include "compile/InlineXmlFormatParser.h"
#include "util/Util.h"
#include "xml/XmlDom.h"
#include "xml/XmlUtil.h"
@@ -33,158 +33,172 @@
* XML Visitor that will find all <aapt:attr> elements for extraction.
*/
class Visitor : public xml::PackageAwareVisitor {
-public:
- using xml::PackageAwareVisitor::visit;
+ public:
+ using xml::PackageAwareVisitor::visit;
- struct InlineDeclaration {
- xml::Element* el;
- std::string attrNamespaceUri;
- std::string attrName;
- };
+ struct InlineDeclaration {
+ xml::Element* el;
+ std::string attrNamespaceUri;
+ std::string attrName;
+ };
- explicit Visitor(IAaptContext* context, xml::XmlResource* xmlResource) :
- mContext(context), mXmlResource(xmlResource) {
+ explicit Visitor(IAaptContext* context, xml::XmlResource* xmlResource)
+ : mContext(context), mXmlResource(xmlResource) {}
+
+ void visit(xml::Element* el) override {
+ if (el->namespaceUri != xml::kSchemaAapt || el->name != "attr") {
+ xml::PackageAwareVisitor::visit(el);
+ return;
}
- void visit(xml::Element* el) override {
- if (el->namespaceUri != xml::kSchemaAapt || el->name != "attr") {
- xml::PackageAwareVisitor::visit(el);
- return;
- }
+ const Source& src = mXmlResource->file.source.withLine(el->lineNumber);
- const Source& src = mXmlResource->file.source.withLine(el->lineNumber);
-
- xml::Attribute* attr = el->findAttribute({}, "name");
- if (!attr) {
- mContext->getDiagnostics()->error(DiagMessage(src) << "missing 'name' attribute");
- mError = true;
- return;
- }
-
- Maybe<Reference> ref = ResourceUtils::parseXmlAttributeName(attr->value);
- if (!ref) {
- mContext->getDiagnostics()->error(DiagMessage(src) << "invalid XML attribute '"
- << attr->value << "'");
- mError = true;
- return;
- }
-
- const ResourceName& name = ref.value().name.value();
-
- // Use an empty string for the compilation package because we don't want to default to
- // the local package if the user specified name="style" or something. This should just
- // be the default namespace.
- Maybe<xml::ExtractedPackage> maybePkg = transformPackageAlias(name.package, {});
- if (!maybePkg) {
- mContext->getDiagnostics()->error(DiagMessage(src) << "invalid namespace prefix '"
- << name.package << "'");
- mError = true;
- return;
- }
-
- const xml::ExtractedPackage& pkg = maybePkg.value();
- const bool privateNamespace = pkg.privateNamespace || ref.value().privateReference;
-
- InlineDeclaration decl;
- decl.el = el;
- decl.attrName = name.entry;
- if (!pkg.package.empty()) {
- decl.attrNamespaceUri = xml::buildPackageNamespace(pkg.package, privateNamespace);
- }
-
- mInlineDeclarations.push_back(std::move(decl));
+ xml::Attribute* attr = el->findAttribute({}, "name");
+ if (!attr) {
+ mContext->getDiagnostics()->error(DiagMessage(src)
+ << "missing 'name' attribute");
+ mError = true;
+ return;
}
- const std::vector<InlineDeclaration>& getInlineDeclarations() const {
- return mInlineDeclarations;
+ Maybe<Reference> ref = ResourceUtils::parseXmlAttributeName(attr->value);
+ if (!ref) {
+ mContext->getDiagnostics()->error(
+ DiagMessage(src) << "invalid XML attribute '" << attr->value << "'");
+ mError = true;
+ return;
}
- bool hasError() const {
- return mError;
+ const ResourceName& name = ref.value().name.value();
+
+ // Use an empty string for the compilation package because we don't want to
+ // default to
+ // the local package if the user specified name="style" or something. This
+ // should just
+ // be the default namespace.
+ Maybe<xml::ExtractedPackage> maybePkg =
+ transformPackageAlias(name.package, {});
+ if (!maybePkg) {
+ mContext->getDiagnostics()->error(DiagMessage(src)
+ << "invalid namespace prefix '"
+ << name.package << "'");
+ mError = true;
+ return;
}
-private:
- DISALLOW_COPY_AND_ASSIGN(Visitor);
+ const xml::ExtractedPackage& pkg = maybePkg.value();
+ const bool privateNamespace =
+ pkg.privateNamespace || ref.value().privateReference;
- IAaptContext* mContext;
- xml::XmlResource* mXmlResource;
- std::vector<InlineDeclaration> mInlineDeclarations;
- bool mError = false;
+ InlineDeclaration decl;
+ decl.el = el;
+ decl.attrName = name.entry;
+ if (!pkg.package.empty()) {
+ decl.attrNamespaceUri =
+ xml::buildPackageNamespace(pkg.package, privateNamespace);
+ }
+
+ mInlineDeclarations.push_back(std::move(decl));
+ }
+
+ const std::vector<InlineDeclaration>& getInlineDeclarations() const {
+ return mInlineDeclarations;
+ }
+
+ bool hasError() const { return mError; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Visitor);
+
+ IAaptContext* mContext;
+ xml::XmlResource* mXmlResource;
+ std::vector<InlineDeclaration> mInlineDeclarations;
+ bool mError = false;
};
-} // namespace
+} // namespace
-bool InlineXmlFormatParser::consume(IAaptContext* context, xml::XmlResource* doc) {
- Visitor visitor(context, doc);
- doc->root->accept(&visitor);
- if (visitor.hasError()) {
+bool InlineXmlFormatParser::consume(IAaptContext* context,
+ xml::XmlResource* doc) {
+ Visitor visitor(context, doc);
+ doc->root->accept(&visitor);
+ if (visitor.hasError()) {
+ return false;
+ }
+
+ size_t nameSuffixCounter = 0;
+ for (const Visitor::InlineDeclaration& decl :
+ visitor.getInlineDeclarations()) {
+ auto newDoc = util::make_unique<xml::XmlResource>();
+ newDoc->file.config = doc->file.config;
+ newDoc->file.source = doc->file.source.withLine(decl.el->lineNumber);
+ newDoc->file.name = doc->file.name;
+
+ // Modify the new entry name. We need to suffix the entry with a number to
+ // avoid
+ // local collisions, then mangle it with the empty package, such that it
+ // won't show up
+ // in R.java.
+
+ newDoc->file.name.entry = NameMangler::mangleEntry(
+ {}, newDoc->file.name.entry + "__" + std::to_string(nameSuffixCounter));
+
+ // Extracted elements must be the only child of <aapt:attr>.
+ // Make sure there is one root node in the children (ignore empty text).
+ for (auto& child : decl.el->children) {
+ const Source childSource = doc->file.source.withLine(child->lineNumber);
+ if (xml::Text* t = xml::nodeCast<xml::Text>(child.get())) {
+ if (!util::trimWhitespace(t->text).empty()) {
+ context->getDiagnostics()->error(
+ DiagMessage(childSource)
+ << "can't extract text into its own resource");
+ return false;
+ }
+ } else if (newDoc->root) {
+ context->getDiagnostics()->error(
+ DiagMessage(childSource)
+ << "inline XML resources must have a single root");
return false;
+ } else {
+ newDoc->root = std::move(child);
+ newDoc->root->parent = nullptr;
+ }
}
- size_t nameSuffixCounter = 0;
- for (const Visitor::InlineDeclaration& decl : visitor.getInlineDeclarations()) {
- auto newDoc = util::make_unique<xml::XmlResource>();
- newDoc->file.config = doc->file.config;
- newDoc->file.source = doc->file.source.withLine(decl.el->lineNumber);
- newDoc->file.name = doc->file.name;
-
- // Modify the new entry name. We need to suffix the entry with a number to avoid
- // local collisions, then mangle it with the empty package, such that it won't show up
- // in R.java.
-
- newDoc->file.name.entry = NameMangler::mangleEntry(
- {}, newDoc->file.name.entry + "__" + std::to_string(nameSuffixCounter));
-
- // Extracted elements must be the only child of <aapt:attr>.
- // Make sure there is one root node in the children (ignore empty text).
- for (auto& child : decl.el->children) {
- const Source childSource = doc->file.source.withLine(child->lineNumber);
- if (xml::Text* t = xml::nodeCast<xml::Text>(child.get())) {
- if (!util::trimWhitespace(t->text).empty()) {
- context->getDiagnostics()->error(DiagMessage(childSource)
- << "can't extract text into its own resource");
- return false;
- }
- } else if (newDoc->root) {
- context->getDiagnostics()->error(DiagMessage(childSource)
- << "inline XML resources must have a single root");
- return false;
- } else {
- newDoc->root = std::move(child);
- newDoc->root->parent = nullptr;
- }
- }
-
- // Walk up and find the parent element.
- xml::Node* node = decl.el;
- xml::Element* parentEl = nullptr;
- while (node->parent && (parentEl = xml::nodeCast<xml::Element>(node->parent)) == nullptr) {
- node = node->parent;
- }
-
- if (!parentEl) {
- context->getDiagnostics()->error(DiagMessage(newDoc->file.source)
- << "no suitable parent for inheriting attribute");
- return false;
- }
-
- // Add the inline attribute to the parent.
- parentEl->attributes.push_back(xml::Attribute{
- decl.attrNamespaceUri, decl.attrName, "@" + newDoc->file.name.toString() });
-
- // Delete the subtree.
- for (auto iter = parentEl->children.begin(); iter != parentEl->children.end(); ++iter) {
- if (iter->get() == node) {
- parentEl->children.erase(iter);
- break;
- }
- }
-
- mQueue.push_back(std::move(newDoc));
-
- nameSuffixCounter++;
+ // Walk up and find the parent element.
+ xml::Node* node = decl.el;
+ xml::Element* parentEl = nullptr;
+ while (node->parent &&
+ (parentEl = xml::nodeCast<xml::Element>(node->parent)) == nullptr) {
+ node = node->parent;
}
- return true;
+
+ if (!parentEl) {
+ context->getDiagnostics()->error(
+ DiagMessage(newDoc->file.source)
+ << "no suitable parent for inheriting attribute");
+ return false;
+ }
+
+ // Add the inline attribute to the parent.
+ parentEl->attributes.push_back(
+ xml::Attribute{decl.attrNamespaceUri, decl.attrName,
+ "@" + newDoc->file.name.toString()});
+
+ // Delete the subtree.
+ for (auto iter = parentEl->children.begin();
+ iter != parentEl->children.end(); ++iter) {
+ if (iter->get() == node) {
+ parentEl->children.erase(iter);
+ break;
+ }
+ }
+
+ mQueue.push_back(std::move(newDoc));
+
+ nameSuffixCounter++;
+ }
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/InlineXmlFormatParser.h b/tools/aapt2/compile/InlineXmlFormatParser.h
index 69065fd..cd8794b 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser.h
+++ b/tools/aapt2/compile/InlineXmlFormatParser.h
@@ -41,25 +41,28 @@
* </aapt:attr>
* </animated-vector>
*
- * The <vector> will be extracted into its own XML file and <animated-vector> will
- * gain an attribute 'android:drawable' set to a reference to the extracted <vector> resource.
+ * The <vector> will be extracted into its own XML file and <animated-vector>
+ * will
+ * gain an attribute 'android:drawable' set to a reference to the extracted
+ * <vector> resource.
*/
class InlineXmlFormatParser : public IXmlResourceConsumer {
-public:
- explicit InlineXmlFormatParser() = default;
+ public:
+ explicit InlineXmlFormatParser() = default;
- bool consume(IAaptContext* context, xml::XmlResource* doc) override;
+ bool consume(IAaptContext* context, xml::XmlResource* doc) override;
- std::vector<std::unique_ptr<xml::XmlResource>>& getExtractedInlineXmlDocuments() {
- return mQueue;
- }
+ std::vector<std::unique_ptr<xml::XmlResource>>&
+ getExtractedInlineXmlDocuments() {
+ return mQueue;
+ }
-private:
- DISALLOW_COPY_AND_ASSIGN(InlineXmlFormatParser);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InlineXmlFormatParser);
- std::vector<std::unique_ptr<xml::XmlResource>> mQueue;
+ std::vector<std::unique_ptr<xml::XmlResource>> mQueue;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_COMPILE_INLINEXMLFORMATPARSER_H */
diff --git a/tools/aapt2/compile/InlineXmlFormatParser_test.cpp b/tools/aapt2/compile/InlineXmlFormatParser_test.cpp
index 8d62210..4adb21c 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser_test.cpp
+++ b/tools/aapt2/compile/InlineXmlFormatParser_test.cpp
@@ -20,22 +20,22 @@
namespace aapt {
TEST(InlineXmlFormatParserTest, PassThrough) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android">
<View android:text="hey">
<View android:id="hi" />
</View>
</View>)EOF");
- InlineXmlFormatParser parser;
- ASSERT_TRUE(parser.consume(context.get(), doc.get()));
- EXPECT_EQ(0u, parser.getExtractedInlineXmlDocuments().size());
+ InlineXmlFormatParser parser;
+ ASSERT_TRUE(parser.consume(context.get(), doc.get()));
+ EXPECT_EQ(0u, parser.getExtractedInlineXmlDocuments().size());
}
TEST(InlineXmlFormatParserTest, ExtractOneXmlResource) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
<View1 xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:text">
@@ -45,47 +45,48 @@
</aapt:attr>
</View1>)EOF");
- doc->file.name = test::parseNameOrDie("layout/main");
+ doc->file.name = test::parseNameOrDie("layout/main");
- InlineXmlFormatParser parser;
- ASSERT_TRUE(parser.consume(context.get(), doc.get()));
+ InlineXmlFormatParser parser;
+ ASSERT_TRUE(parser.consume(context.get(), doc.get()));
- // One XML resource should have been extracted.
- EXPECT_EQ(1u, parser.getExtractedInlineXmlDocuments().size());
+ // One XML resource should have been extracted.
+ EXPECT_EQ(1u, parser.getExtractedInlineXmlDocuments().size());
- xml::Element* el = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, el);
+ xml::Element* el = xml::findRootElement(doc.get());
+ ASSERT_NE(nullptr, el);
- EXPECT_EQ("View1", el->name);
+ EXPECT_EQ("View1", el->name);
- // The <aapt:attr> tag should be extracted.
- EXPECT_EQ(nullptr, el->findChild(xml::kSchemaAapt, "attr"));
+ // The <aapt:attr> tag should be extracted.
+ EXPECT_EQ(nullptr, el->findChild(xml::kSchemaAapt, "attr"));
- // The 'android:text' attribute should be set with a reference.
- xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, "text");
- ASSERT_NE(nullptr, attr);
+ // The 'android:text' attribute should be set with a reference.
+ xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, "text");
+ ASSERT_NE(nullptr, attr);
- ResourceNameRef nameRef;
- ASSERT_TRUE(ResourceUtils::parseReference(attr->value, &nameRef));
+ ResourceNameRef nameRef;
+ ASSERT_TRUE(ResourceUtils::parseReference(attr->value, &nameRef));
- xml::XmlResource* extractedDoc = parser.getExtractedInlineXmlDocuments()[0].get();
- ASSERT_NE(nullptr, extractedDoc);
+ xml::XmlResource* extractedDoc =
+ parser.getExtractedInlineXmlDocuments()[0].get();
+ ASSERT_NE(nullptr, extractedDoc);
- // Make sure the generated reference is correct.
- EXPECT_EQ(nameRef.package, extractedDoc->file.name.package);
- EXPECT_EQ(nameRef.type, extractedDoc->file.name.type);
- EXPECT_EQ(nameRef.entry, extractedDoc->file.name.entry);
+ // Make sure the generated reference is correct.
+ EXPECT_EQ(nameRef.package, extractedDoc->file.name.package);
+ EXPECT_EQ(nameRef.type, extractedDoc->file.name.type);
+ EXPECT_EQ(nameRef.entry, extractedDoc->file.name.entry);
- // Verify the structure of the extracted XML.
- el = xml::findRootElement(extractedDoc);
- ASSERT_NE(nullptr, el);
- EXPECT_EQ("View2", el->name);
- EXPECT_NE(nullptr, el->findChild({}, "View3"));
+ // Verify the structure of the extracted XML.
+ el = xml::findRootElement(extractedDoc);
+ ASSERT_NE(nullptr, el);
+ EXPECT_EQ("View2", el->name);
+ EXPECT_NE(nullptr, el->findChild({}, "View3"));
}
TEST(InlineXmlFormatParserTest, ExtractTwoXmlResources) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
<View1 xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:text">
@@ -99,40 +100,43 @@
</aapt:attr>
</View1>)EOF");
- doc->file.name = test::parseNameOrDie("layout/main");
+ doc->file.name = test::parseNameOrDie("layout/main");
- InlineXmlFormatParser parser;
- ASSERT_TRUE(parser.consume(context.get(), doc.get()));
- ASSERT_EQ(2u, parser.getExtractedInlineXmlDocuments().size());
+ InlineXmlFormatParser parser;
+ ASSERT_TRUE(parser.consume(context.get(), doc.get()));
+ ASSERT_EQ(2u, parser.getExtractedInlineXmlDocuments().size());
- xml::Element* el = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, el);
+ xml::Element* el = xml::findRootElement(doc.get());
+ ASSERT_NE(nullptr, el);
- EXPECT_EQ("View1", el->name);
+ EXPECT_EQ("View1", el->name);
- xml::Attribute* attrText = el->findAttribute(xml::kSchemaAndroid, "text");
- ASSERT_NE(nullptr, attrText);
+ xml::Attribute* attrText = el->findAttribute(xml::kSchemaAndroid, "text");
+ ASSERT_NE(nullptr, attrText);
- xml::Attribute* attrDrawable = el->findAttribute(xml::kSchemaAndroid, "drawable");
- ASSERT_NE(nullptr, attrDrawable);
+ xml::Attribute* attrDrawable =
+ el->findAttribute(xml::kSchemaAndroid, "drawable");
+ ASSERT_NE(nullptr, attrDrawable);
- // The two extracted resources should have different names.
- EXPECT_NE(attrText->value, attrDrawable->value);
+ // The two extracted resources should have different names.
+ EXPECT_NE(attrText->value, attrDrawable->value);
- // The child <aapt:attr> elements should be gone.
- EXPECT_EQ(nullptr, el->findChild(xml::kSchemaAapt, "attr"));
+ // The child <aapt:attr> elements should be gone.
+ EXPECT_EQ(nullptr, el->findChild(xml::kSchemaAapt, "attr"));
- xml::XmlResource* extractedDocText = parser.getExtractedInlineXmlDocuments()[0].get();
- ASSERT_NE(nullptr, extractedDocText);
- el = xml::findRootElement(extractedDocText);
- ASSERT_NE(nullptr, el);
- EXPECT_EQ("View2", el->name);
+ xml::XmlResource* extractedDocText =
+ parser.getExtractedInlineXmlDocuments()[0].get();
+ ASSERT_NE(nullptr, extractedDocText);
+ el = xml::findRootElement(extractedDocText);
+ ASSERT_NE(nullptr, el);
+ EXPECT_EQ("View2", el->name);
- xml::XmlResource* extractedDocDrawable = parser.getExtractedInlineXmlDocuments()[1].get();
- ASSERT_NE(nullptr, extractedDocDrawable);
- el = xml::findRootElement(extractedDocDrawable);
- ASSERT_NE(nullptr, el);
- EXPECT_EQ("vector", el->name);
+ xml::XmlResource* extractedDocDrawable =
+ parser.getExtractedInlineXmlDocuments()[1].get();
+ ASSERT_NE(nullptr, extractedDocDrawable);
+ el = xml::findRootElement(extractedDocDrawable);
+ ASSERT_NE(nullptr, el);
+ EXPECT_EQ("vector", el->name);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/NinePatch.cpp b/tools/aapt2/compile/NinePatch.cpp
index 0fc1c5d..8842eb7 100644
--- a/tools/aapt2/compile/NinePatch.cpp
+++ b/tools/aapt2/compile/NinePatch.cpp
@@ -28,7 +28,7 @@
// Colors in the format 0xAARRGGBB (the way 9-patch expects it).
constexpr static const uint32_t kColorOpaqueWhite = 0xffffffffu;
constexpr static const uint32_t kColorOpaqueBlack = 0xff000000u;
-constexpr static const uint32_t kColorOpaqueRed = 0xffff0000u;
+constexpr static const uint32_t kColorOpaqueRed = 0xffff0000u;
constexpr static const uint32_t kPrimaryColor = kColorOpaqueBlack;
constexpr static const uint32_t kSecondaryColor = kColorOpaqueRed;
@@ -46,35 +46,37 @@
* but we need to ensure consistency throughout the image.
*/
class ColorValidator {
-public:
- virtual ~ColorValidator() = default;
+ public:
+ virtual ~ColorValidator() = default;
- /**
- * Returns true if the color specified is a neutral color
- * (no padding, stretching, or optical bounds).
- */
- virtual bool isNeutralColor(uint32_t color) const = 0;
+ /**
+ * Returns true if the color specified is a neutral color
+ * (no padding, stretching, or optical bounds).
+ */
+ virtual bool isNeutralColor(uint32_t color) const = 0;
- /**
- * Returns true if the color is either a neutral color
- * or one denoting padding, stretching, or optical bounds.
- */
- bool isValidColor(uint32_t color) const {
- switch (color) {
- case kPrimaryColor:
- case kSecondaryColor:
- return true;
- }
- return isNeutralColor(color);
+ /**
+ * Returns true if the color is either a neutral color
+ * or one denoting padding, stretching, or optical bounds.
+ */
+ bool isValidColor(uint32_t color) const {
+ switch (color) {
+ case kPrimaryColor:
+ case kSecondaryColor:
+ return true;
}
+ return isNeutralColor(color);
+ }
};
// Walks an ImageLine and records Ranges of primary and secondary colors.
-// The primary color is black and is used to denote a padding or stretching range,
+// The primary color is black and is used to denote a padding or stretching
+// range,
// depending on which border we're iterating over.
// The secondary color is red and is used to denote optical bounds.
//
-// An ImageLine is a templated-interface that would look something like this if it
+// An ImageLine is a templated-interface that would look something like this if
+// it
// were polymorphic:
//
// class ImageLine {
@@ -87,590 +89,604 @@
static bool fillRanges(const ImageLine* imageLine,
const ColorValidator* colorValidator,
std::vector<Range>* primaryRanges,
- std::vector<Range>* secondaryRanges,
- std::string* err) {
- const int32_t length = imageLine->getLength();
+ std::vector<Range>* secondaryRanges, std::string* err) {
+ const int32_t length = imageLine->getLength();
- uint32_t lastColor = 0xffffffffu;
- for (int32_t idx = 1; idx < length - 1; idx++) {
- const uint32_t color = imageLine->getColor(idx);
- if (!colorValidator->isValidColor(color)) {
- *err = "found an invalid color";
- return false;
- }
-
- if (color != lastColor) {
- // We are ending a range. Which range?
- // note: encode the x offset without the final 1 pixel border.
- if (lastColor == kPrimaryColor) {
- primaryRanges->back().end = idx - 1;
- } else if (lastColor == kSecondaryColor) {
- secondaryRanges->back().end = idx - 1;
- }
-
- // We are starting a range. Which range?
- // note: encode the x offset without the final 1 pixel border.
- if (color == kPrimaryColor) {
- primaryRanges->push_back(Range(idx - 1, length - 2));
- } else if (color == kSecondaryColor) {
- secondaryRanges->push_back(Range(idx - 1, length - 2));
- }
- lastColor = color;
- }
+ uint32_t lastColor = 0xffffffffu;
+ for (int32_t idx = 1; idx < length - 1; idx++) {
+ const uint32_t color = imageLine->getColor(idx);
+ if (!colorValidator->isValidColor(color)) {
+ *err = "found an invalid color";
+ return false;
}
- return true;
+
+ if (color != lastColor) {
+ // We are ending a range. Which range?
+ // note: encode the x offset without the final 1 pixel border.
+ if (lastColor == kPrimaryColor) {
+ primaryRanges->back().end = idx - 1;
+ } else if (lastColor == kSecondaryColor) {
+ secondaryRanges->back().end = idx - 1;
+ }
+
+ // We are starting a range. Which range?
+ // note: encode the x offset without the final 1 pixel border.
+ if (color == kPrimaryColor) {
+ primaryRanges->push_back(Range(idx - 1, length - 2));
+ } else if (color == kSecondaryColor) {
+ secondaryRanges->push_back(Range(idx - 1, length - 2));
+ }
+ lastColor = color;
+ }
+ }
+ return true;
}
/**
- * Iterates over a row in an image. Implements the templated ImageLine interface.
+ * Iterates over a row in an image. Implements the templated ImageLine
+ * interface.
*/
class HorizontalImageLine {
-public:
- explicit HorizontalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
- int32_t length) :
- mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mLength(length) {
- }
+ public:
+ explicit HorizontalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
+ int32_t length)
+ : mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mLength(length) {}
- inline int32_t getLength() const {
- return mLength;
- }
+ inline int32_t getLength() const { return mLength; }
- inline uint32_t getColor(int32_t idx) const {
- return NinePatch::packRGBA(mRows[mYOffset] + (idx + mXOffset) * 4);
- }
+ inline uint32_t getColor(int32_t idx) const {
+ return NinePatch::packRGBA(mRows[mYOffset] + (idx + mXOffset) * 4);
+ }
-private:
- uint8_t** mRows;
- int32_t mXOffset, mYOffset, mLength;
+ private:
+ uint8_t** mRows;
+ int32_t mXOffset, mYOffset, mLength;
- DISALLOW_COPY_AND_ASSIGN(HorizontalImageLine);
+ DISALLOW_COPY_AND_ASSIGN(HorizontalImageLine);
};
/**
- * Iterates over a column in an image. Implements the templated ImageLine interface.
+ * Iterates over a column in an image. Implements the templated ImageLine
+ * interface.
*/
class VerticalImageLine {
-public:
- explicit VerticalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
- int32_t length) :
- mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mLength(length) {
- }
+ public:
+ explicit VerticalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
+ int32_t length)
+ : mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mLength(length) {}
- inline int32_t getLength() const {
- return mLength;
- }
+ inline int32_t getLength() const { return mLength; }
- inline uint32_t getColor(int32_t idx) const {
- return NinePatch::packRGBA(mRows[mYOffset + idx] + (mXOffset * 4));
- }
+ inline uint32_t getColor(int32_t idx) const {
+ return NinePatch::packRGBA(mRows[mYOffset + idx] + (mXOffset * 4));
+ }
-private:
- uint8_t** mRows;
- int32_t mXOffset, mYOffset, mLength;
+ private:
+ uint8_t** mRows;
+ int32_t mXOffset, mYOffset, mLength;
- DISALLOW_COPY_AND_ASSIGN(VerticalImageLine);
+ DISALLOW_COPY_AND_ASSIGN(VerticalImageLine);
};
class DiagonalImageLine {
-public:
- explicit DiagonalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
- int32_t xStep, int32_t yStep, int32_t length) :
- mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mXStep(xStep), mYStep(yStep),
- mLength(length) {
- }
+ public:
+ explicit DiagonalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
+ int32_t xStep, int32_t yStep, int32_t length)
+ : mRows(rows),
+ mXOffset(xOffset),
+ mYOffset(yOffset),
+ mXStep(xStep),
+ mYStep(yStep),
+ mLength(length) {}
- inline int32_t getLength() const {
- return mLength;
- }
+ inline int32_t getLength() const { return mLength; }
- inline uint32_t getColor(int32_t idx) const {
- return NinePatch::packRGBA(
- mRows[mYOffset + (idx * mYStep)] + ((idx + mXOffset) * mXStep) * 4);
- }
+ inline uint32_t getColor(int32_t idx) const {
+ return NinePatch::packRGBA(mRows[mYOffset + (idx * mYStep)] +
+ ((idx + mXOffset) * mXStep) * 4);
+ }
-private:
- uint8_t** mRows;
- int32_t mXOffset, mYOffset, mXStep, mYStep, mLength;
+ private:
+ uint8_t** mRows;
+ int32_t mXOffset, mYOffset, mXStep, mYStep, mLength;
- DISALLOW_COPY_AND_ASSIGN(DiagonalImageLine);
+ DISALLOW_COPY_AND_ASSIGN(DiagonalImageLine);
};
class TransparentNeutralColorValidator : public ColorValidator {
-public:
- bool isNeutralColor(uint32_t color) const override {
- return getAlpha(color) == 0;
- }
+ public:
+ bool isNeutralColor(uint32_t color) const override {
+ return getAlpha(color) == 0;
+ }
};
class WhiteNeutralColorValidator : public ColorValidator {
-public:
- bool isNeutralColor(uint32_t color) const override {
- return color == kColorOpaqueWhite;
- }
+ public:
+ bool isNeutralColor(uint32_t color) const override {
+ return color == kColorOpaqueWhite;
+ }
};
inline static uint32_t getAlpha(uint32_t color) {
- return (color & 0xff000000u) >> 24;
+ return (color & 0xff000000u) >> 24;
}
static bool populateBounds(const std::vector<Range>& padding,
const std::vector<Range>& layoutBounds,
const std::vector<Range>& stretchRegions,
- const int32_t length,
- int32_t* paddingStart, int32_t* paddingEnd,
- int32_t* layoutStart, int32_t* layoutEnd,
- const StringPiece& edgeName,
+ const int32_t length, int32_t* paddingStart,
+ int32_t* paddingEnd, int32_t* layoutStart,
+ int32_t* layoutEnd, const StringPiece& edgeName,
std::string* err) {
- if (padding.size() > 1) {
+ if (padding.size() > 1) {
+ std::stringstream errStream;
+ errStream << "too many padding sections on " << edgeName << " border";
+ *err = errStream.str();
+ return false;
+ }
+
+ *paddingStart = 0;
+ *paddingEnd = 0;
+ if (!padding.empty()) {
+ const Range& range = padding.front();
+ *paddingStart = range.start;
+ *paddingEnd = length - range.end;
+ } else if (!stretchRegions.empty()) {
+ // No padding was defined. Compute the padding from the first and last
+ // stretch regions.
+ *paddingStart = stretchRegions.front().start;
+ *paddingEnd = length - stretchRegions.back().end;
+ }
+
+ if (layoutBounds.size() > 2) {
+ std::stringstream errStream;
+ errStream << "too many layout bounds sections on " << edgeName << " border";
+ *err = errStream.str();
+ return false;
+ }
+
+ *layoutStart = 0;
+ *layoutEnd = 0;
+ if (layoutBounds.size() >= 1) {
+ const Range& range = layoutBounds.front();
+ // If there is only one layout bound segment, it might not start at 0, but
+ // then it should
+ // end at length.
+ if (range.start != 0 && range.end != length) {
+ std::stringstream errStream;
+ errStream << "layout bounds on " << edgeName
+ << " border must start at edge";
+ *err = errStream.str();
+ return false;
+ }
+ *layoutStart = range.end;
+
+ if (layoutBounds.size() >= 2) {
+ const Range& range = layoutBounds.back();
+ if (range.end != length) {
std::stringstream errStream;
- errStream << "too many padding sections on " << edgeName << " border";
+ errStream << "layout bounds on " << edgeName
+ << " border must start at edge";
*err = errStream.str();
return false;
+ }
+ *layoutEnd = length - range.start;
}
-
- *paddingStart = 0;
- *paddingEnd = 0;
- if (!padding.empty()) {
- const Range& range = padding.front();
- *paddingStart = range.start;
- *paddingEnd = length - range.end;
- } else if (!stretchRegions.empty()) {
- // No padding was defined. Compute the padding from the first and last
- // stretch regions.
- *paddingStart = stretchRegions.front().start;
- *paddingEnd = length - stretchRegions.back().end;
- }
-
- if (layoutBounds.size() > 2) {
- std::stringstream errStream;
- errStream << "too many layout bounds sections on " << edgeName << " border";
- *err = errStream.str();
- return false;
- }
-
- *layoutStart = 0;
- *layoutEnd = 0;
- if (layoutBounds.size() >= 1) {
- const Range& range = layoutBounds.front();
- // If there is only one layout bound segment, it might not start at 0, but then it should
- // end at length.
- if (range.start != 0 && range.end != length) {
- std::stringstream errStream;
- errStream << "layout bounds on " << edgeName << " border must start at edge";
- *err = errStream.str();
- return false;
- }
- *layoutStart = range.end;
-
- if (layoutBounds.size() >= 2) {
- const Range& range = layoutBounds.back();
- if (range.end != length) {
- std::stringstream errStream;
- errStream << "layout bounds on " << edgeName << " border must start at edge";
- *err = errStream.str();
- return false;
- }
- *layoutEnd = length - range.start;
- }
- }
- return true;
+ }
+ return true;
}
-static int32_t calculateSegmentCount(const std::vector<Range>& stretchRegions, int32_t length) {
- if (stretchRegions.size() == 0) {
- return 0;
- }
+static int32_t calculateSegmentCount(const std::vector<Range>& stretchRegions,
+ int32_t length) {
+ if (stretchRegions.size() == 0) {
+ return 0;
+ }
- const bool startIsFixed = stretchRegions.front().start != 0;
- const bool endIsFixed = stretchRegions.back().end != length;
- int32_t modifier = 0;
- if (startIsFixed && endIsFixed) {
- modifier = 1;
- } else if (!startIsFixed && !endIsFixed) {
- modifier = -1;
- }
- return static_cast<int32_t>(stretchRegions.size()) * 2 + modifier;
+ const bool startIsFixed = stretchRegions.front().start != 0;
+ const bool endIsFixed = stretchRegions.back().end != length;
+ int32_t modifier = 0;
+ if (startIsFixed && endIsFixed) {
+ modifier = 1;
+ } else if (!startIsFixed && !endIsFixed) {
+ modifier = -1;
+ }
+ return static_cast<int32_t>(stretchRegions.size()) * 2 + modifier;
}
static uint32_t getRegionColor(uint8_t** rows, const Bounds& region) {
- // Sample the first pixel to compare against.
- const uint32_t expectedColor = NinePatch::packRGBA(rows[region.top] + region.left * 4);
- for (int32_t y = region.top; y < region.bottom; y++) {
- const uint8_t* row = rows[y];
- for (int32_t x = region.left; x < region.right; x++) {
- const uint32_t color = NinePatch::packRGBA(row + x * 4);
- if (getAlpha(color) == 0) {
- // The color is transparent.
- // If the expectedColor is not transparent, NO_COLOR.
- if (getAlpha(expectedColor) != 0) {
- return android::Res_png_9patch::NO_COLOR;
- }
- } else if (color != expectedColor) {
- return android::Res_png_9patch::NO_COLOR;
- }
+ // Sample the first pixel to compare against.
+ const uint32_t expectedColor =
+ NinePatch::packRGBA(rows[region.top] + region.left * 4);
+ for (int32_t y = region.top; y < region.bottom; y++) {
+ const uint8_t* row = rows[y];
+ for (int32_t x = region.left; x < region.right; x++) {
+ const uint32_t color = NinePatch::packRGBA(row + x * 4);
+ if (getAlpha(color) == 0) {
+ // The color is transparent.
+ // If the expectedColor is not transparent, NO_COLOR.
+ if (getAlpha(expectedColor) != 0) {
+ return android::Res_png_9patch::NO_COLOR;
}
+ } else if (color != expectedColor) {
+ return android::Res_png_9patch::NO_COLOR;
+ }
}
+ }
- if (getAlpha(expectedColor) == 0) {
- return android::Res_png_9patch::TRANSPARENT_COLOR;
- }
- return expectedColor;
+ if (getAlpha(expectedColor) == 0) {
+ return android::Res_png_9patch::TRANSPARENT_COLOR;
+ }
+ return expectedColor;
}
-// Fills outColors with each 9-patch section's colour. If the whole section is transparent,
-// it gets the special TRANSPARENT colour. If the whole section is the same colour, it is assigned
+// Fills outColors with each 9-patch section's colour. If the whole section is
+// transparent,
+// it gets the special TRANSPARENT colour. If the whole section is the same
+// colour, it is assigned
// that colour. Otherwise it gets the special NO_COLOR colour.
//
-// Note that the rows contain the 9-patch 1px border, and the indices in the stretch regions are
-// already offset to exclude the border. This means that each time the rows are accessed,
+// Note that the rows contain the 9-patch 1px border, and the indices in the
+// stretch regions are
+// already offset to exclude the border. This means that each time the rows are
+// accessed,
// the indices must be offset by 1.
//
// width and height also include the 9-patch 1px border.
-static void calculateRegionColors(uint8_t** rows,
- const std::vector<Range>& horizontalStretchRegions,
- const std::vector<Range>& verticalStretchRegions,
- const int32_t width, const int32_t height,
- std::vector<uint32_t>* outColors) {
- int32_t nextTop = 0;
- Bounds bounds;
- auto rowIter = verticalStretchRegions.begin();
- while (nextTop != height) {
- if (rowIter != verticalStretchRegions.end()) {
- if (nextTop != rowIter->start) {
- // This is a fixed segment.
- // Offset the bounds by 1 to accommodate the border.
- bounds.top = nextTop + 1;
- bounds.bottom = rowIter->start + 1;
- nextTop = rowIter->start;
- } else {
- // This is a stretchy segment.
- // Offset the bounds by 1 to accommodate the border.
- bounds.top = rowIter->start + 1;
- bounds.bottom = rowIter->end + 1;
- nextTop = rowIter->end;
- ++rowIter;
- }
- } else {
- // This is the end, fixed section.
- // Offset the bounds by 1 to accommodate the border.
- bounds.top = nextTop + 1;
- bounds.bottom = height + 1;
- nextTop = height;
- }
-
- int32_t nextLeft = 0;
- auto colIter = horizontalStretchRegions.begin();
- while (nextLeft != width) {
- if (colIter != horizontalStretchRegions.end()) {
- if (nextLeft != colIter->start) {
- // This is a fixed segment.
- // Offset the bounds by 1 to accommodate the border.
- bounds.left = nextLeft + 1;
- bounds.right = colIter->start + 1;
- nextLeft = colIter->start;
- } else {
- // This is a stretchy segment.
- // Offset the bounds by 1 to accommodate the border.
- bounds.left = colIter->start + 1;
- bounds.right = colIter->end + 1;
- nextLeft = colIter->end;
- ++colIter;
- }
- } else {
- // This is the end, fixed section.
- // Offset the bounds by 1 to accommodate the border.
- bounds.left = nextLeft + 1;
- bounds.right = width + 1;
- nextLeft = width;
- }
- outColors->push_back(getRegionColor(rows, bounds));
- }
+static void calculateRegionColors(
+ uint8_t** rows, const std::vector<Range>& horizontalStretchRegions,
+ const std::vector<Range>& verticalStretchRegions, const int32_t width,
+ const int32_t height, std::vector<uint32_t>* outColors) {
+ int32_t nextTop = 0;
+ Bounds bounds;
+ auto rowIter = verticalStretchRegions.begin();
+ while (nextTop != height) {
+ if (rowIter != verticalStretchRegions.end()) {
+ if (nextTop != rowIter->start) {
+ // This is a fixed segment.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.top = nextTop + 1;
+ bounds.bottom = rowIter->start + 1;
+ nextTop = rowIter->start;
+ } else {
+ // This is a stretchy segment.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.top = rowIter->start + 1;
+ bounds.bottom = rowIter->end + 1;
+ nextTop = rowIter->end;
+ ++rowIter;
+ }
+ } else {
+ // This is the end, fixed section.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.top = nextTop + 1;
+ bounds.bottom = height + 1;
+ nextTop = height;
}
+
+ int32_t nextLeft = 0;
+ auto colIter = horizontalStretchRegions.begin();
+ while (nextLeft != width) {
+ if (colIter != horizontalStretchRegions.end()) {
+ if (nextLeft != colIter->start) {
+ // This is a fixed segment.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.left = nextLeft + 1;
+ bounds.right = colIter->start + 1;
+ nextLeft = colIter->start;
+ } else {
+ // This is a stretchy segment.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.left = colIter->start + 1;
+ bounds.right = colIter->end + 1;
+ nextLeft = colIter->end;
+ ++colIter;
+ }
+ } else {
+ // This is the end, fixed section.
+ // Offset the bounds by 1 to accommodate the border.
+ bounds.left = nextLeft + 1;
+ bounds.right = width + 1;
+ nextLeft = width;
+ }
+ outColors->push_back(getRegionColor(rows, bounds));
+ }
+ }
}
-// Calculates the insets of a row/column of pixels based on where the largest alpha value begins
+// Calculates the insets of a row/column of pixels based on where the largest
+// alpha value begins
// (on both sides).
template <typename ImageLine>
-static void findOutlineInsets(const ImageLine* imageLine, int32_t* outStart, int32_t* outEnd) {
- *outStart = 0;
- *outEnd = 0;
+static void findOutlineInsets(const ImageLine* imageLine, int32_t* outStart,
+ int32_t* outEnd) {
+ *outStart = 0;
+ *outEnd = 0;
- const int32_t length = imageLine->getLength();
- if (length < 3) {
- return;
- }
-
- // If the length is odd, we want both sides to process the center pixel,
- // so we use two different midpoints (to account for < and <= in the different loops).
- const int32_t mid2 = length / 2;
- const int32_t mid1 = mid2 + (length % 2);
-
- uint32_t maxAlpha = 0;
- for (int32_t i = 0; i < mid1 && maxAlpha != 0xff; i++) {
- uint32_t alpha = getAlpha(imageLine->getColor(i));
- if (alpha > maxAlpha) {
- maxAlpha = alpha;
- *outStart = i;
- }
- }
-
- maxAlpha = 0;
- for (int32_t i = length - 1; i >= mid2 && maxAlpha != 0xff; i--) {
- uint32_t alpha = getAlpha(imageLine->getColor(i));
- if (alpha > maxAlpha) {
- maxAlpha = alpha;
- *outEnd = length - (i + 1);
- }
- }
+ const int32_t length = imageLine->getLength();
+ if (length < 3) {
return;
+ }
+
+ // If the length is odd, we want both sides to process the center pixel,
+ // so we use two different midpoints (to account for < and <= in the different
+ // loops).
+ const int32_t mid2 = length / 2;
+ const int32_t mid1 = mid2 + (length % 2);
+
+ uint32_t maxAlpha = 0;
+ for (int32_t i = 0; i < mid1 && maxAlpha != 0xff; i++) {
+ uint32_t alpha = getAlpha(imageLine->getColor(i));
+ if (alpha > maxAlpha) {
+ maxAlpha = alpha;
+ *outStart = i;
+ }
+ }
+
+ maxAlpha = 0;
+ for (int32_t i = length - 1; i >= mid2 && maxAlpha != 0xff; i--) {
+ uint32_t alpha = getAlpha(imageLine->getColor(i));
+ if (alpha > maxAlpha) {
+ maxAlpha = alpha;
+ *outEnd = length - (i + 1);
+ }
+ }
+ return;
}
template <typename ImageLine>
static uint32_t findMaxAlpha(const ImageLine* imageLine) {
- const int32_t length = imageLine->getLength();
- uint32_t maxAlpha = 0;
- for (int32_t idx = 0; idx < length && maxAlpha != 0xff; idx++) {
- uint32_t alpha = getAlpha(imageLine->getColor(idx));
- if (alpha > maxAlpha) {
- maxAlpha = alpha;
- }
+ const int32_t length = imageLine->getLength();
+ uint32_t maxAlpha = 0;
+ for (int32_t idx = 0; idx < length && maxAlpha != 0xff; idx++) {
+ uint32_t alpha = getAlpha(imageLine->getColor(idx));
+ if (alpha > maxAlpha) {
+ maxAlpha = alpha;
}
- return maxAlpha;
+ }
+ return maxAlpha;
}
// Pack the pixels in as 0xAARRGGBB (as 9-patch expects it).
uint32_t NinePatch::packRGBA(const uint8_t* pixel) {
- return (pixel[3] << 24) | (pixel[0] << 16) | (pixel[1] << 8) | pixel[2];
+ return (pixel[3] << 24) | (pixel[0] << 16) | (pixel[1] << 8) | pixel[2];
}
std::unique_ptr<NinePatch> NinePatch::create(uint8_t** rows,
- const int32_t width, const int32_t height,
+ const int32_t width,
+ const int32_t height,
std::string* err) {
- if (width < 3 || height < 3) {
- *err = "image must be at least 3x3 (1x1 image with 1 pixel border)";
- return {};
- }
+ if (width < 3 || height < 3) {
+ *err = "image must be at least 3x3 (1x1 image with 1 pixel border)";
+ return {};
+ }
- std::vector<Range> horizontalPadding;
- std::vector<Range> horizontalOpticalBounds;
- std::vector<Range> verticalPadding;
- std::vector<Range> verticalOpticalBounds;
- std::vector<Range> unexpectedRanges;
- std::unique_ptr<ColorValidator> colorValidator;
+ std::vector<Range> horizontalPadding;
+ std::vector<Range> horizontalOpticalBounds;
+ std::vector<Range> verticalPadding;
+ std::vector<Range> verticalOpticalBounds;
+ std::vector<Range> unexpectedRanges;
+ std::unique_ptr<ColorValidator> colorValidator;
- if (rows[0][3] == 0) {
- colorValidator = util::make_unique<TransparentNeutralColorValidator>();
- } else if (packRGBA(rows[0]) == kColorOpaqueWhite) {
- colorValidator = util::make_unique<WhiteNeutralColorValidator>();
- } else {
- *err = "top-left corner pixel must be either opaque white or transparent";
- return {};
- }
+ if (rows[0][3] == 0) {
+ colorValidator = util::make_unique<TransparentNeutralColorValidator>();
+ } else if (packRGBA(rows[0]) == kColorOpaqueWhite) {
+ colorValidator = util::make_unique<WhiteNeutralColorValidator>();
+ } else {
+ *err = "top-left corner pixel must be either opaque white or transparent";
+ return {};
+ }
- // Private constructor, can't use make_unique.
- auto ninePatch = std::unique_ptr<NinePatch>(new NinePatch());
+ // Private constructor, can't use make_unique.
+ auto ninePatch = std::unique_ptr<NinePatch>(new NinePatch());
- HorizontalImageLine topRow(rows, 0, 0, width);
- if (!fillRanges(&topRow, colorValidator.get(), &ninePatch->horizontalStretchRegions,
- &unexpectedRanges, err)) {
- return {};
- }
+ HorizontalImageLine topRow(rows, 0, 0, width);
+ if (!fillRanges(&topRow, colorValidator.get(),
+ &ninePatch->horizontalStretchRegions, &unexpectedRanges,
+ err)) {
+ return {};
+ }
- if (!unexpectedRanges.empty()) {
- const Range& range = unexpectedRanges[0];
- std::stringstream errStream;
- errStream << "found unexpected optical bounds (red pixel) on top border "
- << "at x=" << range.start + 1;
- *err = errStream.str();
- return {};
- }
+ if (!unexpectedRanges.empty()) {
+ const Range& range = unexpectedRanges[0];
+ std::stringstream errStream;
+ errStream << "found unexpected optical bounds (red pixel) on top border "
+ << "at x=" << range.start + 1;
+ *err = errStream.str();
+ return {};
+ }
- VerticalImageLine leftCol(rows, 0, 0, height);
- if (!fillRanges(&leftCol, colorValidator.get(), &ninePatch->verticalStretchRegions,
- &unexpectedRanges, err)) {
- return {};
- }
+ VerticalImageLine leftCol(rows, 0, 0, height);
+ if (!fillRanges(&leftCol, colorValidator.get(),
+ &ninePatch->verticalStretchRegions, &unexpectedRanges, err)) {
+ return {};
+ }
- if (!unexpectedRanges.empty()) {
- const Range& range = unexpectedRanges[0];
- std::stringstream errStream;
- errStream << "found unexpected optical bounds (red pixel) on left border "
- << "at y=" << range.start + 1;
- return {};
- }
+ if (!unexpectedRanges.empty()) {
+ const Range& range = unexpectedRanges[0];
+ std::stringstream errStream;
+ errStream << "found unexpected optical bounds (red pixel) on left border "
+ << "at y=" << range.start + 1;
+ return {};
+ }
- HorizontalImageLine bottomRow(rows, 0, height - 1, width);
- if (!fillRanges(&bottomRow, colorValidator.get(), &horizontalPadding,
- &horizontalOpticalBounds, err)) {
- return {};
- }
+ HorizontalImageLine bottomRow(rows, 0, height - 1, width);
+ if (!fillRanges(&bottomRow, colorValidator.get(), &horizontalPadding,
+ &horizontalOpticalBounds, err)) {
+ return {};
+ }
- if (!populateBounds(horizontalPadding, horizontalOpticalBounds,
- ninePatch->horizontalStretchRegions, width - 2,
- &ninePatch->padding.left, &ninePatch->padding.right,
- &ninePatch->layoutBounds.left, &ninePatch->layoutBounds.right,
- "bottom", err)) {
- return {};
- }
+ if (!populateBounds(horizontalPadding, horizontalOpticalBounds,
+ ninePatch->horizontalStretchRegions, width - 2,
+ &ninePatch->padding.left, &ninePatch->padding.right,
+ &ninePatch->layoutBounds.left,
+ &ninePatch->layoutBounds.right, "bottom", err)) {
+ return {};
+ }
- VerticalImageLine rightCol(rows, width - 1, 0, height);
- if (!fillRanges(&rightCol, colorValidator.get(), &verticalPadding,
- &verticalOpticalBounds, err)) {
- return {};
- }
+ VerticalImageLine rightCol(rows, width - 1, 0, height);
+ if (!fillRanges(&rightCol, colorValidator.get(), &verticalPadding,
+ &verticalOpticalBounds, err)) {
+ return {};
+ }
- if (!populateBounds(verticalPadding, verticalOpticalBounds,
- ninePatch->verticalStretchRegions, height - 2,
- &ninePatch->padding.top, &ninePatch->padding.bottom,
- &ninePatch->layoutBounds.top, &ninePatch->layoutBounds.bottom,
- "right", err)) {
- return {};
- }
+ if (!populateBounds(verticalPadding, verticalOpticalBounds,
+ ninePatch->verticalStretchRegions, height - 2,
+ &ninePatch->padding.top, &ninePatch->padding.bottom,
+ &ninePatch->layoutBounds.top,
+ &ninePatch->layoutBounds.bottom, "right", err)) {
+ return {};
+ }
- // Fill the region colors of the 9-patch.
- const int32_t numRows = calculateSegmentCount(ninePatch->horizontalStretchRegions, width - 2);
- const int32_t numCols = calculateSegmentCount(ninePatch->verticalStretchRegions, height - 2);
- if ((int64_t) numRows * (int64_t) numCols > 0x7f) {
- *err = "too many regions in 9-patch";
- return {};
- }
+ // Fill the region colors of the 9-patch.
+ const int32_t numRows =
+ calculateSegmentCount(ninePatch->horizontalStretchRegions, width - 2);
+ const int32_t numCols =
+ calculateSegmentCount(ninePatch->verticalStretchRegions, height - 2);
+ if ((int64_t)numRows * (int64_t)numCols > 0x7f) {
+ *err = "too many regions in 9-patch";
+ return {};
+ }
- ninePatch->regionColors.reserve(numRows * numCols);
- calculateRegionColors(rows, ninePatch->horizontalStretchRegions,
- ninePatch->verticalStretchRegions,
- width - 2, height - 2,
- &ninePatch->regionColors);
+ ninePatch->regionColors.reserve(numRows * numCols);
+ calculateRegionColors(rows, ninePatch->horizontalStretchRegions,
+ ninePatch->verticalStretchRegions, width - 2,
+ height - 2, &ninePatch->regionColors);
- // Compute the outline based on opacity.
+ // Compute the outline based on opacity.
- // Find left and right extent of 9-patch content on center row.
- HorizontalImageLine midRow(rows, 1, height / 2, width - 2);
- findOutlineInsets(&midRow, &ninePatch->outline.left, &ninePatch->outline.right);
+ // Find left and right extent of 9-patch content on center row.
+ HorizontalImageLine midRow(rows, 1, height / 2, width - 2);
+ findOutlineInsets(&midRow, &ninePatch->outline.left,
+ &ninePatch->outline.right);
- // Find top and bottom extent of 9-patch content on center column.
- VerticalImageLine midCol(rows, width / 2, 1, height - 2);
- findOutlineInsets(&midCol, &ninePatch->outline.top, &ninePatch->outline.bottom);
+ // Find top and bottom extent of 9-patch content on center column.
+ VerticalImageLine midCol(rows, width / 2, 1, height - 2);
+ findOutlineInsets(&midCol, &ninePatch->outline.top,
+ &ninePatch->outline.bottom);
- const int32_t outlineWidth = (width - 2) - ninePatch->outline.left - ninePatch->outline.right;
- const int32_t outlineHeight = (height - 2) - ninePatch->outline.top - ninePatch->outline.bottom;
+ const int32_t outlineWidth =
+ (width - 2) - ninePatch->outline.left - ninePatch->outline.right;
+ const int32_t outlineHeight =
+ (height - 2) - ninePatch->outline.top - ninePatch->outline.bottom;
- // Find the largest alpha value within the outline area.
- HorizontalImageLine outlineMidRow(rows,
- 1 + ninePatch->outline.left,
- 1 + ninePatch->outline.top + (outlineHeight / 2),
- outlineWidth);
- VerticalImageLine outlineMidCol(rows,
- 1 + ninePatch->outline.left + (outlineWidth / 2),
- 1 + ninePatch->outline.top,
- outlineHeight);
- ninePatch->outlineAlpha = std::max(findMaxAlpha(&outlineMidRow), findMaxAlpha(&outlineMidCol));
+ // Find the largest alpha value within the outline area.
+ HorizontalImageLine outlineMidRow(
+ rows, 1 + ninePatch->outline.left,
+ 1 + ninePatch->outline.top + (outlineHeight / 2), outlineWidth);
+ VerticalImageLine outlineMidCol(
+ rows, 1 + ninePatch->outline.left + (outlineWidth / 2),
+ 1 + ninePatch->outline.top, outlineHeight);
+ ninePatch->outlineAlpha =
+ std::max(findMaxAlpha(&outlineMidRow), findMaxAlpha(&outlineMidCol));
- // Assuming the image is a round rect, compute the radius by marching
- // diagonally from the top left corner towards the center.
- DiagonalImageLine diagonal(rows, 1 + ninePatch->outline.left, 1 + ninePatch->outline.top,
- 1, 1, std::min(outlineWidth, outlineHeight));
- int32_t topLeft, bottomRight;
- findOutlineInsets(&diagonal, &topLeft, &bottomRight);
+ // Assuming the image is a round rect, compute the radius by marching
+ // diagonally from the top left corner towards the center.
+ DiagonalImageLine diagonal(rows, 1 + ninePatch->outline.left,
+ 1 + ninePatch->outline.top, 1, 1,
+ std::min(outlineWidth, outlineHeight));
+ int32_t topLeft, bottomRight;
+ findOutlineInsets(&diagonal, &topLeft, &bottomRight);
- /* Determine source radius based upon inset:
- * sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
- * sqrt(2) * r = sqrt(2) * i + r
- * (sqrt(2) - 1) * r = sqrt(2) * i
- * r = sqrt(2) / (sqrt(2) - 1) * i
- */
- ninePatch->outlineRadius = 3.4142f * topLeft;
- return ninePatch;
+ /* Determine source radius based upon inset:
+ * sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
+ * sqrt(2) * r = sqrt(2) * i + r
+ * (sqrt(2) - 1) * r = sqrt(2) * i
+ * r = sqrt(2) / (sqrt(2) - 1) * i
+ */
+ ninePatch->outlineRadius = 3.4142f * topLeft;
+ return ninePatch;
}
std::unique_ptr<uint8_t[]> NinePatch::serializeBase(size_t* outLen) const {
- android::Res_png_9patch data;
- data.numXDivs = static_cast<uint8_t>(horizontalStretchRegions.size()) * 2;
- data.numYDivs = static_cast<uint8_t>(verticalStretchRegions.size()) * 2;
- data.numColors = static_cast<uint8_t>(regionColors.size());
- data.paddingLeft = padding.left;
- data.paddingRight = padding.right;
- data.paddingTop = padding.top;
- data.paddingBottom = padding.bottom;
+ android::Res_png_9patch data;
+ data.numXDivs = static_cast<uint8_t>(horizontalStretchRegions.size()) * 2;
+ data.numYDivs = static_cast<uint8_t>(verticalStretchRegions.size()) * 2;
+ data.numColors = static_cast<uint8_t>(regionColors.size());
+ data.paddingLeft = padding.left;
+ data.paddingRight = padding.right;
+ data.paddingTop = padding.top;
+ data.paddingBottom = padding.bottom;
- auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[data.serializedSize()]);
- android::Res_png_9patch::serialize(data,
- (const int32_t*) horizontalStretchRegions.data(),
- (const int32_t*) verticalStretchRegions.data(),
- regionColors.data(),
- buffer.get());
- // Convert to file endianness.
- reinterpret_cast<android::Res_png_9patch*>(buffer.get())->deviceToFile();
+ auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[data.serializedSize()]);
+ android::Res_png_9patch::serialize(
+ data, (const int32_t*)horizontalStretchRegions.data(),
+ (const int32_t*)verticalStretchRegions.data(), regionColors.data(),
+ buffer.get());
+ // Convert to file endianness.
+ reinterpret_cast<android::Res_png_9patch*>(buffer.get())->deviceToFile();
- *outLen = data.serializedSize();
- return buffer;
+ *outLen = data.serializedSize();
+ return buffer;
}
-std::unique_ptr<uint8_t[]> NinePatch::serializeLayoutBounds(size_t* outLen) const {
- size_t chunkLen = sizeof(uint32_t) * 4;
- auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunkLen]);
- uint8_t* cursor = buffer.get();
+std::unique_ptr<uint8_t[]> NinePatch::serializeLayoutBounds(
+ size_t* outLen) const {
+ size_t chunkLen = sizeof(uint32_t) * 4;
+ auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunkLen]);
+ uint8_t* cursor = buffer.get();
- memcpy(cursor, &layoutBounds.left, sizeof(layoutBounds.left));
- cursor += sizeof(layoutBounds.left);
+ memcpy(cursor, &layoutBounds.left, sizeof(layoutBounds.left));
+ cursor += sizeof(layoutBounds.left);
- memcpy(cursor, &layoutBounds.top, sizeof(layoutBounds.top));
- cursor += sizeof(layoutBounds.top);
+ memcpy(cursor, &layoutBounds.top, sizeof(layoutBounds.top));
+ cursor += sizeof(layoutBounds.top);
- memcpy(cursor, &layoutBounds.right, sizeof(layoutBounds.right));
- cursor += sizeof(layoutBounds.right);
+ memcpy(cursor, &layoutBounds.right, sizeof(layoutBounds.right));
+ cursor += sizeof(layoutBounds.right);
- memcpy(cursor, &layoutBounds.bottom, sizeof(layoutBounds.bottom));
- cursor += sizeof(layoutBounds.bottom);
+ memcpy(cursor, &layoutBounds.bottom, sizeof(layoutBounds.bottom));
+ cursor += sizeof(layoutBounds.bottom);
- *outLen = chunkLen;
- return buffer;
+ *outLen = chunkLen;
+ return buffer;
}
-std::unique_ptr<uint8_t[]> NinePatch::serializeRoundedRectOutline(size_t* outLen) const {
- size_t chunkLen = sizeof(uint32_t) * 6;
- auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunkLen]);
- uint8_t* cursor = buffer.get();
+std::unique_ptr<uint8_t[]> NinePatch::serializeRoundedRectOutline(
+ size_t* outLen) const {
+ size_t chunkLen = sizeof(uint32_t) * 6;
+ auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunkLen]);
+ uint8_t* cursor = buffer.get();
- memcpy(cursor, &outline.left, sizeof(outline.left));
- cursor += sizeof(outline.left);
+ memcpy(cursor, &outline.left, sizeof(outline.left));
+ cursor += sizeof(outline.left);
- memcpy(cursor, &outline.top, sizeof(outline.top));
- cursor += sizeof(outline.top);
+ memcpy(cursor, &outline.top, sizeof(outline.top));
+ cursor += sizeof(outline.top);
- memcpy(cursor, &outline.right, sizeof(outline.right));
- cursor += sizeof(outline.right);
+ memcpy(cursor, &outline.right, sizeof(outline.right));
+ cursor += sizeof(outline.right);
- memcpy(cursor, &outline.bottom, sizeof(outline.bottom));
- cursor += sizeof(outline.bottom);
+ memcpy(cursor, &outline.bottom, sizeof(outline.bottom));
+ cursor += sizeof(outline.bottom);
- *((float*) cursor) = outlineRadius;
- cursor += sizeof(outlineRadius);
+ *((float*)cursor) = outlineRadius;
+ cursor += sizeof(outlineRadius);
- *((uint32_t*) cursor) = outlineAlpha;
+ *((uint32_t*)cursor) = outlineAlpha;
- *outLen = chunkLen;
- return buffer;
+ *outLen = chunkLen;
+ return buffer;
}
::std::ostream& operator<<(::std::ostream& out, const Range& range) {
- return out << "[" << range.start << ", " << range.end << ")";
+ return out << "[" << range.start << ", " << range.end << ")";
}
::std::ostream& operator<<(::std::ostream& out, const Bounds& bounds) {
- return out << "l=" << bounds.left
- << " t=" << bounds.top
- << " r=" << bounds.right
- << " b=" << bounds.bottom;
+ return out << "l=" << bounds.left << " t=" << bounds.top
+ << " r=" << bounds.right << " b=" << bounds.bottom;
}
::std::ostream& operator<<(::std::ostream& out, const NinePatch& ninePatch) {
- return out << "horizontalStretch:" << util::joiner(ninePatch.horizontalStretchRegions, " ")
- << " verticalStretch:" << util::joiner(ninePatch.verticalStretchRegions, " ")
- << " padding: " << ninePatch.padding
- << ", bounds: " << ninePatch.layoutBounds
- << ", outline: " << ninePatch.outline
- << " rad=" << ninePatch.outlineRadius
- << " alpha=" << ninePatch.outlineAlpha;
+ return out << "horizontalStretch:"
+ << util::joiner(ninePatch.horizontalStretchRegions, " ")
+ << " verticalStretch:"
+ << util::joiner(ninePatch.verticalStretchRegions, " ")
+ << " padding: " << ninePatch.padding
+ << ", bounds: " << ninePatch.layoutBounds
+ << ", outline: " << ninePatch.outline
+ << " rad=" << ninePatch.outlineRadius
+ << " alpha=" << ninePatch.outlineAlpha;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/NinePatch_test.cpp b/tools/aapt2/compile/NinePatch_test.cpp
index 3106ff8..b8eda09 100644
--- a/tools/aapt2/compile/NinePatch_test.cpp
+++ b/tools/aapt2/compile/NinePatch_test.cpp
@@ -21,8 +21,8 @@
// Pixels are in RGBA_8888 packing.
-#define RED "\xff\x00\x00\xff"
-#define BLUE "\x00\x00\xff\xff"
+#define RED "\xff\x00\x00\xff"
+#define BLUE "\x00\x00\xff\xff"
#define GREEN "\xff\x00\x00\xff"
#define GR_70 "\xff\x00\x00\xb3"
#define GR_50 "\xff\x00\x00\x80"
@@ -32,327 +32,346 @@
#define TRANS "\x00\x00\x00\x00"
static uint8_t* k2x2[] = {
- (uint8_t*) WHITE WHITE,
- (uint8_t*) WHITE WHITE,
+ (uint8_t*)WHITE WHITE, (uint8_t*)WHITE WHITE,
};
static uint8_t* kMixedNeutralColor3x3[] = {
- (uint8_t*) WHITE BLACK TRANS,
- (uint8_t*) TRANS RED TRANS,
- (uint8_t*) WHITE WHITE WHITE,
+ (uint8_t*)WHITE BLACK TRANS, (uint8_t*)TRANS RED TRANS,
+ (uint8_t*)WHITE WHITE WHITE,
};
static uint8_t* kTransparentNeutralColor3x3[] = {
- (uint8_t*) TRANS BLACK TRANS,
- (uint8_t*) BLACK RED BLACK,
- (uint8_t*) TRANS BLACK TRANS,
+ (uint8_t*)TRANS BLACK TRANS, (uint8_t*)BLACK RED BLACK,
+ (uint8_t*)TRANS BLACK TRANS,
};
static uint8_t* kSingleStretch7x6[] = {
- (uint8_t*) WHITE WHITE BLACK BLACK BLACK WHITE WHITE,
- (uint8_t*) WHITE RED RED RED RED RED WHITE,
- (uint8_t*) BLACK RED RED RED RED RED WHITE,
- (uint8_t*) BLACK RED RED RED RED RED WHITE,
- (uint8_t*) WHITE RED RED RED RED RED WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE BLACK BLACK BLACK WHITE WHITE,
+ (uint8_t*)WHITE RED RED RED RED RED WHITE,
+ (uint8_t*)BLACK RED RED RED RED RED WHITE,
+ (uint8_t*)BLACK RED RED RED RED RED WHITE,
+ (uint8_t*)WHITE RED RED RED RED RED WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
};
static uint8_t* kMultipleStretch10x7[] = {
- (uint8_t*) WHITE WHITE BLACK WHITE BLACK BLACK WHITE BLACK WHITE WHITE,
- (uint8_t*) BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
- (uint8_t*) BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
- (uint8_t*) WHITE RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
- (uint8_t*) BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
- (uint8_t*) BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE BLACK WHITE BLACK BLACK WHITE BLACK WHITE WHITE,
+ (uint8_t*)BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*)BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*)WHITE RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*)BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*)BLACK RED BLUE RED BLUE BLUE RED BLUE RED WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
};
static uint8_t* kPadding6x5[] = {
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE BLACK,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE BLACK BLACK WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE BLACK,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE BLACK BLACK WHITE WHITE,
};
static uint8_t* kLayoutBoundsWrongEdge3x3[] = {
- (uint8_t*) WHITE RED WHITE,
- (uint8_t*) RED WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE,
+ (uint8_t*)WHITE RED WHITE, (uint8_t*)RED WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE,
};
static uint8_t* kLayoutBoundsNotEdgeAligned5x5[] = {
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE RED,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE RED WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE RED,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE RED WHITE WHITE,
};
static uint8_t* kLayoutBounds5x5[] = {
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE RED,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE RED,
- (uint8_t*) WHITE RED WHITE RED WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE RED,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE RED,
+ (uint8_t*)WHITE RED WHITE RED WHITE,
};
static uint8_t* kAsymmetricLayoutBounds5x5[] = {
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE RED,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE RED WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE RED,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE RED WHITE WHITE WHITE,
};
static uint8_t* kPaddingAndLayoutBounds5x5[] = {
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE RED,
- (uint8_t*) WHITE WHITE WHITE WHITE BLACK,
- (uint8_t*) WHITE WHITE WHITE WHITE RED,
- (uint8_t*) WHITE RED BLACK RED WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE RED,
+ (uint8_t*)WHITE WHITE WHITE WHITE BLACK,
+ (uint8_t*)WHITE WHITE WHITE WHITE RED,
+ (uint8_t*)WHITE RED BLACK RED WHITE,
};
static uint8_t* kColorfulImage5x5[] = {
- (uint8_t*) WHITE BLACK WHITE BLACK WHITE,
- (uint8_t*) BLACK RED BLUE GREEN WHITE,
- (uint8_t*) BLACK RED GREEN GREEN WHITE,
- (uint8_t*) WHITE TRANS BLUE GREEN WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE BLACK WHITE BLACK WHITE,
+ (uint8_t*)BLACK RED BLUE GREEN WHITE,
+ (uint8_t*)BLACK RED GREEN GREEN WHITE,
+ (uint8_t*)WHITE TRANS BLUE GREEN WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
};
static uint8_t* kOutlineOpaque10x10[] = {
- (uint8_t*) WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
+ (uint8_t*)WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
};
static uint8_t* kOutlineTranslucent10x10[] = {
- (uint8_t*) WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
- (uint8_t*) WHITE TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
+ (uint8_t*)WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+ (uint8_t*)WHITE TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+ (uint8_t*)WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
};
static uint8_t* kOutlineOffsetTranslucent12x10[] = {
- (uint8_t*) WHITE WHITE WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
- (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)
+ WHITE WHITE WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
+ (uint8_t*)
+ WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*)
+ WHITE TRANS TRANS TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+ (uint8_t*)
+ WHITE TRANS TRANS TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+ (uint8_t*)
+ WHITE TRANS TRANS TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+ (uint8_t*)
+ WHITE TRANS TRANS TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+ (uint8_t*)
+ WHITE TRANS TRANS TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+ (uint8_t*)
+ WHITE TRANS TRANS TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+ (uint8_t*)
+ WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+ (uint8_t*)
+ WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
};
static uint8_t* kOutlineRadius5x5[] = {
- (uint8_t*) WHITE BLACK BLACK BLACK WHITE,
- (uint8_t*) BLACK TRANS GREEN TRANS WHITE,
- (uint8_t*) BLACK GREEN GREEN GREEN WHITE,
- (uint8_t*) BLACK TRANS GREEN TRANS WHITE,
- (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+ (uint8_t*)WHITE BLACK BLACK BLACK WHITE,
+ (uint8_t*)BLACK TRANS GREEN TRANS WHITE,
+ (uint8_t*)BLACK GREEN GREEN GREEN WHITE,
+ (uint8_t*)BLACK TRANS GREEN TRANS WHITE,
+ (uint8_t*)WHITE WHITE WHITE WHITE WHITE,
};
static uint8_t* kStretchAndPadding5x5[] = {
- (uint8_t*) WHITE WHITE BLACK WHITE WHITE,
- (uint8_t*) WHITE RED RED RED WHITE,
- (uint8_t*) BLACK RED RED RED BLACK,
- (uint8_t*) WHITE RED RED RED WHITE,
- (uint8_t*) WHITE WHITE BLACK WHITE WHITE,
+ (uint8_t*)WHITE WHITE BLACK WHITE WHITE, (uint8_t*)WHITE RED RED RED WHITE,
+ (uint8_t*)BLACK RED RED RED BLACK, (uint8_t*)WHITE RED RED RED WHITE,
+ (uint8_t*)WHITE WHITE BLACK WHITE WHITE,
};
TEST(NinePatchTest, Minimum3x3) {
- std::string err;
- EXPECT_EQ(nullptr, NinePatch::create(k2x2, 2, 2, &err));
- EXPECT_FALSE(err.empty());
+ std::string err;
+ EXPECT_EQ(nullptr, NinePatch::create(k2x2, 2, 2, &err));
+ EXPECT_FALSE(err.empty());
}
TEST(NinePatchTest, MixedNeutralColors) {
- std::string err;
- EXPECT_EQ(nullptr, NinePatch::create(kMixedNeutralColor3x3, 3, 3, &err));
- EXPECT_FALSE(err.empty());
+ std::string err;
+ EXPECT_EQ(nullptr, NinePatch::create(kMixedNeutralColor3x3, 3, 3, &err));
+ EXPECT_FALSE(err.empty());
}
TEST(NinePatchTest, TransparentNeutralColor) {
- std::string err;
- EXPECT_NE(nullptr, NinePatch::create(kTransparentNeutralColor3x3, 3, 3, &err));
+ std::string err;
+ EXPECT_NE(nullptr,
+ NinePatch::create(kTransparentNeutralColor3x3, 3, 3, &err));
}
TEST(NinePatchTest, SingleStretchRegion) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kSingleStretch7x6, 7, 6, &err);
- ASSERT_NE(nullptr, ninePatch);
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch =
+ NinePatch::create(kSingleStretch7x6, 7, 6, &err);
+ ASSERT_NE(nullptr, ninePatch);
- ASSERT_EQ(1u, ninePatch->horizontalStretchRegions.size());
- ASSERT_EQ(1u, ninePatch->verticalStretchRegions.size());
+ ASSERT_EQ(1u, ninePatch->horizontalStretchRegions.size());
+ ASSERT_EQ(1u, ninePatch->verticalStretchRegions.size());
- EXPECT_EQ(Range(1, 4), ninePatch->horizontalStretchRegions.front());
- EXPECT_EQ(Range(1, 3), ninePatch->verticalStretchRegions.front());
+ EXPECT_EQ(Range(1, 4), ninePatch->horizontalStretchRegions.front());
+ EXPECT_EQ(Range(1, 3), ninePatch->verticalStretchRegions.front());
}
TEST(NinePatchTest, MultipleStretchRegions) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kMultipleStretch10x7, 10, 7, &err);
- ASSERT_NE(nullptr, ninePatch);
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch =
+ NinePatch::create(kMultipleStretch10x7, 10, 7, &err);
+ ASSERT_NE(nullptr, ninePatch);
- ASSERT_EQ(3u, ninePatch->horizontalStretchRegions.size());
- ASSERT_EQ(2u, ninePatch->verticalStretchRegions.size());
+ ASSERT_EQ(3u, ninePatch->horizontalStretchRegions.size());
+ ASSERT_EQ(2u, ninePatch->verticalStretchRegions.size());
- EXPECT_EQ(Range(1, 2), ninePatch->horizontalStretchRegions[0]);
- EXPECT_EQ(Range(3, 5), ninePatch->horizontalStretchRegions[1]);
- EXPECT_EQ(Range(6, 7), ninePatch->horizontalStretchRegions[2]);
+ EXPECT_EQ(Range(1, 2), ninePatch->horizontalStretchRegions[0]);
+ EXPECT_EQ(Range(3, 5), ninePatch->horizontalStretchRegions[1]);
+ EXPECT_EQ(Range(6, 7), ninePatch->horizontalStretchRegions[2]);
- EXPECT_EQ(Range(0, 2), ninePatch->verticalStretchRegions[0]);
- EXPECT_EQ(Range(3, 5), ninePatch->verticalStretchRegions[1]);
+ EXPECT_EQ(Range(0, 2), ninePatch->verticalStretchRegions[0]);
+ EXPECT_EQ(Range(3, 5), ninePatch->verticalStretchRegions[1]);
}
TEST(NinePatchTest, InferPaddingFromStretchRegions) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kMultipleStretch10x7, 10, 7, &err);
- ASSERT_NE(nullptr, ninePatch);
- EXPECT_EQ(Bounds(1, 0, 1, 0), ninePatch->padding);
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch =
+ NinePatch::create(kMultipleStretch10x7, 10, 7, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(1, 0, 1, 0), ninePatch->padding);
}
TEST(NinePatchTest, Padding) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kPadding6x5, 6, 5, &err);
- ASSERT_NE(nullptr, ninePatch);
- EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->padding);
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch =
+ NinePatch::create(kPadding6x5, 6, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->padding);
}
TEST(NinePatchTest, LayoutBoundsAreOnWrongEdge) {
- std::string err;
- EXPECT_EQ(nullptr, NinePatch::create(kLayoutBoundsWrongEdge3x3, 3, 3, &err));
- EXPECT_FALSE(err.empty());
+ std::string err;
+ EXPECT_EQ(nullptr, NinePatch::create(kLayoutBoundsWrongEdge3x3, 3, 3, &err));
+ EXPECT_FALSE(err.empty());
}
TEST(NinePatchTest, LayoutBoundsMustTouchEdges) {
- std::string err;
- EXPECT_EQ(nullptr, NinePatch::create(kLayoutBoundsNotEdgeAligned5x5, 5, 5, &err));
- EXPECT_FALSE(err.empty());
+ std::string err;
+ EXPECT_EQ(nullptr,
+ NinePatch::create(kLayoutBoundsNotEdgeAligned5x5, 5, 5, &err));
+ EXPECT_FALSE(err.empty());
}
TEST(NinePatchTest, LayoutBounds) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kLayoutBounds5x5, 5, 5, &err);
- ASSERT_NE(nullptr, ninePatch);
- EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->layoutBounds);
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch =
+ NinePatch::create(kLayoutBounds5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->layoutBounds);
- ninePatch = NinePatch::create(kAsymmetricLayoutBounds5x5, 5, 5, &err);
- ASSERT_NE(nullptr, ninePatch);
- EXPECT_EQ(Bounds(1, 1, 0, 0), ninePatch->layoutBounds);
+ ninePatch = NinePatch::create(kAsymmetricLayoutBounds5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(1, 1, 0, 0), ninePatch->layoutBounds);
}
TEST(NinePatchTest, PaddingAndLayoutBounds) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kPaddingAndLayoutBounds5x5, 5, 5,
- &err);
- ASSERT_NE(nullptr, ninePatch);
- EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->padding);
- EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->layoutBounds);
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch =
+ NinePatch::create(kPaddingAndLayoutBounds5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->padding);
+ EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->layoutBounds);
}
TEST(NinePatchTest, RegionColorsAreCorrect) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kColorfulImage5x5, 5, 5, &err);
- ASSERT_NE(nullptr, ninePatch);
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch =
+ NinePatch::create(kColorfulImage5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
- std::vector<uint32_t> expectedColors = {
- NinePatch::packRGBA((uint8_t*) RED),
- (uint32_t) android::Res_png_9patch::NO_COLOR,
- NinePatch::packRGBA((uint8_t*) GREEN),
- (uint32_t) android::Res_png_9patch::TRANSPARENT_COLOR,
- NinePatch::packRGBA((uint8_t*) BLUE),
- NinePatch::packRGBA((uint8_t*) GREEN),
- };
- EXPECT_EQ(expectedColors, ninePatch->regionColors);
+ std::vector<uint32_t> expectedColors = {
+ NinePatch::packRGBA((uint8_t*)RED),
+ (uint32_t)android::Res_png_9patch::NO_COLOR,
+ NinePatch::packRGBA((uint8_t*)GREEN),
+ (uint32_t)android::Res_png_9patch::TRANSPARENT_COLOR,
+ NinePatch::packRGBA((uint8_t*)BLUE),
+ NinePatch::packRGBA((uint8_t*)GREEN),
+ };
+ EXPECT_EQ(expectedColors, ninePatch->regionColors);
}
TEST(NinePatchTest, OutlineFromOpaqueImage) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineOpaque10x10, 10, 10, &err);
- ASSERT_NE(nullptr, ninePatch);
- EXPECT_EQ(Bounds(2, 2, 2, 2), ninePatch->outline);
- EXPECT_EQ(0x000000ffu, ninePatch->outlineAlpha);
- EXPECT_EQ(0.0f, ninePatch->outlineRadius);
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch =
+ NinePatch::create(kOutlineOpaque10x10, 10, 10, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(2, 2, 2, 2), ninePatch->outline);
+ EXPECT_EQ(0x000000ffu, ninePatch->outlineAlpha);
+ EXPECT_EQ(0.0f, ninePatch->outlineRadius);
}
TEST(NinePatchTest, OutlineFromTranslucentImage) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineTranslucent10x10, 10, 10,
- &err);
- ASSERT_NE(nullptr, ninePatch);
- EXPECT_EQ(Bounds(3, 3, 3, 3), ninePatch->outline);
- EXPECT_EQ(0x000000b3u, ninePatch->outlineAlpha);
- EXPECT_EQ(0.0f, ninePatch->outlineRadius);
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch =
+ NinePatch::create(kOutlineTranslucent10x10, 10, 10, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(3, 3, 3, 3), ninePatch->outline);
+ EXPECT_EQ(0x000000b3u, ninePatch->outlineAlpha);
+ EXPECT_EQ(0.0f, ninePatch->outlineRadius);
}
TEST(NinePatchTest, OutlineFromOffCenterImage) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineOffsetTranslucent12x10, 12, 10,
- &err);
- ASSERT_NE(nullptr, ninePatch);
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch =
+ NinePatch::create(kOutlineOffsetTranslucent12x10, 12, 10, &err);
+ ASSERT_NE(nullptr, ninePatch);
- // TODO(adamlesinski): The old AAPT algorithm searches from the outside to the middle
- // for each inset. If the outline is shifted, the search may not find a closer bounds.
- // This check should be:
- // EXPECT_EQ(Bounds(5, 3, 3, 3), ninePatch->outline);
- // but until I know what behaviour I'm breaking, I will leave it at the incorrect:
- EXPECT_EQ(Bounds(4, 3, 3, 3), ninePatch->outline);
+ // TODO(adamlesinski): The old AAPT algorithm searches from the outside to the
+ // middle
+ // for each inset. If the outline is shifted, the search may not find a closer
+ // bounds.
+ // This check should be:
+ // EXPECT_EQ(Bounds(5, 3, 3, 3), ninePatch->outline);
+ // but until I know what behaviour I'm breaking, I will leave it at the
+ // incorrect:
+ EXPECT_EQ(Bounds(4, 3, 3, 3), ninePatch->outline);
- EXPECT_EQ(0x000000b3u, ninePatch->outlineAlpha);
- EXPECT_EQ(0.0f, ninePatch->outlineRadius);
+ EXPECT_EQ(0x000000b3u, ninePatch->outlineAlpha);
+ EXPECT_EQ(0.0f, ninePatch->outlineRadius);
}
TEST(NinePatchTest, OutlineRadius) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineRadius5x5, 5, 5, &err);
- ASSERT_NE(nullptr, ninePatch);
- EXPECT_EQ(Bounds(0, 0, 0, 0), ninePatch->outline);
- EXPECT_EQ(3.4142f, ninePatch->outlineRadius);
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch =
+ NinePatch::create(kOutlineRadius5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
+ EXPECT_EQ(Bounds(0, 0, 0, 0), ninePatch->outline);
+ EXPECT_EQ(3.4142f, ninePatch->outlineRadius);
}
::testing::AssertionResult bigEndianOne(uint8_t* cursor) {
- if (cursor[0] == 0 && cursor[1] == 0 && cursor[2] == 0 && cursor[3] == 1) {
- return ::testing::AssertionSuccess();
- }
- return ::testing::AssertionFailure() << "Not BigEndian 1";
+ if (cursor[0] == 0 && cursor[1] == 0 && cursor[2] == 0 && cursor[3] == 1) {
+ return ::testing::AssertionSuccess();
+ }
+ return ::testing::AssertionFailure() << "Not BigEndian 1";
}
TEST(NinePatchTest, SerializePngEndianness) {
- std::string err;
- std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kStretchAndPadding5x5, 5, 5, &err);
- ASSERT_NE(nullptr, ninePatch);
+ std::string err;
+ std::unique_ptr<NinePatch> ninePatch =
+ NinePatch::create(kStretchAndPadding5x5, 5, 5, &err);
+ ASSERT_NE(nullptr, ninePatch);
- size_t len;
- std::unique_ptr<uint8_t[]> data = ninePatch->serializeBase(&len);
- ASSERT_NE(nullptr, data);
- ASSERT_NE(0u, len);
+ size_t len;
+ std::unique_ptr<uint8_t[]> data = ninePatch->serializeBase(&len);
+ ASSERT_NE(nullptr, data);
+ ASSERT_NE(0u, len);
- // Skip past wasDeserialized + numXDivs + numYDivs + numColors + xDivsOffset + yDivsOffset
- // (12 bytes)
- uint8_t* cursor = data.get() + 12;
+ // Skip past wasDeserialized + numXDivs + numYDivs + numColors + xDivsOffset +
+ // yDivsOffset
+ // (12 bytes)
+ uint8_t* cursor = data.get() + 12;
- // Check that padding is big-endian. Expecting value 1.
- EXPECT_TRUE(bigEndianOne(cursor));
- EXPECT_TRUE(bigEndianOne(cursor + 4));
- EXPECT_TRUE(bigEndianOne(cursor + 8));
- EXPECT_TRUE(bigEndianOne(cursor + 12));
+ // Check that padding is big-endian. Expecting value 1.
+ EXPECT_TRUE(bigEndianOne(cursor));
+ EXPECT_TRUE(bigEndianOne(cursor + 4));
+ EXPECT_TRUE(bigEndianOne(cursor + 8));
+ EXPECT_TRUE(bigEndianOne(cursor + 12));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/Png.cpp b/tools/aapt2/compile/Png.cpp
index 055d8b5..9b5fa7e09 100644
--- a/tools/aapt2/compile/Png.cpp
+++ b/tools/aapt2/compile/Png.cpp
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-#include "util/BigBuffer.h"
#include "Png.h"
#include "Source.h"
+#include "util/BigBuffer.h"
#include "util/Util.h"
#include <androidfw/ResourceTypes.h>
-#include <iostream>
#include <png.h>
+#include <zlib.h>
+#include <iostream>
#include <sstream>
#include <string>
#include <vector>
-#include <zlib.h>
namespace aapt {
@@ -33,158 +33,166 @@
constexpr size_t kPngSignatureSize = 8u;
struct PngInfo {
- ~PngInfo() {
- for (png_bytep row : rows) {
- if (row != nullptr) {
- delete[] row;
- }
- }
-
- delete[] xDivs;
- delete[] yDivs;
+ ~PngInfo() {
+ for (png_bytep row : rows) {
+ if (row != nullptr) {
+ delete[] row;
+ }
}
- void* serialize9Patch() {
- void* serialized = android::Res_png_9patch::serialize(info9Patch, xDivs, yDivs,
- colors.data());
- reinterpret_cast<android::Res_png_9patch*>(serialized)->deviceToFile();
- return serialized;
- }
+ delete[] xDivs;
+ delete[] yDivs;
+ }
- uint32_t width = 0;
- uint32_t height = 0;
- std::vector<png_bytep> rows;
+ void* serialize9Patch() {
+ void* serialized = android::Res_png_9patch::serialize(info9Patch, xDivs,
+ yDivs, colors.data());
+ reinterpret_cast<android::Res_png_9patch*>(serialized)->deviceToFile();
+ return serialized;
+ }
- bool is9Patch = false;
- android::Res_png_9patch info9Patch;
- int32_t* xDivs = nullptr;
- int32_t* yDivs = nullptr;
- std::vector<uint32_t> colors;
+ uint32_t width = 0;
+ uint32_t height = 0;
+ std::vector<png_bytep> rows;
- // Layout padding.
- bool haveLayoutBounds = false;
- int32_t layoutBoundsLeft;
- int32_t layoutBoundsTop;
- int32_t layoutBoundsRight;
- int32_t layoutBoundsBottom;
+ bool is9Patch = false;
+ android::Res_png_9patch info9Patch;
+ int32_t* xDivs = nullptr;
+ int32_t* yDivs = nullptr;
+ std::vector<uint32_t> colors;
- // Round rect outline description.
- int32_t outlineInsetsLeft;
- int32_t outlineInsetsTop;
- int32_t outlineInsetsRight;
- int32_t outlineInsetsBottom;
- float outlineRadius;
- uint8_t outlineAlpha;
+ // Layout padding.
+ bool haveLayoutBounds = false;
+ int32_t layoutBoundsLeft;
+ int32_t layoutBoundsTop;
+ int32_t layoutBoundsRight;
+ int32_t layoutBoundsBottom;
+
+ // Round rect outline description.
+ int32_t outlineInsetsLeft;
+ int32_t outlineInsetsTop;
+ int32_t outlineInsetsRight;
+ int32_t outlineInsetsBottom;
+ float outlineRadius;
+ uint8_t outlineAlpha;
};
-static void readDataFromStream(png_structp readPtr, png_bytep data, png_size_t length) {
- std::istream* input = reinterpret_cast<std::istream*>(png_get_io_ptr(readPtr));
- if (!input->read(reinterpret_cast<char*>(data), length)) {
- png_error(readPtr, strerror(errno));
- }
+static void readDataFromStream(png_structp readPtr, png_bytep data,
+ png_size_t length) {
+ std::istream* input =
+ reinterpret_cast<std::istream*>(png_get_io_ptr(readPtr));
+ if (!input->read(reinterpret_cast<char*>(data), length)) {
+ png_error(readPtr, strerror(errno));
+ }
}
-static void writeDataToStream(png_structp writePtr, png_bytep data, png_size_t length) {
- BigBuffer* outBuffer = reinterpret_cast<BigBuffer*>(png_get_io_ptr(writePtr));
- png_bytep buf = outBuffer->nextBlock<png_byte>(length);
- memcpy(buf, data, length);
+static void writeDataToStream(png_structp writePtr, png_bytep data,
+ png_size_t length) {
+ BigBuffer* outBuffer = reinterpret_cast<BigBuffer*>(png_get_io_ptr(writePtr));
+ png_bytep buf = outBuffer->nextBlock<png_byte>(length);
+ memcpy(buf, data, length);
}
-static void flushDataToStream(png_structp /*writePtr*/) {
-}
+static void flushDataToStream(png_structp /*writePtr*/) {}
static void logWarning(png_structp readPtr, png_const_charp warningMessage) {
- IDiagnostics* diag = reinterpret_cast<IDiagnostics*>(png_get_error_ptr(readPtr));
- diag->warn(DiagMessage() << warningMessage);
+ IDiagnostics* diag =
+ reinterpret_cast<IDiagnostics*>(png_get_error_ptr(readPtr));
+ diag->warn(DiagMessage() << warningMessage);
}
+static bool readPng(IDiagnostics* diag, png_structp readPtr, png_infop infoPtr,
+ PngInfo* outInfo) {
+ if (setjmp(png_jmpbuf(readPtr))) {
+ diag->error(DiagMessage() << "failed reading png");
+ return false;
+ }
-static bool readPng(IDiagnostics* diag, png_structp readPtr, png_infop infoPtr, PngInfo* outInfo) {
- if (setjmp(png_jmpbuf(readPtr))) {
- diag->error(DiagMessage() << "failed reading png");
- return false;
- }
+ png_set_sig_bytes(readPtr, kPngSignatureSize);
+ png_read_info(readPtr, infoPtr);
- png_set_sig_bytes(readPtr, kPngSignatureSize);
- png_read_info(readPtr, infoPtr);
+ int colorType, bitDepth, interlaceType, compressionType;
+ png_get_IHDR(readPtr, infoPtr, &outInfo->width, &outInfo->height, &bitDepth,
+ &colorType, &interlaceType, &compressionType, nullptr);
- int colorType, bitDepth, interlaceType, compressionType;
- png_get_IHDR(readPtr, infoPtr, &outInfo->width, &outInfo->height, &bitDepth, &colorType,
- &interlaceType, &compressionType, nullptr);
+ if (colorType == PNG_COLOR_TYPE_PALETTE) {
+ png_set_palette_to_rgb(readPtr);
+ }
- if (colorType == PNG_COLOR_TYPE_PALETTE) {
- png_set_palette_to_rgb(readPtr);
- }
+ if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
+ png_set_expand_gray_1_2_4_to_8(readPtr);
+ }
- if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
- png_set_expand_gray_1_2_4_to_8(readPtr);
- }
+ if (png_get_valid(readPtr, infoPtr, PNG_INFO_tRNS)) {
+ png_set_tRNS_to_alpha(readPtr);
+ }
- if (png_get_valid(readPtr, infoPtr, PNG_INFO_tRNS)) {
- png_set_tRNS_to_alpha(readPtr);
- }
+ if (bitDepth == 16) {
+ png_set_strip_16(readPtr);
+ }
- if (bitDepth == 16) {
- png_set_strip_16(readPtr);
- }
+ if (!(colorType & PNG_COLOR_MASK_ALPHA)) {
+ png_set_add_alpha(readPtr, 0xFF, PNG_FILLER_AFTER);
+ }
- if (!(colorType & PNG_COLOR_MASK_ALPHA)) {
- png_set_add_alpha(readPtr, 0xFF, PNG_FILLER_AFTER);
- }
+ if (colorType == PNG_COLOR_TYPE_GRAY ||
+ colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ png_set_gray_to_rgb(readPtr);
+ }
- if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
- png_set_gray_to_rgb(readPtr);
- }
+ png_set_interlace_handling(readPtr);
+ png_read_update_info(readPtr, infoPtr);
- png_set_interlace_handling(readPtr);
- png_read_update_info(readPtr, infoPtr);
+ const uint32_t rowBytes = png_get_rowbytes(readPtr, infoPtr);
+ outInfo->rows.resize(outInfo->height);
+ for (size_t i = 0; i < outInfo->height; i++) {
+ outInfo->rows[i] = new png_byte[rowBytes];
+ }
- const uint32_t rowBytes = png_get_rowbytes(readPtr, infoPtr);
- outInfo->rows.resize(outInfo->height);
- for (size_t i = 0; i < outInfo->height; i++) {
- outInfo->rows[i] = new png_byte[rowBytes];
- }
-
- png_read_image(readPtr, outInfo->rows.data());
- png_read_end(readPtr, infoPtr);
- return true;
+ png_read_image(readPtr, outInfo->rows.data());
+ png_read_end(readPtr, infoPtr);
+ return true;
}
-static void checkNinePatchSerialization(android::Res_png_9patch* inPatch, void* data) {
- size_t patchSize = inPatch->serializedSize();
- void* newData = malloc(patchSize);
- memcpy(newData, data, patchSize);
- android::Res_png_9patch* outPatch = inPatch->deserialize(newData);
- outPatch->fileToDevice();
- // deserialization is done in place, so outPatch == newData
- assert(outPatch == newData);
- assert(outPatch->numXDivs == inPatch->numXDivs);
- assert(outPatch->numYDivs == inPatch->numYDivs);
- assert(outPatch->paddingLeft == inPatch->paddingLeft);
- assert(outPatch->paddingRight == inPatch->paddingRight);
- assert(outPatch->paddingTop == inPatch->paddingTop);
- assert(outPatch->paddingBottom == inPatch->paddingBottom);
-/* for (int i = 0; i < outPatch->numXDivs; i++) {
- assert(outPatch->getXDivs()[i] == inPatch->getXDivs()[i]);
- }
- for (int i = 0; i < outPatch->numYDivs; i++) {
- assert(outPatch->getYDivs()[i] == inPatch->getYDivs()[i]);
- }
- for (int i = 0; i < outPatch->numColors; i++) {
- assert(outPatch->getColors()[i] == inPatch->getColors()[i]);
- }*/
- free(newData);
+static void checkNinePatchSerialization(android::Res_png_9patch* inPatch,
+ void* data) {
+ size_t patchSize = inPatch->serializedSize();
+ void* newData = malloc(patchSize);
+ memcpy(newData, data, patchSize);
+ android::Res_png_9patch* outPatch = inPatch->deserialize(newData);
+ outPatch->fileToDevice();
+ // deserialization is done in place, so outPatch == newData
+ assert(outPatch == newData);
+ assert(outPatch->numXDivs == inPatch->numXDivs);
+ assert(outPatch->numYDivs == inPatch->numYDivs);
+ assert(outPatch->paddingLeft == inPatch->paddingLeft);
+ assert(outPatch->paddingRight == inPatch->paddingRight);
+ assert(outPatch->paddingTop == inPatch->paddingTop);
+ assert(outPatch->paddingBottom == inPatch->paddingBottom);
+ /* for (int i = 0; i < outPatch->numXDivs; i++) {
+ assert(outPatch->getXDivs()[i] == inPatch->getXDivs()[i]);
+ }
+ for (int i = 0; i < outPatch->numYDivs; i++) {
+ assert(outPatch->getYDivs()[i] == inPatch->getYDivs()[i]);
+ }
+ for (int i = 0; i < outPatch->numColors; i++) {
+ assert(outPatch->getColors()[i] == inPatch->getColors()[i]);
+ }*/
+ free(newData);
}
-/*static void dump_image(int w, int h, const png_byte* const* rows, int color_type) {
+/*static void dump_image(int w, int h, const png_byte* const* rows, int
+color_type) {
int i, j, rr, gg, bb, aa;
int bpp;
- if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY) {
+ if (color_type == PNG_COLOR_TYPE_PALETTE || color_type ==
+PNG_COLOR_TYPE_GRAY) {
bpp = 1;
} else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
bpp = 2;
- } else if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
+ } else if (color_type == PNG_COLOR_TYPE_RGB || color_type ==
+PNG_COLOR_TYPE_RGB_ALPHA) {
// We use a padding byte even when there is no alpha
bpp = 4;
} else {
@@ -224,1055 +232,1083 @@
}
}*/
-#define MAX(a,b) ((a)>(b)?(a):(b))
-#define ABS(a) ((a)<0?-(a):(a))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define ABS(a) ((a) < 0 ? -(a) : (a))
-static void analyze_image(IDiagnostics* diag, const PngInfo& imageInfo, int grayscaleTolerance,
- png_colorp rgbPalette, png_bytep alphaPalette,
- int *paletteEntries, bool *hasTransparency, int *colorType,
+static void analyze_image(IDiagnostics* diag, const PngInfo& imageInfo,
+ int grayscaleTolerance, png_colorp rgbPalette,
+ png_bytep alphaPalette, int* paletteEntries,
+ bool* hasTransparency, int* colorType,
png_bytepp outRows) {
- int w = imageInfo.width;
- int h = imageInfo.height;
- int i, j, rr, gg, bb, aa, idx;
- uint32_t colors[256], col;
- int num_colors = 0;
- int maxGrayDeviation = 0;
+ int w = imageInfo.width;
+ int h = imageInfo.height;
+ int i, j, rr, gg, bb, aa, idx;
+ uint32_t colors[256], col;
+ int num_colors = 0;
+ int maxGrayDeviation = 0;
- bool isOpaque = true;
- bool isPalette = true;
- bool isGrayscale = true;
+ bool isOpaque = true;
+ bool isPalette = true;
+ bool isGrayscale = true;
- // Scan the entire image and determine if:
- // 1. Every pixel has R == G == B (grayscale)
- // 2. Every pixel has A == 255 (opaque)
- // 3. There are no more than 256 distinct RGBA colors
+ // Scan the entire image and determine if:
+ // 1. Every pixel has R == G == B (grayscale)
+ // 2. Every pixel has A == 255 (opaque)
+ // 3. There are no more than 256 distinct RGBA colors
- if (kDebug) {
- printf("Initial image data:\n");
- //dump_image(w, h, imageInfo.rows.data(), PNG_COLOR_TYPE_RGB_ALPHA);
- }
+ if (kDebug) {
+ printf("Initial image data:\n");
+ // dump_image(w, h, imageInfo.rows.data(), PNG_COLOR_TYPE_RGB_ALPHA);
+ }
- for (j = 0; j < h; j++) {
- const png_byte* row = imageInfo.rows[j];
- png_bytep out = outRows[j];
- for (i = 0; i < w; i++) {
- rr = *row++;
- gg = *row++;
- bb = *row++;
- aa = *row++;
+ for (j = 0; j < h; j++) {
+ const png_byte* row = imageInfo.rows[j];
+ png_bytep out = outRows[j];
+ for (i = 0; i < w; i++) {
+ rr = *row++;
+ gg = *row++;
+ bb = *row++;
+ aa = *row++;
- int odev = maxGrayDeviation;
- maxGrayDeviation = MAX(ABS(rr - gg), maxGrayDeviation);
- maxGrayDeviation = MAX(ABS(gg - bb), maxGrayDeviation);
- maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation);
- if (maxGrayDeviation > odev) {
- if (kDebug) {
- printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n",
- maxGrayDeviation, i, j, rr, gg, bb, aa);
- }
- }
-
- // Check if image is really grayscale
- if (isGrayscale) {
- if (rr != gg || rr != bb) {
- if (kDebug) {
- printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n",
- i, j, rr, gg, bb, aa);
- }
- isGrayscale = false;
- }
- }
-
- // Check if image is really opaque
- if (isOpaque) {
- if (aa != 0xff) {
- if (kDebug) {
- printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n",
- i, j, rr, gg, bb, aa);
- }
- isOpaque = false;
- }
- }
-
- // Check if image is really <= 256 colors
- if (isPalette) {
- col = (uint32_t) ((rr << 24) | (gg << 16) | (bb << 8) | aa);
- bool match = false;
- for (idx = 0; idx < num_colors; idx++) {
- if (colors[idx] == col) {
- match = true;
- break;
- }
- }
-
- // Write the palette index for the pixel to outRows optimistically
- // We might overwrite it later if we decide to encode as gray or
- // gray + alpha
- *out++ = idx;
- if (!match) {
- if (num_colors == 256) {
- if (kDebug) {
- printf("Found 257th color at %d, %d\n", i, j);
- }
- isPalette = false;
- } else {
- colors[num_colors++] = col;
- }
- }
- }
+ int odev = maxGrayDeviation;
+ maxGrayDeviation = MAX(ABS(rr - gg), maxGrayDeviation);
+ maxGrayDeviation = MAX(ABS(gg - bb), maxGrayDeviation);
+ maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation);
+ if (maxGrayDeviation > odev) {
+ if (kDebug) {
+ printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n",
+ maxGrayDeviation, i, j, rr, gg, bb, aa);
}
- }
+ }
- *paletteEntries = 0;
- *hasTransparency = !isOpaque;
- int bpp = isOpaque ? 3 : 4;
- int paletteSize = w * h + bpp * num_colors;
-
- if (kDebug) {
- printf("isGrayscale = %s\n", isGrayscale ? "true" : "false");
- printf("isOpaque = %s\n", isOpaque ? "true" : "false");
- printf("isPalette = %s\n", isPalette ? "true" : "false");
- printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n",
- paletteSize, 2 * w * h, bpp * w * h);
- printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation, grayscaleTolerance);
- }
-
- // Choose the best color type for the image.
- // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel
- // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct combinations
- // is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA
- // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is sufficiently
- // small, otherwise use COLOR_TYPE_RGB{_ALPHA}
- if (isGrayscale) {
- if (isOpaque) {
- *colorType = PNG_COLOR_TYPE_GRAY; // 1 byte/pixel
- } else {
- // Use a simple heuristic to determine whether using a palette will
- // save space versus using gray + alpha for each pixel.
- // This doesn't take into account chunk overhead, filtering, LZ
- // compression, etc.
- if (isPalette && (paletteSize < 2 * w * h)) {
- *colorType = PNG_COLOR_TYPE_PALETTE; // 1 byte/pixel + 4 bytes/color
- } else {
- *colorType = PNG_COLOR_TYPE_GRAY_ALPHA; // 2 bytes per pixel
- }
+ // Check if image is really grayscale
+ if (isGrayscale) {
+ if (rr != gg || rr != bb) {
+ if (kDebug) {
+ printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n", i, j,
+ rr, gg, bb, aa);
+ }
+ isGrayscale = false;
}
- } else if (isPalette && (paletteSize < bpp * w * h)) {
- *colorType = PNG_COLOR_TYPE_PALETTE;
+ }
+
+ // Check if image is really opaque
+ if (isOpaque) {
+ if (aa != 0xff) {
+ if (kDebug) {
+ printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n", i, j,
+ rr, gg, bb, aa);
+ }
+ isOpaque = false;
+ }
+ }
+
+ // Check if image is really <= 256 colors
+ if (isPalette) {
+ col = (uint32_t)((rr << 24) | (gg << 16) | (bb << 8) | aa);
+ bool match = false;
+ for (idx = 0; idx < num_colors; idx++) {
+ if (colors[idx] == col) {
+ match = true;
+ break;
+ }
+ }
+
+ // Write the palette index for the pixel to outRows optimistically
+ // We might overwrite it later if we decide to encode as gray or
+ // gray + alpha
+ *out++ = idx;
+ if (!match) {
+ if (num_colors == 256) {
+ if (kDebug) {
+ printf("Found 257th color at %d, %d\n", i, j);
+ }
+ isPalette = false;
+ } else {
+ colors[num_colors++] = col;
+ }
+ }
+ }
+ }
+ }
+
+ *paletteEntries = 0;
+ *hasTransparency = !isOpaque;
+ int bpp = isOpaque ? 3 : 4;
+ int paletteSize = w * h + bpp * num_colors;
+
+ if (kDebug) {
+ printf("isGrayscale = %s\n", isGrayscale ? "true" : "false");
+ printf("isOpaque = %s\n", isOpaque ? "true" : "false");
+ printf("isPalette = %s\n", isPalette ? "true" : "false");
+ printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n", paletteSize,
+ 2 * w * h, bpp * w * h);
+ printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation,
+ grayscaleTolerance);
+ }
+
+ // Choose the best color type for the image.
+ // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel
+ // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct
+ // combinations
+ // is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA
+ // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is
+ // sufficiently
+ // small, otherwise use COLOR_TYPE_RGB{_ALPHA}
+ if (isGrayscale) {
+ if (isOpaque) {
+ *colorType = PNG_COLOR_TYPE_GRAY; // 1 byte/pixel
} else {
- if (maxGrayDeviation <= grayscaleTolerance) {
- diag->note(DiagMessage()
- << "forcing image to gray (max deviation = "
- << maxGrayDeviation << ")");
- *colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA;
+ // Use a simple heuristic to determine whether using a palette will
+ // save space versus using gray + alpha for each pixel.
+ // This doesn't take into account chunk overhead, filtering, LZ
+ // compression, etc.
+ if (isPalette && (paletteSize < 2 * w * h)) {
+ *colorType = PNG_COLOR_TYPE_PALETTE; // 1 byte/pixel + 4 bytes/color
+ } else {
+ *colorType = PNG_COLOR_TYPE_GRAY_ALPHA; // 2 bytes per pixel
+ }
+ }
+ } else if (isPalette && (paletteSize < bpp * w * h)) {
+ *colorType = PNG_COLOR_TYPE_PALETTE;
+ } else {
+ if (maxGrayDeviation <= grayscaleTolerance) {
+ diag->note(DiagMessage() << "forcing image to gray (max deviation = "
+ << maxGrayDeviation << ")");
+ *colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA;
+ } else {
+ *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
+ }
+ }
+
+ // Perform postprocessing of the image or palette data based on the final
+ // color type chosen
+
+ if (*colorType == PNG_COLOR_TYPE_PALETTE) {
+ // Create separate RGB and Alpha palettes and set the number of colors
+ *paletteEntries = num_colors;
+
+ // Create the RGB and alpha palettes
+ for (int idx = 0; idx < num_colors; idx++) {
+ col = colors[idx];
+ rgbPalette[idx].red = (png_byte)((col >> 24) & 0xff);
+ rgbPalette[idx].green = (png_byte)((col >> 16) & 0xff);
+ rgbPalette[idx].blue = (png_byte)((col >> 8) & 0xff);
+ alphaPalette[idx] = (png_byte)(col & 0xff);
+ }
+ } else if (*colorType == PNG_COLOR_TYPE_GRAY ||
+ *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ // If the image is gray or gray + alpha, compact the pixels into outRows
+ for (j = 0; j < h; j++) {
+ const png_byte* row = imageInfo.rows[j];
+ png_bytep out = outRows[j];
+ for (i = 0; i < w; i++) {
+ rr = *row++;
+ gg = *row++;
+ bb = *row++;
+ aa = *row++;
+
+ if (isGrayscale) {
+ *out++ = rr;
} else {
- *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
+ *out++ = (png_byte)(rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
}
+ if (!isOpaque) {
+ *out++ = aa;
+ }
+ }
}
-
- // Perform postprocessing of the image or palette data based on the final
- // color type chosen
-
- if (*colorType == PNG_COLOR_TYPE_PALETTE) {
- // Create separate RGB and Alpha palettes and set the number of colors
- *paletteEntries = num_colors;
-
- // Create the RGB and alpha palettes
- for (int idx = 0; idx < num_colors; idx++) {
- col = colors[idx];
- rgbPalette[idx].red = (png_byte) ((col >> 24) & 0xff);
- rgbPalette[idx].green = (png_byte) ((col >> 16) & 0xff);
- rgbPalette[idx].blue = (png_byte) ((col >> 8) & 0xff);
- alphaPalette[idx] = (png_byte) (col & 0xff);
- }
- } else if (*colorType == PNG_COLOR_TYPE_GRAY || *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
- // If the image is gray or gray + alpha, compact the pixels into outRows
- for (j = 0; j < h; j++) {
- const png_byte* row = imageInfo.rows[j];
- png_bytep out = outRows[j];
- for (i = 0; i < w; i++) {
- rr = *row++;
- gg = *row++;
- bb = *row++;
- aa = *row++;
-
- if (isGrayscale) {
- *out++ = rr;
- } else {
- *out++ = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
- }
- if (!isOpaque) {
- *out++ = aa;
- }
- }
- }
- }
+ }
}
-static bool writePng(IDiagnostics* diag, png_structp writePtr, png_infop infoPtr, PngInfo* info,
- int grayScaleTolerance) {
- if (setjmp(png_jmpbuf(writePtr))) {
- diag->error(DiagMessage() << "failed to write png");
- return false;
+static bool writePng(IDiagnostics* diag, png_structp writePtr,
+ png_infop infoPtr, PngInfo* info, int grayScaleTolerance) {
+ if (setjmp(png_jmpbuf(writePtr))) {
+ diag->error(DiagMessage() << "failed to write png");
+ return false;
+ }
+
+ uint32_t width, height;
+ int colorType, bitDepth, interlaceType, compressionType;
+
+ png_unknown_chunk unknowns[3];
+ unknowns[0].data = nullptr;
+ unknowns[1].data = nullptr;
+ unknowns[2].data = nullptr;
+
+ png_bytepp outRows =
+ (png_bytepp)malloc((int)info->height * sizeof(png_bytep));
+ if (outRows == (png_bytepp)0) {
+ printf("Can't allocate output buffer!\n");
+ exit(1);
+ }
+ for (uint32_t i = 0; i < info->height; i++) {
+ outRows[i] = (png_bytep)malloc(2 * (int)info->width);
+ if (outRows[i] == (png_bytep)0) {
+ printf("Can't allocate output buffer!\n");
+ exit(1);
}
+ }
- uint32_t width, height;
- int colorType, bitDepth, interlaceType, compressionType;
+ png_set_compression_level(writePtr, Z_BEST_COMPRESSION);
- png_unknown_chunk unknowns[3];
- unknowns[0].data = nullptr;
- unknowns[1].data = nullptr;
- unknowns[2].data = nullptr;
+ if (kDebug) {
+ diag->note(DiagMessage() << "writing image: w = " << info->width
+ << ", h = " << info->height);
+ }
- png_bytepp outRows = (png_bytepp) malloc((int) info->height * sizeof(png_bytep));
- if (outRows == (png_bytepp) 0) {
- printf("Can't allocate output buffer!\n");
- exit(1);
- }
- for (uint32_t i = 0; i < info->height; i++) {
- outRows[i] = (png_bytep) malloc(2 * (int) info->width);
- if (outRows[i] == (png_bytep) 0) {
- printf("Can't allocate output buffer!\n");
- exit(1);
- }
- }
+ png_color rgbPalette[256];
+ png_byte alphaPalette[256];
+ bool hasTransparency;
+ int paletteEntries;
- png_set_compression_level(writePtr, Z_BEST_COMPRESSION);
+ analyze_image(diag, *info, grayScaleTolerance, rgbPalette, alphaPalette,
+ &paletteEntries, &hasTransparency, &colorType, outRows);
- if (kDebug) {
+ // If the image is a 9-patch, we need to preserve it as a ARGB file to make
+ // sure the pixels will not be pre-dithered/clamped until we decide they are
+ if (info->is9Patch &&
+ (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_GRAY ||
+ colorType == PNG_COLOR_TYPE_PALETTE)) {
+ colorType = PNG_COLOR_TYPE_RGB_ALPHA;
+ }
+
+ if (kDebug) {
+ switch (colorType) {
+ case PNG_COLOR_TYPE_PALETTE:
+ diag->note(DiagMessage() << "has " << paletteEntries << " colors"
+ << (hasTransparency ? " (with alpha)" : "")
+ << ", using PNG_COLOR_TYPE_PALLETTE");
+ break;
+ case PNG_COLOR_TYPE_GRAY:
diag->note(DiagMessage()
- << "writing image: w = " << info->width
- << ", h = " << info->height);
+ << "is opaque gray, using PNG_COLOR_TYPE_GRAY");
+ break;
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ diag->note(DiagMessage()
+ << "is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA");
+ break;
+ case PNG_COLOR_TYPE_RGB:
+ diag->note(DiagMessage() << "is opaque RGB, using PNG_COLOR_TYPE_RGB");
+ break;
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ diag->note(DiagMessage()
+ << "is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA");
+ break;
}
+ }
- png_color rgbPalette[256];
- png_byte alphaPalette[256];
- bool hasTransparency;
- int paletteEntries;
+ png_set_IHDR(writePtr, infoPtr, info->width, info->height, 8, colorType,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
- analyze_image(diag, *info, grayScaleTolerance, rgbPalette, alphaPalette,
- &paletteEntries, &hasTransparency, &colorType, outRows);
-
- // If the image is a 9-patch, we need to preserve it as a ARGB file to make
- // sure the pixels will not be pre-dithered/clamped until we decide they are
- if (info->is9Patch && (colorType == PNG_COLOR_TYPE_RGB ||
- colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_PALETTE)) {
- colorType = PNG_COLOR_TYPE_RGB_ALPHA;
+ if (colorType == PNG_COLOR_TYPE_PALETTE) {
+ png_set_PLTE(writePtr, infoPtr, rgbPalette, paletteEntries);
+ if (hasTransparency) {
+ png_set_tRNS(writePtr, infoPtr, alphaPalette, paletteEntries,
+ (png_color_16p)0);
}
+ png_set_filter(writePtr, 0, PNG_NO_FILTERS);
+ } else {
+ png_set_filter(writePtr, 0, PNG_ALL_FILTERS);
+ }
+ if (info->is9Patch) {
+ int chunkCount = 2 + (info->haveLayoutBounds ? 1 : 0);
+ int pIndex = info->haveLayoutBounds ? 2 : 1;
+ int bIndex = 1;
+ int oIndex = 0;
+
+ // Chunks ordered thusly because older platforms depend on the base 9 patch
+ // data being last
+ png_bytep chunkNames = info->haveLayoutBounds
+ ? (png_bytep) "npOl\0npLb\0npTc\0"
+ : (png_bytep) "npOl\0npTc";
+
+ // base 9 patch data
if (kDebug) {
- switch (colorType) {
- case PNG_COLOR_TYPE_PALETTE:
- diag->note(DiagMessage()
- << "has " << paletteEntries
- << " colors" << (hasTransparency ? " (with alpha)" : "")
- << ", using PNG_COLOR_TYPE_PALLETTE");
- break;
- case PNG_COLOR_TYPE_GRAY:
- diag->note(DiagMessage() << "is opaque gray, using PNG_COLOR_TYPE_GRAY");
- break;
- case PNG_COLOR_TYPE_GRAY_ALPHA:
- diag->note(DiagMessage() << "is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA");
- break;
- case PNG_COLOR_TYPE_RGB:
- diag->note(DiagMessage() << "is opaque RGB, using PNG_COLOR_TYPE_RGB");
- break;
- case PNG_COLOR_TYPE_RGB_ALPHA:
- diag->note(DiagMessage() << "is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA");
- break;
- }
+ diag->note(DiagMessage() << "adding 9-patch info..");
+ }
+ strcpy((char*)unknowns[pIndex].name, "npTc");
+ unknowns[pIndex].data = (png_byte*)info->serialize9Patch();
+ unknowns[pIndex].size = info->info9Patch.serializedSize();
+ // TODO: remove the check below when everything works
+ checkNinePatchSerialization(&info->info9Patch, unknowns[pIndex].data);
+
+ // automatically generated 9 patch outline data
+ int chunkSize = sizeof(png_uint_32) * 6;
+ strcpy((char*)unknowns[oIndex].name, "npOl");
+ unknowns[oIndex].data = (png_byte*)calloc(chunkSize, 1);
+ png_byte outputData[chunkSize];
+ memcpy(&outputData, &info->outlineInsetsLeft, 4 * sizeof(png_uint_32));
+ ((float*)outputData)[4] = info->outlineRadius;
+ ((png_uint_32*)outputData)[5] = info->outlineAlpha;
+ memcpy(unknowns[oIndex].data, &outputData, chunkSize);
+ unknowns[oIndex].size = chunkSize;
+
+ // optional optical inset / layout bounds data
+ if (info->haveLayoutBounds) {
+ int chunkSize = sizeof(png_uint_32) * 4;
+ strcpy((char*)unknowns[bIndex].name, "npLb");
+ unknowns[bIndex].data = (png_byte*)calloc(chunkSize, 1);
+ memcpy(unknowns[bIndex].data, &info->layoutBoundsLeft, chunkSize);
+ unknowns[bIndex].size = chunkSize;
}
- png_set_IHDR(writePtr, infoPtr, info->width, info->height, 8, colorType,
- PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
-
- if (colorType == PNG_COLOR_TYPE_PALETTE) {
- png_set_PLTE(writePtr, infoPtr, rgbPalette, paletteEntries);
- if (hasTransparency) {
- png_set_tRNS(writePtr, infoPtr, alphaPalette, paletteEntries, (png_color_16p) 0);
- }
- png_set_filter(writePtr, 0, PNG_NO_FILTERS);
- } else {
- png_set_filter(writePtr, 0, PNG_ALL_FILTERS);
+ for (int i = 0; i < chunkCount; i++) {
+ unknowns[i].location = PNG_HAVE_PLTE;
}
-
- if (info->is9Patch) {
- int chunkCount = 2 + (info->haveLayoutBounds ? 1 : 0);
- int pIndex = info->haveLayoutBounds ? 2 : 1;
- int bIndex = 1;
- int oIndex = 0;
-
- // Chunks ordered thusly because older platforms depend on the base 9 patch data being last
- png_bytep chunkNames = info->haveLayoutBounds
- ? (png_bytep)"npOl\0npLb\0npTc\0"
- : (png_bytep)"npOl\0npTc";
-
- // base 9 patch data
- if (kDebug) {
- diag->note(DiagMessage() << "adding 9-patch info..");
- }
- strcpy((char*)unknowns[pIndex].name, "npTc");
- unknowns[pIndex].data = (png_byte*) info->serialize9Patch();
- unknowns[pIndex].size = info->info9Patch.serializedSize();
- // TODO: remove the check below when everything works
- checkNinePatchSerialization(&info->info9Patch, unknowns[pIndex].data);
-
- // automatically generated 9 patch outline data
- int chunkSize = sizeof(png_uint_32) * 6;
- strcpy((char*)unknowns[oIndex].name, "npOl");
- unknowns[oIndex].data = (png_byte*) calloc(chunkSize, 1);
- png_byte outputData[chunkSize];
- memcpy(&outputData, &info->outlineInsetsLeft, 4 * sizeof(png_uint_32));
- ((float*) outputData)[4] = info->outlineRadius;
- ((png_uint_32*) outputData)[5] = info->outlineAlpha;
- memcpy(unknowns[oIndex].data, &outputData, chunkSize);
- unknowns[oIndex].size = chunkSize;
-
- // optional optical inset / layout bounds data
- if (info->haveLayoutBounds) {
- int chunkSize = sizeof(png_uint_32) * 4;
- strcpy((char*)unknowns[bIndex].name, "npLb");
- unknowns[bIndex].data = (png_byte*) calloc(chunkSize, 1);
- memcpy(unknowns[bIndex].data, &info->layoutBoundsLeft, chunkSize);
- unknowns[bIndex].size = chunkSize;
- }
-
- for (int i = 0; i < chunkCount; i++) {
- unknowns[i].location = PNG_HAVE_PLTE;
- }
- png_set_keep_unknown_chunks(writePtr, PNG_HANDLE_CHUNK_ALWAYS,
- chunkNames, chunkCount);
- png_set_unknown_chunks(writePtr, infoPtr, unknowns, chunkCount);
+ png_set_keep_unknown_chunks(writePtr, PNG_HANDLE_CHUNK_ALWAYS, chunkNames,
+ chunkCount);
+ png_set_unknown_chunks(writePtr, infoPtr, unknowns, chunkCount);
#if PNG_LIBPNG_VER < 10600
- // Deal with unknown chunk location bug in 1.5.x and earlier.
- png_set_unknown_chunk_location(writePtr, infoPtr, 0, PNG_HAVE_PLTE);
- if (info->haveLayoutBounds) {
- png_set_unknown_chunk_location(writePtr, infoPtr, 1, PNG_HAVE_PLTE);
- }
+ // Deal with unknown chunk location bug in 1.5.x and earlier.
+ png_set_unknown_chunk_location(writePtr, infoPtr, 0, PNG_HAVE_PLTE);
+ if (info->haveLayoutBounds) {
+ png_set_unknown_chunk_location(writePtr, infoPtr, 1, PNG_HAVE_PLTE);
+ }
#endif
+ }
+
+ png_write_info(writePtr, infoPtr);
+
+ png_bytepp rows;
+ if (colorType == PNG_COLOR_TYPE_RGB ||
+ colorType == PNG_COLOR_TYPE_RGB_ALPHA) {
+ if (colorType == PNG_COLOR_TYPE_RGB) {
+ png_set_filler(writePtr, 0, PNG_FILLER_AFTER);
}
+ rows = info->rows.data();
+ } else {
+ rows = outRows;
+ }
+ png_write_image(writePtr, rows);
- png_write_info(writePtr, infoPtr);
+ if (kDebug) {
+ printf("Final image data:\n");
+ // dump_image(info->width, info->height, rows, colorType);
+ }
- png_bytepp rows;
- if (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_RGB_ALPHA) {
- if (colorType == PNG_COLOR_TYPE_RGB) {
- png_set_filler(writePtr, 0, PNG_FILLER_AFTER);
- }
- rows = info->rows.data();
- } else {
- rows = outRows;
- }
- png_write_image(writePtr, rows);
+ png_write_end(writePtr, infoPtr);
- if (kDebug) {
- printf("Final image data:\n");
- //dump_image(info->width, info->height, rows, colorType);
- }
+ for (uint32_t i = 0; i < info->height; i++) {
+ free(outRows[i]);
+ }
+ free(outRows);
+ free(unknowns[0].data);
+ free(unknowns[1].data);
+ free(unknowns[2].data);
- png_write_end(writePtr, infoPtr);
+ png_get_IHDR(writePtr, infoPtr, &width, &height, &bitDepth, &colorType,
+ &interlaceType, &compressionType, nullptr);
- for (uint32_t i = 0; i < info->height; i++) {
- free(outRows[i]);
- }
- free(outRows);
- free(unknowns[0].data);
- free(unknowns[1].data);
- free(unknowns[2].data);
-
- png_get_IHDR(writePtr, infoPtr, &width, &height, &bitDepth, &colorType, &interlaceType,
- &compressionType, nullptr);
-
- if (kDebug) {
- diag->note(DiagMessage()
- << "image written: w = " << width << ", h = " << height
- << ", d = " << bitDepth << ", colors = " << colorType
- << ", inter = " << interlaceType << ", comp = " << compressionType);
- }
- return true;
+ if (kDebug) {
+ diag->note(DiagMessage() << "image written: w = " << width
+ << ", h = " << height << ", d = " << bitDepth
+ << ", colors = " << colorType
+ << ", inter = " << interlaceType
+ << ", comp = " << compressionType);
+ }
+ return true;
}
constexpr uint32_t kColorWhite = 0xffffffffu;
constexpr uint32_t kColorTick = 0xff000000u;
constexpr uint32_t kColorLayoutBoundsTick = 0xff0000ffu;
-enum class TickType {
- kNone,
- kTick,
- kLayoutBounds,
- kBoth
-};
+enum class TickType { kNone, kTick, kLayoutBounds, kBoth };
static TickType tickType(png_bytep p, bool transparent, const char** outError) {
- png_uint_32 color = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+ png_uint_32 color = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
- if (transparent) {
- if (p[3] == 0) {
- return TickType::kNone;
- }
- if (color == kColorLayoutBoundsTick) {
- return TickType::kLayoutBounds;
- }
- if (color == kColorTick) {
- return TickType::kTick;
- }
-
- // Error cases
- if (p[3] != 0xff) {
- *outError = "Frame pixels must be either solid or transparent "
- "(not intermediate alphas)";
- return TickType::kNone;
- }
-
- if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
- *outError = "Ticks in transparent frame must be black or red";
- }
- return TickType::kTick;
- }
-
- if (p[3] != 0xFF) {
- *outError = "White frame must be a solid color (no alpha)";
- }
- if (color == kColorWhite) {
- return TickType::kNone;
- }
- if (color == kColorTick) {
- return TickType::kTick;
+ if (transparent) {
+ if (p[3] == 0) {
+ return TickType::kNone;
}
if (color == kColorLayoutBoundsTick) {
- return TickType::kLayoutBounds;
+ return TickType::kLayoutBounds;
+ }
+ if (color == kColorTick) {
+ return TickType::kTick;
+ }
+
+ // Error cases
+ if (p[3] != 0xff) {
+ *outError =
+ "Frame pixels must be either solid or transparent "
+ "(not intermediate alphas)";
+ return TickType::kNone;
}
if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
- *outError = "Ticks in white frame must be black or red";
- return TickType::kNone;
+ *outError = "Ticks in transparent frame must be black or red";
}
return TickType::kTick;
+ }
+
+ if (p[3] != 0xFF) {
+ *outError = "White frame must be a solid color (no alpha)";
+ }
+ if (color == kColorWhite) {
+ return TickType::kNone;
+ }
+ if (color == kColorTick) {
+ return TickType::kTick;
+ }
+ if (color == kColorLayoutBoundsTick) {
+ return TickType::kLayoutBounds;
+ }
+
+ if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
+ *outError = "Ticks in white frame must be black or red";
+ return TickType::kNone;
+ }
+ return TickType::kTick;
}
-enum class TickState {
- kStart,
- kInside1,
- kOutside1
-};
+enum class TickState { kStart, kInside1, kOutside1 };
-static bool getHorizontalTicks(png_bytep row, int width, bool transparent, bool required,
- int32_t* outLeft, int32_t* outRight, const char** outError,
+static bool getHorizontalTicks(png_bytep row, int width, bool transparent,
+ bool required, int32_t* outLeft,
+ int32_t* outRight, const char** outError,
uint8_t* outDivs, bool multipleAllowed) {
- *outLeft = *outRight = -1;
- TickState state = TickState::kStart;
- bool found = false;
+ *outLeft = *outRight = -1;
+ TickState state = TickState::kStart;
+ bool found = false;
- for (int i = 1; i < width - 1; i++) {
- if (tickType(row+i*4, transparent, outError) == TickType::kTick) {
- if (state == TickState::kStart ||
- (state == TickState::kOutside1 && multipleAllowed)) {
- *outLeft = i-1;
- *outRight = width-2;
- found = true;
- if (outDivs != NULL) {
- *outDivs += 2;
- }
- state = TickState::kInside1;
- } else if (state == TickState::kOutside1) {
- *outError = "Can't have more than one marked region along edge";
- *outLeft = i;
- return false;
- }
- } else if (!*outError) {
- if (state == TickState::kInside1) {
- // We're done with this div. Move on to the next.
- *outRight = i-1;
- outRight += 2;
- outLeft += 2;
- state = TickState::kOutside1;
- }
- } else {
- *outLeft = i;
- return false;
+ for (int i = 1; i < width - 1; i++) {
+ if (tickType(row + i * 4, transparent, outError) == TickType::kTick) {
+ if (state == TickState::kStart ||
+ (state == TickState::kOutside1 && multipleAllowed)) {
+ *outLeft = i - 1;
+ *outRight = width - 2;
+ found = true;
+ if (outDivs != NULL) {
+ *outDivs += 2;
}
- }
-
- if (required && !found) {
- *outError = "No marked region found along edge";
- *outLeft = -1;
+ state = TickState::kInside1;
+ } else if (state == TickState::kOutside1) {
+ *outError = "Can't have more than one marked region along edge";
+ *outLeft = i;
return false;
+ }
+ } else if (!*outError) {
+ if (state == TickState::kInside1) {
+ // We're done with this div. Move on to the next.
+ *outRight = i - 1;
+ outRight += 2;
+ outLeft += 2;
+ state = TickState::kOutside1;
+ }
+ } else {
+ *outLeft = i;
+ return false;
}
- return true;
+ }
+
+ if (required && !found) {
+ *outError = "No marked region found along edge";
+ *outLeft = -1;
+ return false;
+ }
+ return true;
}
-static bool getVerticalTicks(png_bytepp rows, int offset, int height, bool transparent,
- bool required, int32_t* outTop, int32_t* outBottom,
- const char** outError, uint8_t* outDivs, bool multipleAllowed) {
- *outTop = *outBottom = -1;
- TickState state = TickState::kStart;
- bool found = false;
+static bool getVerticalTicks(png_bytepp rows, int offset, int height,
+ bool transparent, bool required, int32_t* outTop,
+ int32_t* outBottom, const char** outError,
+ uint8_t* outDivs, bool multipleAllowed) {
+ *outTop = *outBottom = -1;
+ TickState state = TickState::kStart;
+ bool found = false;
- for (int i = 1; i < height - 1; i++) {
- if (tickType(rows[i]+offset, transparent, outError) == TickType::kTick) {
- if (state == TickState::kStart ||
- (state == TickState::kOutside1 && multipleAllowed)) {
- *outTop = i-1;
- *outBottom = height-2;
- found = true;
- if (outDivs != NULL) {
- *outDivs += 2;
- }
- state = TickState::kInside1;
- } else if (state == TickState::kOutside1) {
- *outError = "Can't have more than one marked region along edge";
- *outTop = i;
- return false;
- }
- } else if (!*outError) {
- if (state == TickState::kInside1) {
- // We're done with this div. Move on to the next.
- *outBottom = i-1;
- outTop += 2;
- outBottom += 2;
- state = TickState::kOutside1;
- }
- } else {
- *outTop = i;
- return false;
+ for (int i = 1; i < height - 1; i++) {
+ if (tickType(rows[i] + offset, transparent, outError) == TickType::kTick) {
+ if (state == TickState::kStart ||
+ (state == TickState::kOutside1 && multipleAllowed)) {
+ *outTop = i - 1;
+ *outBottom = height - 2;
+ found = true;
+ if (outDivs != NULL) {
+ *outDivs += 2;
}
- }
-
- if (required && !found) {
- *outError = "No marked region found along edge";
- *outTop = -1;
+ state = TickState::kInside1;
+ } else if (state == TickState::kOutside1) {
+ *outError = "Can't have more than one marked region along edge";
+ *outTop = i;
return false;
+ }
+ } else if (!*outError) {
+ if (state == TickState::kInside1) {
+ // We're done with this div. Move on to the next.
+ *outBottom = i - 1;
+ outTop += 2;
+ outBottom += 2;
+ state = TickState::kOutside1;
+ }
+ } else {
+ *outTop = i;
+ return false;
}
- return true;
+ }
+
+ if (required && !found) {
+ *outError = "No marked region found along edge";
+ *outTop = -1;
+ return false;
+ }
+ return true;
}
-static bool getHorizontalLayoutBoundsTicks(png_bytep row, int width, bool transparent,
- bool /* required */, int32_t* outLeft,
- int32_t* outRight, const char** outError) {
- *outLeft = *outRight = 0;
+static bool getHorizontalLayoutBoundsTicks(png_bytep row, int width,
+ bool transparent,
+ bool /* required */,
+ int32_t* outLeft, int32_t* outRight,
+ const char** outError) {
+ *outLeft = *outRight = 0;
- // Look for left tick
- if (tickType(row + 4, transparent, outError) == TickType::kLayoutBounds) {
- // Starting with a layout padding tick
- int i = 1;
- while (i < width - 1) {
- (*outLeft)++;
- i++;
- if (tickType(row + i * 4, transparent, outError) != TickType::kLayoutBounds) {
- break;
- }
- }
+ // Look for left tick
+ if (tickType(row + 4, transparent, outError) == TickType::kLayoutBounds) {
+ // Starting with a layout padding tick
+ int i = 1;
+ while (i < width - 1) {
+ (*outLeft)++;
+ i++;
+ if (tickType(row + i * 4, transparent, outError) !=
+ TickType::kLayoutBounds) {
+ break;
+ }
}
+ }
- // Look for right tick
- if (tickType(row + (width - 2) * 4, transparent, outError) == TickType::kLayoutBounds) {
- // Ending with a layout padding tick
- int i = width - 2;
- while (i > 1) {
- (*outRight)++;
- i--;
- if (tickType(row+i*4, transparent, outError) != TickType::kLayoutBounds) {
- break;
- }
- }
+ // Look for right tick
+ if (tickType(row + (width - 2) * 4, transparent, outError) ==
+ TickType::kLayoutBounds) {
+ // Ending with a layout padding tick
+ int i = width - 2;
+ while (i > 1) {
+ (*outRight)++;
+ i--;
+ if (tickType(row + i * 4, transparent, outError) !=
+ TickType::kLayoutBounds) {
+ break;
+ }
}
- return true;
+ }
+ return true;
}
-static bool getVerticalLayoutBoundsTicks(png_bytepp rows, int offset, int height, bool transparent,
- bool /* required */, int32_t* outTop, int32_t* outBottom,
+static bool getVerticalLayoutBoundsTicks(png_bytepp rows, int offset,
+ int height, bool transparent,
+ bool /* required */, int32_t* outTop,
+ int32_t* outBottom,
const char** outError) {
- *outTop = *outBottom = 0;
+ *outTop = *outBottom = 0;
- // Look for top tick
- if (tickType(rows[1] + offset, transparent, outError) == TickType::kLayoutBounds) {
- // Starting with a layout padding tick
- int i = 1;
- while (i < height - 1) {
- (*outTop)++;
- i++;
- if (tickType(rows[i] + offset, transparent, outError) != TickType::kLayoutBounds) {
- break;
- }
- }
+ // Look for top tick
+ if (tickType(rows[1] + offset, transparent, outError) ==
+ TickType::kLayoutBounds) {
+ // Starting with a layout padding tick
+ int i = 1;
+ while (i < height - 1) {
+ (*outTop)++;
+ i++;
+ if (tickType(rows[i] + offset, transparent, outError) !=
+ TickType::kLayoutBounds) {
+ break;
+ }
}
+ }
- // Look for bottom tick
- if (tickType(rows[height - 2] + offset, transparent, outError) == TickType::kLayoutBounds) {
- // Ending with a layout padding tick
- int i = height - 2;
- while (i > 1) {
- (*outBottom)++;
- i--;
- if (tickType(rows[i] + offset, transparent, outError) != TickType::kLayoutBounds) {
- break;
- }
- }
+ // Look for bottom tick
+ if (tickType(rows[height - 2] + offset, transparent, outError) ==
+ TickType::kLayoutBounds) {
+ // Ending with a layout padding tick
+ int i = height - 2;
+ while (i > 1) {
+ (*outBottom)++;
+ i--;
+ if (tickType(rows[i] + offset, transparent, outError) !=
+ TickType::kLayoutBounds) {
+ break;
+ }
}
- return true;
+ }
+ return true;
}
-static void findMaxOpacity(png_bytepp rows, int startX, int startY, int endX, int endY,
- int dX, int dY, int* outInset) {
- uint8_t maxOpacity = 0;
- int inset = 0;
- *outInset = 0;
- for (int x = startX, y = startY; x != endX && y != endY; x += dX, y += dY, inset++) {
- png_byte* color = rows[y] + x * 4;
- uint8_t opacity = color[3];
- if (opacity > maxOpacity) {
- maxOpacity = opacity;
- *outInset = inset;
- }
- if (opacity == 0xff) return;
+static void findMaxOpacity(png_bytepp rows, int startX, int startY, int endX,
+ int endY, int dX, int dY, int* outInset) {
+ uint8_t maxOpacity = 0;
+ int inset = 0;
+ *outInset = 0;
+ for (int x = startX, y = startY; x != endX && y != endY;
+ x += dX, y += dY, inset++) {
+ png_byte* color = rows[y] + x * 4;
+ uint8_t opacity = color[3];
+ if (opacity > maxOpacity) {
+ maxOpacity = opacity;
+ *outInset = inset;
}
+ if (opacity == 0xff) return;
+ }
}
static uint8_t maxAlphaOverRow(png_bytep row, int startX, int endX) {
- uint8_t maxAlpha = 0;
- for (int x = startX; x < endX; x++) {
- uint8_t alpha = (row + x * 4)[3];
- if (alpha > maxAlpha) maxAlpha = alpha;
- }
- return maxAlpha;
+ uint8_t maxAlpha = 0;
+ for (int x = startX; x < endX; x++) {
+ uint8_t alpha = (row + x * 4)[3];
+ if (alpha > maxAlpha) maxAlpha = alpha;
+ }
+ return maxAlpha;
}
-static uint8_t maxAlphaOverCol(png_bytepp rows, int offsetX, int startY, int endY) {
- uint8_t maxAlpha = 0;
- for (int y = startY; y < endY; y++) {
- uint8_t alpha = (rows[y] + offsetX * 4)[3];
- if (alpha > maxAlpha) maxAlpha = alpha;
- }
- return maxAlpha;
+static uint8_t maxAlphaOverCol(png_bytepp rows, int offsetX, int startY,
+ int endY) {
+ uint8_t maxAlpha = 0;
+ for (int y = startY; y < endY; y++) {
+ uint8_t alpha = (rows[y] + offsetX * 4)[3];
+ if (alpha > maxAlpha) maxAlpha = alpha;
+ }
+ return maxAlpha;
}
static void getOutline(PngInfo* image) {
- int midX = image->width / 2;
- int midY = image->height / 2;
- int endX = image->width - 2;
- int endY = image->height - 2;
+ int midX = image->width / 2;
+ int midY = image->height / 2;
+ int endX = image->width - 2;
+ int endY = image->height - 2;
- // find left and right extent of nine patch content on center row
- if (image->width > 4) {
- findMaxOpacity(image->rows.data(), 1, midY, midX, -1, 1, 0, &image->outlineInsetsLeft);
- findMaxOpacity(image->rows.data(), endX, midY, midX, -1, -1, 0,
- &image->outlineInsetsRight);
- } else {
- image->outlineInsetsLeft = 0;
- image->outlineInsetsRight = 0;
- }
+ // find left and right extent of nine patch content on center row
+ if (image->width > 4) {
+ findMaxOpacity(image->rows.data(), 1, midY, midX, -1, 1, 0,
+ &image->outlineInsetsLeft);
+ findMaxOpacity(image->rows.data(), endX, midY, midX, -1, -1, 0,
+ &image->outlineInsetsRight);
+ } else {
+ image->outlineInsetsLeft = 0;
+ image->outlineInsetsRight = 0;
+ }
- // find top and bottom extent of nine patch content on center column
- if (image->height > 4) {
- findMaxOpacity(image->rows.data(), midX, 1, -1, midY, 0, 1, &image->outlineInsetsTop);
- findMaxOpacity(image->rows.data(), midX, endY, -1, midY, 0, -1,
- &image->outlineInsetsBottom);
- } else {
- image->outlineInsetsTop = 0;
- image->outlineInsetsBottom = 0;
- }
+ // find top and bottom extent of nine patch content on center column
+ if (image->height > 4) {
+ findMaxOpacity(image->rows.data(), midX, 1, -1, midY, 0, 1,
+ &image->outlineInsetsTop);
+ findMaxOpacity(image->rows.data(), midX, endY, -1, midY, 0, -1,
+ &image->outlineInsetsBottom);
+ } else {
+ image->outlineInsetsTop = 0;
+ image->outlineInsetsBottom = 0;
+ }
- int innerStartX = 1 + image->outlineInsetsLeft;
- int innerStartY = 1 + image->outlineInsetsTop;
- int innerEndX = endX - image->outlineInsetsRight;
- int innerEndY = endY - image->outlineInsetsBottom;
- int innerMidX = (innerEndX + innerStartX) / 2;
- int innerMidY = (innerEndY + innerStartY) / 2;
+ int innerStartX = 1 + image->outlineInsetsLeft;
+ int innerStartY = 1 + image->outlineInsetsTop;
+ int innerEndX = endX - image->outlineInsetsRight;
+ int innerEndY = endY - image->outlineInsetsBottom;
+ int innerMidX = (innerEndX + innerStartX) / 2;
+ int innerMidY = (innerEndY + innerStartY) / 2;
- // assuming the image is a round rect, compute the radius by marching
- // diagonally from the top left corner towards the center
- image->outlineAlpha = std::max(
- maxAlphaOverRow(image->rows[innerMidY], innerStartX, innerEndX),
- maxAlphaOverCol(image->rows.data(), innerMidX, innerStartY, innerStartY));
+ // assuming the image is a round rect, compute the radius by marching
+ // diagonally from the top left corner towards the center
+ image->outlineAlpha = std::max(
+ maxAlphaOverRow(image->rows[innerMidY], innerStartX, innerEndX),
+ maxAlphaOverCol(image->rows.data(), innerMidX, innerStartY, innerStartY));
- int diagonalInset = 0;
- findMaxOpacity(image->rows.data(), innerStartX, innerStartY, innerMidX, innerMidY, 1, 1,
- &diagonalInset);
+ int diagonalInset = 0;
+ findMaxOpacity(image->rows.data(), innerStartX, innerStartY, innerMidX,
+ innerMidY, 1, 1, &diagonalInset);
- /* Determine source radius based upon inset:
- * sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
- * sqrt(2) * r = sqrt(2) * i + r
- * (sqrt(2) - 1) * r = sqrt(2) * i
- * r = sqrt(2) / (sqrt(2) - 1) * i
- */
- image->outlineRadius = 3.4142f * diagonalInset;
+ /* Determine source radius based upon inset:
+ * sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
+ * sqrt(2) * r = sqrt(2) * i + r
+ * (sqrt(2) - 1) * r = sqrt(2) * i
+ * r = sqrt(2) / (sqrt(2) - 1) * i
+ */
+ image->outlineRadius = 3.4142f * diagonalInset;
- if (kDebug) {
- printf("outline insets %d %d %d %d, rad %f, alpha %x\n",
- image->outlineInsetsLeft,
- image->outlineInsetsTop,
- image->outlineInsetsRight,
- image->outlineInsetsBottom,
- image->outlineRadius,
- image->outlineAlpha);
- }
+ if (kDebug) {
+ printf("outline insets %d %d %d %d, rad %f, alpha %x\n",
+ image->outlineInsetsLeft, image->outlineInsetsTop,
+ image->outlineInsetsRight, image->outlineInsetsBottom,
+ image->outlineRadius, image->outlineAlpha);
+ }
}
-static uint32_t getColor(png_bytepp rows, int left, int top, int right, int bottom) {
- png_bytep color = rows[top] + left*4;
+static uint32_t getColor(png_bytepp rows, int left, int top, int right,
+ int bottom) {
+ png_bytep color = rows[top] + left * 4;
- if (left > right || top > bottom) {
- return android::Res_png_9patch::TRANSPARENT_COLOR;
- }
+ if (left > right || top > bottom) {
+ return android::Res_png_9patch::TRANSPARENT_COLOR;
+ }
- while (top <= bottom) {
- for (int i = left; i <= right; i++) {
- png_bytep p = rows[top]+i*4;
- if (color[3] == 0) {
- if (p[3] != 0) {
- return android::Res_png_9patch::NO_COLOR;
- }
- } else if (p[0] != color[0] || p[1] != color[1] ||
- p[2] != color[2] || p[3] != color[3]) {
- return android::Res_png_9patch::NO_COLOR;
- }
+ while (top <= bottom) {
+ for (int i = left; i <= right; i++) {
+ png_bytep p = rows[top] + i * 4;
+ if (color[3] == 0) {
+ if (p[3] != 0) {
+ return android::Res_png_9patch::NO_COLOR;
}
- top++;
+ } else if (p[0] != color[0] || p[1] != color[1] || p[2] != color[2] ||
+ p[3] != color[3]) {
+ return android::Res_png_9patch::NO_COLOR;
+ }
}
+ top++;
+ }
- if (color[3] == 0) {
- return android::Res_png_9patch::TRANSPARENT_COLOR;
- }
- return (color[3]<<24) | (color[0]<<16) | (color[1]<<8) | color[2];
+ if (color[3] == 0) {
+ return android::Res_png_9patch::TRANSPARENT_COLOR;
+ }
+ return (color[3] << 24) | (color[0] << 16) | (color[1] << 8) | color[2];
}
static bool do9Patch(PngInfo* image, std::string* outError) {
- image->is9Patch = true;
+ image->is9Patch = true;
- int W = image->width;
- int H = image->height;
- int i, j;
+ int W = image->width;
+ int H = image->height;
+ int i, j;
- const int maxSizeXDivs = W * sizeof(int32_t);
- const int maxSizeYDivs = H * sizeof(int32_t);
- int32_t* xDivs = image->xDivs = new int32_t[W];
- int32_t* yDivs = image->yDivs = new int32_t[H];
- uint8_t numXDivs = 0;
- uint8_t numYDivs = 0;
+ const int maxSizeXDivs = W * sizeof(int32_t);
+ const int maxSizeYDivs = H * sizeof(int32_t);
+ int32_t* xDivs = image->xDivs = new int32_t[W];
+ int32_t* yDivs = image->yDivs = new int32_t[H];
+ uint8_t numXDivs = 0;
+ uint8_t numYDivs = 0;
- int8_t numColors;
- int numRows;
- int numCols;
- int top;
- int left;
- int right;
- int bottom;
- memset(xDivs, -1, maxSizeXDivs);
- memset(yDivs, -1, maxSizeYDivs);
- image->info9Patch.paddingLeft = image->info9Patch.paddingRight = -1;
- image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1;
- image->layoutBoundsLeft = image->layoutBoundsRight = 0;
- image->layoutBoundsTop = image->layoutBoundsBottom = 0;
+ int8_t numColors;
+ int numRows;
+ int numCols;
+ int top;
+ int left;
+ int right;
+ int bottom;
+ memset(xDivs, -1, maxSizeXDivs);
+ memset(yDivs, -1, maxSizeYDivs);
+ image->info9Patch.paddingLeft = image->info9Patch.paddingRight = -1;
+ image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1;
+ image->layoutBoundsLeft = image->layoutBoundsRight = 0;
+ image->layoutBoundsTop = image->layoutBoundsBottom = 0;
- png_bytep p = image->rows[0];
- bool transparent = p[3] == 0;
- bool hasColor = false;
+ png_bytep p = image->rows[0];
+ bool transparent = p[3] == 0;
+ bool hasColor = false;
- const char* errorMsg = nullptr;
- int errorPixel = -1;
- const char* errorEdge = nullptr;
+ const char* errorMsg = nullptr;
+ int errorPixel = -1;
+ const char* errorEdge = nullptr;
- int colorIndex = 0;
- std::vector<png_bytep> newRows;
+ int colorIndex = 0;
+ std::vector<png_bytep> newRows;
- // Validate size...
- if (W < 3 || H < 3) {
- errorMsg = "Image must be at least 3x3 (1x1 without frame) pixels";
- goto getout;
+ // Validate size...
+ if (W < 3 || H < 3) {
+ errorMsg = "Image must be at least 3x3 (1x1 without frame) pixels";
+ goto getout;
+ }
+
+ // Validate frame...
+ if (!transparent &&
+ (p[0] != 0xFF || p[1] != 0xFF || p[2] != 0xFF || p[3] != 0xFF)) {
+ errorMsg = "Must have one-pixel frame that is either transparent or white";
+ goto getout;
+ }
+
+ // Find left and right of sizing areas...
+ if (!getHorizontalTicks(p, W, transparent, true, &xDivs[0], &xDivs[1],
+ &errorMsg, &numXDivs, true)) {
+ errorPixel = xDivs[0];
+ errorEdge = "top";
+ goto getout;
+ }
+
+ // Find top and bottom of sizing areas...
+ if (!getVerticalTicks(image->rows.data(), 0, H, transparent, true, &yDivs[0],
+ &yDivs[1], &errorMsg, &numYDivs, true)) {
+ errorPixel = yDivs[0];
+ errorEdge = "left";
+ goto getout;
+ }
+
+ // Copy patch size data into image...
+ image->info9Patch.numXDivs = numXDivs;
+ image->info9Patch.numYDivs = numYDivs;
+
+ // Find left and right of padding area...
+ if (!getHorizontalTicks(image->rows[H - 1], W, transparent, false,
+ &image->info9Patch.paddingLeft,
+ &image->info9Patch.paddingRight, &errorMsg, nullptr,
+ false)) {
+ errorPixel = image->info9Patch.paddingLeft;
+ errorEdge = "bottom";
+ goto getout;
+ }
+
+ // Find top and bottom of padding area...
+ if (!getVerticalTicks(image->rows.data(), (W - 1) * 4, H, transparent, false,
+ &image->info9Patch.paddingTop,
+ &image->info9Patch.paddingBottom, &errorMsg, nullptr,
+ false)) {
+ errorPixel = image->info9Patch.paddingTop;
+ errorEdge = "right";
+ goto getout;
+ }
+
+ // Find left and right of layout padding...
+ getHorizontalLayoutBoundsTicks(image->rows[H - 1], W, transparent, false,
+ &image->layoutBoundsLeft,
+ &image->layoutBoundsRight, &errorMsg);
+
+ getVerticalLayoutBoundsTicks(image->rows.data(), (W - 1) * 4, H, transparent,
+ false, &image->layoutBoundsTop,
+ &image->layoutBoundsBottom, &errorMsg);
+
+ image->haveLayoutBounds =
+ image->layoutBoundsLeft != 0 || image->layoutBoundsRight != 0 ||
+ image->layoutBoundsTop != 0 || image->layoutBoundsBottom != 0;
+
+ if (image->haveLayoutBounds) {
+ if (kDebug) {
+ printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft,
+ image->layoutBoundsTop, image->layoutBoundsRight,
+ image->layoutBoundsBottom);
}
+ }
- // Validate frame...
- if (!transparent &&
- (p[0] != 0xFF || p[1] != 0xFF || p[2] != 0xFF || p[3] != 0xFF)) {
- errorMsg = "Must have one-pixel frame that is either transparent or white";
- goto getout;
- }
+ // use opacity of pixels to estimate the round rect outline
+ getOutline(image);
- // Find left and right of sizing areas...
- if (!getHorizontalTicks(p, W, transparent, true, &xDivs[0], &xDivs[1], &errorMsg, &numXDivs,
- true)) {
- errorPixel = xDivs[0];
- errorEdge = "top";
- goto getout;
- }
+ // If padding is not yet specified, take values from size.
+ if (image->info9Patch.paddingLeft < 0) {
+ image->info9Patch.paddingLeft = xDivs[0];
+ image->info9Patch.paddingRight = W - 2 - xDivs[1];
+ } else {
+ // Adjust value to be correct!
+ image->info9Patch.paddingRight = W - 2 - image->info9Patch.paddingRight;
+ }
+ if (image->info9Patch.paddingTop < 0) {
+ image->info9Patch.paddingTop = yDivs[0];
+ image->info9Patch.paddingBottom = H - 2 - yDivs[1];
+ } else {
+ // Adjust value to be correct!
+ image->info9Patch.paddingBottom = H - 2 - image->info9Patch.paddingBottom;
+ }
- // Find top and bottom of sizing areas...
- if (!getVerticalTicks(image->rows.data(), 0, H, transparent, true, &yDivs[0], &yDivs[1],
- &errorMsg, &numYDivs, true)) {
- errorPixel = yDivs[0];
- errorEdge = "left";
- goto getout;
- }
+ /* if (kDebug) {
+ printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName,
+ xDivs[0], xDivs[1],
+ yDivs[0], yDivs[1]);
+ printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName,
+ image->info9Patch.paddingLeft, image->info9Patch.paddingRight,
+ image->info9Patch.paddingTop,
+ image->info9Patch.paddingBottom);
+ }*/
- // Copy patch size data into image...
- image->info9Patch.numXDivs = numXDivs;
- image->info9Patch.numYDivs = numYDivs;
+ // Remove frame from image.
+ newRows.resize(H - 2);
+ for (i = 0; i < H - 2; i++) {
+ newRows[i] = image->rows[i + 1];
+ memmove(newRows[i], newRows[i] + 4, (W - 2) * 4);
+ }
+ image->rows.swap(newRows);
- // Find left and right of padding area...
- if (!getHorizontalTicks(image->rows[H-1], W, transparent, false,
- &image->info9Patch.paddingLeft, &image->info9Patch.paddingRight,
- &errorMsg, nullptr, false)) {
- errorPixel = image->info9Patch.paddingLeft;
- errorEdge = "bottom";
- goto getout;
- }
+ image->width -= 2;
+ W = image->width;
+ image->height -= 2;
+ H = image->height;
- // Find top and bottom of padding area...
- if (!getVerticalTicks(image->rows.data(), (W-1)*4, H, transparent, false,
- &image->info9Patch.paddingTop, &image->info9Patch.paddingBottom,
- &errorMsg, nullptr, false)) {
- errorPixel = image->info9Patch.paddingTop;
- errorEdge = "right";
- goto getout;
- }
+ // Figure out the number of rows and columns in the N-patch
+ numCols = numXDivs + 1;
+ if (xDivs[0] == 0) { // Column 1 is strechable
+ numCols--;
+ }
+ if (xDivs[numXDivs - 1] == W) {
+ numCols--;
+ }
+ numRows = numYDivs + 1;
+ if (yDivs[0] == 0) { // Row 1 is strechable
+ numRows--;
+ }
+ if (yDivs[numYDivs - 1] == H) {
+ numRows--;
+ }
- // Find left and right of layout padding...
- getHorizontalLayoutBoundsTicks(image->rows[H-1], W, transparent, false,
- &image->layoutBoundsLeft, &image->layoutBoundsRight, &errorMsg);
+ // Make sure the amount of rows and columns will fit in the number of
+ // colors we can use in the 9-patch format.
+ if (numRows * numCols > 0x7F) {
+ errorMsg = "Too many rows and columns in 9-patch perimeter";
+ goto getout;
+ }
- getVerticalLayoutBoundsTicks(image->rows.data(), (W-1)*4, H, transparent, false,
- &image->layoutBoundsTop, &image->layoutBoundsBottom, &errorMsg);
+ numColors = numRows * numCols;
+ image->info9Patch.numColors = numColors;
+ image->colors.resize(numColors);
- image->haveLayoutBounds = image->layoutBoundsLeft != 0
- || image->layoutBoundsRight != 0
- || image->layoutBoundsTop != 0
- || image->layoutBoundsBottom != 0;
+ // Fill in color information for each patch.
- if (image->haveLayoutBounds) {
- if (kDebug) {
- printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, image->layoutBoundsTop,
- image->layoutBoundsRight, image->layoutBoundsBottom);
- }
- }
+ uint32_t c;
+ top = 0;
- // use opacity of pixels to estimate the round rect outline
- getOutline(image);
+ // The first row always starts with the top being at y=0 and the bottom
+ // being either yDivs[1] (if yDivs[0]=0) of yDivs[0]. In the former case
+ // the first row is stretchable along the Y axis, otherwise it is fixed.
+ // The last row always ends with the bottom being bitmap.height and the top
+ // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
+ // yDivs[numYDivs-1]. In the former case the last row is stretchable along
+ // the Y axis, otherwise it is fixed.
+ //
+ // The first and last columns are similarly treated with respect to the X
+ // axis.
+ //
+ // The above is to help explain some of the special casing that goes on the
+ // code below.
- // If padding is not yet specified, take values from size.
- if (image->info9Patch.paddingLeft < 0) {
- image->info9Patch.paddingLeft = xDivs[0];
- image->info9Patch.paddingRight = W - 2 - xDivs[1];
+ // The initial yDiv and whether the first row is considered stretchable or
+ // not depends on whether yDiv[0] was zero or not.
+ for (j = (yDivs[0] == 0 ? 1 : 0); j <= numYDivs && top < H; j++) {
+ if (j == numYDivs) {
+ bottom = H;
} else {
- // Adjust value to be correct!
- image->info9Patch.paddingRight = W - 2 - image->info9Patch.paddingRight;
+ bottom = yDivs[j];
}
- if (image->info9Patch.paddingTop < 0) {
- image->info9Patch.paddingTop = yDivs[0];
- image->info9Patch.paddingBottom = H - 2 - yDivs[1];
- } else {
- // Adjust value to be correct!
- image->info9Patch.paddingBottom = H - 2 - image->info9Patch.paddingBottom;
- }
-
-/* if (kDebug) {
- printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName,
- xDivs[0], xDivs[1],
- yDivs[0], yDivs[1]);
- printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName,
- image->info9Patch.paddingLeft, image->info9Patch.paddingRight,
- image->info9Patch.paddingTop, image->info9Patch.paddingBottom);
- }*/
-
- // Remove frame from image.
- newRows.resize(H - 2);
- for (i = 0; i < H - 2; i++) {
- newRows[i] = image->rows[i + 1];
- memmove(newRows[i], newRows[i] + 4, (W - 2) * 4);
- }
- image->rows.swap(newRows);
-
- image->width -= 2;
- W = image->width;
- image->height -= 2;
- H = image->height;
-
- // Figure out the number of rows and columns in the N-patch
- numCols = numXDivs + 1;
- if (xDivs[0] == 0) { // Column 1 is strechable
- numCols--;
- }
- if (xDivs[numXDivs - 1] == W) {
- numCols--;
- }
- numRows = numYDivs + 1;
- if (yDivs[0] == 0) { // Row 1 is strechable
- numRows--;
- }
- if (yDivs[numYDivs - 1] == H) {
- numRows--;
- }
-
- // Make sure the amount of rows and columns will fit in the number of
- // colors we can use in the 9-patch format.
- if (numRows * numCols > 0x7F) {
- errorMsg = "Too many rows and columns in 9-patch perimeter";
- goto getout;
- }
-
- numColors = numRows * numCols;
- image->info9Patch.numColors = numColors;
- image->colors.resize(numColors);
-
- // Fill in color information for each patch.
-
- uint32_t c;
- top = 0;
-
- // The first row always starts with the top being at y=0 and the bottom
- // being either yDivs[1] (if yDivs[0]=0) of yDivs[0]. In the former case
- // the first row is stretchable along the Y axis, otherwise it is fixed.
- // The last row always ends with the bottom being bitmap.height and the top
- // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
- // yDivs[numYDivs-1]. In the former case the last row is stretchable along
- // the Y axis, otherwise it is fixed.
- //
- // The first and last columns are similarly treated with respect to the X
- // axis.
- //
- // The above is to help explain some of the special casing that goes on the
- // code below.
-
- // The initial yDiv and whether the first row is considered stretchable or
- // not depends on whether yDiv[0] was zero or not.
- for (j = (yDivs[0] == 0 ? 1 : 0); j <= numYDivs && top < H; j++) {
- if (j == numYDivs) {
- bottom = H;
- } else {
- bottom = yDivs[j];
+ left = 0;
+ // The initial xDiv and whether the first column is considered
+ // stretchable or not depends on whether xDiv[0] was zero or not.
+ for (i = xDivs[0] == 0 ? 1 : 0; i <= numXDivs && left < W; i++) {
+ if (i == numXDivs) {
+ right = W;
+ } else {
+ right = xDivs[i];
+ }
+ c = getColor(image->rows.data(), left, top, right - 1, bottom - 1);
+ image->colors[colorIndex++] = c;
+ if (kDebug) {
+ if (c != android::Res_png_9patch::NO_COLOR) {
+ hasColor = true;
}
- left = 0;
- // The initial xDiv and whether the first column is considered
- // stretchable or not depends on whether xDiv[0] was zero or not.
- for (i = xDivs[0] == 0 ? 1 : 0; i <= numXDivs && left < W; i++) {
- if (i == numXDivs) {
- right = W;
- } else {
- right = xDivs[i];
- }
- c = getColor(image->rows.data(), left, top, right - 1, bottom - 1);
- image->colors[colorIndex++] = c;
- if (kDebug) {
- if (c != android::Res_png_9patch::NO_COLOR) {
- hasColor = true;
- }
- }
- left = right;
- }
- top = bottom;
+ }
+ left = right;
}
+ top = bottom;
+ }
- assert(colorIndex == numColors);
+ assert(colorIndex == numColors);
- if (kDebug && hasColor) {
- for (i = 0; i < numColors; i++) {
- if (i == 0) printf("Colors:\n");
- printf(" #%08x", image->colors[i]);
- if (i == numColors - 1) printf("\n");
- }
+ if (kDebug && hasColor) {
+ for (i = 0; i < numColors; i++) {
+ if (i == 0) printf("Colors:\n");
+ printf(" #%08x", image->colors[i]);
+ if (i == numColors - 1) printf("\n");
}
+ }
getout:
- if (errorMsg) {
- std::stringstream err;
- err << "9-patch malformed: " << errorMsg;
- if (errorEdge) {
- err << "." << std::endl;
- if (errorPixel >= 0) {
- err << "Found at pixel #" << errorPixel << " along " << errorEdge << " edge";
- } else {
- err << "Found along " << errorEdge << " edge";
- }
- }
- *outError = err.str();
- return false;
+ if (errorMsg) {
+ std::stringstream err;
+ err << "9-patch malformed: " << errorMsg;
+ if (errorEdge) {
+ err << "." << std::endl;
+ if (errorPixel >= 0) {
+ err << "Found at pixel #" << errorPixel << " along " << errorEdge
+ << " edge";
+ } else {
+ err << "Found along " << errorEdge << " edge";
+ }
}
- return true;
+ *outError = err.str();
+ return false;
+ }
+ return true;
}
+bool Png::process(const Source& source, std::istream* input,
+ BigBuffer* outBuffer, const PngOptions& options) {
+ png_byte signature[kPngSignatureSize];
-bool Png::process(const Source& source, std::istream* input, BigBuffer* outBuffer,
- const PngOptions& options) {
- png_byte signature[kPngSignatureSize];
+ // Read the PNG signature first.
+ if (!input->read(reinterpret_cast<char*>(signature), kPngSignatureSize)) {
+ mDiag->error(DiagMessage() << strerror(errno));
+ return false;
+ }
- // Read the PNG signature first.
- if (!input->read(reinterpret_cast<char*>(signature), kPngSignatureSize)) {
- mDiag->error(DiagMessage() << strerror(errno));
- return false;
+ // If the PNG signature doesn't match, bail early.
+ if (png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
+ mDiag->error(DiagMessage() << "not a valid png file");
+ return false;
+ }
+
+ bool result = false;
+ png_structp readPtr = nullptr;
+ png_infop infoPtr = nullptr;
+ png_structp writePtr = nullptr;
+ png_infop writeInfoPtr = nullptr;
+ PngInfo pngInfo = {};
+
+ readPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr);
+ if (!readPtr) {
+ mDiag->error(DiagMessage() << "failed to allocate read ptr");
+ goto bail;
+ }
+
+ infoPtr = png_create_info_struct(readPtr);
+ if (!infoPtr) {
+ mDiag->error(DiagMessage() << "failed to allocate info ptr");
+ goto bail;
+ }
+
+ png_set_error_fn(readPtr, reinterpret_cast<png_voidp>(mDiag), nullptr,
+ logWarning);
+
+ // Set the read function to read from std::istream.
+ png_set_read_fn(readPtr, (png_voidp)input, readDataFromStream);
+
+ if (!readPng(mDiag, readPtr, infoPtr, &pngInfo)) {
+ goto bail;
+ }
+
+ if (util::stringEndsWith(source.path, ".9.png")) {
+ std::string errorMsg;
+ if (!do9Patch(&pngInfo, &errorMsg)) {
+ mDiag->error(DiagMessage() << errorMsg);
+ goto bail;
}
+ }
- // If the PNG signature doesn't match, bail early.
- if (png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
- mDiag->error(DiagMessage() << "not a valid png file");
- return false;
- }
+ writePtr =
+ png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr);
+ if (!writePtr) {
+ mDiag->error(DiagMessage() << "failed to allocate write ptr");
+ goto bail;
+ }
- bool result = false;
- png_structp readPtr = nullptr;
- png_infop infoPtr = nullptr;
- png_structp writePtr = nullptr;
- png_infop writeInfoPtr = nullptr;
- PngInfo pngInfo = {};
+ writeInfoPtr = png_create_info_struct(writePtr);
+ if (!writeInfoPtr) {
+ mDiag->error(DiagMessage() << "failed to allocate write info ptr");
+ goto bail;
+ }
- readPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr);
- if (!readPtr) {
- mDiag->error(DiagMessage() << "failed to allocate read ptr");
- goto bail;
- }
+ png_set_error_fn(writePtr, nullptr, nullptr, logWarning);
- infoPtr = png_create_info_struct(readPtr);
- if (!infoPtr) {
- mDiag->error(DiagMessage() << "failed to allocate info ptr");
- goto bail;
- }
+ // Set the write function to write to std::ostream.
+ png_set_write_fn(writePtr, (png_voidp)outBuffer, writeDataToStream,
+ flushDataToStream);
- png_set_error_fn(readPtr, reinterpret_cast<png_voidp>(mDiag), nullptr, logWarning);
+ if (!writePng(mDiag, writePtr, writeInfoPtr, &pngInfo,
+ options.grayScaleTolerance)) {
+ goto bail;
+ }
- // Set the read function to read from std::istream.
- png_set_read_fn(readPtr, (png_voidp) input, readDataFromStream);
-
- if (!readPng(mDiag, readPtr, infoPtr, &pngInfo)) {
- goto bail;
- }
-
- if (util::stringEndsWith(source.path, ".9.png")) {
- std::string errorMsg;
- if (!do9Patch(&pngInfo, &errorMsg)) {
- mDiag->error(DiagMessage() << errorMsg);
- goto bail;
- }
- }
-
- writePtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr);
- if (!writePtr) {
- mDiag->error(DiagMessage() << "failed to allocate write ptr");
- goto bail;
- }
-
- writeInfoPtr = png_create_info_struct(writePtr);
- if (!writeInfoPtr) {
- mDiag->error(DiagMessage() << "failed to allocate write info ptr");
- goto bail;
- }
-
- png_set_error_fn(writePtr, nullptr, nullptr, logWarning);
-
- // Set the write function to write to std::ostream.
- png_set_write_fn(writePtr, (png_voidp)outBuffer, writeDataToStream, flushDataToStream);
-
- if (!writePng(mDiag, writePtr, writeInfoPtr, &pngInfo, options.grayScaleTolerance)) {
- goto bail;
- }
-
- result = true;
+ result = true;
bail:
- if (readPtr) {
- png_destroy_read_struct(&readPtr, &infoPtr, nullptr);
- }
+ if (readPtr) {
+ png_destroy_read_struct(&readPtr, &infoPtr, nullptr);
+ }
- if (writePtr) {
- png_destroy_write_struct(&writePtr, &writeInfoPtr);
- }
- return result;
+ if (writePtr) {
+ png_destroy_write_struct(&writePtr, &writeInfoPtr);
+ }
+ return result;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/Png.h b/tools/aapt2/compile/Png.h
index 4a15d95..f9038af 100644
--- a/tools/aapt2/compile/Png.h
+++ b/tools/aapt2/compile/Png.h
@@ -31,51 +31,48 @@
namespace aapt {
struct PngOptions {
- int grayScaleTolerance = 0;
+ int grayScaleTolerance = 0;
};
class Png {
-public:
- explicit Png(IDiagnostics* diag) : mDiag(diag) {
- }
+ public:
+ explicit Png(IDiagnostics* diag) : mDiag(diag) {}
- bool process(const Source& source, std::istream* input, BigBuffer* outBuffer,
- const PngOptions& options);
+ bool process(const Source& source, std::istream* input, BigBuffer* outBuffer,
+ const PngOptions& options);
-private:
- IDiagnostics* mDiag;
+ private:
+ IDiagnostics* mDiag;
- DISALLOW_COPY_AND_ASSIGN(Png);
+ DISALLOW_COPY_AND_ASSIGN(Png);
};
/**
* An InputStream that filters out unimportant PNG chunks.
*/
class PngChunkFilter : public io::InputStream {
-public:
- explicit PngChunkFilter(const StringPiece& data);
+ public:
+ explicit PngChunkFilter(const StringPiece& data);
- bool Next(const void** buffer, int* len) override;
- void BackUp(int count) override;
- bool Skip(int count) override;
+ bool Next(const void** buffer, int* len) override;
+ void BackUp(int count) override;
+ bool Skip(int count) override;
- int64_t ByteCount() const override {
- return static_cast<int64_t>(mWindowStart);
- }
+ int64_t ByteCount() const override {
+ return static_cast<int64_t>(mWindowStart);
+ }
- bool HadError() const override {
- return mError;
- }
+ bool HadError() const override { return mError; }
-private:
- bool consumeWindow(const void** buffer, int* len);
+ private:
+ bool consumeWindow(const void** buffer, int* len);
- StringPiece mData;
- size_t mWindowStart = 0;
- size_t mWindowEnd = 0;
- bool mError = false;
+ StringPiece mData;
+ size_t mWindowStart = 0;
+ size_t mWindowEnd = 0;
+ bool mError = false;
- DISALLOW_COPY_AND_ASSIGN(PngChunkFilter);
+ DISALLOW_COPY_AND_ASSIGN(PngChunkFilter);
};
/**
@@ -84,11 +81,13 @@
std::unique_ptr<Image> readPng(IAaptContext* context, io::InputStream* in);
/**
- * Writes the RGBA Image, with optional 9-patch meta-data, into the OutputStream as a PNG.
+ * Writes the RGBA Image, with optional 9-patch meta-data, into the OutputStream
+ * as a PNG.
*/
-bool writePng(IAaptContext* context, const Image* image, const NinePatch* ninePatch,
- io::OutputStream* out, const PngOptions& options);
+bool writePng(IAaptContext* context, const Image* image,
+ const NinePatch* ninePatch, io::OutputStream* out,
+ const PngOptions& options);
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_PNG_H
+#endif // AAPT_PNG_H
diff --git a/tools/aapt2/compile/PngChunkFilter.cpp b/tools/aapt2/compile/PngChunkFilter.cpp
index 70a881f..fb7fe92 100644
--- a/tools/aapt2/compile/PngChunkFilter.cpp
+++ b/tools/aapt2/compile/PngChunkFilter.cpp
@@ -25,149 +25,149 @@
// Useful helper function that encodes individual bytes into a uint32
// at compile time.
constexpr uint32_t u32(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
- return (((uint32_t) a) << 24)
- | (((uint32_t) b) << 16)
- | (((uint32_t) c) << 8)
- | ((uint32_t) d);
+ return (((uint32_t)a) << 24) | (((uint32_t)b) << 16) | (((uint32_t)c) << 8) |
+ ((uint32_t)d);
}
// Whitelist of PNG chunk types that we want to keep in the resulting PNG.
enum PngChunkTypes {
- kPngChunkIHDR = u32(73, 72, 68, 82),
- kPngChunkIDAT = u32(73, 68, 65, 84),
- kPngChunkIEND = u32(73, 69, 78, 68),
- kPngChunkPLTE = u32(80, 76, 84, 69),
- kPngChunktRNS = u32(116, 82, 78, 83),
- kPngChunksRGB = u32(115, 82, 71, 66),
+ kPngChunkIHDR = u32(73, 72, 68, 82),
+ kPngChunkIDAT = u32(73, 68, 65, 84),
+ kPngChunkIEND = u32(73, 69, 78, 68),
+ kPngChunkPLTE = u32(80, 76, 84, 69),
+ kPngChunktRNS = u32(116, 82, 78, 83),
+ kPngChunksRGB = u32(115, 82, 71, 66),
};
static uint32_t peek32LE(const char* data) {
- uint32_t word = ((uint32_t) data[0]) & 0x000000ff;
- word <<= 8;
- word |= ((uint32_t) data[1]) & 0x000000ff;
- word <<= 8;
- word |= ((uint32_t) data[2]) & 0x000000ff;
- word <<= 8;
- word |= ((uint32_t) data[3]) & 0x000000ff;
- return word;
+ uint32_t word = ((uint32_t)data[0]) & 0x000000ff;
+ word <<= 8;
+ word |= ((uint32_t)data[1]) & 0x000000ff;
+ word <<= 8;
+ word |= ((uint32_t)data[2]) & 0x000000ff;
+ word <<= 8;
+ word |= ((uint32_t)data[3]) & 0x000000ff;
+ return word;
}
static bool isPngChunkWhitelisted(uint32_t type) {
- switch (type) {
+ switch (type) {
case kPngChunkIHDR:
case kPngChunkIDAT:
case kPngChunkIEND:
case kPngChunkPLTE:
case kPngChunktRNS:
case kPngChunksRGB:
- return true;
+ return true;
default:
- return false;
- }
+ return false;
+ }
}
PngChunkFilter::PngChunkFilter(const StringPiece& data) : mData(data) {
- if (util::stringStartsWith(mData, kPngSignature)) {
- mWindowStart = 0;
- mWindowEnd = strlen(kPngSignature);
- } else {
- mError = true;
- }
+ if (util::stringStartsWith(mData, kPngSignature)) {
+ mWindowStart = 0;
+ mWindowEnd = strlen(kPngSignature);
+ } else {
+ mError = true;
+ }
}
bool PngChunkFilter::consumeWindow(const void** buffer, int* len) {
- if (mWindowStart != mWindowEnd) {
- // We have bytes to give from our window.
- const int bytesRead = (int) (mWindowEnd - mWindowStart);
- *buffer = mData.data() + mWindowStart;
- *len = bytesRead;
- mWindowStart = mWindowEnd;
- return true;
- }
- return false;
+ if (mWindowStart != mWindowEnd) {
+ // We have bytes to give from our window.
+ const int bytesRead = (int)(mWindowEnd - mWindowStart);
+ *buffer = mData.data() + mWindowStart;
+ *len = bytesRead;
+ mWindowStart = mWindowEnd;
+ return true;
+ }
+ return false;
}
bool PngChunkFilter::Next(const void** buffer, int* len) {
- if (mError) {
- return false;
- }
-
- // In case BackUp was called, we must consume the window.
- if (consumeWindow(buffer, len)) {
- return true;
- }
-
- // Advance the window as far as possible (until we meet a chunk that
- // we want to strip).
- while (mWindowEnd < mData.size()) {
- // Chunk length (4 bytes) + type (4 bytes) + crc32 (4 bytes) = 12 bytes.
- const size_t kMinChunkHeaderSize = 3 * sizeof(uint32_t);
-
- // Is there enough room for a chunk header?
- if (mData.size() - mWindowStart < kMinChunkHeaderSize) {
- mError = true;
- return false;
- }
-
- // Verify the chunk length.
- const uint32_t chunkLen = peek32LE(mData.data() + mWindowEnd);
- if (((uint64_t) chunkLen) + ((uint64_t) mWindowEnd) + sizeof(uint32_t) > mData.size()) {
- // Overflow.
- mError = true;
- return false;
- }
-
- // Do we strip this chunk?
- const uint32_t chunkType = peek32LE(mData.data() + mWindowEnd + sizeof(uint32_t));
- if (isPngChunkWhitelisted(chunkType)) {
- // Advance the window to include this chunk.
- mWindowEnd += kMinChunkHeaderSize + chunkLen;
- } else {
- // We want to strip this chunk. If we accumulated a window,
- // we must return the window now.
- if (mWindowStart != mWindowEnd) {
- break;
- }
-
- // The window is empty, so we can advance past this chunk
- // and keep looking for the next good chunk,
- mWindowEnd += kMinChunkHeaderSize + chunkLen;
- mWindowStart = mWindowEnd;
- }
- }
-
- if (consumeWindow(buffer, len)) {
- return true;
- }
+ if (mError) {
return false;
+ }
+
+ // In case BackUp was called, we must consume the window.
+ if (consumeWindow(buffer, len)) {
+ return true;
+ }
+
+ // Advance the window as far as possible (until we meet a chunk that
+ // we want to strip).
+ while (mWindowEnd < mData.size()) {
+ // Chunk length (4 bytes) + type (4 bytes) + crc32 (4 bytes) = 12 bytes.
+ const size_t kMinChunkHeaderSize = 3 * sizeof(uint32_t);
+
+ // Is there enough room for a chunk header?
+ if (mData.size() - mWindowStart < kMinChunkHeaderSize) {
+ mError = true;
+ return false;
+ }
+
+ // Verify the chunk length.
+ const uint32_t chunkLen = peek32LE(mData.data() + mWindowEnd);
+ if (((uint64_t)chunkLen) + ((uint64_t)mWindowEnd) + sizeof(uint32_t) >
+ mData.size()) {
+ // Overflow.
+ mError = true;
+ return false;
+ }
+
+ // Do we strip this chunk?
+ const uint32_t chunkType =
+ peek32LE(mData.data() + mWindowEnd + sizeof(uint32_t));
+ if (isPngChunkWhitelisted(chunkType)) {
+ // Advance the window to include this chunk.
+ mWindowEnd += kMinChunkHeaderSize + chunkLen;
+ } else {
+ // We want to strip this chunk. If we accumulated a window,
+ // we must return the window now.
+ if (mWindowStart != mWindowEnd) {
+ break;
+ }
+
+ // The window is empty, so we can advance past this chunk
+ // and keep looking for the next good chunk,
+ mWindowEnd += kMinChunkHeaderSize + chunkLen;
+ mWindowStart = mWindowEnd;
+ }
+ }
+
+ if (consumeWindow(buffer, len)) {
+ return true;
+ }
+ return false;
}
void PngChunkFilter::BackUp(int count) {
- if (mError) {
- return;
- }
- mWindowStart -= count;
+ if (mError) {
+ return;
+ }
+ mWindowStart -= count;
}
bool PngChunkFilter::Skip(int count) {
- if (mError) {
- return false;
- }
+ if (mError) {
+ return false;
+ }
- const void* buffer;
- int len;
- while (count > 0) {
- if (!Next(&buffer, &len)) {
- return false;
- }
- if (len > count) {
- BackUp(len - count);
- count = 0;
- } else {
- count -= len;
- }
+ const void* buffer;
+ int len;
+ while (count > 0) {
+ if (!Next(&buffer, &len)) {
+ return false;
}
- return true;
+ if (len > count) {
+ BackUp(len - count);
+ count = 0;
+ } else {
+ count -= len;
+ }
+ }
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/PngCrunch.cpp b/tools/aapt2/compile/PngCrunch.cpp
index a2e3f4f..4a74f7af7 100644
--- a/tools/aapt2/compile/PngCrunch.cpp
+++ b/tools/aapt2/compile/PngCrunch.cpp
@@ -16,13 +16,13 @@
#include "compile/Png.h"
-#include <algorithm>
#include <android-base/errors.h>
#include <android-base/macros.h>
#include <png.h>
+#include <zlib.h>
+#include <algorithm>
#include <unordered_map>
#include <unordered_set>
-#include <zlib.h>
namespace aapt {
@@ -33,250 +33,261 @@
* Custom deleter that destroys libpng read and info structs.
*/
class PngReadStructDeleter {
-public:
- explicit PngReadStructDeleter(png_structp readPtr, png_infop infoPtr) :
- mReadPtr(readPtr), mInfoPtr(infoPtr) {
- }
+ public:
+ explicit PngReadStructDeleter(png_structp readPtr, png_infop infoPtr)
+ : mReadPtr(readPtr), mInfoPtr(infoPtr) {}
- ~PngReadStructDeleter() {
- png_destroy_read_struct(&mReadPtr, &mInfoPtr, nullptr);
- }
+ ~PngReadStructDeleter() {
+ png_destroy_read_struct(&mReadPtr, &mInfoPtr, nullptr);
+ }
-private:
- png_structp mReadPtr;
- png_infop mInfoPtr;
+ private:
+ png_structp mReadPtr;
+ png_infop mInfoPtr;
- DISALLOW_COPY_AND_ASSIGN(PngReadStructDeleter);
+ DISALLOW_COPY_AND_ASSIGN(PngReadStructDeleter);
};
/**
* Custom deleter that destroys libpng write and info structs.
*/
class PngWriteStructDeleter {
-public:
- explicit PngWriteStructDeleter(png_structp writePtr, png_infop infoPtr) :
- mWritePtr(writePtr), mInfoPtr(infoPtr) {
- }
+ public:
+ explicit PngWriteStructDeleter(png_structp writePtr, png_infop infoPtr)
+ : mWritePtr(writePtr), mInfoPtr(infoPtr) {}
- ~PngWriteStructDeleter() {
- png_destroy_write_struct(&mWritePtr, &mInfoPtr);
- }
+ ~PngWriteStructDeleter() { png_destroy_write_struct(&mWritePtr, &mInfoPtr); }
-private:
- png_structp mWritePtr;
- png_infop mInfoPtr;
+ private:
+ png_structp mWritePtr;
+ png_infop mInfoPtr;
- DISALLOW_COPY_AND_ASSIGN(PngWriteStructDeleter);
+ DISALLOW_COPY_AND_ASSIGN(PngWriteStructDeleter);
};
// Custom warning logging method that uses IDiagnostics.
static void logWarning(png_structp pngPtr, png_const_charp warningMsg) {
- IDiagnostics* diag = (IDiagnostics*) png_get_error_ptr(pngPtr);
- diag->warn(DiagMessage() << warningMsg);
+ IDiagnostics* diag = (IDiagnostics*)png_get_error_ptr(pngPtr);
+ diag->warn(DiagMessage() << warningMsg);
}
// Custom error logging method that uses IDiagnostics.
static void logError(png_structp pngPtr, png_const_charp errorMsg) {
- IDiagnostics* diag = (IDiagnostics*) png_get_error_ptr(pngPtr);
- diag->error(DiagMessage() << errorMsg);
+ IDiagnostics* diag = (IDiagnostics*)png_get_error_ptr(pngPtr);
+ diag->error(DiagMessage() << errorMsg);
}
-static void readDataFromStream(png_structp pngPtr, png_bytep buffer, png_size_t len) {
- io::InputStream* in = (io::InputStream*) png_get_io_ptr(pngPtr);
+static void readDataFromStream(png_structp pngPtr, png_bytep buffer,
+ png_size_t len) {
+ io::InputStream* in = (io::InputStream*)png_get_io_ptr(pngPtr);
- const void* inBuffer;
- int inLen;
- if (!in->Next(&inBuffer, &inLen)) {
- if (in->HadError()) {
- std::string err = in->GetError();
- png_error(pngPtr, err.c_str());
- }
- return;
+ const void* inBuffer;
+ int inLen;
+ if (!in->Next(&inBuffer, &inLen)) {
+ if (in->HadError()) {
+ std::string err = in->GetError();
+ png_error(pngPtr, err.c_str());
}
+ return;
+ }
- const size_t bytesRead = std::min(static_cast<size_t>(inLen), len);
- memcpy(buffer, inBuffer, bytesRead);
- if (bytesRead != static_cast<size_t>(inLen)) {
- in->BackUp(inLen - static_cast<int>(bytesRead));
- }
+ const size_t bytesRead = std::min(static_cast<size_t>(inLen), len);
+ memcpy(buffer, inBuffer, bytesRead);
+ if (bytesRead != static_cast<size_t>(inLen)) {
+ in->BackUp(inLen - static_cast<int>(bytesRead));
+ }
}
-static void writeDataToStream(png_structp pngPtr, png_bytep buffer, png_size_t len) {
- io::OutputStream* out = (io::OutputStream*) png_get_io_ptr(pngPtr);
+static void writeDataToStream(png_structp pngPtr, png_bytep buffer,
+ png_size_t len) {
+ io::OutputStream* out = (io::OutputStream*)png_get_io_ptr(pngPtr);
- void* outBuffer;
- int outLen;
- while (len > 0) {
- if (!out->Next(&outBuffer, &outLen)) {
- if (out->HadError()) {
- std::string err = out->GetError();
- png_error(pngPtr, err.c_str());
- }
- return;
- }
-
- const size_t bytesWritten = std::min(static_cast<size_t>(outLen), len);
- memcpy(outBuffer, buffer, bytesWritten);
-
- // Advance the input buffer.
- buffer += bytesWritten;
- len -= bytesWritten;
-
- // Advance the output buffer.
- outLen -= static_cast<int>(bytesWritten);
+ void* outBuffer;
+ int outLen;
+ while (len > 0) {
+ if (!out->Next(&outBuffer, &outLen)) {
+ if (out->HadError()) {
+ std::string err = out->GetError();
+ png_error(pngPtr, err.c_str());
+ }
+ return;
}
- // If the entire output buffer wasn't used, backup.
- if (outLen > 0) {
- out->BackUp(outLen);
- }
+ const size_t bytesWritten = std::min(static_cast<size_t>(outLen), len);
+ memcpy(outBuffer, buffer, bytesWritten);
+
+ // Advance the input buffer.
+ buffer += bytesWritten;
+ len -= bytesWritten;
+
+ // Advance the output buffer.
+ outLen -= static_cast<int>(bytesWritten);
+ }
+
+ // If the entire output buffer wasn't used, backup.
+ if (outLen > 0) {
+ out->BackUp(outLen);
+ }
}
std::unique_ptr<Image> readPng(IAaptContext* context, io::InputStream* in) {
- // Read the first 8 bytes of the file looking for the PNG signature.
- // Bail early if it does not match.
- const png_byte* signature;
- int bufferSize;
- if (!in->Next((const void**) &signature, &bufferSize)) {
- context->getDiagnostics()->error(DiagMessage()
- << android::base::SystemErrorCodeToString(errno));
- return {};
- }
+ // Read the first 8 bytes of the file looking for the PNG signature.
+ // Bail early if it does not match.
+ const png_byte* signature;
+ int bufferSize;
+ if (!in->Next((const void**)&signature, &bufferSize)) {
+ context->getDiagnostics()->error(
+ DiagMessage() << android::base::SystemErrorCodeToString(errno));
+ return {};
+ }
- if (static_cast<size_t>(bufferSize) < kPngSignatureSize
- || png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
- context->getDiagnostics()->error(DiagMessage()
- << "file signature does not match PNG signature");
- return {};
- }
+ if (static_cast<size_t>(bufferSize) < kPngSignatureSize ||
+ png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
+ context->getDiagnostics()->error(
+ DiagMessage() << "file signature does not match PNG signature");
+ return {};
+ }
- // Start at the beginning of the first chunk.
- in->BackUp(bufferSize - static_cast<int>(kPngSignatureSize));
+ // Start at the beginning of the first chunk.
+ in->BackUp(bufferSize - static_cast<int>(kPngSignatureSize));
- // Create and initialize the png_struct with the default error and warning handlers.
- // The header version is also passed in to ensure that this was built against the same
- // version of libpng.
- png_structp readPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
- if (readPtr == nullptr) {
- context->getDiagnostics()->error(DiagMessage()
- << "failed to create libpng read png_struct");
- return {};
- }
+ // Create and initialize the png_struct with the default error and warning
+ // handlers.
+ // The header version is also passed in to ensure that this was built against
+ // the same
+ // version of libpng.
+ png_structp readPtr =
+ png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+ if (readPtr == nullptr) {
+ context->getDiagnostics()->error(
+ DiagMessage() << "failed to create libpng read png_struct");
+ return {};
+ }
- // Create and initialize the memory for image header and data.
- png_infop infoPtr = png_create_info_struct(readPtr);
- if (infoPtr == nullptr) {
- context->getDiagnostics()->error(DiagMessage() << "failed to create libpng read png_info");
- png_destroy_read_struct(&readPtr, nullptr, nullptr);
- return {};
- }
+ // Create and initialize the memory for image header and data.
+ png_infop infoPtr = png_create_info_struct(readPtr);
+ if (infoPtr == nullptr) {
+ context->getDiagnostics()->error(
+ DiagMessage() << "failed to create libpng read png_info");
+ png_destroy_read_struct(&readPtr, nullptr, nullptr);
+ return {};
+ }
- // Automatically release PNG resources at end of scope.
- PngReadStructDeleter pngReadDeleter(readPtr, infoPtr);
+ // Automatically release PNG resources at end of scope.
+ PngReadStructDeleter pngReadDeleter(readPtr, infoPtr);
- // libpng uses longjmp to jump to an error handling routine.
- // setjmp will only return true if it was jumped to, aka there was
- // an error.
- if (setjmp(png_jmpbuf(readPtr))) {
- return {};
- }
+ // libpng uses longjmp to jump to an error handling routine.
+ // setjmp will only return true if it was jumped to, aka there was
+ // an error.
+ if (setjmp(png_jmpbuf(readPtr))) {
+ return {};
+ }
- // Handle warnings ourselves via IDiagnostics.
- png_set_error_fn(readPtr, (png_voidp) context->getDiagnostics(), logError, logWarning);
+ // Handle warnings ourselves via IDiagnostics.
+ png_set_error_fn(readPtr, (png_voidp)context->getDiagnostics(), logError,
+ logWarning);
- // Set up the read functions which read from our custom data sources.
- png_set_read_fn(readPtr, (png_voidp) in, readDataFromStream);
+ // Set up the read functions which read from our custom data sources.
+ png_set_read_fn(readPtr, (png_voidp)in, readDataFromStream);
- // Skip the signature that we already read.
- png_set_sig_bytes(readPtr, kPngSignatureSize);
+ // Skip the signature that we already read.
+ png_set_sig_bytes(readPtr, kPngSignatureSize);
- // Read the chunk headers.
- png_read_info(readPtr, infoPtr);
+ // Read the chunk headers.
+ png_read_info(readPtr, infoPtr);
- // Extract image meta-data from the various chunk headers.
- uint32_t width, height;
- int bitDepth, colorType, interlaceMethod, compressionMethod, filterMethod;
- png_get_IHDR(readPtr, infoPtr, &width, &height, &bitDepth, &colorType, &interlaceMethod,
- &compressionMethod, &filterMethod);
+ // Extract image meta-data from the various chunk headers.
+ uint32_t width, height;
+ int bitDepth, colorType, interlaceMethod, compressionMethod, filterMethod;
+ png_get_IHDR(readPtr, infoPtr, &width, &height, &bitDepth, &colorType,
+ &interlaceMethod, &compressionMethod, &filterMethod);
- // When the image is read, expand it so that it is in RGBA 8888 format
- // so that image handling is uniform.
+ // When the image is read, expand it so that it is in RGBA 8888 format
+ // so that image handling is uniform.
- if (colorType == PNG_COLOR_TYPE_PALETTE) {
- png_set_palette_to_rgb(readPtr);
- }
+ if (colorType == PNG_COLOR_TYPE_PALETTE) {
+ png_set_palette_to_rgb(readPtr);
+ }
- if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
- png_set_expand_gray_1_2_4_to_8(readPtr);
- }
+ if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
+ png_set_expand_gray_1_2_4_to_8(readPtr);
+ }
- if (png_get_valid(readPtr, infoPtr, PNG_INFO_tRNS)) {
- png_set_tRNS_to_alpha(readPtr);
- }
+ if (png_get_valid(readPtr, infoPtr, PNG_INFO_tRNS)) {
+ png_set_tRNS_to_alpha(readPtr);
+ }
- if (bitDepth == 16) {
- png_set_strip_16(readPtr);
- }
+ if (bitDepth == 16) {
+ png_set_strip_16(readPtr);
+ }
- if (!(colorType & PNG_COLOR_MASK_ALPHA)) {
- png_set_add_alpha(readPtr, 0xFF, PNG_FILLER_AFTER);
- }
+ if (!(colorType & PNG_COLOR_MASK_ALPHA)) {
+ png_set_add_alpha(readPtr, 0xFF, PNG_FILLER_AFTER);
+ }
- if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
- png_set_gray_to_rgb(readPtr);
- }
+ if (colorType == PNG_COLOR_TYPE_GRAY ||
+ colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ png_set_gray_to_rgb(readPtr);
+ }
- if (interlaceMethod != PNG_INTERLACE_NONE) {
- png_set_interlace_handling(readPtr);
- }
+ if (interlaceMethod != PNG_INTERLACE_NONE) {
+ png_set_interlace_handling(readPtr);
+ }
- // Once all the options for reading have been set, we need to flush
- // them to libpng.
- png_read_update_info(readPtr, infoPtr);
+ // Once all the options for reading have been set, we need to flush
+ // them to libpng.
+ png_read_update_info(readPtr, infoPtr);
- // 9-patch uses int32_t to index images, so we cap the image dimensions to something
- // that can always be represented by 9-patch.
- if (width > std::numeric_limits<int32_t>::max() ||
- height > std::numeric_limits<int32_t>::max()) {
- context->getDiagnostics()->error(DiagMessage() << "PNG image dimensions are too large: "
- << width << "x" << height);
- return {};
- }
+ // 9-patch uses int32_t to index images, so we cap the image dimensions to
+ // something
+ // that can always be represented by 9-patch.
+ if (width > std::numeric_limits<int32_t>::max() ||
+ height > std::numeric_limits<int32_t>::max()) {
+ context->getDiagnostics()->error(DiagMessage()
+ << "PNG image dimensions are too large: "
+ << width << "x" << height);
+ return {};
+ }
- std::unique_ptr<Image> outputImage = util::make_unique<Image>();
- outputImage->width = static_cast<int32_t>(width);
- outputImage->height = static_cast<int32_t>(height);
+ std::unique_ptr<Image> outputImage = util::make_unique<Image>();
+ outputImage->width = static_cast<int32_t>(width);
+ outputImage->height = static_cast<int32_t>(height);
- const size_t rowBytes = png_get_rowbytes(readPtr, infoPtr);
- assert(rowBytes == 4 * width); // RGBA
+ const size_t rowBytes = png_get_rowbytes(readPtr, infoPtr);
+ assert(rowBytes == 4 * width); // RGBA
- // Allocate one large block to hold the image.
- outputImage->data = std::unique_ptr<uint8_t[]>(new uint8_t[height * rowBytes]);
+ // Allocate one large block to hold the image.
+ outputImage->data =
+ std::unique_ptr<uint8_t[]>(new uint8_t[height * rowBytes]);
- // Create an array of rows that index into the data block.
- outputImage->rows = std::unique_ptr<uint8_t*[]>(new uint8_t*[height]);
- for (uint32_t h = 0; h < height; h++) {
- outputImage->rows[h] = outputImage->data.get() + (h * rowBytes);
- }
+ // Create an array of rows that index into the data block.
+ outputImage->rows = std::unique_ptr<uint8_t* []>(new uint8_t*[height]);
+ for (uint32_t h = 0; h < height; h++) {
+ outputImage->rows[h] = outputImage->data.get() + (h * rowBytes);
+ }
- // Actually read the image pixels.
- png_read_image(readPtr, outputImage->rows.get());
+ // Actually read the image pixels.
+ png_read_image(readPtr, outputImage->rows.get());
- // Finish reading. This will read any other chunks after the image data.
- png_read_end(readPtr, infoPtr);
+ // Finish reading. This will read any other chunks after the image data.
+ png_read_end(readPtr, infoPtr);
- return outputImage;
+ return outputImage;
}
/**
- * Experimentally chosen constant to be added to the overhead of using color type
- * PNG_COLOR_TYPE_PALETTE to account for the uncompressability of the palette chunk.
- * Without this, many small PNGs encoded with palettes are larger after compression than
+ * Experimentally chosen constant to be added to the overhead of using color
+ * type
+ * PNG_COLOR_TYPE_PALETTE to account for the uncompressability of the palette
+ * chunk.
+ * Without this, many small PNGs encoded with palettes are larger after
+ * compression than
* the same PNGs encoded as RGBA.
*/
constexpr static const size_t kPaletteOverheadConstant = 1024u * 10u;
-// Pick a color type by which to encode the image, based on which color type will take
+// Pick a color type by which to encode the image, based on which color type
+// will take
// the least amount of disk space.
//
// 9-patch images traditionally have not been encoded with palettes.
@@ -298,427 +309,450 @@
// - Grayscale + cheap alpha
// - Grayscale + alpha
//
-static int pickColorType(int32_t width, int32_t height,
- bool grayScale, bool convertibleToGrayScale, bool hasNinePatch,
+static int pickColorType(int32_t width, int32_t height, bool grayScale,
+ bool convertibleToGrayScale, bool hasNinePatch,
size_t colorPaletteSize, size_t alphaPaletteSize) {
- const size_t paletteChunkSize = 16 + colorPaletteSize * 3;
- const size_t alphaChunkSize = 16 + alphaPaletteSize;
- const size_t colorAlphaDataChunkSize = 16 + 4 * width * height;
- const size_t colorDataChunkSize = 16 + 3 * width * height;
- const size_t grayScaleAlphaDataChunkSize = 16 + 2 * width * height;
- const size_t paletteDataChunkSize = 16 + width * height;
+ const size_t paletteChunkSize = 16 + colorPaletteSize * 3;
+ const size_t alphaChunkSize = 16 + alphaPaletteSize;
+ const size_t colorAlphaDataChunkSize = 16 + 4 * width * height;
+ const size_t colorDataChunkSize = 16 + 3 * width * height;
+ const size_t grayScaleAlphaDataChunkSize = 16 + 2 * width * height;
+ const size_t paletteDataChunkSize = 16 + width * height;
- if (grayScale) {
- if (alphaPaletteSize == 0) {
- // This is the smallest the data can be.
- return PNG_COLOR_TYPE_GRAY;
- } else if (colorPaletteSize <= 256 && !hasNinePatch) {
- // This grayscale has alpha and can fit within a palette.
- // See if it is worth fitting into a palette.
- const size_t paletteThreshold = paletteChunkSize + alphaChunkSize +
- paletteDataChunkSize + kPaletteOverheadConstant;
- if (grayScaleAlphaDataChunkSize > paletteThreshold) {
- return PNG_COLOR_TYPE_PALETTE;
- }
- }
- return PNG_COLOR_TYPE_GRAY_ALPHA;
- }
-
-
- if (colorPaletteSize <= 256 && !hasNinePatch) {
- // This image can fit inside a palette. Let's see if it is worth it.
- size_t totalSizeWithPalette = paletteDataChunkSize + paletteChunkSize;
- size_t totalSizeWithoutPalette = colorDataChunkSize;
- if (alphaPaletteSize > 0) {
- totalSizeWithPalette += alphaPaletteSize;
- totalSizeWithoutPalette = colorAlphaDataChunkSize;
- }
-
- if (totalSizeWithoutPalette > totalSizeWithPalette + kPaletteOverheadConstant) {
- return PNG_COLOR_TYPE_PALETTE;
- }
- }
-
- if (convertibleToGrayScale) {
- if (alphaPaletteSize == 0) {
- return PNG_COLOR_TYPE_GRAY;
- } else {
- return PNG_COLOR_TYPE_GRAY_ALPHA;
- }
- }
-
+ if (grayScale) {
if (alphaPaletteSize == 0) {
- return PNG_COLOR_TYPE_RGB;
+ // This is the smallest the data can be.
+ return PNG_COLOR_TYPE_GRAY;
+ } else if (colorPaletteSize <= 256 && !hasNinePatch) {
+ // This grayscale has alpha and can fit within a palette.
+ // See if it is worth fitting into a palette.
+ const size_t paletteThreshold = paletteChunkSize + alphaChunkSize +
+ paletteDataChunkSize +
+ kPaletteOverheadConstant;
+ if (grayScaleAlphaDataChunkSize > paletteThreshold) {
+ return PNG_COLOR_TYPE_PALETTE;
+ }
}
- return PNG_COLOR_TYPE_RGBA;
+ return PNG_COLOR_TYPE_GRAY_ALPHA;
+ }
+
+ if (colorPaletteSize <= 256 && !hasNinePatch) {
+ // This image can fit inside a palette. Let's see if it is worth it.
+ size_t totalSizeWithPalette = paletteDataChunkSize + paletteChunkSize;
+ size_t totalSizeWithoutPalette = colorDataChunkSize;
+ if (alphaPaletteSize > 0) {
+ totalSizeWithPalette += alphaPaletteSize;
+ totalSizeWithoutPalette = colorAlphaDataChunkSize;
+ }
+
+ if (totalSizeWithoutPalette >
+ totalSizeWithPalette + kPaletteOverheadConstant) {
+ return PNG_COLOR_TYPE_PALETTE;
+ }
+ }
+
+ if (convertibleToGrayScale) {
+ if (alphaPaletteSize == 0) {
+ return PNG_COLOR_TYPE_GRAY;
+ } else {
+ return PNG_COLOR_TYPE_GRAY_ALPHA;
+ }
+ }
+
+ if (alphaPaletteSize == 0) {
+ return PNG_COLOR_TYPE_RGB;
+ }
+ return PNG_COLOR_TYPE_RGBA;
}
-// Assigns indices to the color and alpha palettes, encodes them, and then invokes
+// Assigns indices to the color and alpha palettes, encodes them, and then
+// invokes
// png_set_PLTE/png_set_tRNS.
// This must be done before writing image data.
-// Image data must be transformed to use the indices assigned within the palette.
+// Image data must be transformed to use the indices assigned within the
+// palette.
static void writePalette(png_structp writePtr, png_infop writeInfoPtr,
- std::unordered_map<uint32_t, int>* colorPalette,
- std::unordered_set<uint32_t>* alphaPalette) {
- assert(colorPalette->size() <= 256);
- assert(alphaPalette->size() <= 256);
+ std::unordered_map<uint32_t, int>* colorPalette,
+ std::unordered_set<uint32_t>* alphaPalette) {
+ assert(colorPalette->size() <= 256);
+ assert(alphaPalette->size() <= 256);
- // Populate the PNG palette struct and assign indices to the color
- // palette.
+ // Populate the PNG palette struct and assign indices to the color
+ // palette.
- // Colors in the alpha palette should have smaller indices.
- // This will ensure that we can truncate the alpha palette if it is
- // smaller than the color palette.
- int index = 0;
- for (uint32_t color : *alphaPalette) {
- (*colorPalette)[color] = index++;
+ // Colors in the alpha palette should have smaller indices.
+ // This will ensure that we can truncate the alpha palette if it is
+ // smaller than the color palette.
+ int index = 0;
+ for (uint32_t color : *alphaPalette) {
+ (*colorPalette)[color] = index++;
+ }
+
+ // Assign the rest of the entries.
+ for (auto& entry : *colorPalette) {
+ if (entry.second == -1) {
+ entry.second = index++;
}
+ }
- // Assign the rest of the entries.
- for (auto& entry : *colorPalette) {
- if (entry.second == -1) {
- entry.second = index++;
- }
+ // Create the PNG color palette struct.
+ auto colorPaletteBytes =
+ std::unique_ptr<png_color[]>(new png_color[colorPalette->size()]);
+
+ std::unique_ptr<png_byte[]> alphaPaletteBytes;
+ if (!alphaPalette->empty()) {
+ alphaPaletteBytes =
+ std::unique_ptr<png_byte[]>(new png_byte[alphaPalette->size()]);
+ }
+
+ for (const auto& entry : *colorPalette) {
+ const uint32_t color = entry.first;
+ const int index = entry.second;
+ assert(index >= 0);
+ assert(static_cast<size_t>(index) < colorPalette->size());
+
+ png_colorp slot = colorPaletteBytes.get() + index;
+ slot->red = color >> 24;
+ slot->green = color >> 16;
+ slot->blue = color >> 8;
+
+ const png_byte alpha = color & 0x000000ff;
+ if (alpha != 0xff && alphaPaletteBytes) {
+ assert(static_cast<size_t>(index) < alphaPalette->size());
+ alphaPaletteBytes[index] = alpha;
}
+ }
- // Create the PNG color palette struct.
- auto colorPaletteBytes = std::unique_ptr<png_color[]>(new png_color[colorPalette->size()]);
+ // The bytes get copied here, so it is safe to release colorPaletteBytes at
+ // the end of function
+ // scope.
+ png_set_PLTE(writePtr, writeInfoPtr, colorPaletteBytes.get(),
+ colorPalette->size());
- std::unique_ptr<png_byte[]> alphaPaletteBytes;
- if (!alphaPalette->empty()) {
- alphaPaletteBytes = std::unique_ptr<png_byte[]>(new png_byte[alphaPalette->size()]);
- }
-
- for (const auto& entry : *colorPalette) {
- const uint32_t color = entry.first;
- const int index = entry.second;
- assert(index >= 0);
- assert(static_cast<size_t>(index) < colorPalette->size());
-
- png_colorp slot = colorPaletteBytes.get() + index;
- slot->red = color >> 24;
- slot->green = color >> 16;
- slot->blue = color >> 8;
-
- const png_byte alpha = color & 0x000000ff;
- if (alpha != 0xff && alphaPaletteBytes) {
- assert(static_cast<size_t>(index) < alphaPalette->size());
- alphaPaletteBytes[index] = alpha;
- }
- }
-
- // The bytes get copied here, so it is safe to release colorPaletteBytes at the end of function
- // scope.
- png_set_PLTE(writePtr, writeInfoPtr, colorPaletteBytes.get(), colorPalette->size());
-
- if (alphaPaletteBytes) {
- png_set_tRNS(writePtr, writeInfoPtr, alphaPaletteBytes.get(), alphaPalette->size(),
- nullptr);
- }
+ if (alphaPaletteBytes) {
+ png_set_tRNS(writePtr, writeInfoPtr, alphaPaletteBytes.get(),
+ alphaPalette->size(), nullptr);
+ }
}
// Write the 9-patch custom PNG chunks to writeInfoPtr. This must be done before
// writing image data.
static void writeNinePatch(png_structp writePtr, png_infop writeInfoPtr,
const NinePatch* ninePatch) {
- // The order of the chunks is important.
- // 9-patch code in older platforms expects the 9-patch chunk to
- // be last.
+ // The order of the chunks is important.
+ // 9-patch code in older platforms expects the 9-patch chunk to
+ // be last.
- png_unknown_chunk unknownChunks[3];
- memset(unknownChunks, 0, sizeof(unknownChunks));
+ png_unknown_chunk unknownChunks[3];
+ memset(unknownChunks, 0, sizeof(unknownChunks));
- size_t index = 0;
- size_t chunkLen = 0;
+ size_t index = 0;
+ size_t chunkLen = 0;
- std::unique_ptr<uint8_t[]> serializedOutline =
- ninePatch->serializeRoundedRectOutline(&chunkLen);
- strcpy((char*) unknownChunks[index].name, "npOl");
+ std::unique_ptr<uint8_t[]> serializedOutline =
+ ninePatch->serializeRoundedRectOutline(&chunkLen);
+ strcpy((char*)unknownChunks[index].name, "npOl");
+ unknownChunks[index].size = chunkLen;
+ unknownChunks[index].data = (png_bytep)serializedOutline.get();
+ unknownChunks[index].location = PNG_HAVE_PLTE;
+ index++;
+
+ std::unique_ptr<uint8_t[]> serializedLayoutBounds;
+ if (ninePatch->layoutBounds.nonZero()) {
+ serializedLayoutBounds = ninePatch->serializeLayoutBounds(&chunkLen);
+ strcpy((char*)unknownChunks[index].name, "npLb");
unknownChunks[index].size = chunkLen;
- unknownChunks[index].data = (png_bytep) serializedOutline.get();
+ unknownChunks[index].data = (png_bytep)serializedLayoutBounds.get();
unknownChunks[index].location = PNG_HAVE_PLTE;
index++;
+ }
- std::unique_ptr<uint8_t[]> serializedLayoutBounds;
- if (ninePatch->layoutBounds.nonZero()) {
- serializedLayoutBounds = ninePatch->serializeLayoutBounds(&chunkLen);
- strcpy((char*) unknownChunks[index].name, "npLb");
- unknownChunks[index].size = chunkLen;
- unknownChunks[index].data = (png_bytep) serializedLayoutBounds.get();
- unknownChunks[index].location = PNG_HAVE_PLTE;
- index++;
- }
+ std::unique_ptr<uint8_t[]> serializedNinePatch =
+ ninePatch->serializeBase(&chunkLen);
+ strcpy((char*)unknownChunks[index].name, "npTc");
+ unknownChunks[index].size = chunkLen;
+ unknownChunks[index].data = (png_bytep)serializedNinePatch.get();
+ unknownChunks[index].location = PNG_HAVE_PLTE;
+ index++;
- std::unique_ptr<uint8_t[]> serializedNinePatch = ninePatch->serializeBase(&chunkLen);
- strcpy((char*) unknownChunks[index].name, "npTc");
- unknownChunks[index].size = chunkLen;
- unknownChunks[index].data = (png_bytep) serializedNinePatch.get();
- unknownChunks[index].location = PNG_HAVE_PLTE;
- index++;
+ // Handle all unknown chunks. We are manually setting the chunks here,
+ // so we will only ever handle our custom chunks.
+ png_set_keep_unknown_chunks(writePtr, PNG_HANDLE_CHUNK_ALWAYS, nullptr, 0);
- // Handle all unknown chunks. We are manually setting the chunks here,
- // so we will only ever handle our custom chunks.
- png_set_keep_unknown_chunks(writePtr, PNG_HANDLE_CHUNK_ALWAYS, nullptr, 0);
-
- // Set the actual chunks here. The data gets copied, so our buffers can
- // safely go out of scope.
- png_set_unknown_chunks(writePtr, writeInfoPtr, unknownChunks, index);
+ // Set the actual chunks here. The data gets copied, so our buffers can
+ // safely go out of scope.
+ png_set_unknown_chunks(writePtr, writeInfoPtr, unknownChunks, index);
}
-bool writePng(IAaptContext* context, const Image* image, const NinePatch* ninePatch,
- io::OutputStream* out, const PngOptions& options) {
- // Create and initialize the write png_struct with the default error and warning handlers.
- // The header version is also passed in to ensure that this was built against the same
- // version of libpng.
- png_structp writePtr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
- nullptr, nullptr, nullptr);
- if (writePtr == nullptr) {
- context->getDiagnostics()->error(DiagMessage()
- << "failed to create libpng write png_struct");
- return false;
+bool writePng(IAaptContext* context, const Image* image,
+ const NinePatch* ninePatch, io::OutputStream* out,
+ const PngOptions& options) {
+ // Create and initialize the write png_struct with the default error and
+ // warning handlers.
+ // The header version is also passed in to ensure that this was built against
+ // the same
+ // version of libpng.
+ png_structp writePtr =
+ png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+ if (writePtr == nullptr) {
+ context->getDiagnostics()->error(
+ DiagMessage() << "failed to create libpng write png_struct");
+ return false;
+ }
+
+ // Allocate memory to store image header data.
+ png_infop writeInfoPtr = png_create_info_struct(writePtr);
+ if (writeInfoPtr == nullptr) {
+ context->getDiagnostics()->error(
+ DiagMessage() << "failed to create libpng write png_info");
+ png_destroy_write_struct(&writePtr, nullptr);
+ return false;
+ }
+
+ // Automatically release PNG resources at end of scope.
+ PngWriteStructDeleter pngWriteDeleter(writePtr, writeInfoPtr);
+
+ // libpng uses longjmp to jump to error handling routines.
+ // setjmp will return true only if it was jumped to, aka, there was an error.
+ if (setjmp(png_jmpbuf(writePtr))) {
+ return false;
+ }
+
+ // Handle warnings with our IDiagnostics.
+ png_set_error_fn(writePtr, (png_voidp)context->getDiagnostics(), logError,
+ logWarning);
+
+ // Set up the write functions which write to our custom data sources.
+ png_set_write_fn(writePtr, (png_voidp)out, writeDataToStream, nullptr);
+
+ // We want small files and can take the performance hit to achieve this goal.
+ png_set_compression_level(writePtr, Z_BEST_COMPRESSION);
+
+ // Begin analysis of the image data.
+ // Scan the entire image and determine if:
+ // 1. Every pixel has R == G == B (grayscale)
+ // 2. Every pixel has A == 255 (opaque)
+ // 3. There are no more than 256 distinct RGBA colors (palette).
+ std::unordered_map<uint32_t, int> colorPalette;
+ std::unordered_set<uint32_t> alphaPalette;
+ bool needsToZeroRGBChannelsOfTransparentPixels = false;
+ bool grayScale = true;
+ int maxGrayDeviation = 0;
+
+ for (int32_t y = 0; y < image->height; y++) {
+ const uint8_t* row = image->rows[y];
+ for (int32_t x = 0; x < image->width; x++) {
+ int red = *row++;
+ int green = *row++;
+ int blue = *row++;
+ int alpha = *row++;
+
+ if (alpha == 0) {
+ // The color is completely transparent.
+ // For purposes of palettes and grayscale optimization,
+ // treat all channels as 0x00.
+ needsToZeroRGBChannelsOfTransparentPixels =
+ needsToZeroRGBChannelsOfTransparentPixels ||
+ (red != 0 || green != 0 || blue != 0);
+ red = green = blue = 0;
+ }
+
+ // Insert the color into the color palette.
+ const uint32_t color = red << 24 | green << 16 | blue << 8 | alpha;
+ colorPalette[color] = -1;
+
+ // If the pixel has non-opaque alpha, insert it into the
+ // alpha palette.
+ if (alpha != 0xff) {
+ alphaPalette.insert(color);
+ }
+
+ // Check if the image is indeed grayscale.
+ if (grayScale) {
+ if (red != green || red != blue) {
+ grayScale = false;
+ }
+ }
+
+ // Calculate the gray scale deviation so that it can be compared
+ // with the threshold.
+ maxGrayDeviation = std::max(std::abs(red - green), maxGrayDeviation);
+ maxGrayDeviation = std::max(std::abs(green - blue), maxGrayDeviation);
+ maxGrayDeviation = std::max(std::abs(blue - red), maxGrayDeviation);
}
+ }
- // Allocate memory to store image header data.
- png_infop writeInfoPtr = png_create_info_struct(writePtr);
- if (writeInfoPtr == nullptr) {
- context->getDiagnostics()->error(DiagMessage() << "failed to create libpng write png_info");
- png_destroy_write_struct(&writePtr, nullptr);
- return false;
+ if (context->verbose()) {
+ DiagMessage msg;
+ msg << " paletteSize=" << colorPalette.size()
+ << " alphaPaletteSize=" << alphaPalette.size()
+ << " maxGrayDeviation=" << maxGrayDeviation
+ << " grayScale=" << (grayScale ? "true" : "false");
+ context->getDiagnostics()->note(msg);
+ }
+
+ const bool convertibleToGrayScale =
+ maxGrayDeviation <= options.grayScaleTolerance;
+
+ const int newColorType = pickColorType(
+ image->width, image->height, grayScale, convertibleToGrayScale,
+ ninePatch != nullptr, colorPalette.size(), alphaPalette.size());
+
+ if (context->verbose()) {
+ DiagMessage msg;
+ msg << "encoding PNG ";
+ if (ninePatch) {
+ msg << "(with 9-patch) as ";
}
-
- // Automatically release PNG resources at end of scope.
- PngWriteStructDeleter pngWriteDeleter(writePtr, writeInfoPtr);
-
- // libpng uses longjmp to jump to error handling routines.
- // setjmp will return true only if it was jumped to, aka, there was an error.
- if (setjmp(png_jmpbuf(writePtr))) {
- return false;
+ switch (newColorType) {
+ case PNG_COLOR_TYPE_GRAY:
+ msg << "GRAY";
+ break;
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ msg << "GRAY + ALPHA";
+ break;
+ case PNG_COLOR_TYPE_RGB:
+ msg << "RGB";
+ break;
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ msg << "RGBA";
+ break;
+ case PNG_COLOR_TYPE_PALETTE:
+ msg << "PALETTE";
+ break;
+ default:
+ msg << "unknown type " << newColorType;
+ break;
}
+ context->getDiagnostics()->note(msg);
+ }
- // Handle warnings with our IDiagnostics.
- png_set_error_fn(writePtr, (png_voidp) context->getDiagnostics(), logError, logWarning);
+ png_set_IHDR(writePtr, writeInfoPtr, image->width, image->height, 8,
+ newColorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
- // Set up the write functions which write to our custom data sources.
- png_set_write_fn(writePtr, (png_voidp) out, writeDataToStream, nullptr);
+ if (newColorType & PNG_COLOR_MASK_PALETTE) {
+ // Assigns indices to the palette, and writes the encoded palette to the
+ // libpng writePtr.
+ writePalette(writePtr, writeInfoPtr, &colorPalette, &alphaPalette);
+ png_set_filter(writePtr, 0, PNG_NO_FILTERS);
+ } else {
+ png_set_filter(writePtr, 0, PNG_ALL_FILTERS);
+ }
- // We want small files and can take the performance hit to achieve this goal.
- png_set_compression_level(writePtr, Z_BEST_COMPRESSION);
+ if (ninePatch) {
+ writeNinePatch(writePtr, writeInfoPtr, ninePatch);
+ }
- // Begin analysis of the image data.
- // Scan the entire image and determine if:
- // 1. Every pixel has R == G == B (grayscale)
- // 2. Every pixel has A == 255 (opaque)
- // 3. There are no more than 256 distinct RGBA colors (palette).
- std::unordered_map<uint32_t, int> colorPalette;
- std::unordered_set<uint32_t> alphaPalette;
- bool needsToZeroRGBChannelsOfTransparentPixels = false;
- bool grayScale = true;
- int maxGrayDeviation = 0;
+ // Flush our updates to the header.
+ png_write_info(writePtr, writeInfoPtr);
+
+ // Write out each row of image data according to its encoding.
+ if (newColorType == PNG_COLOR_TYPE_PALETTE) {
+ // 1 byte/pixel.
+ auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width]);
for (int32_t y = 0; y < image->height; y++) {
- const uint8_t* row = image->rows[y];
- for (int32_t x = 0; x < image->width; x++) {
- int red = *row++;
- int green = *row++;
- int blue = *row++;
- int alpha = *row++;
-
- if (alpha == 0) {
- // The color is completely transparent.
- // For purposes of palettes and grayscale optimization,
- // treat all channels as 0x00.
- needsToZeroRGBChannelsOfTransparentPixels =
- needsToZeroRGBChannelsOfTransparentPixels ||
- (red != 0 || green != 0 || blue != 0);
- red = green = blue = 0;
- }
-
- // Insert the color into the color palette.
- const uint32_t color = red << 24 | green << 16 | blue << 8 | alpha;
- colorPalette[color] = -1;
-
- // If the pixel has non-opaque alpha, insert it into the
- // alpha palette.
- if (alpha != 0xff) {
- alphaPalette.insert(color);
- }
-
- // Check if the image is indeed grayscale.
- if (grayScale) {
- if (red != green || red != blue) {
- grayScale = false;
- }
- }
-
- // Calculate the gray scale deviation so that it can be compared
- // with the threshold.
- maxGrayDeviation = std::max(std::abs(red - green), maxGrayDeviation);
- maxGrayDeviation = std::max(std::abs(green - blue), maxGrayDeviation);
- maxGrayDeviation = std::max(std::abs(blue - red), maxGrayDeviation);
+ png_const_bytep inRow = image->rows[y];
+ for (int32_t x = 0; x < image->width; x++) {
+ int rr = *inRow++;
+ int gg = *inRow++;
+ int bb = *inRow++;
+ int aa = *inRow++;
+ if (aa == 0) {
+ // Zero out color channels when transparent.
+ rr = gg = bb = 0;
}
+
+ const uint32_t color = rr << 24 | gg << 16 | bb << 8 | aa;
+ const int idx = colorPalette[color];
+ assert(idx != -1);
+ outRow[x] = static_cast<png_byte>(idx);
+ }
+ png_write_row(writePtr, outRow.get());
}
+ } else if (newColorType == PNG_COLOR_TYPE_GRAY ||
+ newColorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ const size_t bpp = newColorType == PNG_COLOR_TYPE_GRAY ? 1 : 2;
+ auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
- if (context->verbose()) {
- DiagMessage msg;
- msg << " paletteSize=" << colorPalette.size()
- << " alphaPaletteSize=" << alphaPalette.size()
- << " maxGrayDeviation=" << maxGrayDeviation
- << " grayScale=" << (grayScale ? "true" : "false");
- context->getDiagnostics()->note(msg);
- }
-
- const bool convertibleToGrayScale = maxGrayDeviation <= options.grayScaleTolerance;
-
- const int newColorType = pickColorType(image->width, image->height, grayScale,
- convertibleToGrayScale, ninePatch != nullptr,
- colorPalette.size(), alphaPalette.size());
-
- if (context->verbose()) {
- DiagMessage msg;
- msg << "encoding PNG ";
- if (ninePatch) {
- msg << "(with 9-patch) as ";
+ for (int32_t y = 0; y < image->height; y++) {
+ png_const_bytep inRow = image->rows[y];
+ for (int32_t x = 0; x < image->width; x++) {
+ int rr = inRow[x * 4];
+ int gg = inRow[x * 4 + 1];
+ int bb = inRow[x * 4 + 2];
+ int aa = inRow[x * 4 + 3];
+ if (aa == 0) {
+ // Zero out the gray channel when transparent.
+ rr = gg = bb = 0;
}
- switch (newColorType) {
- case PNG_COLOR_TYPE_GRAY:
- msg << "GRAY";
- break;
- case PNG_COLOR_TYPE_GRAY_ALPHA:
- msg << "GRAY + ALPHA";
- break;
- case PNG_COLOR_TYPE_RGB:
- msg << "RGB";
- break;
- case PNG_COLOR_TYPE_RGB_ALPHA:
- msg << "RGBA";
- break;
- case PNG_COLOR_TYPE_PALETTE:
- msg << "PALETTE";
- break;
- default:
- msg << "unknown type " << newColorType;
- break;
- }
- context->getDiagnostics()->note(msg);
- }
- png_set_IHDR(writePtr, writeInfoPtr, image->width, image->height, 8, newColorType,
- PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
-
- if (newColorType & PNG_COLOR_MASK_PALETTE) {
- // Assigns indices to the palette, and writes the encoded palette to the libpng writePtr.
- writePalette(writePtr, writeInfoPtr, &colorPalette, &alphaPalette);
- png_set_filter(writePtr, 0, PNG_NO_FILTERS);
- } else {
- png_set_filter(writePtr, 0, PNG_ALL_FILTERS);
- }
-
- if (ninePatch) {
- writeNinePatch(writePtr, writeInfoPtr, ninePatch);
- }
-
- // Flush our updates to the header.
- png_write_info(writePtr, writeInfoPtr);
-
- // Write out each row of image data according to its encoding.
- if (newColorType == PNG_COLOR_TYPE_PALETTE) {
- // 1 byte/pixel.
- auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width]);
-
- for (int32_t y = 0; y < image->height; y++) {
- png_const_bytep inRow = image->rows[y];
- for (int32_t x = 0; x < image->width; x++) {
- int rr = *inRow++;
- int gg = *inRow++;
- int bb = *inRow++;
- int aa = *inRow++;
- if (aa == 0) {
- // Zero out color channels when transparent.
- rr = gg = bb = 0;
- }
-
- const uint32_t color = rr << 24 | gg << 16 | bb << 8 | aa;
- const int idx = colorPalette[color];
- assert(idx != -1);
- outRow[x] = static_cast<png_byte>(idx);
- }
- png_write_row(writePtr, outRow.get());
- }
- } else if (newColorType == PNG_COLOR_TYPE_GRAY || newColorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
- const size_t bpp = newColorType == PNG_COLOR_TYPE_GRAY ? 1 : 2;
- auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
-
- for (int32_t y = 0; y < image->height; y++) {
- png_const_bytep inRow = image->rows[y];
- for (int32_t x = 0; x < image->width; x++) {
- int rr = inRow[x * 4];
- int gg = inRow[x * 4 + 1];
- int bb = inRow[x * 4 + 2];
- int aa = inRow[x * 4 + 3];
- if (aa == 0) {
- // Zero out the gray channel when transparent.
- rr = gg = bb = 0;
- }
-
- if (grayScale) {
- // The image was already grayscale, red == green == blue.
- outRow[x * bpp] = inRow[x * 4];
- } else {
- // The image is convertible to grayscale, use linear-luminance of
- // sRGB colorspace: https://en.wikipedia.org/wiki/Grayscale#Colorimetric_.28luminance-preserving.29_conversion_to_grayscale
- outRow[x * bpp] = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
- }
-
- if (bpp == 2) {
- // Write out alpha if we have it.
- outRow[x * bpp + 1] = aa;
- }
- }
- png_write_row(writePtr, outRow.get());
- }
- } else if (newColorType == PNG_COLOR_TYPE_RGB || newColorType == PNG_COLOR_TYPE_RGBA) {
- const size_t bpp = newColorType == PNG_COLOR_TYPE_RGB ? 3 : 4;
- if (needsToZeroRGBChannelsOfTransparentPixels) {
- // The source RGBA data can't be used as-is, because we need to zero out the RGB
- // values of transparent pixels.
- auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
-
- for (int32_t y = 0; y < image->height; y++) {
- png_const_bytep inRow = image->rows[y];
- for (int32_t x = 0; x < image->width; x++) {
- int rr = *inRow++;
- int gg = *inRow++;
- int bb = *inRow++;
- int aa = *inRow++;
- if (aa == 0) {
- // Zero out the RGB channels when transparent.
- rr = gg = bb = 0;
- }
- outRow[x * bpp] = rr;
- outRow[x * bpp + 1] = gg;
- outRow[x * bpp + 2] = bb;
- if (bpp == 4) {
- outRow[x * bpp + 3] = aa;
- }
- }
- png_write_row(writePtr, outRow.get());
- }
+ if (grayScale) {
+ // The image was already grayscale, red == green == blue.
+ outRow[x * bpp] = inRow[x * 4];
} else {
- // The source image can be used as-is, just tell libpng whether or not to ignore
- // the alpha channel.
- if (newColorType == PNG_COLOR_TYPE_RGB) {
- // Delete the extraneous alpha values that we appended to our buffer
- // when reading the original values.
- png_set_filler(writePtr, 0, PNG_FILLER_AFTER);
- }
- png_write_image(writePtr, image->rows.get());
+ // The image is convertible to grayscale, use linear-luminance of
+ // sRGB colorspace:
+ // https://en.wikipedia.org/wiki/Grayscale#Colorimetric_.28luminance-preserving.29_conversion_to_grayscale
+ outRow[x * bpp] =
+ (png_byte)(rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
}
- } else {
- assert(false && "unreachable");
- }
- png_write_end(writePtr, writeInfoPtr);
- return true;
+ if (bpp == 2) {
+ // Write out alpha if we have it.
+ outRow[x * bpp + 1] = aa;
+ }
+ }
+ png_write_row(writePtr, outRow.get());
+ }
+ } else if (newColorType == PNG_COLOR_TYPE_RGB ||
+ newColorType == PNG_COLOR_TYPE_RGBA) {
+ const size_t bpp = newColorType == PNG_COLOR_TYPE_RGB ? 3 : 4;
+ if (needsToZeroRGBChannelsOfTransparentPixels) {
+ // The source RGBA data can't be used as-is, because we need to zero out
+ // the RGB
+ // values of transparent pixels.
+ auto outRow =
+ std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
+
+ for (int32_t y = 0; y < image->height; y++) {
+ png_const_bytep inRow = image->rows[y];
+ for (int32_t x = 0; x < image->width; x++) {
+ int rr = *inRow++;
+ int gg = *inRow++;
+ int bb = *inRow++;
+ int aa = *inRow++;
+ if (aa == 0) {
+ // Zero out the RGB channels when transparent.
+ rr = gg = bb = 0;
+ }
+ outRow[x * bpp] = rr;
+ outRow[x * bpp + 1] = gg;
+ outRow[x * bpp + 2] = bb;
+ if (bpp == 4) {
+ outRow[x * bpp + 3] = aa;
+ }
+ }
+ png_write_row(writePtr, outRow.get());
+ }
+ } else {
+ // The source image can be used as-is, just tell libpng whether or not to
+ // ignore
+ // the alpha channel.
+ if (newColorType == PNG_COLOR_TYPE_RGB) {
+ // Delete the extraneous alpha values that we appended to our buffer
+ // when reading the original values.
+ png_set_filler(writePtr, 0, PNG_FILLER_AFTER);
+ }
+ png_write_image(writePtr, image->rows.get());
+ }
+ } else {
+ assert(false && "unreachable");
+ }
+
+ png_write_end(writePtr, writeInfoPtr);
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp
index 732101f..d8ed0bb 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -14,235 +14,242 @@
* limitations under the License.
*/
+#include "compile/PseudolocaleGenerator.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
-#include "compile/PseudolocaleGenerator.h"
#include "compile/Pseudolocalizer.h"
#include <algorithm>
namespace aapt {
-std::unique_ptr<StyledString> pseudolocalizeStyledString(StyledString* string,
- Pseudolocalizer::Method method,
- StringPool* pool) {
- Pseudolocalizer localizer(method);
+std::unique_ptr<StyledString> pseudolocalizeStyledString(
+ StyledString* string, Pseudolocalizer::Method method, StringPool* pool) {
+ Pseudolocalizer localizer(method);
- const StringPiece originalText = *string->value->str;
+ const StringPiece originalText = *string->value->str;
- StyleString localized;
+ StyleString localized;
- // Copy the spans. We will update their offsets when we localize.
- localized.spans.reserve(string->value->spans.size());
- for (const StringPool::Span& span : string->value->spans) {
- localized.spans.push_back(Span{ *span.name, span.firstChar, span.lastChar });
+ // Copy the spans. We will update their offsets when we localize.
+ localized.spans.reserve(string->value->spans.size());
+ for (const StringPool::Span& span : string->value->spans) {
+ localized.spans.push_back(Span{*span.name, span.firstChar, span.lastChar});
+ }
+
+ // The ranges are all represented with a single value. This is the start of
+ // one range and
+ // end of another.
+ struct Range {
+ size_t start;
+
+ // Once the new string is localized, these are the pointers to the spans to
+ // adjust.
+ // Since this struct represents the start of one range and end of another,
+ // we have
+ // the two pointers respectively.
+ uint32_t* updateStart;
+ uint32_t* updateEnd;
+ };
+
+ auto cmp = [](const Range& r, size_t index) -> bool {
+ return r.start < index;
+ };
+
+ // Construct the ranges. The ranges are represented like so: [0, 2, 5, 7]
+ // The ranges are the spaces in between. In this example, with a total string
+ // length of 9,
+ // the vector represents: (0,1], (2,4], (5,6], (7,9]
+ //
+ std::vector<Range> ranges;
+ ranges.push_back(Range{0});
+ ranges.push_back(Range{originalText.size() - 1});
+ for (size_t i = 0; i < string->value->spans.size(); i++) {
+ const StringPool::Span& span = string->value->spans[i];
+
+ // Insert or update the Range marker for the start of this span.
+ auto iter =
+ std::lower_bound(ranges.begin(), ranges.end(), span.firstChar, cmp);
+ if (iter != ranges.end() && iter->start == span.firstChar) {
+ iter->updateStart = &localized.spans[i].firstChar;
+ } else {
+ ranges.insert(
+ iter, Range{span.firstChar, &localized.spans[i].firstChar, nullptr});
}
- // The ranges are all represented with a single value. This is the start of one range and
- // end of another.
- struct Range {
- size_t start;
+ // Insert or update the Range marker for the end of this span.
+ iter = std::lower_bound(ranges.begin(), ranges.end(), span.lastChar, cmp);
+ if (iter != ranges.end() && iter->start == span.lastChar) {
+ iter->updateEnd = &localized.spans[i].lastChar;
+ } else {
+ ranges.insert(
+ iter, Range{span.lastChar, nullptr, &localized.spans[i].lastChar});
+ }
+ }
- // Once the new string is localized, these are the pointers to the spans to adjust.
- // Since this struct represents the start of one range and end of another, we have
- // the two pointers respectively.
- uint32_t* updateStart;
- uint32_t* updateEnd;
- };
+ localized.str += localizer.start();
- auto cmp = [](const Range& r, size_t index) -> bool {
- return r.start < index;
- };
-
- // Construct the ranges. The ranges are represented like so: [0, 2, 5, 7]
- // The ranges are the spaces in between. In this example, with a total string length of 9,
- // the vector represents: (0,1], (2,4], (5,6], (7,9]
- //
- std::vector<Range> ranges;
- ranges.push_back(Range{ 0 });
- ranges.push_back(Range{ originalText.size() - 1 });
- for (size_t i = 0; i < string->value->spans.size(); i++) {
- const StringPool::Span& span = string->value->spans[i];
-
- // Insert or update the Range marker for the start of this span.
- auto iter = std::lower_bound(ranges.begin(), ranges.end(), span.firstChar, cmp);
- if (iter != ranges.end() && iter->start == span.firstChar) {
- iter->updateStart = &localized.spans[i].firstChar;
- } else {
- ranges.insert(iter,
- Range{ span.firstChar, &localized.spans[i].firstChar, nullptr });
- }
-
- // Insert or update the Range marker for the end of this span.
- iter = std::lower_bound(ranges.begin(), ranges.end(), span.lastChar, cmp);
- if (iter != ranges.end() && iter->start == span.lastChar) {
- iter->updateEnd = &localized.spans[i].lastChar;
- } else {
- ranges.insert(iter,
- Range{ span.lastChar, nullptr, &localized.spans[i].lastChar });
- }
+ // Iterate over the ranges and localize each section.
+ for (size_t i = 0; i < ranges.size(); i++) {
+ const size_t start = ranges[i].start;
+ size_t len = originalText.size() - start;
+ if (i + 1 < ranges.size()) {
+ len = ranges[i + 1].start - start;
}
- localized.str += localizer.start();
-
- // Iterate over the ranges and localize each section.
- for (size_t i = 0; i < ranges.size(); i++) {
- const size_t start = ranges[i].start;
- size_t len = originalText.size() - start;
- if (i + 1 < ranges.size()) {
- len = ranges[i + 1].start - start;
- }
-
- if (ranges[i].updateStart) {
- *ranges[i].updateStart = localized.str.size();
- }
-
- if (ranges[i].updateEnd) {
- *ranges[i].updateEnd = localized.str.size();
- }
-
- localized.str += localizer.text(originalText.substr(start, len));
+ if (ranges[i].updateStart) {
+ *ranges[i].updateStart = localized.str.size();
}
- localized.str += localizer.end();
+ if (ranges[i].updateEnd) {
+ *ranges[i].updateEnd = localized.str.size();
+ }
- std::unique_ptr<StyledString> localizedString = util::make_unique<StyledString>(
- pool->makeRef(localized));
- localizedString->setSource(string->getSource());
- return localizedString;
+ localized.str += localizer.text(originalText.substr(start, len));
+ }
+
+ localized.str += localizer.end();
+
+ std::unique_ptr<StyledString> localizedString =
+ util::make_unique<StyledString>(pool->makeRef(localized));
+ localizedString->setSource(string->getSource());
+ return localizedString;
}
namespace {
struct Visitor : public RawValueVisitor {
- StringPool* mPool;
- Pseudolocalizer::Method mMethod;
- Pseudolocalizer mLocalizer;
+ StringPool* mPool;
+ Pseudolocalizer::Method mMethod;
+ Pseudolocalizer mLocalizer;
- // Either value or item will be populated upon visiting the value.
- std::unique_ptr<Value> mValue;
- std::unique_ptr<Item> mItem;
+ // Either value or item will be populated upon visiting the value.
+ std::unique_ptr<Value> mValue;
+ std::unique_ptr<Item> mItem;
- Visitor(StringPool* pool, Pseudolocalizer::Method method) :
- mPool(pool), mMethod(method), mLocalizer(method) {
- }
+ Visitor(StringPool* pool, Pseudolocalizer::Method method)
+ : mPool(pool), mMethod(method), mLocalizer(method) {}
- void visit(Plural* plural) override {
- std::unique_ptr<Plural> localized = util::make_unique<Plural>();
- for (size_t i = 0; i < plural->values.size(); i++) {
- Visitor subVisitor(mPool, mMethod);
- if (plural->values[i]) {
- plural->values[i]->accept(&subVisitor);
- if (subVisitor.mValue) {
- localized->values[i] = std::move(subVisitor.mItem);
- } else {
- localized->values[i] = std::unique_ptr<Item>(plural->values[i]->clone(mPool));
- }
- }
+ void visit(Plural* plural) override {
+ std::unique_ptr<Plural> localized = util::make_unique<Plural>();
+ for (size_t i = 0; i < plural->values.size(); i++) {
+ Visitor subVisitor(mPool, mMethod);
+ if (plural->values[i]) {
+ plural->values[i]->accept(&subVisitor);
+ if (subVisitor.mValue) {
+ localized->values[i] = std::move(subVisitor.mItem);
+ } else {
+ localized->values[i] =
+ std::unique_ptr<Item>(plural->values[i]->clone(mPool));
}
- localized->setSource(plural->getSource());
- localized->setWeak(true);
- mValue = std::move(localized);
+ }
}
+ localized->setSource(plural->getSource());
+ localized->setWeak(true);
+ mValue = std::move(localized);
+ }
- void visit(String* string) override {
- 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());
- localized->setWeak(true);
- mItem = std::move(localized);
- }
+ void visit(String* string) override {
+ 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());
+ localized->setWeak(true);
+ mItem = std::move(localized);
+ }
- void visit(StyledString* string) override {
- mItem = pseudolocalizeStyledString(string, mMethod, mPool);
- mItem->setWeak(true);
- }
+ void visit(StyledString* string) override {
+ mItem = pseudolocalizeStyledString(string, mMethod, mPool);
+ mItem->setWeak(true);
+ }
};
ConfigDescription modifyConfigForPseudoLocale(const ConfigDescription& base,
Pseudolocalizer::Method m) {
- ConfigDescription modified = base;
- switch (m) {
+ ConfigDescription modified = base;
+ switch (m) {
case Pseudolocalizer::Method::kAccent:
- modified.language[0] = 'e';
- modified.language[1] = 'n';
- modified.country[0] = 'X';
- modified.country[1] = 'A';
- break;
+ modified.language[0] = 'e';
+ modified.language[1] = 'n';
+ modified.country[0] = 'X';
+ modified.country[1] = 'A';
+ break;
case Pseudolocalizer::Method::kBidi:
- modified.language[0] = 'a';
- modified.language[1] = 'r';
- modified.country[0] = 'X';
- modified.country[1] = 'B';
- break;
+ modified.language[0] = 'a';
+ modified.language[1] = 'r';
+ modified.country[0] = 'X';
+ modified.country[1] = 'B';
+ break;
default:
- break;
- }
- return modified;
+ break;
+ }
+ return modified;
}
void pseudolocalizeIfNeeded(const Pseudolocalizer::Method method,
ResourceConfigValue* originalValue,
- StringPool* pool,
- ResourceEntry* entry) {
- Visitor visitor(pool, method);
- originalValue->value->accept(&visitor);
+ StringPool* pool, ResourceEntry* entry) {
+ Visitor visitor(pool, method);
+ originalValue->value->accept(&visitor);
- std::unique_ptr<Value> localizedValue;
- if (visitor.mValue) {
- localizedValue = std::move(visitor.mValue);
- } else if (visitor.mItem) {
- localizedValue = std::move(visitor.mItem);
- }
+ std::unique_ptr<Value> localizedValue;
+ if (visitor.mValue) {
+ localizedValue = std::move(visitor.mValue);
+ } else if (visitor.mItem) {
+ localizedValue = std::move(visitor.mItem);
+ }
- if (!localizedValue) {
- return;
- }
+ if (!localizedValue) {
+ return;
+ }
- ConfigDescription configWithAccent = modifyConfigForPseudoLocale(
- originalValue->config, method);
+ ConfigDescription configWithAccent =
+ modifyConfigForPseudoLocale(originalValue->config, method);
- ResourceConfigValue* newConfigValue = entry->findOrCreateValue(
- configWithAccent, originalValue->product);
- if (!newConfigValue->value) {
- // Only use auto-generated pseudo-localization if none is defined.
- newConfigValue->value = std::move(localizedValue);
- }
+ ResourceConfigValue* newConfigValue =
+ entry->findOrCreateValue(configWithAccent, originalValue->product);
+ if (!newConfigValue->value) {
+ // Only use auto-generated pseudo-localization if none is defined.
+ newConfigValue->value = std::move(localizedValue);
+ }
}
/**
- * A value is pseudolocalizable if it does not define a locale (or is the default locale)
+ * A value is pseudolocalizable if it does not define a locale (or is the
+ * default locale)
* and is translateable.
*/
static bool isPseudolocalizable(ResourceConfigValue* configValue) {
- const int diff = configValue->config.diff(ConfigDescription::defaultConfig());
- if (diff & ConfigDescription::CONFIG_LOCALE) {
- return false;
- }
- return configValue->value->isTranslateable();
+ const int diff = configValue->config.diff(ConfigDescription::defaultConfig());
+ if (diff & ConfigDescription::CONFIG_LOCALE) {
+ return false;
+ }
+ return configValue->value->isTranslateable();
}
-} // namespace
+} // namespace
-bool PseudolocaleGenerator::consume(IAaptContext* context, ResourceTable* table) {
- for (auto& package : table->packages) {
- for (auto& type : package->types) {
- for (auto& entry : type->entries) {
- std::vector<ResourceConfigValue*> values = entry->findValuesIf(isPseudolocalizable);
+bool PseudolocaleGenerator::consume(IAaptContext* context,
+ ResourceTable* table) {
+ for (auto& package : table->packages) {
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
+ std::vector<ResourceConfigValue*> values =
+ entry->findValuesIf(isPseudolocalizable);
- for (ResourceConfigValue* value : values) {
- pseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value,
- &table->stringPool, entry.get());
- pseudolocalizeIfNeeded(Pseudolocalizer::Method::kBidi, value,
- &table->stringPool, entry.get());
- }
- }
+ for (ResourceConfigValue* value : values) {
+ pseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value,
+ &table->stringPool, entry.get());
+ pseudolocalizeIfNeeded(Pseudolocalizer::Method::kBidi, value,
+ &table->stringPool, entry.get());
}
+ }
}
- return true;
+ }
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.h b/tools/aapt2/compile/PseudolocaleGenerator.h
index 4fbc516..4e97cb9 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.h
+++ b/tools/aapt2/compile/PseudolocaleGenerator.h
@@ -23,14 +23,13 @@
namespace aapt {
-std::unique_ptr<StyledString> pseudolocalizeStyledString(StyledString* string,
- Pseudolocalizer::Method method,
- StringPool* pool);
+std::unique_ptr<StyledString> pseudolocalizeStyledString(
+ StyledString* string, Pseudolocalizer::Method method, StringPool* pool);
struct PseudolocaleGenerator : public IResourceTableConsumer {
- bool consume(IAaptContext* context, ResourceTable* table) override;
+ bool consume(IAaptContext* context, ResourceTable* table) override;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_COMPILE_PSEUDOLOCALEGENERATOR_H */
diff --git a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
index 1f62f90..64a3e97 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
@@ -23,99 +23,110 @@
namespace aapt {
TEST(PseudolocaleGeneratorTest, PseudolocalizeStyledString) {
- StringPool pool;
- StyleString originalStyle;
- originalStyle.str = "Hello world!";
- originalStyle.spans = { Span{ "b", 2, 3 }, Span{ "b", 6, 7 }, Span{ "i", 1, 10 } };
+ StringPool pool;
+ StyleString originalStyle;
+ 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(),
- Pseudolocalizer::Method::kNone, &pool);
+ std::unique_ptr<StyledString> newString = pseudolocalizeStyledString(
+ util::make_unique<StyledString>(pool.makeRef(originalStyle)).get(),
+ Pseudolocalizer::Method::kNone, &pool);
- EXPECT_EQ(originalStyle.str, *newString->value->str);
- ASSERT_EQ(originalStyle.spans.size(), newString->value->spans.size());
+ EXPECT_EQ(originalStyle.str, *newString->value->str);
+ ASSERT_EQ(originalStyle.spans.size(), newString->value->spans.size());
- 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(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(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(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(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);
+ 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{ "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);
+ newString = pseudolocalizeStyledString(
+ util::make_unique<StyledString>(pool.makeRef(originalStyle)).get(),
+ Pseudolocalizer::Method::kAccent, &pool);
- EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð¡ one two]"), *newString->value->str);
- ASSERT_EQ(originalStyle.spans.size(), newString->value->spans.size());
+ EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð¡ one two]"), *newString->value->str);
+ ASSERT_EQ(originalStyle.spans.size(), newString->value->spans.size());
- EXPECT_EQ(std::string("[Ĥé").size(), newString->value->spans[0].firstChar);
- EXPECT_EQ(std::string("[Ĥéļ").size(), 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(std::string("[Ĥéļļö ").size(), newString->value->spans[1].firstChar);
- EXPECT_EQ(std::string("[Ĥéļļö ŵ").size(), 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(std::string("[Ĥ").size(), newString->value->spans[2].firstChar);
- EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļ").size(), 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(std::string("[").size(), newString->value->spans[3].firstChar);
- EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð").size(), 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("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();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .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(), "android:string/four");
- ASSERT_NE(nullptr, val);
- val->setTranslateable(false);
+ String* val = test::getValue<String>(table.get(), "android:string/four");
+ ASSERT_NE(nullptr, val);
+ val->setTranslateable(false);
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- PseudolocaleGenerator generator;
- ASSERT_TRUE(generator.consume(context.get(), table.get()));
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ PseudolocaleGenerator generator;
+ ASSERT_TRUE(generator.consume(context.get(), table.get()));
- // Normal pseudolocalization should take place.
- ASSERT_NE(nullptr, test::getValueForConfig<String>(table.get(), "android:string/one",
- test::parseConfigOrDie("en-rXA")));
- ASSERT_NE(nullptr, test::getValueForConfig<String>(table.get(), "android:string/one",
- test::parseConfigOrDie("ar-rXB")));
+ // Normal pseudolocalization should take place.
+ ASSERT_NE(nullptr,
+ test::getValueForConfig<String>(table.get(), "android:string/one",
+ test::parseConfigOrDie("en-rXA")));
+ 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(), "android:string/two",
- test::parseConfigOrDie("en-rXA")));
- ASSERT_EQ(nullptr, test::getValueForConfig<String>(table.get(), "android:string/two",
- test::parseConfigOrDie("ar-rXB")));
+ // No default config for android:string/two, so no pseudlocales should exist.
+ ASSERT_EQ(nullptr,
+ test::getValueForConfig<String>(table.get(), "android:string/two",
+ test::parseConfigOrDie("en-rXA")));
+ 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(), "android:string/three",
+ test::parseConfigOrDie("en-rXA"));
+ ASSERT_NE(nullptr, val);
+ EXPECT_EQ(std::string("three"), *val->value);
- // Check that we didn't override manual pseudolocalization.
- val = test::getValueForConfig<String>(table.get(), "android:string/three",
- test::parseConfigOrDie("en-rXA"));
- ASSERT_NE(nullptr, val);
- EXPECT_EQ(std::string("three"), *val->value);
+ ASSERT_NE(nullptr,
+ test::getValueForConfig<String>(table.get(), "android:string/three",
+ test::parseConfigOrDie("ar-rXB")));
- 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(), "android:string/four",
- test::parseConfigOrDie("en-rXA")));
- ASSERT_EQ(nullptr, test::getValueForConfig<String>(table.get(), "android:string/four",
- test::parseConfigOrDie("ar-rXB")));
-
+ // Check that four's translateable marker was honored.
+ ASSERT_EQ(nullptr,
+ test::getValueForConfig<String>(table.get(), "android:string/four",
+ test::parseConfigOrDie("en-rXA")));
+ ASSERT_EQ(nullptr,
+ test::getValueForConfig<String>(table.get(), "android:string/four",
+ test::parseConfigOrDie("ar-rXB")));
}
-} // namespace aapt
-
+} // namespace aapt
diff --git a/tools/aapt2/compile/Pseudolocalizer.cpp b/tools/aapt2/compile/Pseudolocalizer.cpp
index 90d0d85..c3aec98 100644
--- a/tools/aapt2/compile/Pseudolocalizer.cpp
+++ b/tools/aapt2/compile/Pseudolocalizer.cpp
@@ -20,9 +20,10 @@
namespace aapt {
// String basis to generate expansion
-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";
+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::string k_rlm = "\u200f";
@@ -37,229 +38,310 @@
static const char k_arg_end = '}';
class PseudoMethodNone : public PseudoMethodImpl {
-public:
- std::string text(const StringPiece& text) override { return text.toString(); }
- std::string placeholder(const StringPiece& text) override { return text.toString(); }
+ public:
+ 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::string text(const StringPiece& text) override;
- std::string placeholder(const StringPiece& text) override;
+ public:
+ 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::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;
- size_t mLength;
+ public:
+ PseudoMethodAccent() : mDepth(0), mWordCount(0), mLength(0) {}
+ 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;
+ size_t mLength;
};
Pseudolocalizer::Pseudolocalizer(Method method) : mLastDepth(0) {
- setMethod(method);
+ setMethod(method);
}
void Pseudolocalizer::setMethod(Method method) {
- switch (method) {
+ switch (method) {
case Method::kNone:
- mImpl = util::make_unique<PseudoMethodNone>();
- break;
+ mImpl = util::make_unique<PseudoMethodNone>();
+ break;
case Method::kAccent:
- mImpl = util::make_unique<PseudoMethodAccent>();
- break;
+ mImpl = util::make_unique<PseudoMethodAccent>();
+ break;
case Method::kBidi:
- mImpl = util::make_unique<PseudoMethodBidi>();
- break;
- }
+ mImpl = util::make_unique<PseudoMethodBidi>();
+ break;
+ }
}
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 char* str = text.data();
- bool escaped = false;
- for (lastpos = pos = 0; pos < length; pos++) {
- char16_t c = str[pos];
- if (escaped) {
- escaped = false;
- continue;
- }
- if (c == '\'') {
- escaped = true;
- continue;
- }
-
- if (c == k_arg_start) {
- depth++;
- } else if (c == k_arg_end && depth) {
- depth--;
- }
-
- if (mLastDepth != depth || pos == length - 1) {
- bool pseudo = ((mLastDepth % 2) == 0);
- size_t nextpos = pos;
- if (!pseudo || depth == mLastDepth) {
- nextpos++;
- }
- size_t size = nextpos - lastpos;
- if (size) {
- 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) {
- chunk = mImpl->placeholder(chunk);
- }
- out.append(chunk);
- }
- if (pseudo && depth < mLastDepth) { // End of message
- out.append(mImpl->end());
- } else if (!pseudo && depth > mLastDepth) { // Start of message
- out.append(mImpl->start());
- }
- lastpos = nextpos;
- mLastDepth = depth;
- }
+ std::string out;
+ size_t depth = mLastDepth;
+ size_t lastpos, pos;
+ const size_t length = text.size();
+ const char* str = text.data();
+ bool escaped = false;
+ for (lastpos = pos = 0; pos < length; pos++) {
+ char16_t c = str[pos];
+ if (escaped) {
+ escaped = false;
+ continue;
}
- return out;
+ if (c == '\'') {
+ escaped = true;
+ continue;
+ }
+
+ if (c == k_arg_start) {
+ depth++;
+ } else if (c == k_arg_end && depth) {
+ depth--;
+ }
+
+ if (mLastDepth != depth || pos == length - 1) {
+ bool pseudo = ((mLastDepth % 2) == 0);
+ size_t nextpos = pos;
+ if (!pseudo || depth == mLastDepth) {
+ nextpos++;
+ }
+ size_t size = nextpos - lastpos;
+ if (size) {
+ 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) {
+ chunk = mImpl->placeholder(chunk);
+ }
+ out.append(chunk);
+ }
+ if (pseudo && depth < mLastDepth) { // End of message
+ out.append(mImpl->end());
+ } else if (!pseudo && depth > mLastDepth) { // Start of message
+ out.append(mImpl->start());
+ }
+ lastpos = nextpos;
+ mLastDepth = depth;
+ }
+ }
+ return out;
}
static const char* pseudolocalizeChar(const char c) {
- switch (c) {
- 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;
- }
+ switch (c) {
+ 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 char c) {
- switch (c) {
- case 's': return true;
- case 'S': return true;
- case 'c': return true;
- case 'C': return true;
- case 'd': return true;
- case 'o': return true;
- case 'x': return true;
- case 'X': return true;
- case 'f': return true;
- case 'e': return true;
- case 'E': return true;
- case 'g': return true;
- case 'G': return true;
- case 'a': return true;
- case 'A': return true;
- case 'b': return true;
- case 'B': return true;
- case 'h': return true;
- case 'H': return true;
- case '%': return true;
- case 'n': return true;
- default: return false;
- }
+ switch (c) {
+ case 's':
+ return true;
+ case 'S':
+ return true;
+ case 'c':
+ return true;
+ case 'C':
+ return true;
+ case 'd':
+ return true;
+ case 'o':
+ return true;
+ case 'x':
+ return true;
+ case 'X':
+ return true;
+ case 'f':
+ return true;
+ case 'e':
+ return true;
+ case 'E':
+ return true;
+ case 'g':
+ return true;
+ case 'G':
+ return true;
+ case 'a':
+ return true;
+ case 'A':
+ return true;
+ case 'b':
+ return true;
+ case 'B':
+ return true;
+ case 'h':
+ return true;
+ case 'H':
+ return true;
+ case '%':
+ return true;
+ case 'n':
+ return true;
+ default:
+ return false;
+ }
}
static std::string pseudoGenerateExpansion(const unsigned int length) {
- std::string result = k_expansion_string;
- const char* s = result.data();
- if (result.size() < length) {
- result += " ";
- result += pseudoGenerateExpansion(length - result.size());
- } else {
- int ext = 0;
- // Should contain only whole words, so looking for a space
- for (unsigned int i = length + 1; i < result.size(); ++i) {
- ++ext;
- if (s[i] == ' ') {
- break;
- }
- }
- result = result.substr(0, length + ext);
+ std::string result = k_expansion_string;
+ const char* s = result.data();
+ if (result.size() < length) {
+ result += " ";
+ result += pseudoGenerateExpansion(length - result.size());
+ } else {
+ int ext = 0;
+ // Should contain only whole words, so looking for a space
+ for (unsigned int i = length + 1; i < result.size(); ++i) {
+ ++ext;
+ if (s[i] == ' ') {
+ break;
+ }
}
- return result;
+ result = result.substr(0, length + ext);
+ }
+ return result;
}
std::string PseudoMethodAccent::start() {
- std::string result;
- if (mDepth == 0) {
- result = "[";
- }
- mWordCount = mLength = 0;
- mDepth++;
- return result;
+ std::string result;
+ if (mDepth == 0) {
+ result = "[";
+ }
+ mWordCount = mLength = 0;
+ mDepth++;
+ return result;
}
std::string PseudoMethodAccent::end() {
- std::string result;
- if (mLength) {
- result += " ";
- result += pseudoGenerateExpansion(mWordCount > 3 ? mLength : mLength / 2);
- }
- mWordCount = mLength = 0;
- mDepth--;
- if (mDepth == 0) {
- result += "]";
- }
- return result;
+ std::string result;
+ if (mLength) {
+ result += " ";
+ result += pseudoGenerateExpansion(mWordCount > 3 ? mLength : mLength / 2);
+ }
+ mWordCount = mLength = 0;
+ mDepth--;
+ if (mDepth == 0) {
+ result += "]";
+ }
+ return result;
}
/**
@@ -267,128 +349,125 @@
*
* Note: This leaves placeholder syntax untouched.
*/
-std::string PseudoMethodAccent::text(const StringPiece& source)
-{
- 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++) {
- char c = s[i];
- if (c == '%') {
- // Placeholder syntax, no need to pseudolocalize
- std::string chunk;
- bool end = false;
- chunk.append(&c, 1);
- while (!end && i + 1 < I) {
- ++i;
- c = s[i];
- chunk.append(&c, 1);
- if (isPossibleNormalPlaceholderEnd(c)) {
- end = true;
- } else if (i + 1 < I && c == 't') {
- ++i;
- c = s[i];
- chunk.append(&c, 1);
- end = true;
- }
- }
- // Treat chunk as a placeholder unless it ends with %.
- result += ((c == '%') ? chunk : placeholder(chunk));
- } else if (c == '<' || c == '&') {
- // html syntax, no need to pseudolocalize
- bool tag_closed = false;
- while (!tag_closed && i < I) {
- if (c == '&') {
- std::string escapeText;
- escapeText.append(&c, 1);
- bool end = false;
- size_t htmlCodePos = i;
- while (!end && htmlCodePos < I) {
- ++htmlCodePos;
- c = s[htmlCodePos];
- escapeText.append(&c, 1);
- // Valid html code
- if (c == ';') {
- end = true;
- i = htmlCodePos;
- }
- // Wrong html code
- else if (!((c == '#' ||
- (c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') ||
- (c >= '0' && c <= '9')))) {
- end = true;
- }
- }
- result += escapeText;
- if (escapeText != "<") {
- tag_closed = true;
- }
- continue;
- }
- if (c == '>') {
- tag_closed = true;
- result.append(&c, 1);
- continue;
- }
- result.append(&c, 1);
- i++;
- c = s[i];
- }
- } else {
- // This is a pure text that should be pseudolocalized
- const char* p = pseudolocalizeChar(c);
- if (p != nullptr) {
- result += p;
- } else {
- bool space = isspace(c);
- if (lastspace && !space) {
- mWordCount++;
- }
- lastspace = space;
- result.append(&c, 1);
- }
- // Count only pseudolocalizable chars and delimiters
- mLength++;
+std::string PseudoMethodAccent::text(const StringPiece& source) {
+ 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++) {
+ char c = s[i];
+ if (c == '%') {
+ // Placeholder syntax, no need to pseudolocalize
+ std::string chunk;
+ bool end = false;
+ chunk.append(&c, 1);
+ while (!end && i + 1 < I) {
+ ++i;
+ c = s[i];
+ chunk.append(&c, 1);
+ if (isPossibleNormalPlaceholderEnd(c)) {
+ end = true;
+ } else if (i + 1 < I && c == 't') {
+ ++i;
+ c = s[i];
+ chunk.append(&c, 1);
+ end = true;
}
- }
- return result;
-}
-
-std::string PseudoMethodAccent::placeholder(const StringPiece& source) {
- // Surround a placeholder with brackets
- return k_placeholder_open + source.toString() + k_placeholder_close;
-}
-
-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++) {
- char c = s[i];
- space = isspace(c);
+ }
+ // Treat chunk as a placeholder unless it ends with %.
+ result += ((c == '%') ? chunk : placeholder(chunk));
+ } else if (c == '<' || c == '&') {
+ // html syntax, no need to pseudolocalize
+ bool tag_closed = false;
+ while (!tag_closed && i < I) {
+ if (c == '&') {
+ std::string escapeText;
+ escapeText.append(&c, 1);
+ bool end = false;
+ size_t htmlCodePos = i;
+ while (!end && htmlCodePos < I) {
+ ++htmlCodePos;
+ c = s[htmlCodePos];
+ escapeText.append(&c, 1);
+ // Valid html code
+ if (c == ';') {
+ end = true;
+ i = htmlCodePos;
+ }
+ // Wrong html code
+ else if (!((c == '#' || (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')))) {
+ end = true;
+ }
+ }
+ result += escapeText;
+ if (escapeText != "<") {
+ tag_closed = true;
+ }
+ continue;
+ }
+ if (c == '>') {
+ tag_closed = true;
+ result.append(&c, 1);
+ continue;
+ }
+ result.append(&c, 1);
+ i++;
+ c = s[i];
+ }
+ } else {
+ // This is a pure text that should be pseudolocalized
+ const char* p = pseudolocalizeChar(c);
+ if (p != nullptr) {
+ result += p;
+ } else {
+ bool space = isspace(c);
if (lastspace && !space) {
- // Word start
- result += k_rlm + k_rlo;
- } else if (!lastspace && space) {
- // Word end
- result += k_pdf + k_rlm;
+ mWordCount++;
}
lastspace = space;
result.append(&c, 1);
+ }
+ // Count only pseudolocalizable chars and delimiters
+ mLength++;
}
- if (!lastspace) {
- // End of last word
- result += k_pdf + k_rlm;
+ }
+ return result;
+}
+
+std::string PseudoMethodAccent::placeholder(const StringPiece& source) {
+ // Surround a placeholder with brackets
+ return k_placeholder_open + source.toString() + k_placeholder_close;
+}
+
+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++) {
+ char c = s[i];
+ space = isspace(c);
+ if (lastspace && !space) {
+ // Word start
+ result += k_rlm + k_rlo;
+ } else if (!lastspace && space) {
+ // Word end
+ result += k_pdf + k_rlm;
}
- return result;
+ lastspace = space;
+ result.append(&c, 1);
+ }
+ if (!lastspace) {
+ // End of last word
+ result += k_pdf + k_rlm;
+ }
+ return result;
}
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;
+ // Surround a placeholder with directionality change sequence
+ return k_rlm + k_rlo + source.toString() + k_pdf + k_rlm;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/Pseudolocalizer.h b/tools/aapt2/compile/Pseudolocalizer.h
index 91d17d1..a526877 100644
--- a/tools/aapt2/compile/Pseudolocalizer.h
+++ b/tools/aapt2/compile/Pseudolocalizer.h
@@ -27,32 +27,33 @@
namespace aapt {
class PseudoMethodImpl {
-public:
- virtual ~PseudoMethodImpl() {}
- 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;
+ public:
+ virtual ~PseudoMethodImpl() {}
+ 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 {
-public:
- enum class Method {
- kNone,
- kAccent,
- kBidi,
- };
+ public:
+ enum class Method {
+ kNone,
+ kAccent,
+ kBidi,
+ };
- explicit Pseudolocalizer(Method method);
- void setMethod(Method method);
- 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;
+ explicit Pseudolocalizer(Method method);
+ void setMethod(Method method);
+ 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;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_COMPILE_PSEUDOLOCALIZE_H */
diff --git a/tools/aapt2/compile/Pseudolocalizer_test.cpp b/tools/aapt2/compile/Pseudolocalizer_test.cpp
index c33e152..a152ed6 100644
--- a/tools/aapt2/compile/Pseudolocalizer_test.cpp
+++ b/tools/aapt2/compile/Pseudolocalizer_test.cpp
@@ -25,199 +25,207 @@
// In this context, 'Axis' represents a particular field in the configuration,
// such as language or density.
-static ::testing::AssertionResult simpleHelper(const char* input, const char* expected,
+static ::testing::AssertionResult simpleHelper(const char* input,
+ const char* expected,
Pseudolocalizer::Method method) {
- Pseudolocalizer pseudo(method);
- std::string result = pseudo.start() + pseudo.text(input) + pseudo.end();
- if (result != expected) {
- return ::testing::AssertionFailure() << expected << " != " << result;
- }
- return ::testing::AssertionSuccess();
+ Pseudolocalizer pseudo(method);
+ std::string result = pseudo.start() + pseudo.text(input) + pseudo.end();
+ if (result != expected) {
+ return ::testing::AssertionFailure() << expected << " != " << result;
+ }
+ return ::testing::AssertionSuccess();
}
-static ::testing::AssertionResult compoundHelper(const char* in1, const char* in2, const char *in3,
- const char* expected,
- Pseudolocalizer::Method method) {
- Pseudolocalizer pseudo(method);
- 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();
+static ::testing::AssertionResult compoundHelper(
+ const char* in1, const char* in2, const char* in3, const char* expected,
+ Pseudolocalizer::Method method) {
+ Pseudolocalizer pseudo(method);
+ 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();
}
TEST(PseudolocalizerTest, NoPseudolocalization) {
- EXPECT_TRUE(simpleHelper("", "", Pseudolocalizer::Method::kNone));
- EXPECT_TRUE(simpleHelper("Hello, world", "Hello, world", Pseudolocalizer::Method::kNone));
+ EXPECT_TRUE(simpleHelper("", "", Pseudolocalizer::Method::kNone));
+ EXPECT_TRUE(simpleHelper("Hello, world", "Hello, world",
+ Pseudolocalizer::Method::kNone));
- EXPECT_TRUE(compoundHelper("Hello,", " world", "",
- "Hello, world", Pseudolocalizer::Method::kNone));
+ EXPECT_TRUE(compoundHelper("Hello,", " world", "", "Hello, world",
+ Pseudolocalizer::Method::kNone));
}
TEST(PseudolocalizerTest, PlaintextAccent) {
- EXPECT_TRUE(simpleHelper("", "[]", Pseudolocalizer::Method::kAccent));
- EXPECT_TRUE(simpleHelper("Hello, world",
- "[Ĥéļļö, ŵöŕļð one two]", Pseudolocalizer::Method::kAccent));
+ EXPECT_TRUE(simpleHelper("", "[]", Pseudolocalizer::Method::kAccent));
+ EXPECT_TRUE(simpleHelper("Hello, world", "[Ĥéļļö, ŵöŕļð one two]",
+ Pseudolocalizer::Method::kAccent));
- EXPECT_TRUE(simpleHelper("Hello, %1d",
- "[Ĥéļļö, »%1d« one two]", Pseudolocalizer::Method::kAccent));
+ EXPECT_TRUE(simpleHelper("Hello, %1d", "[Ĥéļļö, »%1d« one two]",
+ Pseudolocalizer::Method::kAccent));
- 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));
+ 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));
}
TEST(PseudolocalizerTest, PlaintextBidi) {
- EXPECT_TRUE(simpleHelper("", "", Pseudolocalizer::Method::kBidi));
- EXPECT_TRUE(simpleHelper("word",
- "\xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f",
- Pseudolocalizer::Method::kBidi));
- EXPECT_TRUE(simpleHelper(" word ",
- " \xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f ",
- Pseudolocalizer::Method::kBidi));
- EXPECT_TRUE(simpleHelper(" word ",
- " \xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f ",
- Pseudolocalizer::Method::kBidi));
- EXPECT_TRUE(simpleHelper("hello\n world\n",
- "\xe2\x80\x8f\xE2\x80\xaehello\xE2\x80\xac\xe2\x80\x8f\n" \
- " \xe2\x80\x8f\xE2\x80\xaeworld\xE2\x80\xac\xe2\x80\x8f\n",
- Pseudolocalizer::Method::kBidi));
- EXPECT_TRUE(compoundHelper("hello", "\n ", " world\n",
- "\xe2\x80\x8f\xE2\x80\xaehello\xE2\x80\xac\xe2\x80\x8f\n" \
- " \xe2\x80\x8f\xE2\x80\xaeworld\xE2\x80\xac\xe2\x80\x8f\n",
- Pseudolocalizer::Method::kBidi));
+ EXPECT_TRUE(simpleHelper("", "", Pseudolocalizer::Method::kBidi));
+ EXPECT_TRUE(simpleHelper(
+ "word", "\xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f",
+ Pseudolocalizer::Method::kBidi));
+ EXPECT_TRUE(simpleHelper(
+ " word ", " \xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f ",
+ Pseudolocalizer::Method::kBidi));
+ EXPECT_TRUE(simpleHelper(
+ " word ", " \xe2\x80\x8f\xE2\x80\xaeword\xE2\x80\xac\xe2\x80\x8f ",
+ Pseudolocalizer::Method::kBidi));
+ EXPECT_TRUE(
+ simpleHelper("hello\n world\n",
+ "\xe2\x80\x8f\xE2\x80\xaehello\xE2\x80\xac\xe2\x80\x8f\n"
+ " \xe2\x80\x8f\xE2\x80\xaeworld\xE2\x80\xac\xe2\x80\x8f\n",
+ Pseudolocalizer::Method::kBidi));
+ EXPECT_TRUE(compoundHelper(
+ "hello", "\n ", " world\n",
+ "\xe2\x80\x8f\xE2\x80\xaehello\xE2\x80\xac\xe2\x80\x8f\n"
+ " \xe2\x80\x8f\xE2\x80\xaeworld\xE2\x80\xac\xe2\x80\x8f\n",
+ Pseudolocalizer::Method::kBidi));
}
TEST(PseudolocalizerTest, SimpleICU) {
- // Single-fragment messages
- EXPECT_TRUE(simpleHelper("{placeholder}", "[»{placeholder}«]",
+ // Single-fragment messages
+ EXPECT_TRUE(simpleHelper("{placeholder}", "[»{placeholder}«]",
+ Pseudolocalizer::Method::kAccent));
+ EXPECT_TRUE(simpleHelper("{USER} is offline", "[»{USER}« îš öƒƒļîñé one two]",
+ Pseudolocalizer::Method::kAccent));
+ EXPECT_TRUE(simpleHelper("Copy from {path1} to {path2}",
+ "[Çöþý ƒŕöḿ »{path1}« ţö »{path2}« one two three]",
+ Pseudolocalizer::Method::kAccent));
+ EXPECT_TRUE(simpleHelper("Today is {1,date} {1,time}",
+ "[Ţöðåý îš »{1,date}« »{1,time}« one two]",
+ Pseudolocalizer::Method::kAccent));
+
+ // Multi-fragment messages
+ EXPECT_TRUE(compoundHelper("{USER}", " ", "is offline",
+ "[»{USER}« îš öƒƒļîñé one two]",
Pseudolocalizer::Method::kAccent));
- EXPECT_TRUE(simpleHelper("{USER} is offline",
- "[»{USER}« îš öƒƒļîñé one two]", Pseudolocalizer::Method::kAccent));
- EXPECT_TRUE(simpleHelper("Copy from {path1} to {path2}",
+ EXPECT_TRUE(compoundHelper("Copy from ", "{path1}", " to {path2}",
"[Çöþý ƒŕöḿ »{path1}« ţö »{path2}« one two three]",
Pseudolocalizer::Method::kAccent));
- EXPECT_TRUE(simpleHelper("Today is {1,date} {1,time}",
- "[Ţöðåý îš »{1,date}« »{1,time}« one two]",
- Pseudolocalizer::Method::kAccent));
-
- // Multi-fragment messages
- EXPECT_TRUE(compoundHelper("{USER}", " ", "is offline",
- "[»{USER}« îš öƒƒļîñé one two]",
- Pseudolocalizer::Method::kAccent));
- EXPECT_TRUE(compoundHelper("Copy from ", "{path1}", " to {path2}",
- "[Çöþý ƒŕöḿ »{path1}« ţö »{path2}« one two three]",
- Pseudolocalizer::Method::kAccent));
}
TEST(PseudolocalizerTest, ICUBidi) {
- // Single-fragment messages
- EXPECT_TRUE(simpleHelper("{placeholder}",
- "\xe2\x80\x8f\xE2\x80\xae{placeholder}\xE2\x80\xac\xe2\x80\x8f",
- Pseudolocalizer::Method::kBidi));
- EXPECT_TRUE(simpleHelper(
- "{COUNT, plural, one {one} other {other}}",
- "{COUNT, plural, " \
- "one {\xe2\x80\x8f\xE2\x80\xaeone\xE2\x80\xac\xe2\x80\x8f} " \
- "other {\xe2\x80\x8f\xE2\x80\xaeother\xE2\x80\xac\xe2\x80\x8f}}",
- Pseudolocalizer::Method::kBidi));
+ // Single-fragment messages
+ EXPECT_TRUE(simpleHelper(
+ "{placeholder}",
+ "\xe2\x80\x8f\xE2\x80\xae{placeholder}\xE2\x80\xac\xe2\x80\x8f",
+ Pseudolocalizer::Method::kBidi));
+ EXPECT_TRUE(simpleHelper(
+ "{COUNT, plural, one {one} other {other}}",
+ "{COUNT, plural, "
+ "one {\xe2\x80\x8f\xE2\x80\xaeone\xE2\x80\xac\xe2\x80\x8f} "
+ "other {\xe2\x80\x8f\xE2\x80\xaeother\xE2\x80\xac\xe2\x80\x8f}}",
+ Pseudolocalizer::Method::kBidi));
}
TEST(PseudolocalizerTest, Escaping) {
- // Single-fragment messages
- EXPECT_TRUE(simpleHelper("'{USER'} is offline",
- "['{ÛŠÉŔ'} îš öƒƒļîñé one two three]",
- Pseudolocalizer::Method::kAccent));
+ // Single-fragment messages
+ EXPECT_TRUE(simpleHelper("'{USER'} is offline",
+ "['{ÛŠÉŔ'} îš öƒƒļîñé one two three]",
+ Pseudolocalizer::Method::kAccent));
- // Multi-fragment messages
- EXPECT_TRUE(compoundHelper("'{USER}", " ", "''is offline",
- "['{ÛŠÉŔ} ''îš öƒƒļîñé one two three]",
- Pseudolocalizer::Method::kAccent));
+ // Multi-fragment messages
+ EXPECT_TRUE(compoundHelper("'{USER}", " ", "''is offline",
+ "['{ÛŠÉŔ} ''îš öƒƒļîñé one two three]",
+ Pseudolocalizer::Method::kAccent));
}
TEST(PseudolocalizerTest, PluralsAndSelects) {
- EXPECT_TRUE(simpleHelper(
- "{COUNT, plural, one {Delete a file} other {Delete {COUNT} files}}",
- "[{COUNT, plural, one {Ðéļéţé å ƒîļé one two} " \
+ EXPECT_TRUE(simpleHelper(
+ "{COUNT, plural, one {Delete a file} other {Delete {COUNT} files}}",
+ "[{COUNT, plural, one {Ðéļéţé å ƒîļé one two} "
+ "other {Ðéļéţé »{COUNT}« ƒîļéš one two}}]",
+ Pseudolocalizer::Method::kAccent));
+
+ EXPECT_TRUE(
+ simpleHelper("Distance is {COUNT, plural, one {# mile} other {# miles}}",
+ "[Ðîšţåñçé îš {COUNT, plural, one {# ḿîļé one two} "
+ "other {# ḿîļéš one two}}]",
+ Pseudolocalizer::Method::kAccent));
+
+ EXPECT_TRUE(simpleHelper(
+ "{1, select, female {{1} added you} "
+ "male {{1} added you} other {{1} added you}}",
+ "[{1, select, female {»{1}« åððéð ýöû one two} "
+ "male {»{1}« åððéð ýöû one two} other {»{1}« åððéð ýöû one two}}]",
+ Pseudolocalizer::Method::kAccent));
+
+ EXPECT_TRUE(
+ compoundHelper("{COUNT, plural, one {Delete a file} "
+ "other {Delete ",
+ "{COUNT}", " files}}",
+ "[{COUNT, plural, one {Ðéļéţé å ƒîļé one two} "
"other {Ðéļéţé »{COUNT}« ƒîļéš one two}}]",
- Pseudolocalizer::Method::kAccent));
-
- EXPECT_TRUE(simpleHelper(
- "Distance is {COUNT, plural, one {# mile} other {# miles}}",
- "[Ðîšţåñçé îš {COUNT, plural, one {# ḿîļé one two} " \
- "other {# ḿîļéš one two}}]",
- Pseudolocalizer::Method::kAccent));
-
- EXPECT_TRUE(simpleHelper(
- "{1, select, female {{1} added you} " \
- "male {{1} added you} other {{1} added you}}",
- "[{1, select, female {»{1}« åððéð ýöû one two} " \
- "male {»{1}« åððéð ýöû one two} other {»{1}« åððéð ýöû one two}}]",
- Pseudolocalizer::Method::kAccent));
-
- EXPECT_TRUE(compoundHelper(
- "{COUNT, plural, one {Delete a file} " \
- "other {Delete ", "{COUNT}", " files}}",
- "[{COUNT, plural, one {Ðéļéţé å ƒîļé one two} " \
- "other {Ðéļéţé »{COUNT}« ƒîļéš one two}}]",
- Pseudolocalizer::Method::kAccent));
+ Pseudolocalizer::Method::kAccent));
}
TEST(PseudolocalizerTest, NestedICU) {
- EXPECT_TRUE(simpleHelper(
- "{person, select, " \
- "female {" \
- "{num_circles, plural," \
- "=0{{person} didn't add you to any of her circles.}" \
- "=1{{person} added you to one of her circles.}" \
- "other{{person} added you to her # circles.}}}" \
- "male {" \
- "{num_circles, plural," \
- "=0{{person} didn't add you to any of his circles.}" \
- "=1{{person} added you to one of his circles.}" \
- "other{{person} added you to his # circles.}}}" \
- "other {" \
- "{num_circles, plural," \
- "=0{{person} didn't add you to any of their circles.}" \
- "=1{{person} added you to one of their circles.}" \
- "other{{person} added you to their # circles.}}}}",
- "[{person, select, " \
- "female {" \
- "{num_circles, plural," \
- "=0{»{person}« ðîðñ'ţ åðð ýöû ţö åñý öƒ ĥéŕ çîŕçļéš." \
- " one two three four five}" \
- "=1{»{person}« åððéð ýöû ţö öñé öƒ ĥéŕ çîŕçļéš." \
- " one two three four}" \
- "other{»{person}« åððéð ýöû ţö ĥéŕ # çîŕçļéš." \
- " one two three four}}}" \
- "male {" \
- "{num_circles, plural," \
- "=0{»{person}« ðîðñ'ţ åðð ýöû ţö åñý öƒ ĥîš çîŕçļéš." \
- " one two three four five}" \
- "=1{»{person}« åððéð ýöû ţö öñé öƒ ĥîš çîŕçļéš." \
- " one two three four}" \
- "other{»{person}« åððéð ýöû ţö ĥîš # çîŕçļéš." \
- " one two three four}}}" \
- "other {{num_circles, plural," \
- "=0{»{person}« ðîðñ'ţ åðð ýöû ţö åñý öƒ ţĥéîŕ çîŕçļéš." \
- " one two three four five}" \
- "=1{»{person}« åððéð ýöû ţö öñé öƒ ţĥéîŕ çîŕçļéš." \
- " one two three four}" \
- "other{»{person}« åððéð ýöû ţö ţĥéîŕ # çîŕçļéš." \
- " one two three four}}}}]",
- Pseudolocalizer::Method::kAccent));
+ EXPECT_TRUE(
+ simpleHelper("{person, select, "
+ "female {"
+ "{num_circles, plural,"
+ "=0{{person} didn't add you to any of her circles.}"
+ "=1{{person} added you to one of her circles.}"
+ "other{{person} added you to her # circles.}}}"
+ "male {"
+ "{num_circles, plural,"
+ "=0{{person} didn't add you to any of his circles.}"
+ "=1{{person} added you to one of his circles.}"
+ "other{{person} added you to his # circles.}}}"
+ "other {"
+ "{num_circles, plural,"
+ "=0{{person} didn't add you to any of their circles.}"
+ "=1{{person} added you to one of their circles.}"
+ "other{{person} added you to their # circles.}}}}",
+ "[{person, select, "
+ "female {"
+ "{num_circles, plural,"
+ "=0{»{person}« ðîðñ'ţ åðð ýöû ţö åñý öƒ ĥéŕ çîŕçļéš."
+ " one two three four five}"
+ "=1{»{person}« åððéð ýöû ţö öñé öƒ ĥéŕ çîŕçļéš."
+ " one two three four}"
+ "other{»{person}« åððéð ýöû ţö ĥéŕ # çîŕçļéš."
+ " one two three four}}}"
+ "male {"
+ "{num_circles, plural,"
+ "=0{»{person}« ðîðñ'ţ åðð ýöû ţö åñý öƒ ĥîš çîŕçļéš."
+ " one two three four five}"
+ "=1{»{person}« åððéð ýöû ţö öñé öƒ ĥîš çîŕçļéš."
+ " one two three four}"
+ "other{»{person}« åððéð ýöû ţö ĥîš # çîŕçļéš."
+ " one two three four}}}"
+ "other {{num_circles, plural,"
+ "=0{»{person}« ðîðñ'ţ åðð ýöû ţö åñý öƒ ţĥéîŕ çîŕçļéš."
+ " one two three four five}"
+ "=1{»{person}« åððéð ýöû ţö öñé öƒ ţĥéîŕ çîŕçļéš."
+ " one two three four}"
+ "other{»{person}« åððéð ýöû ţö ţĥéîŕ # çîŕçļéš."
+ " one two three four}}}}]",
+ Pseudolocalizer::Method::kAccent));
}
TEST(PseudolocalizerTest, RedefineMethod) {
- Pseudolocalizer pseudo(Pseudolocalizer::Method::kAccent);
- std::string result = pseudo.text("Hello, ");
- pseudo.setMethod(Pseudolocalizer::Method::kNone);
- result += pseudo.text("world!");
- ASSERT_EQ(StringPiece("Ĥéļļö, world!"), result);
+ Pseudolocalizer pseudo(Pseudolocalizer::Method::kAccent);
+ std::string result = pseudo.text("Hello, ");
+ pseudo.setMethod(Pseudolocalizer::Method::kNone);
+ result += pseudo.text("world!");
+ ASSERT_EQ(StringPiece("Ĥéļļö, world!"), result);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/XmlIdCollector.cpp b/tools/aapt2/compile/XmlIdCollector.cpp
index 3901419..aa8b1df 100644
--- a/tools/aapt2/compile/XmlIdCollector.cpp
+++ b/tools/aapt2/compile/XmlIdCollector.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
+#include "compile/XmlIdCollector.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
-#include "compile/XmlIdCollector.h"
#include "xml/XmlDom.h"
#include <algorithm>
@@ -27,44 +27,44 @@
namespace {
static bool cmpName(const SourcedResourceName& a, const ResourceNameRef& b) {
- return a.name < b;
+ return a.name < b;
}
struct IdCollector : public xml::Visitor {
- using xml::Visitor::visit;
+ using xml::Visitor::visit;
- std::vector<SourcedResourceName>* mOutSymbols;
+ std::vector<SourcedResourceName>* mOutSymbols;
- explicit IdCollector(std::vector<SourcedResourceName>* outSymbols) : mOutSymbols(outSymbols) {
- }
+ explicit IdCollector(std::vector<SourcedResourceName>* outSymbols)
+ : mOutSymbols(outSymbols) {}
- void visit(xml::Element* element) override {
- for (xml::Attribute& attr : element->attributes) {
- ResourceNameRef name;
- bool create = false;
- if (ResourceUtils::parseReference(attr.value, &name, &create, nullptr)) {
- if (create && name.type == ResourceType::kId) {
- auto iter = std::lower_bound(mOutSymbols->begin(), mOutSymbols->end(),
- name, cmpName);
- if (iter == mOutSymbols->end() || iter->name != name) {
- mOutSymbols->insert(iter, SourcedResourceName{ name.toResourceName(),
- element->lineNumber });
- }
- }
- }
+ void visit(xml::Element* element) override {
+ for (xml::Attribute& attr : element->attributes) {
+ ResourceNameRef name;
+ bool create = false;
+ if (ResourceUtils::parseReference(attr.value, &name, &create, nullptr)) {
+ if (create && name.type == ResourceType::kId) {
+ auto iter = std::lower_bound(mOutSymbols->begin(), mOutSymbols->end(),
+ name, cmpName);
+ if (iter == mOutSymbols->end() || iter->name != name) {
+ mOutSymbols->insert(iter, SourcedResourceName{name.toResourceName(),
+ element->lineNumber});
+ }
}
-
- xml::Visitor::visit(element);
+ }
}
+
+ xml::Visitor::visit(element);
+ }
};
-} // namespace
+} // namespace
bool XmlIdCollector::consume(IAaptContext* context, xml::XmlResource* xmlRes) {
- xmlRes->file.exportedSymbols.clear();
- IdCollector collector(&xmlRes->file.exportedSymbols);
- xmlRes->root->accept(&collector);
- return true;
+ xmlRes->file.exportedSymbols.clear();
+ IdCollector collector(&xmlRes->file.exportedSymbols);
+ xmlRes->root->accept(&collector);
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/compile/XmlIdCollector.h b/tools/aapt2/compile/XmlIdCollector.h
index 1b14944..8423f48 100644
--- a/tools/aapt2/compile/XmlIdCollector.h
+++ b/tools/aapt2/compile/XmlIdCollector.h
@@ -23,9 +23,9 @@
namespace aapt {
struct XmlIdCollector : public IXmlResourceConsumer {
- bool consume(IAaptContext* context, xml::XmlResource* xmlRes) override;
+ bool consume(IAaptContext* context, xml::XmlResource* xmlRes) override;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_XMLIDCOLLECTOR_H */
diff --git a/tools/aapt2/compile/XmlIdCollector_test.cpp b/tools/aapt2/compile/XmlIdCollector_test.cpp
index 2c9eab8..08ca7b1 100644
--- a/tools/aapt2/compile/XmlIdCollector_test.cpp
+++ b/tools/aapt2/compile/XmlIdCollector_test.cpp
@@ -18,15 +18,15 @@
#include "test/Builders.h"
#include "test/Context.h"
-#include <algorithm>
#include <gtest/gtest.h>
+#include <algorithm>
namespace aapt {
TEST(XmlIdCollectorTest, CollectsIds) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/foo"
text="@+id/bar">
@@ -34,28 +34,35 @@
class="@+id/bar"/>
</View>)EOF");
- XmlIdCollector collector;
- ASSERT_TRUE(collector.consume(context.get(), doc.get()));
+ XmlIdCollector collector;
+ ASSERT_TRUE(collector.consume(context.get(), doc.get()));
- EXPECT_EQ(1, std::count(doc->file.exportedSymbols.begin(), doc->file.exportedSymbols.end(),
- SourcedResourceName{ test::parseNameOrDie("id/foo"), 3u }));
+ EXPECT_EQ(
+ 1, std::count(doc->file.exportedSymbols.begin(),
+ doc->file.exportedSymbols.end(),
+ SourcedResourceName{test::parseNameOrDie("id/foo"), 3u}));
- EXPECT_EQ(1, std::count(doc->file.exportedSymbols.begin(), doc->file.exportedSymbols.end(),
- SourcedResourceName{ test::parseNameOrDie("id/bar"), 3u }));
+ EXPECT_EQ(
+ 1, std::count(doc->file.exportedSymbols.begin(),
+ doc->file.exportedSymbols.end(),
+ SourcedResourceName{test::parseNameOrDie("id/bar"), 3u}));
- EXPECT_EQ(1, std::count(doc->file.exportedSymbols.begin(), doc->file.exportedSymbols.end(),
- SourcedResourceName{ test::parseNameOrDie("id/car"), 6u }));
+ EXPECT_EQ(
+ 1, std::count(doc->file.exportedSymbols.begin(),
+ doc->file.exportedSymbols.end(),
+ SourcedResourceName{test::parseNameOrDie("id/car"), 6u}));
}
TEST(XmlIdCollectorTest, DontCollectNonIds) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom("<View foo=\"@+string/foo\"/>");
+ std::unique_ptr<xml::XmlResource> doc =
+ test::buildXmlDom("<View foo=\"@+string/foo\"/>");
- XmlIdCollector collector;
- ASSERT_TRUE(collector.consume(context.get(), doc.get()));
+ XmlIdCollector collector;
+ ASSERT_TRUE(collector.consume(context.get(), doc.get()));
- EXPECT_TRUE(doc->file.exportedSymbols.empty());
+ EXPECT_TRUE(doc->file.exportedSymbols.empty());
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/diff/Diff.cpp b/tools/aapt2/diff/Diff.cpp
index 9b1f057..01f4539 100644
--- a/tools/aapt2/diff/Diff.cpp
+++ b/tools/aapt2/diff/Diff.cpp
@@ -27,412 +27,401 @@
namespace aapt {
class DiffContext : public IAaptContext {
-public:
- const std::string& getCompilationPackage() override {
- return mEmpty;
- }
+ public:
+ const std::string& getCompilationPackage() override { return mEmpty; }
- uint8_t getPackageId() override {
- return 0x0;
- }
+ uint8_t getPackageId() override { return 0x0; }
- IDiagnostics* getDiagnostics() override {
- return &mDiagnostics;
- }
+ IDiagnostics* getDiagnostics() override { return &mDiagnostics; }
- NameMangler* getNameMangler() override {
- return &mNameMangler;
- }
+ NameMangler* getNameMangler() override { return &mNameMangler; }
- SymbolTable* getExternalSymbols() override {
- return &mSymbolTable;
- }
+ SymbolTable* getExternalSymbols() override { return &mSymbolTable; }
- bool verbose() override {
- return false;
- }
+ bool verbose() override { return false; }
- int getMinSdkVersion() override {
- return 0;
- }
+ int getMinSdkVersion() override { return 0; }
-private:
- std::string mEmpty;
- StdErrDiagnostics mDiagnostics;
- NameMangler mNameMangler = NameMangler(NameManglerPolicy{});
- SymbolTable mSymbolTable;
+ private:
+ std::string mEmpty;
+ StdErrDiagnostics mDiagnostics;
+ NameMangler mNameMangler = NameMangler(NameManglerPolicy{});
+ SymbolTable mSymbolTable;
};
class LoadedApk {
-public:
- LoadedApk(const Source& source, std::unique_ptr<io::IFileCollection> apk,
- std::unique_ptr<ResourceTable> table) :
- mSource(source), mApk(std::move(apk)), mTable(std::move(table)) {
- }
+ public:
+ LoadedApk(const Source& source, std::unique_ptr<io::IFileCollection> apk,
+ std::unique_ptr<ResourceTable> table)
+ : mSource(source), mApk(std::move(apk)), mTable(std::move(table)) {}
- io::IFileCollection* getFileCollection() {
- return mApk.get();
- }
+ io::IFileCollection* getFileCollection() { return mApk.get(); }
- ResourceTable* getResourceTable() {
- return mTable.get();
- }
+ ResourceTable* getResourceTable() { return mTable.get(); }
- const Source& getSource() {
- return mSource;
- }
+ const Source& getSource() { return mSource; }
-private:
- Source mSource;
- std::unique_ptr<io::IFileCollection> mApk;
- std::unique_ptr<ResourceTable> mTable;
+ private:
+ Source mSource;
+ std::unique_ptr<io::IFileCollection> mApk;
+ std::unique_ptr<ResourceTable> mTable;
- DISALLOW_COPY_AND_ASSIGN(LoadedApk);
+ DISALLOW_COPY_AND_ASSIGN(LoadedApk);
};
-static std::unique_ptr<LoadedApk> loadApkFromPath(IAaptContext* context, const StringPiece& path) {
- Source source(path);
- std::string error;
- std::unique_ptr<io::ZipFileCollection> apk = io::ZipFileCollection::create(path, &error);
- if (!apk) {
- context->getDiagnostics()->error(DiagMessage(source) << error);
- return {};
- }
+static std::unique_ptr<LoadedApk> loadApkFromPath(IAaptContext* context,
+ const StringPiece& path) {
+ Source source(path);
+ std::string error;
+ std::unique_ptr<io::ZipFileCollection> apk =
+ io::ZipFileCollection::create(path, &error);
+ if (!apk) {
+ context->getDiagnostics()->error(DiagMessage(source) << error);
+ return {};
+ }
- io::IFile* file = apk->findFile("resources.arsc");
- if (!file) {
- context->getDiagnostics()->error(DiagMessage(source) << "no resources.arsc found");
- return {};
- }
+ io::IFile* file = apk->findFile("resources.arsc");
+ if (!file) {
+ context->getDiagnostics()->error(DiagMessage(source)
+ << "no resources.arsc found");
+ return {};
+ }
- std::unique_ptr<io::IData> data = file->openAsData();
- if (!data) {
- context->getDiagnostics()->error(DiagMessage(source) << "could not open resources.arsc");
- return {};
- }
+ std::unique_ptr<io::IData> data = file->openAsData();
+ if (!data) {
+ context->getDiagnostics()->error(DiagMessage(source)
+ << "could not open resources.arsc");
+ return {};
+ }
- std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
- BinaryResourceParser parser(context, table.get(), source, data->data(), data->size());
- if (!parser.parse()) {
- return {};
- }
+ std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
+ BinaryResourceParser parser(context, table.get(), source, data->data(),
+ data->size());
+ if (!parser.parse()) {
+ return {};
+ }
- return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table));
+ return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table));
}
static void emitDiffLine(const Source& source, const StringPiece& message) {
- std::cerr << source << ": " << message << "\n";
+ std::cerr << source << ": " << message << "\n";
}
-static bool isSymbolVisibilityDifferent(const Symbol& symbolA, const Symbol& symbolB) {
- return symbolA.state != symbolB.state;
+static bool isSymbolVisibilityDifferent(const Symbol& symbolA,
+ const Symbol& symbolB) {
+ return symbolA.state != symbolB.state;
}
template <typename Id>
static bool isIdDiff(const Symbol& symbolA, const Maybe<Id>& idA,
const Symbol& symbolB, const Maybe<Id>& idB) {
- if (symbolA.state == SymbolState::kPublic || symbolB.state == SymbolState::kPublic) {
- return idA != idB;
- }
- return false;
+ if (symbolA.state == SymbolState::kPublic ||
+ symbolB.state == SymbolState::kPublic) {
+ return idA != idB;
+ }
+ return false;
}
-static bool emitResourceConfigValueDiff(IAaptContext* context,
- LoadedApk* apkA,
- ResourceTablePackage* pkgA,
- ResourceTableType* typeA,
- ResourceEntry* entryA,
- ResourceConfigValue* configValueA,
- LoadedApk* apkB,
- ResourceTablePackage* pkgB,
- ResourceTableType* typeB,
- ResourceEntry* entryB,
- ResourceConfigValue* configValueB) {
- Value* valueA = configValueA->value.get();
- Value* valueB = configValueB->value.get();
- if (!valueA->equals(valueB)) {
- std::stringstream strStream;
- strStream << "value " << pkgA->name << ":" << typeA->type << "/" << entryA->name
- << " config=" << configValueA->config << " does not match:\n";
- valueA->print(&strStream);
- strStream << "\n vs \n";
- valueB->print(&strStream);
- emitDiffLine(apkB->getSource(), strStream.str());
- return true;
- }
- return false;
+static bool emitResourceConfigValueDiff(
+ IAaptContext* context, LoadedApk* apkA, ResourceTablePackage* pkgA,
+ ResourceTableType* typeA, ResourceEntry* entryA,
+ ResourceConfigValue* configValueA, LoadedApk* apkB,
+ ResourceTablePackage* pkgB, ResourceTableType* typeB, ResourceEntry* entryB,
+ ResourceConfigValue* configValueB) {
+ Value* valueA = configValueA->value.get();
+ Value* valueB = configValueB->value.get();
+ if (!valueA->equals(valueB)) {
+ std::stringstream strStream;
+ strStream << "value " << pkgA->name << ":" << typeA->type << "/"
+ << entryA->name << " config=" << configValueA->config
+ << " does not match:\n";
+ valueA->print(&strStream);
+ strStream << "\n vs \n";
+ valueB->print(&strStream);
+ emitDiffLine(apkB->getSource(), strStream.str());
+ return true;
+ }
+ return false;
}
-static bool emitResourceEntryDiff(IAaptContext* context,
- LoadedApk* apkA,
+static bool emitResourceEntryDiff(IAaptContext* context, LoadedApk* apkA,
ResourceTablePackage* pkgA,
ResourceTableType* typeA,
- ResourceEntry* entryA,
- LoadedApk* apkB,
+ ResourceEntry* entryA, LoadedApk* apkB,
ResourceTablePackage* pkgB,
ResourceTableType* typeB,
ResourceEntry* entryB) {
- bool diff = false;
- for (std::unique_ptr<ResourceConfigValue>& configValueA : entryA->values) {
- ResourceConfigValue* configValueB = entryB->findValue(configValueA->config);
- if (!configValueB) {
- std::stringstream strStream;
- strStream << "missing " << pkgA->name << ":" << typeA->type << "/" << entryA->name
- << " config=" << configValueA->config;
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
- } else {
- diff |= emitResourceConfigValueDiff(context, apkA, pkgA, typeA, entryA,
- configValueA.get(), apkB, pkgB, typeB, entryB,
- configValueB);
- }
+ bool diff = false;
+ for (std::unique_ptr<ResourceConfigValue>& configValueA : entryA->values) {
+ ResourceConfigValue* configValueB = entryB->findValue(configValueA->config);
+ if (!configValueB) {
+ std::stringstream strStream;
+ strStream << "missing " << pkgA->name << ":" << typeA->type << "/"
+ << entryA->name << " config=" << configValueA->config;
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ } else {
+ diff |= emitResourceConfigValueDiff(context, apkA, pkgA, typeA, entryA,
+ configValueA.get(), apkB, pkgB, typeB,
+ entryB, configValueB);
}
+ }
- // Check for any newly added config values.
- for (std::unique_ptr<ResourceConfigValue>& configValueB : entryB->values) {
- ResourceConfigValue* configValueA = entryA->findValue(configValueB->config);
- if (!configValueA) {
- std::stringstream strStream;
- strStream << "new config " << pkgB->name << ":" << typeB->type << "/" << entryB->name
- << " config=" << configValueB->config;
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
- }
+ // Check for any newly added config values.
+ for (std::unique_ptr<ResourceConfigValue>& configValueB : entryB->values) {
+ ResourceConfigValue* configValueA = entryA->findValue(configValueB->config);
+ if (!configValueA) {
+ std::stringstream strStream;
+ strStream << "new config " << pkgB->name << ":" << typeB->type << "/"
+ << entryB->name << " config=" << configValueB->config;
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
}
- return false;
+ }
+ return false;
}
-static bool emitResourceTypeDiff(IAaptContext* context,
- LoadedApk* apkA,
+static bool emitResourceTypeDiff(IAaptContext* context, LoadedApk* apkA,
ResourceTablePackage* pkgA,
- ResourceTableType* typeA,
- LoadedApk* apkB,
+ ResourceTableType* typeA, LoadedApk* apkB,
ResourceTablePackage* pkgB,
ResourceTableType* typeB) {
- bool diff = false;
- for (std::unique_ptr<ResourceEntry>& entryA : typeA->entries) {
- ResourceEntry* entryB = typeB->findEntry(entryA->name);
- if (!entryB) {
- std::stringstream strStream;
- strStream << "missing " << pkgA->name << ":" << typeA->type << "/" << entryA->name;
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
+ bool diff = false;
+ for (std::unique_ptr<ResourceEntry>& entryA : typeA->entries) {
+ ResourceEntry* entryB = typeB->findEntry(entryA->name);
+ if (!entryB) {
+ std::stringstream strStream;
+ strStream << "missing " << pkgA->name << ":" << typeA->type << "/"
+ << entryA->name;
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ } else {
+ if (isSymbolVisibilityDifferent(entryA->symbolStatus,
+ entryB->symbolStatus)) {
+ std::stringstream strStream;
+ strStream << pkgA->name << ":" << typeA->type << "/" << entryA->name
+ << " has different visibility (";
+ if (entryB->symbolStatus.state == SymbolState::kPublic) {
+ strStream << "PUBLIC";
} else {
- if (isSymbolVisibilityDifferent(entryA->symbolStatus, entryB->symbolStatus)) {
- std::stringstream strStream;
- strStream << pkgA->name << ":" << typeA->type << "/" << entryA->name
- << " has different visibility (";
- if (entryB->symbolStatus.state == SymbolState::kPublic) {
- strStream << "PUBLIC";
- } else {
- strStream << "PRIVATE";
- }
- strStream << " vs ";
- if (entryA->symbolStatus.state == SymbolState::kPublic) {
- strStream << "PUBLIC";
- } else {
- strStream << "PRIVATE";
- }
- strStream << ")";
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
- } else if (isIdDiff(entryA->symbolStatus, entryA->id,
- entryB->symbolStatus, entryB->id)) {
- std::stringstream strStream;
- strStream << pkgA->name << ":" << typeA->type << "/" << entryA->name
- << " has different public ID (";
- if (entryB->id) {
- strStream << "0x" << std::hex << entryB->id.value();
- } else {
- strStream << "none";
- }
- strStream << " vs ";
- if (entryA->id) {
- strStream << "0x " << std::hex << entryA->id.value();
- } else {
- strStream << "none";
- }
- strStream << ")";
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
- }
- diff |= emitResourceEntryDiff(context, apkA, pkgA, typeA, entryA.get(),
- apkB, pkgB, typeB, entryB);
+ strStream << "PRIVATE";
}
+ strStream << " vs ";
+ if (entryA->symbolStatus.state == SymbolState::kPublic) {
+ strStream << "PUBLIC";
+ } else {
+ strStream << "PRIVATE";
+ }
+ strStream << ")";
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ } else if (isIdDiff(entryA->symbolStatus, entryA->id,
+ entryB->symbolStatus, entryB->id)) {
+ std::stringstream strStream;
+ strStream << pkgA->name << ":" << typeA->type << "/" << entryA->name
+ << " has different public ID (";
+ if (entryB->id) {
+ strStream << "0x" << std::hex << entryB->id.value();
+ } else {
+ strStream << "none";
+ }
+ strStream << " vs ";
+ if (entryA->id) {
+ strStream << "0x " << std::hex << entryA->id.value();
+ } else {
+ strStream << "none";
+ }
+ strStream << ")";
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ }
+ diff |= emitResourceEntryDiff(context, apkA, pkgA, typeA, entryA.get(),
+ apkB, pkgB, typeB, entryB);
}
+ }
- // Check for any newly added entries.
- for (std::unique_ptr<ResourceEntry>& entryB : typeB->entries) {
- ResourceEntry* entryA = typeA->findEntry(entryB->name);
- if (!entryA) {
- std::stringstream strStream;
- strStream << "new entry " << pkgB->name << ":" << typeB->type << "/" << entryB->name;
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
- }
+ // Check for any newly added entries.
+ for (std::unique_ptr<ResourceEntry>& entryB : typeB->entries) {
+ ResourceEntry* entryA = typeA->findEntry(entryB->name);
+ if (!entryA) {
+ std::stringstream strStream;
+ strStream << "new entry " << pkgB->name << ":" << typeB->type << "/"
+ << entryB->name;
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
}
- return diff;
+ }
+ return diff;
}
static bool emitResourcePackageDiff(IAaptContext* context, LoadedApk* apkA,
- ResourceTablePackage* pkgA,
- LoadedApk* apkB, ResourceTablePackage* pkgB) {
- bool diff = false;
- for (std::unique_ptr<ResourceTableType>& typeA : pkgA->types) {
- ResourceTableType* typeB = pkgB->findType(typeA->type);
- if (!typeB) {
- std::stringstream strStream;
- strStream << "missing " << pkgA->name << ":" << typeA->type;
- emitDiffLine(apkA->getSource(), strStream.str());
- diff = true;
+ ResourceTablePackage* pkgA, LoadedApk* apkB,
+ ResourceTablePackage* pkgB) {
+ bool diff = false;
+ for (std::unique_ptr<ResourceTableType>& typeA : pkgA->types) {
+ ResourceTableType* typeB = pkgB->findType(typeA->type);
+ if (!typeB) {
+ std::stringstream strStream;
+ strStream << "missing " << pkgA->name << ":" << typeA->type;
+ emitDiffLine(apkA->getSource(), strStream.str());
+ diff = true;
+ } else {
+ if (isSymbolVisibilityDifferent(typeA->symbolStatus,
+ typeB->symbolStatus)) {
+ std::stringstream strStream;
+ strStream << pkgA->name << ":" << typeA->type
+ << " has different visibility (";
+ if (typeB->symbolStatus.state == SymbolState::kPublic) {
+ strStream << "PUBLIC";
} else {
- if (isSymbolVisibilityDifferent(typeA->symbolStatus, typeB->symbolStatus)) {
- std::stringstream strStream;
- strStream << pkgA->name << ":" << typeA->type << " has different visibility (";
- if (typeB->symbolStatus.state == SymbolState::kPublic) {
- strStream << "PUBLIC";
- } else {
- strStream << "PRIVATE";
- }
- strStream << " vs ";
- if (typeA->symbolStatus.state == SymbolState::kPublic) {
- strStream << "PUBLIC";
- } else {
- strStream << "PRIVATE";
- }
- strStream << ")";
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
- } else if (isIdDiff(typeA->symbolStatus, typeA->id, typeB->symbolStatus, typeB->id)) {
- std::stringstream strStream;
- strStream << pkgA->name << ":" << typeA->type << " has different public ID (";
- if (typeB->id) {
- strStream << "0x" << std::hex << typeB->id.value();
- } else {
- strStream << "none";
- }
- strStream << " vs ";
- if (typeA->id) {
- strStream << "0x " << std::hex << typeA->id.value();
- } else {
- strStream << "none";
- }
- strStream << ")";
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
- }
- diff |= emitResourceTypeDiff(context, apkA, pkgA, typeA.get(), apkB, pkgB, typeB);
+ strStream << "PRIVATE";
}
+ strStream << " vs ";
+ if (typeA->symbolStatus.state == SymbolState::kPublic) {
+ strStream << "PUBLIC";
+ } else {
+ strStream << "PRIVATE";
+ }
+ strStream << ")";
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ } else if (isIdDiff(typeA->symbolStatus, typeA->id, typeB->symbolStatus,
+ typeB->id)) {
+ std::stringstream strStream;
+ strStream << pkgA->name << ":" << typeA->type
+ << " has different public ID (";
+ if (typeB->id) {
+ strStream << "0x" << std::hex << typeB->id.value();
+ } else {
+ strStream << "none";
+ }
+ strStream << " vs ";
+ if (typeA->id) {
+ strStream << "0x " << std::hex << typeA->id.value();
+ } else {
+ strStream << "none";
+ }
+ strStream << ")";
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ }
+ diff |= emitResourceTypeDiff(context, apkA, pkgA, typeA.get(), apkB, pkgB,
+ typeB);
}
+ }
- // Check for any newly added types.
- for (std::unique_ptr<ResourceTableType>& typeB : pkgB->types) {
- ResourceTableType* typeA = pkgA->findType(typeB->type);
- if (!typeA) {
- std::stringstream strStream;
- strStream << "new type " << pkgB->name << ":" << typeB->type;
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
- }
+ // Check for any newly added types.
+ for (std::unique_ptr<ResourceTableType>& typeB : pkgB->types) {
+ ResourceTableType* typeA = pkgA->findType(typeB->type);
+ if (!typeA) {
+ std::stringstream strStream;
+ strStream << "new type " << pkgB->name << ":" << typeB->type;
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
}
- return diff;
+ }
+ return diff;
}
-static bool emitResourceTableDiff(IAaptContext* context, LoadedApk* apkA, LoadedApk* apkB) {
- ResourceTable* tableA = apkA->getResourceTable();
- ResourceTable* tableB = apkB->getResourceTable();
+static bool emitResourceTableDiff(IAaptContext* context, LoadedApk* apkA,
+ LoadedApk* apkB) {
+ ResourceTable* tableA = apkA->getResourceTable();
+ ResourceTable* tableB = apkB->getResourceTable();
- bool diff = false;
- for (std::unique_ptr<ResourceTablePackage>& pkgA : tableA->packages) {
- ResourceTablePackage* pkgB = tableB->findPackage(pkgA->name);
- if (!pkgB) {
- std::stringstream strStream;
- strStream << "missing package " << pkgA->name;
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
+ bool diff = false;
+ for (std::unique_ptr<ResourceTablePackage>& pkgA : tableA->packages) {
+ ResourceTablePackage* pkgB = tableB->findPackage(pkgA->name);
+ if (!pkgB) {
+ std::stringstream strStream;
+ strStream << "missing package " << pkgA->name;
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ } else {
+ if (pkgA->id != pkgB->id) {
+ std::stringstream strStream;
+ strStream << "package '" << pkgA->name << "' has different id (";
+ if (pkgB->id) {
+ strStream << "0x" << std::hex << pkgB->id.value();
} else {
- if (pkgA->id != pkgB->id) {
- std::stringstream strStream;
- strStream << "package '" << pkgA->name << "' has different id (";
- if (pkgB->id) {
- strStream << "0x" << std::hex << pkgB->id.value();
- } else {
- strStream << "none";
- }
- strStream << " vs ";
- if (pkgA->id) {
- strStream << "0x" << std::hex << pkgA->id.value();
- } else {
- strStream << "none";
- }
- strStream << ")";
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
- }
- diff |= emitResourcePackageDiff(context, apkA, pkgA.get(), apkB, pkgB);
+ strStream << "none";
}
+ strStream << " vs ";
+ if (pkgA->id) {
+ strStream << "0x" << std::hex << pkgA->id.value();
+ } else {
+ strStream << "none";
+ }
+ strStream << ")";
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
+ }
+ diff |= emitResourcePackageDiff(context, apkA, pkgA.get(), apkB, pkgB);
}
+ }
- // Check for any newly added packages.
- for (std::unique_ptr<ResourceTablePackage>& pkgB : tableB->packages) {
- ResourceTablePackage* pkgA = tableA->findPackage(pkgB->name);
- if (!pkgA) {
- std::stringstream strStream;
- strStream << "new package " << pkgB->name;
- emitDiffLine(apkB->getSource(), strStream.str());
- diff = true;
- }
+ // Check for any newly added packages.
+ for (std::unique_ptr<ResourceTablePackage>& pkgB : tableB->packages) {
+ ResourceTablePackage* pkgA = tableA->findPackage(pkgB->name);
+ if (!pkgA) {
+ std::stringstream strStream;
+ strStream << "new package " << pkgB->name;
+ emitDiffLine(apkB->getSource(), strStream.str());
+ diff = true;
}
- return diff;
+ }
+ return diff;
}
class ZeroingReferenceVisitor : public ValueVisitor {
-public:
- using ValueVisitor::visit;
+ public:
+ using ValueVisitor::visit;
- void visit(Reference* ref) override {
- if (ref->name && ref->id) {
- if (ref->id.value().packageId() == 0x7f) {
- ref->id = {};
- }
- }
+ 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);
+ ZeroingReferenceVisitor visitor;
+ visitAllValuesInTable(table, &visitor);
}
int diff(const std::vector<StringPiece>& args) {
- DiffContext context;
+ DiffContext context;
- Flags flags;
- if (!flags.parse("aapt2 diff", args, &std::cerr)) {
- return 1;
- }
+ Flags flags;
+ if (!flags.parse("aapt2 diff", args, &std::cerr)) {
+ return 1;
+ }
- if (flags.getArgs().size() != 2u) {
- std::cerr << "must have two apks as arguments.\n\n";
- flags.usage("aapt2 diff", &std::cerr);
- return 1;
- }
+ if (flags.getArgs().size() != 2u) {
+ std::cerr << "must have two apks as arguments.\n\n";
+ flags.usage("aapt2 diff", &std::cerr);
+ return 1;
+ }
- std::unique_ptr<LoadedApk> apkA = loadApkFromPath(&context, flags.getArgs()[0]);
- std::unique_ptr<LoadedApk> apkB = loadApkFromPath(&context, flags.getArgs()[1]);
- if (!apkA || !apkB) {
- return 1;
- }
+ std::unique_ptr<LoadedApk> apkA =
+ loadApkFromPath(&context, flags.getArgs()[0]);
+ std::unique_ptr<LoadedApk> apkB =
+ loadApkFromPath(&context, flags.getArgs()[1]);
+ if (!apkA || !apkB) {
+ return 1;
+ }
- // Zero out Application IDs in references.
- zeroOutAppReferences(apkA->getResourceTable());
- zeroOutAppReferences(apkB->getResourceTable());
+ // 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;
- }
- return 0;
+ if (emitResourceTableDiff(&context, apkA.get(), apkB.get())) {
+ // We emitted a diff, so return 1 (failure).
+ return 1;
+ }
+ return 0;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/dump/Dump.cpp b/tools/aapt2/dump/Dump.cpp
index f61ec94..3556cd88 100644
--- a/tools/aapt2/dump/Dump.cpp
+++ b/tools/aapt2/dump/Dump.cpp
@@ -28,183 +28,181 @@
namespace aapt {
-//struct DumpOptions {
+// struct DumpOptions {
//
//};
-void dumpCompiledFile(const pb::CompiledFile& pbFile, const void* data, size_t len,
- const Source& source, IAaptContext* context) {
- std::unique_ptr<ResourceFile> file = deserializeCompiledFileFromPb(pbFile, source,
- context->getDiagnostics());
- if (!file) {
- context->getDiagnostics()->warn(DiagMessage() << "failed to read compiled file");
- return;
- }
+void dumpCompiledFile(const pb::CompiledFile& pbFile, const void* data,
+ size_t len, const Source& source, IAaptContext* context) {
+ std::unique_ptr<ResourceFile> file =
+ deserializeCompiledFileFromPb(pbFile, source, context->getDiagnostics());
+ if (!file) {
+ context->getDiagnostics()->warn(DiagMessage()
+ << "failed to read compiled file");
+ return;
+ }
- std::cout << "Resource: " << file->name << "\n"
- << "Config: " << file->config << "\n"
- << "Source: " << file->source << "\n";
+ std::cout << "Resource: " << file->name << "\n"
+ << "Config: " << file->config << "\n"
+ << "Source: " << file->source << "\n";
}
void tryDumpFile(IAaptContext* context, const std::string& filePath) {
- std::unique_ptr<ResourceTable> table;
+ std::unique_ptr<ResourceTable> table;
- std::string err;
- std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::create(filePath, &err);
- if (zip) {
- io::IFile* file = zip->findFile("resources.arsc.flat");
- if (file) {
- std::unique_ptr<io::IData> data = file->openAsData();
- if (!data) {
- context->getDiagnostics()->error(DiagMessage(filePath)
- << "failed to open resources.arsc.flat");
- return;
- }
+ std::string err;
+ std::unique_ptr<io::ZipFileCollection> zip =
+ io::ZipFileCollection::create(filePath, &err);
+ if (zip) {
+ io::IFile* file = zip->findFile("resources.arsc.flat");
+ if (file) {
+ std::unique_ptr<io::IData> data = file->openAsData();
+ if (!data) {
+ context->getDiagnostics()->error(
+ DiagMessage(filePath) << "failed to open resources.arsc.flat");
+ return;
+ }
- pb::ResourceTable pbTable;
- if (!pbTable.ParseFromArray(data->data(), data->size())) {
- context->getDiagnostics()->error(DiagMessage(filePath)
- << "invalid resources.arsc.flat");
- return;
- }
+ pb::ResourceTable pbTable;
+ if (!pbTable.ParseFromArray(data->data(), data->size())) {
+ context->getDiagnostics()->error(DiagMessage(filePath)
+ << "invalid resources.arsc.flat");
+ return;
+ }
- table = deserializeTableFromPb(
- pbTable, Source(filePath), context->getDiagnostics());
- if (!table) {
- 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;
- }
- }
- }
+ table = deserializeTableFromPb(pbTable, Source(filePath),
+ context->getDiagnostics());
+ if (!table) {
+ return;
+ }
}
if (!table) {
- Maybe<android::FileMap> file = file::mmapPath(filePath, &err);
- if (!file) {
- context->getDiagnostics()->error(DiagMessage(filePath) << err);
- return;
+ 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;
}
- android::FileMap* fileMap = &file.value();
-
- // Try as a compiled table.
- pb::ResourceTable pbTable;
- if (pbTable.ParseFromArray(fileMap->getDataPtr(), fileMap->getDataLength())) {
- table = deserializeTableFromPb(pbTable, Source(filePath), context->getDiagnostics());
+ table = util::make_unique<ResourceTable>();
+ BinaryResourceParser parser(context, table.get(), Source(filePath),
+ data->data(), data->size());
+ if (!parser.parse()) {
+ return;
}
+ }
+ }
+ }
- if (!table) {
- // Try as a compiled file.
- CompiledFileInputStream input(fileMap->getDataPtr(), fileMap->getDataLength());
-
- uint32_t numFiles = 0;
- if (!input.ReadLittleEndian32(&numFiles)) {
- return;
- }
-
- for (uint32_t i = 0; i < numFiles; i++) {
- pb::CompiledFile compiledFile;
- if (!input.ReadCompiledFile(&compiledFile)) {
- context->getDiagnostics()->warn(DiagMessage() << "failed to read compiled file");
- return;
- }
-
- uint64_t offset, len;
- if (!input.ReadDataMetaData(&offset, &len)) {
- context->getDiagnostics()->warn(DiagMessage() << "failed to read meta data");
- return;
- }
-
- const void* data = static_cast<const uint8_t*>(fileMap->getDataPtr()) + offset;
- dumpCompiledFile(compiledFile, data, len, Source(filePath), context);
- }
- }
+ if (!table) {
+ Maybe<android::FileMap> file = file::mmapPath(filePath, &err);
+ if (!file) {
+ context->getDiagnostics()->error(DiagMessage(filePath) << err);
+ return;
}
- if (table) {
- DebugPrintTableOptions debugPrintTableOptions;
- debugPrintTableOptions.showSources = true;
- Debug::printTable(table.get(), debugPrintTableOptions);
+ android::FileMap* fileMap = &file.value();
+
+ // 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());
+
+ uint32_t numFiles = 0;
+ if (!input.ReadLittleEndian32(&numFiles)) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < numFiles; i++) {
+ pb::CompiledFile compiledFile;
+ if (!input.ReadCompiledFile(&compiledFile)) {
+ context->getDiagnostics()->warn(DiagMessage()
+ << "failed to read compiled file");
+ return;
+ }
+
+ uint64_t offset, len;
+ if (!input.ReadDataMetaData(&offset, &len)) {
+ context->getDiagnostics()->warn(DiagMessage()
+ << "failed to read meta data");
+ return;
+ }
+
+ const void* data =
+ static_cast<const uint8_t*>(fileMap->getDataPtr()) + offset;
+ dumpCompiledFile(compiledFile, data, len, Source(filePath), context);
+ }
+ }
+ }
+
+ if (table) {
+ DebugPrintTableOptions debugPrintTableOptions;
+ debugPrintTableOptions.showSources = true;
+ Debug::printTable(table.get(), debugPrintTableOptions);
+ }
}
class DumpContext : public IAaptContext {
-public:
- IDiagnostics* getDiagnostics() override {
- return &mDiagnostics;
- }
+ public:
+ IDiagnostics* getDiagnostics() override { return &mDiagnostics; }
- NameMangler* getNameMangler() override {
- abort();
- return nullptr;
- }
+ NameMangler* getNameMangler() override {
+ abort();
+ return nullptr;
+ }
- const std::string& getCompilationPackage() override {
- static std::string empty;
- return empty;
- }
+ const std::string& getCompilationPackage() override {
+ static std::string empty;
+ return empty;
+ }
- uint8_t getPackageId() override {
- return 0;
- }
+ uint8_t getPackageId() override { return 0; }
- SymbolTable* getExternalSymbols() override {
- abort();
- return nullptr;
- }
+ SymbolTable* getExternalSymbols() override {
+ abort();
+ return nullptr;
+ }
- bool verbose() override {
- return mVerbose;
- }
+ bool verbose() override { return mVerbose; }
- void setVerbose(bool val) {
- mVerbose = val;
- }
+ void setVerbose(bool val) { mVerbose = val; }
- int getMinSdkVersion() override {
- return 0;
- }
+ int getMinSdkVersion() override { return 0; }
-private:
- StdErrDiagnostics mDiagnostics;
- bool mVerbose = false;
+ private:
+ StdErrDiagnostics mDiagnostics;
+ bool mVerbose = false;
};
/**
* Entry point for dump command.
*/
int dump(const std::vector<StringPiece>& args) {
- bool verbose = false;
- Flags flags = Flags()
- .optionalSwitch("-v", "increase verbosity of output", &verbose);
- if (!flags.parse("aapt2 dump", args, &std::cerr)) {
- return 1;
- }
+ bool verbose = false;
+ Flags flags =
+ Flags().optionalSwitch("-v", "increase verbosity of output", &verbose);
+ if (!flags.parse("aapt2 dump", args, &std::cerr)) {
+ return 1;
+ }
- DumpContext context;
- context.setVerbose(verbose);
+ DumpContext context;
+ context.setVerbose(verbose);
- for (const std::string& arg : flags.getArgs()) {
- tryDumpFile(&context, arg);
- }
- return 0;
+ for (const std::string& arg : flags.getArgs()) {
+ tryDumpFile(&context, arg);
+ }
+ return 0;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/filter/ConfigFilter.cpp b/tools/aapt2/filter/ConfigFilter.cpp
index 68a017d..5af996c 100644
--- a/tools/aapt2/filter/ConfigFilter.cpp
+++ b/tools/aapt2/filter/ConfigFilter.cpp
@@ -14,64 +14,68 @@
* limitations under the License.
*/
-#include "ConfigDescription.h"
#include "filter/ConfigFilter.h"
+#include "ConfigDescription.h"
#include <androidfw/ResourceTypes.h>
namespace aapt {
void AxisConfigFilter::addConfig(ConfigDescription config) {
- uint32_t diffMask = ConfigDescription::defaultConfig().diff(config);
+ uint32_t diffMask = ConfigDescription::defaultConfig().diff(config);
- // Ignore the version
- diffMask &= ~android::ResTable_config::CONFIG_VERSION;
+ // Ignore the version
+ diffMask &= ~android::ResTable_config::CONFIG_VERSION;
- // Ignore any densities. Those are best handled in --preferred-density
- if ((diffMask & android::ResTable_config::CONFIG_DENSITY) != 0) {
- config.density = 0;
- diffMask &= ~android::ResTable_config::CONFIG_DENSITY;
- }
+ // Ignore any densities. Those are best handled in --preferred-density
+ if ((diffMask & android::ResTable_config::CONFIG_DENSITY) != 0) {
+ config.density = 0;
+ diffMask &= ~android::ResTable_config::CONFIG_DENSITY;
+ }
- mConfigs.insert(std::make_pair(config, diffMask));
- mConfigMask |= diffMask;
+ mConfigs.insert(std::make_pair(config, diffMask));
+ mConfigMask |= diffMask;
}
bool AxisConfigFilter::match(const ConfigDescription& config) const {
- const uint32_t mask = ConfigDescription::defaultConfig().diff(config);
- if ((mConfigMask & mask) == 0) {
- // The two configurations don't have any common axis.
- return true;
- }
+ const uint32_t mask = ConfigDescription::defaultConfig().diff(config);
+ if ((mConfigMask & mask) == 0) {
+ // The two configurations don't have any common axis.
+ return true;
+ }
- uint32_t matchedAxis = 0;
- for (const auto& entry : mConfigs) {
- const ConfigDescription& target = entry.first;
- const uint32_t diffMask = entry.second;
- uint32_t diff = target.diff(config);
- if ((diff & diffMask) == 0) {
- // Mark the axis that was matched.
- matchedAxis |= diffMask;
- } else if ((diff & diffMask) == android::ResTable_config::CONFIG_LOCALE) {
- // If the locales differ, but the languages are the same and
- // the locale we are matching only has a language specified,
- // we match.
- if (config.language[0] &&
- memcmp(config.language, target.language, sizeof(config.language)) == 0) {
- if (config.country[0] == 0) {
- matchedAxis |= android::ResTable_config::CONFIG_LOCALE;
- }
- }
- } else if ((diff & diffMask) == android::ResTable_config::CONFIG_SMALLEST_SCREEN_SIZE) {
- // Special case if the smallest screen width doesn't match. We check that the
- // config being matched has a smaller screen width than the filter specified.
- if (config.smallestScreenWidthDp != 0 &&
- config.smallestScreenWidthDp < target.smallestScreenWidthDp) {
- matchedAxis |= android::ResTable_config::CONFIG_SMALLEST_SCREEN_SIZE;
- }
+ uint32_t matchedAxis = 0;
+ for (const auto& entry : mConfigs) {
+ const ConfigDescription& target = entry.first;
+ const uint32_t diffMask = entry.second;
+ uint32_t diff = target.diff(config);
+ if ((diff & diffMask) == 0) {
+ // Mark the axis that was matched.
+ matchedAxis |= diffMask;
+ } else if ((diff & diffMask) == android::ResTable_config::CONFIG_LOCALE) {
+ // If the locales differ, but the languages are the same and
+ // the locale we are matching only has a language specified,
+ // we match.
+ if (config.language[0] &&
+ memcmp(config.language, target.language, sizeof(config.language)) ==
+ 0) {
+ if (config.country[0] == 0) {
+ matchedAxis |= android::ResTable_config::CONFIG_LOCALE;
}
+ }
+ } else if ((diff & diffMask) ==
+ android::ResTable_config::CONFIG_SMALLEST_SCREEN_SIZE) {
+ // Special case if the smallest screen width doesn't match. We check that
+ // the
+ // config being matched has a smaller screen width than the filter
+ // specified.
+ if (config.smallestScreenWidthDp != 0 &&
+ config.smallestScreenWidthDp < target.smallestScreenWidthDp) {
+ matchedAxis |= android::ResTable_config::CONFIG_SMALLEST_SCREEN_SIZE;
+ }
}
- return matchedAxis == (mConfigMask & mask);
+ }
+ return matchedAxis == (mConfigMask & mask);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/filter/ConfigFilter.h b/tools/aapt2/filter/ConfigFilter.h
index 36e9c44..c50160b 100644
--- a/tools/aapt2/filter/ConfigFilter.h
+++ b/tools/aapt2/filter/ConfigFilter.h
@@ -28,34 +28,37 @@
* Matches ConfigDescriptions based on some pattern.
*/
class IConfigFilter {
-public:
- virtual ~IConfigFilter() = default;
+ public:
+ virtual ~IConfigFilter() = default;
- /**
- * Returns true if the filter matches the configuration, false otherwise.
- */
- virtual bool match(const ConfigDescription& config) const = 0;
+ /**
+ * Returns true if the filter matches the configuration, false otherwise.
+ */
+ virtual bool match(const ConfigDescription& config) const = 0;
};
/**
- * Implements config axis matching. An axis is one component of a configuration, like screen
- * density or locale. If an axis is specified in the filter, and the axis is specified in
- * the configuration to match, they must be compatible. Otherwise the configuration to match is
+ * Implements config axis matching. An axis is one component of a configuration,
+ * like screen
+ * density or locale. If an axis is specified in the filter, and the axis is
+ * specified in
+ * the configuration to match, they must be compatible. Otherwise the
+ * configuration to match is
* accepted.
*
* Used when handling "-c" options.
*/
class AxisConfigFilter : public IConfigFilter {
-public:
- void addConfig(ConfigDescription config);
+ public:
+ void addConfig(ConfigDescription config);
- bool match(const ConfigDescription& config) const override;
+ bool match(const ConfigDescription& config) const override;
-private:
- std::set<std::pair<ConfigDescription, uint32_t>> mConfigs;
- uint32_t mConfigMask = 0;
+ private:
+ std::set<std::pair<ConfigDescription, uint32_t>> mConfigs;
+ uint32_t mConfigMask = 0;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_FILTER_CONFIGFILTER_H */
diff --git a/tools/aapt2/filter/ConfigFilter_test.cpp b/tools/aapt2/filter/ConfigFilter_test.cpp
index f6b4955..edb40a8 100644
--- a/tools/aapt2/filter/ConfigFilter_test.cpp
+++ b/tools/aapt2/filter/ConfigFilter_test.cpp
@@ -22,91 +22,92 @@
namespace aapt {
TEST(ConfigFilterTest, EmptyFilterMatchesAnything) {
- AxisConfigFilter filter;
+ AxisConfigFilter filter;
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("320dpi")));
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("fr")));
+ EXPECT_TRUE(filter.match(test::parseConfigOrDie("320dpi")));
+ EXPECT_TRUE(filter.match(test::parseConfigOrDie("fr")));
}
TEST(ConfigFilterTest, MatchesConfigWithUnrelatedAxis) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("fr"));
+ AxisConfigFilter filter;
+ filter.addConfig(test::parseConfigOrDie("fr"));
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("320dpi")));
+ EXPECT_TRUE(filter.match(test::parseConfigOrDie("320dpi")));
}
TEST(ConfigFilterTest, MatchesConfigWithSameValueAxis) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("fr"));
+ AxisConfigFilter filter;
+ filter.addConfig(test::parseConfigOrDie("fr"));
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("fr")));
+ EXPECT_TRUE(filter.match(test::parseConfigOrDie("fr")));
}
TEST(ConfigFilterTest, MatchesConfigWithSameValueAxisAndOtherUnrelatedAxis) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("fr"));
+ AxisConfigFilter filter;
+ filter.addConfig(test::parseConfigOrDie("fr"));
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("fr-320dpi")));
+ EXPECT_TRUE(filter.match(test::parseConfigOrDie("fr-320dpi")));
}
TEST(ConfigFilterTest, MatchesConfigWithOneMatchingAxis) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("fr-rFR"));
- filter.addConfig(test::parseConfigOrDie("sw360dp"));
- filter.addConfig(test::parseConfigOrDie("normal"));
- filter.addConfig(test::parseConfigOrDie("en-rUS"));
+ AxisConfigFilter filter;
+ filter.addConfig(test::parseConfigOrDie("fr-rFR"));
+ filter.addConfig(test::parseConfigOrDie("sw360dp"));
+ filter.addConfig(test::parseConfigOrDie("normal"));
+ filter.addConfig(test::parseConfigOrDie("en-rUS"));
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("en")));
+ EXPECT_TRUE(filter.match(test::parseConfigOrDie("en")));
}
TEST(ConfigFilterTest, DoesNotMatchConfigWithDifferentValueAxis) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("fr"));
+ AxisConfigFilter filter;
+ filter.addConfig(test::parseConfigOrDie("fr"));
- EXPECT_FALSE(filter.match(test::parseConfigOrDie("de")));
+ EXPECT_FALSE(filter.match(test::parseConfigOrDie("de")));
}
TEST(ConfigFilterTest, DoesNotMatchWhenOneQualifierIsExplicitlyNotMatched) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("fr-rFR"));
- filter.addConfig(test::parseConfigOrDie("en-rUS"));
- filter.addConfig(test::parseConfigOrDie("normal"));
- filter.addConfig(test::parseConfigOrDie("large"));
- filter.addConfig(test::parseConfigOrDie("xxhdpi"));
- filter.addConfig(test::parseConfigOrDie("sw320dp"));
+ AxisConfigFilter filter;
+ filter.addConfig(test::parseConfigOrDie("fr-rFR"));
+ filter.addConfig(test::parseConfigOrDie("en-rUS"));
+ filter.addConfig(test::parseConfigOrDie("normal"));
+ filter.addConfig(test::parseConfigOrDie("large"));
+ filter.addConfig(test::parseConfigOrDie("xxhdpi"));
+ filter.addConfig(test::parseConfigOrDie("sw320dp"));
- EXPECT_FALSE(filter.match(test::parseConfigOrDie("fr-sw600dp-v13")));
+ EXPECT_FALSE(filter.match(test::parseConfigOrDie("fr-sw600dp-v13")));
}
TEST(ConfigFilterTest, MatchesSmallestWidthWhenSmaller) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("sw600dp"));
+ AxisConfigFilter filter;
+ filter.addConfig(test::parseConfigOrDie("sw600dp"));
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("fr-sw320dp-v13")));
+ EXPECT_TRUE(filter.match(test::parseConfigOrDie("fr-sw320dp-v13")));
}
TEST(ConfigFilterTest, MatchesConfigWithSameLanguageButNoRegionSpecified) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("de-rDE"));
+ AxisConfigFilter filter;
+ filter.addConfig(test::parseConfigOrDie("de-rDE"));
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("de")));
+ EXPECT_TRUE(filter.match(test::parseConfigOrDie("de")));
}
TEST(ConfigFilterTest, IgnoresVersion) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("normal-v4"));
+ AxisConfigFilter filter;
+ filter.addConfig(test::parseConfigOrDie("normal-v4"));
- // The configs don't match on any axis besides version, which should be ignored.
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("sw600dp-v13")));
+ // The configs don't match on any axis besides version, which should be
+ // ignored.
+ EXPECT_TRUE(filter.match(test::parseConfigOrDie("sw600dp-v13")));
}
TEST(ConfigFilterTest, MatchesConfigWithRegion) {
- AxisConfigFilter filter;
- filter.addConfig(test::parseConfigOrDie("kok"));
- filter.addConfig(test::parseConfigOrDie("kok-rIN"));
- filter.addConfig(test::parseConfigOrDie("kok-v419"));
+ AxisConfigFilter filter;
+ filter.addConfig(test::parseConfigOrDie("kok"));
+ filter.addConfig(test::parseConfigOrDie("kok-rIN"));
+ filter.addConfig(test::parseConfigOrDie("kok-v419"));
- EXPECT_TRUE(filter.match(test::parseConfigOrDie("kok-rIN")));
+ EXPECT_TRUE(filter.match(test::parseConfigOrDie("kok-rIN")));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/flatten/Archive.cpp b/tools/aapt2/flatten/Archive.cpp
index 3a244c0..ae08f65 100644
--- a/tools/aapt2/flatten/Archive.cpp
+++ b/tools/aapt2/flatten/Archive.cpp
@@ -18,167 +18,169 @@
#include "util/Files.h"
#include "util/StringPiece.h"
+#include <ziparchive/zip_writer.h>
#include <cstdio>
#include <memory>
#include <string>
#include <vector>
-#include <ziparchive/zip_writer.h>
namespace aapt {
namespace {
struct DirectoryWriter : public IArchiveWriter {
- std::string mOutDir;
- std::unique_ptr<FILE, decltype(fclose)*> mFile = { nullptr, fclose };
+ std::string mOutDir;
+ std::unique_ptr<FILE, decltype(fclose)*> mFile = {nullptr, fclose};
- bool open(IDiagnostics* diag, const StringPiece& outDir) {
- mOutDir = outDir.toString();
- file::FileType type = file::getFileType(mOutDir);
- if (type == file::FileType::kNonexistant) {
- diag->error(DiagMessage() << "directory " << mOutDir << " does not exist");
- return false;
- } else if (type != file::FileType::kDirectory) {
- diag->error(DiagMessage() << mOutDir << " is not a directory");
- return false;
- }
- return true;
+ bool open(IDiagnostics* diag, const StringPiece& outDir) {
+ mOutDir = outDir.toString();
+ file::FileType type = file::getFileType(mOutDir);
+ if (type == file::FileType::kNonexistant) {
+ diag->error(DiagMessage() << "directory " << mOutDir
+ << " does not exist");
+ return false;
+ } else if (type != file::FileType::kDirectory) {
+ diag->error(DiagMessage() << mOutDir << " is not a directory");
+ return false;
+ }
+ return true;
+ }
+
+ bool startEntry(const StringPiece& path, uint32_t flags) override {
+ if (mFile) {
+ return false;
}
- bool startEntry(const StringPiece& path, uint32_t flags) override {
- if (mFile) {
- return false;
- }
+ std::string fullPath = mOutDir;
+ file::appendPath(&fullPath, path);
+ file::mkdirs(file::getStem(fullPath));
- std::string fullPath = mOutDir;
- file::appendPath(&fullPath, path);
- file::mkdirs(file::getStem(fullPath));
+ mFile = {fopen(fullPath.data(), "wb"), fclose};
+ if (!mFile) {
+ return false;
+ }
+ return true;
+ }
- mFile = { fopen(fullPath.data(), "wb"), fclose };
- if (!mFile) {
- return false;
- }
- return true;
+ bool writeEntry(const BigBuffer& buffer) override {
+ if (!mFile) {
+ return false;
}
- bool writeEntry(const BigBuffer& buffer) override {
- if (!mFile) {
- return false;
- }
-
- for (const BigBuffer::Block& b : buffer) {
- if (fwrite(b.buffer.get(), 1, b.size, mFile.get()) != b.size) {
- mFile.reset(nullptr);
- return false;
- }
- }
- return true;
- }
-
- bool writeEntry(const void* data, size_t len) override {
- if (fwrite(data, 1, len, mFile.get()) != len) {
- mFile.reset(nullptr);
- return false;
- }
- return true;
- }
-
- bool finishEntry() override {
- if (!mFile) {
- return false;
- }
+ for (const BigBuffer::Block& b : buffer) {
+ if (fwrite(b.buffer.get(), 1, b.size, mFile.get()) != b.size) {
mFile.reset(nullptr);
- return true;
+ return false;
+ }
}
+ return true;
+ }
+
+ bool writeEntry(const void* data, size_t len) override {
+ if (fwrite(data, 1, len, mFile.get()) != len) {
+ mFile.reset(nullptr);
+ return false;
+ }
+ return true;
+ }
+
+ bool finishEntry() override {
+ if (!mFile) {
+ return false;
+ }
+ mFile.reset(nullptr);
+ return true;
+ }
};
struct ZipFileWriter : public IArchiveWriter {
- std::unique_ptr<FILE, decltype(fclose)*> mFile = { nullptr, fclose };
- std::unique_ptr<ZipWriter> mWriter;
+ std::unique_ptr<FILE, decltype(fclose)*> mFile = {nullptr, fclose};
+ std::unique_ptr<ZipWriter> mWriter;
- bool open(IDiagnostics* diag, const StringPiece& path) {
- mFile = { fopen(path.data(), "w+b"), fclose };
- if (!mFile) {
- diag->error(DiagMessage() << "failed to open " << path << ": " << strerror(errno));
- return false;
- }
- mWriter = util::make_unique<ZipWriter>(mFile.get());
- return true;
+ bool open(IDiagnostics* diag, const StringPiece& path) {
+ mFile = {fopen(path.data(), "w+b"), fclose};
+ if (!mFile) {
+ diag->error(DiagMessage() << "failed to open " << path << ": "
+ << strerror(errno));
+ return false;
+ }
+ mWriter = util::make_unique<ZipWriter>(mFile.get());
+ return true;
+ }
+
+ bool startEntry(const StringPiece& path, uint32_t flags) override {
+ if (!mWriter) {
+ return false;
}
- bool startEntry(const StringPiece& path, uint32_t flags) override {
- if (!mWriter) {
- return false;
- }
-
- size_t zipFlags = 0;
- if (flags & ArchiveEntry::kCompress) {
- zipFlags |= ZipWriter::kCompress;
- }
-
- if (flags & ArchiveEntry::kAlign) {
- zipFlags |= ZipWriter::kAlign32;
- }
-
- int32_t result = mWriter->StartEntry(path.data(), zipFlags);
- if (result != 0) {
- return false;
- }
- return true;
+ size_t zipFlags = 0;
+ if (flags & ArchiveEntry::kCompress) {
+ zipFlags |= ZipWriter::kCompress;
}
- bool writeEntry(const void* data, size_t len) override {
- int32_t result = mWriter->WriteBytes(data, len);
- if (result != 0) {
- return false;
- }
- return true;
+ if (flags & ArchiveEntry::kAlign) {
+ zipFlags |= ZipWriter::kAlign32;
}
- bool writeEntry(const BigBuffer& buffer) override {
- for (const BigBuffer::Block& b : buffer) {
- int32_t result = mWriter->WriteBytes(b.buffer.get(), b.size);
- if (result != 0) {
- return false;
- }
- }
- return true;
+ int32_t result = mWriter->StartEntry(path.data(), zipFlags);
+ if (result != 0) {
+ return false;
}
+ return true;
+ }
- bool finishEntry() override {
- int32_t result = mWriter->FinishEntry();
- if (result != 0) {
- return false;
- }
- return true;
+ bool writeEntry(const void* data, size_t len) override {
+ int32_t result = mWriter->WriteBytes(data, len);
+ if (result != 0) {
+ return false;
}
+ return true;
+ }
- virtual ~ZipFileWriter() {
- if (mWriter) {
- mWriter->Finish();
- }
+ bool writeEntry(const BigBuffer& buffer) override {
+ for (const BigBuffer::Block& b : buffer) {
+ int32_t result = mWriter->WriteBytes(b.buffer.get(), b.size);
+ if (result != 0) {
+ return false;
+ }
}
+ return true;
+ }
+
+ bool finishEntry() override {
+ int32_t result = mWriter->FinishEntry();
+ if (result != 0) {
+ return false;
+ }
+ return true;
+ }
+
+ virtual ~ZipFileWriter() {
+ if (mWriter) {
+ mWriter->Finish();
+ }
+ }
};
-} // namespace
+} // namespace
-std::unique_ptr<IArchiveWriter> createDirectoryArchiveWriter(IDiagnostics* diag,
- const StringPiece& path) {
-
- std::unique_ptr<DirectoryWriter> writer = util::make_unique<DirectoryWriter>();
- if (!writer->open(diag, path)) {
- return {};
- }
- return std::move(writer);
+std::unique_ptr<IArchiveWriter> createDirectoryArchiveWriter(
+ IDiagnostics* diag, const StringPiece& path) {
+ std::unique_ptr<DirectoryWriter> writer =
+ util::make_unique<DirectoryWriter>();
+ if (!writer->open(diag, path)) {
+ return {};
+ }
+ return std::move(writer);
}
-std::unique_ptr<IArchiveWriter> createZipFileArchiveWriter(IDiagnostics* diag,
- const StringPiece& path) {
- std::unique_ptr<ZipFileWriter> writer = util::make_unique<ZipFileWriter>();
- if (!writer->open(diag, path)) {
- return {};
- }
- return std::move(writer);
+std::unique_ptr<IArchiveWriter> createZipFileArchiveWriter(
+ IDiagnostics* diag, const StringPiece& path) {
+ std::unique_ptr<ZipFileWriter> writer = util::make_unique<ZipFileWriter>();
+ if (!writer->open(diag, path)) {
+ return {};
+ }
+ return std::move(writer);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/flatten/Archive.h b/tools/aapt2/flatten/Archive.h
index 96d8512..46cd0ac 100644
--- a/tools/aapt2/flatten/Archive.h
+++ b/tools/aapt2/flatten/Archive.h
@@ -31,37 +31,37 @@
namespace aapt {
struct ArchiveEntry {
- enum : uint32_t {
- kCompress = 0x01,
- kAlign = 0x02,
- };
+ enum : uint32_t {
+ kCompress = 0x01,
+ kAlign = 0x02,
+ };
- std::string path;
- uint32_t flags;
- size_t uncompressedSize;
+ std::string path;
+ uint32_t flags;
+ size_t uncompressedSize;
};
class IArchiveWriter : public google::protobuf::io::CopyingOutputStream {
-public:
- virtual ~IArchiveWriter() = default;
+ public:
+ virtual ~IArchiveWriter() = default;
- virtual bool startEntry(const StringPiece& path, uint32_t flags) = 0;
- virtual bool writeEntry(const BigBuffer& buffer) = 0;
- virtual bool writeEntry(const void* data, size_t len) = 0;
- virtual bool finishEntry() = 0;
+ virtual bool startEntry(const StringPiece& path, uint32_t flags) = 0;
+ virtual bool writeEntry(const BigBuffer& buffer) = 0;
+ virtual bool writeEntry(const void* data, size_t len) = 0;
+ virtual bool finishEntry() = 0;
- // CopyingOutputStream implementations.
- bool Write(const void* buffer, int size) override {
- return writeEntry(buffer, size);
- }
+ // CopyingOutputStream implementations.
+ bool Write(const void* buffer, int size) override {
+ return writeEntry(buffer, size);
+ }
};
-std::unique_ptr<IArchiveWriter> createDirectoryArchiveWriter(IDiagnostics* diag,
- const StringPiece& path);
+std::unique_ptr<IArchiveWriter> createDirectoryArchiveWriter(
+ IDiagnostics* diag, const StringPiece& path);
-std::unique_ptr<IArchiveWriter> createZipFileArchiveWriter(IDiagnostics* diag,
- const StringPiece& path);
+std::unique_ptr<IArchiveWriter> createZipFileArchiveWriter(
+ IDiagnostics* diag, const StringPiece& path);
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_FLATTEN_ARCHIVE_H */
diff --git a/tools/aapt2/flatten/ChunkWriter.h b/tools/aapt2/flatten/ChunkWriter.h
index de1d87a..852afd4 100644
--- a/tools/aapt2/flatten/ChunkWriter.h
+++ b/tools/aapt2/flatten/ChunkWriter.h
@@ -25,63 +25,56 @@
namespace aapt {
class ChunkWriter {
-private:
- BigBuffer* mBuffer;
- size_t mStartSize = 0;
- android::ResChunk_header* mHeader = nullptr;
+ private:
+ BigBuffer* mBuffer;
+ size_t mStartSize = 0;
+ android::ResChunk_header* mHeader = nullptr;
-public:
- explicit inline ChunkWriter(BigBuffer* buffer) : mBuffer(buffer) {
- }
+ public:
+ explicit inline ChunkWriter(BigBuffer* buffer) : mBuffer(buffer) {}
- ChunkWriter(const ChunkWriter&) = delete;
- ChunkWriter& operator=(const ChunkWriter&) = delete;
- ChunkWriter(ChunkWriter&&) = default;
- ChunkWriter& operator=(ChunkWriter&&) = default;
+ ChunkWriter(const ChunkWriter&) = delete;
+ ChunkWriter& operator=(const ChunkWriter&) = delete;
+ ChunkWriter(ChunkWriter&&) = default;
+ ChunkWriter& operator=(ChunkWriter&&) = default;
- template <typename T>
- inline T* startChunk(uint16_t type) {
- mStartSize = mBuffer->size();
- T* chunk = mBuffer->nextBlock<T>();
- mHeader = &chunk->header;
- mHeader->type = util::hostToDevice16(type);
- mHeader->headerSize = util::hostToDevice16(sizeof(T));
- return chunk;
- }
+ template <typename T>
+ inline T* startChunk(uint16_t type) {
+ mStartSize = mBuffer->size();
+ T* chunk = mBuffer->nextBlock<T>();
+ mHeader = &chunk->header;
+ mHeader->type = util::hostToDevice16(type);
+ mHeader->headerSize = util::hostToDevice16(sizeof(T));
+ return chunk;
+ }
- template <typename T>
- inline T* nextBlock(size_t count = 1) {
- return mBuffer->nextBlock<T>(count);
- }
+ template <typename T>
+ inline T* nextBlock(size_t count = 1) {
+ return mBuffer->nextBlock<T>(count);
+ }
- inline BigBuffer* getBuffer() {
- return mBuffer;
- }
+ inline BigBuffer* getBuffer() { return mBuffer; }
- inline android::ResChunk_header* getChunkHeader() {
- return mHeader;
- }
+ inline android::ResChunk_header* getChunkHeader() { return mHeader; }
- inline size_t size() {
- return mBuffer->size() - mStartSize;
- }
+ inline size_t size() { return mBuffer->size() - mStartSize; }
- inline android::ResChunk_header* finish() {
- mBuffer->align4();
- mHeader->size = util::hostToDevice32(mBuffer->size() - mStartSize);
- return mHeader;
- }
+ inline android::ResChunk_header* finish() {
+ mBuffer->align4();
+ mHeader->size = util::hostToDevice32(mBuffer->size() - mStartSize);
+ return mHeader;
+ }
};
template <>
inline android::ResChunk_header* ChunkWriter::startChunk(uint16_t type) {
- mStartSize = mBuffer->size();
- mHeader = mBuffer->nextBlock<android::ResChunk_header>();
- mHeader->type = util::hostToDevice16(type);
- mHeader->headerSize = util::hostToDevice16(sizeof(android::ResChunk_header));
- return mHeader;
+ mStartSize = mBuffer->size();
+ mHeader = mBuffer->nextBlock<android::ResChunk_header>();
+ mHeader->type = util::hostToDevice16(type);
+ mHeader->headerSize = util::hostToDevice16(sizeof(android::ResChunk_header));
+ return mHeader;
}
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_FLATTEN_CHUNKWRITER_H */
diff --git a/tools/aapt2/flatten/ResourceTypeExtensions.h b/tools/aapt2/flatten/ResourceTypeExtensions.h
index 3e20ad6..0b19240 100644
--- a/tools/aapt2/flatten/ResourceTypeExtensions.h
+++ b/tools/aapt2/flatten/ResourceTypeExtensions.h
@@ -22,15 +22,16 @@
namespace aapt {
/**
- * An alternative struct to use instead of ResTable_map_entry. This one is a standard_layout
+ * An alternative struct to use instead of ResTable_map_entry. This one is a
+ * standard_layout
* struct.
*/
struct ResTable_entry_ext {
- android::ResTable_entry entry;
- android::ResTable_ref parent;
- uint32_t count;
+ android::ResTable_entry entry;
+ android::ResTable_ref parent;
+ uint32_t count;
};
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_RESOURCE_TYPE_EXTENSIONS_H
+#endif // AAPT_RESOURCE_TYPE_EXTENSIONS_H
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp
index d5067b1..d4ea6c0 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/flatten/TableFlattener.cpp
@@ -25,9 +25,9 @@
#include <android-base/macros.h>
#include <algorithm>
+#include <numeric>
#include <sstream>
#include <type_traits>
-#include <numeric>
using namespace android;
@@ -37,438 +37,454 @@
template <typename T>
static bool cmpIds(const T* a, const T* b) {
- return a->id.value() < b->id.value();
+ return a->id.value() < b->id.value();
}
static void strcpy16_htod(uint16_t* dst, size_t len, const StringPiece16& src) {
- if (len == 0) {
- return;
- }
+ if (len == 0) {
+ return;
+ }
- size_t i;
- const char16_t* srcData = src.data();
- for (i = 0; i < len - 1 && i < src.size(); i++) {
- dst[i] = util::hostToDevice16((uint16_t) srcData[i]);
- }
- dst[i] = 0;
+ size_t i;
+ const char16_t* srcData = src.data();
+ for (i = 0; i < len - 1 && i < src.size(); i++) {
+ dst[i] = util::hostToDevice16((uint16_t)srcData[i]);
+ }
+ dst[i] = 0;
}
static bool cmpStyleEntries(const Style::Entry& a, const Style::Entry& b) {
- if (a.key.id) {
- if (b.key.id) {
- return a.key.id.value() < b.key.id.value();
- }
- return true;
- } else if (!b.key.id) {
- return a.key.name.value() < b.key.name.value();
- }
- return false;
+ if (a.key.id) {
+ if (b.key.id) {
+ return a.key.id.value() < b.key.id.value();
+ }
+ return true;
+ } else if (!b.key.id) {
+ return a.key.name.value() < b.key.name.value();
+ }
+ return false;
}
struct FlatEntry {
- ResourceEntry* entry;
- Value* value;
+ ResourceEntry* entry;
+ Value* value;
- // The entry string pool index to the entry's name.
- uint32_t entryKey;
+ // The entry string pool index to the entry's name.
+ uint32_t entryKey;
};
class MapFlattenVisitor : public RawValueVisitor {
-public:
- using RawValueVisitor::visit;
+ public:
+ using RawValueVisitor::visit;
- MapFlattenVisitor(ResTable_entry_ext* outEntry, BigBuffer* buffer) :
- mOutEntry(outEntry), mBuffer(buffer) {
+ MapFlattenVisitor(ResTable_entry_ext* outEntry, BigBuffer* buffer)
+ : mOutEntry(outEntry), mBuffer(buffer) {}
+
+ void visit(Attribute* attr) override {
+ {
+ Reference key = Reference(ResourceId(ResTable_map::ATTR_TYPE));
+ BinaryPrimitive val(Res_value::TYPE_INT_DEC, attr->typeMask);
+ flattenEntry(&key, &val);
}
- void visit(Attribute* attr) override {
- {
- Reference key = Reference(ResourceId(ResTable_map::ATTR_TYPE));
- BinaryPrimitive val(Res_value::TYPE_INT_DEC, attr->typeMask);
- flattenEntry(&key, &val);
- }
-
- if (attr->minInt != std::numeric_limits<int32_t>::min()) {
- Reference key = Reference(ResourceId(ResTable_map::ATTR_MIN));
- BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->minInt));
- flattenEntry(&key, &val);
- }
-
- if (attr->maxInt != std::numeric_limits<int32_t>::max()) {
- Reference key = Reference(ResourceId(ResTable_map::ATTR_MAX));
- BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->maxInt));
- flattenEntry(&key, &val);
- }
-
- for (Attribute::Symbol& s : attr->symbols) {
- BinaryPrimitive val(Res_value::TYPE_INT_DEC, s.value);
- flattenEntry(&s.symbol, &val);
- }
+ if (attr->minInt != std::numeric_limits<int32_t>::min()) {
+ Reference key = Reference(ResourceId(ResTable_map::ATTR_MIN));
+ BinaryPrimitive val(Res_value::TYPE_INT_DEC,
+ static_cast<uint32_t>(attr->minInt));
+ flattenEntry(&key, &val);
}
- void visit(Style* style) override {
- if (style->parent) {
- const Reference& parentRef = style->parent.value();
- assert(parentRef.id && "parent has no ID");
- mOutEntry->parent.ident = util::hostToDevice32(parentRef.id.value().id);
- }
-
- // Sort the style.
- std::sort(style->entries.begin(), style->entries.end(), cmpStyleEntries);
-
- for (Style::Entry& entry : style->entries) {
- flattenEntry(&entry.key, entry.value.get());
- }
+ if (attr->maxInt != std::numeric_limits<int32_t>::max()) {
+ Reference key = Reference(ResourceId(ResTable_map::ATTR_MAX));
+ BinaryPrimitive val(Res_value::TYPE_INT_DEC,
+ static_cast<uint32_t>(attr->maxInt));
+ flattenEntry(&key, &val);
}
- void visit(Styleable* styleable) override {
- for (auto& attrRef : styleable->entries) {
- BinaryPrimitive val(Res_value{});
- flattenEntry(&attrRef, &val);
- }
+ for (Attribute::Symbol& s : attr->symbols) {
+ BinaryPrimitive val(Res_value::TYPE_INT_DEC, s.value);
+ flattenEntry(&s.symbol, &val);
+ }
+ }
+ void visit(Style* style) override {
+ if (style->parent) {
+ const Reference& parentRef = style->parent.value();
+ assert(parentRef.id && "parent has no ID");
+ mOutEntry->parent.ident = util::hostToDevice32(parentRef.id.value().id);
}
- void visit(Array* array) override {
- for (auto& item : array->items) {
- ResTable_map* outEntry = mBuffer->nextBlock<ResTable_map>();
- flattenValue(item.get(), outEntry);
- outEntry->value.size = util::hostToDevice16(sizeof(outEntry->value));
- mEntryCount++;
- }
+ // Sort the style.
+ std::sort(style->entries.begin(), style->entries.end(), cmpStyleEntries);
+
+ for (Style::Entry& entry : style->entries) {
+ flattenEntry(&entry.key, entry.value.get());
}
+ }
- void visit(Plural* plural) override {
- const size_t count = plural->values.size();
- for (size_t i = 0; i < count; i++) {
- if (!plural->values[i]) {
- continue;
- }
-
- ResourceId q;
- switch (i) {
- case Plural::Zero:
- q.id = android::ResTable_map::ATTR_ZERO;
- break;
-
- case Plural::One:
- q.id = android::ResTable_map::ATTR_ONE;
- break;
-
- case Plural::Two:
- q.id = android::ResTable_map::ATTR_TWO;
- break;
-
- case Plural::Few:
- q.id = android::ResTable_map::ATTR_FEW;
- break;
-
- case Plural::Many:
- q.id = android::ResTable_map::ATTR_MANY;
- break;
-
- case Plural::Other:
- q.id = android::ResTable_map::ATTR_OTHER;
- break;
-
- default:
- assert(false);
- break;
- }
-
- Reference key(q);
- flattenEntry(&key, plural->values[i].get());
- }
+ void visit(Styleable* styleable) override {
+ for (auto& attrRef : styleable->entries) {
+ BinaryPrimitive val(Res_value{});
+ flattenEntry(&attrRef, &val);
}
+ }
- /**
- * Call this after visiting a Value. This will finish any work that
- * needs to be done to prepare the entry.
- */
- void finish() {
- mOutEntry->count = util::hostToDevice32(mEntryCount);
+ void visit(Array* array) override {
+ for (auto& item : array->items) {
+ ResTable_map* outEntry = mBuffer->nextBlock<ResTable_map>();
+ flattenValue(item.get(), outEntry);
+ outEntry->value.size = util::hostToDevice16(sizeof(outEntry->value));
+ mEntryCount++;
}
+ }
-private:
- void flattenKey(Reference* key, ResTable_map* outEntry) {
- assert(key->id && "key has no ID");
- outEntry->name.ident = util::hostToDevice32(key->id.value().id);
+ void visit(Plural* plural) override {
+ const size_t count = plural->values.size();
+ for (size_t i = 0; i < count; i++) {
+ if (!plural->values[i]) {
+ continue;
+ }
+
+ ResourceId q;
+ switch (i) {
+ case Plural::Zero:
+ q.id = android::ResTable_map::ATTR_ZERO;
+ break;
+
+ case Plural::One:
+ q.id = android::ResTable_map::ATTR_ONE;
+ break;
+
+ case Plural::Two:
+ q.id = android::ResTable_map::ATTR_TWO;
+ break;
+
+ case Plural::Few:
+ q.id = android::ResTable_map::ATTR_FEW;
+ break;
+
+ case Plural::Many:
+ q.id = android::ResTable_map::ATTR_MANY;
+ break;
+
+ case Plural::Other:
+ q.id = android::ResTable_map::ATTR_OTHER;
+ break;
+
+ default:
+ assert(false);
+ break;
+ }
+
+ Reference key(q);
+ flattenEntry(&key, plural->values[i].get());
}
+ }
- void flattenValue(Item* value, ResTable_map* outEntry) {
- bool result = value->flatten(&outEntry->value);
- assert(result && "flatten failed");
- }
+ /**
+ * Call this after visiting a Value. This will finish any work that
+ * needs to be done to prepare the entry.
+ */
+ void finish() { mOutEntry->count = util::hostToDevice32(mEntryCount); }
- void flattenEntry(Reference* key, Item* value) {
- ResTable_map* outEntry = mBuffer->nextBlock<ResTable_map>();
- flattenKey(key, outEntry);
- flattenValue(value, outEntry);
- outEntry->value.size = util::hostToDevice16(sizeof(outEntry->value));
- mEntryCount++;
- }
+ private:
+ void flattenKey(Reference* key, ResTable_map* outEntry) {
+ assert(key->id && "key has no ID");
+ outEntry->name.ident = util::hostToDevice32(key->id.value().id);
+ }
- ResTable_entry_ext* mOutEntry;
- BigBuffer* mBuffer;
- size_t mEntryCount = 0;
+ void flattenValue(Item* value, ResTable_map* outEntry) {
+ bool result = value->flatten(&outEntry->value);
+ assert(result && "flatten failed");
+ }
+
+ void flattenEntry(Reference* key, Item* value) {
+ ResTable_map* outEntry = mBuffer->nextBlock<ResTable_map>();
+ flattenKey(key, outEntry);
+ flattenValue(value, outEntry);
+ outEntry->value.size = util::hostToDevice16(sizeof(outEntry->value));
+ mEntryCount++;
+ }
+
+ ResTable_entry_ext* mOutEntry;
+ BigBuffer* mBuffer;
+ size_t mEntryCount = 0;
};
class PackageFlattener {
-public:
- PackageFlattener(IDiagnostics* diag, ResourceTablePackage* package) :
- mDiag(diag), mPackage(package) {
+ public:
+ PackageFlattener(IDiagnostics* diag, ResourceTablePackage* package)
+ : mDiag(diag), mPackage(package) {}
+
+ bool flattenPackage(BigBuffer* buffer) {
+ ChunkWriter pkgWriter(buffer);
+ ResTable_package* pkgHeader =
+ pkgWriter.startChunk<ResTable_package>(RES_TABLE_PACKAGE_TYPE);
+ pkgHeader->id = util::hostToDevice32(mPackage->id.value());
+
+ if (mPackage->name.size() >= arraysize(pkgHeader->name)) {
+ mDiag->error(DiagMessage() << "package name '" << mPackage->name
+ << "' is too long");
+ return false;
}
- bool flattenPackage(BigBuffer* buffer) {
- ChunkWriter pkgWriter(buffer);
- ResTable_package* pkgHeader = pkgWriter.startChunk<ResTable_package>(
- RES_TABLE_PACKAGE_TYPE);
- pkgHeader->id = util::hostToDevice32(mPackage->id.value());
+ // Copy the package name in device endianness.
+ strcpy16_htod(pkgHeader->name, arraysize(pkgHeader->name),
+ util::utf8ToUtf16(mPackage->name));
- if (mPackage->name.size() >= arraysize(pkgHeader->name)) {
- mDiag->error(DiagMessage() <<
- "package name '" << mPackage->name << "' is too long");
- return false;
- }
+ // Serialize the types. We do this now so that our type and key strings
+ // are populated. We write those first.
+ BigBuffer typeBuffer(1024);
+ flattenTypes(&typeBuffer);
- // Copy the package name in device endianness.
- strcpy16_htod(pkgHeader->name, arraysize(pkgHeader->name),
- util::utf8ToUtf16(mPackage->name));
+ pkgHeader->typeStrings = util::hostToDevice32(pkgWriter.size());
+ StringPool::flattenUtf16(pkgWriter.getBuffer(), mTypePool);
- // Serialize the types. We do this now so that our type and key strings
- // are populated. We write those first.
- BigBuffer typeBuffer(1024);
- flattenTypes(&typeBuffer);
+ pkgHeader->keyStrings = util::hostToDevice32(pkgWriter.size());
+ StringPool::flattenUtf8(pkgWriter.getBuffer(), mKeyPool);
- pkgHeader->typeStrings = util::hostToDevice32(pkgWriter.size());
- StringPool::flattenUtf16(pkgWriter.getBuffer(), mTypePool);
+ // Append the types.
+ buffer->appendBuffer(std::move(typeBuffer));
- pkgHeader->keyStrings = util::hostToDevice32(pkgWriter.size());
- StringPool::flattenUtf8(pkgWriter.getBuffer(), mKeyPool);
+ pkgWriter.finish();
+ return true;
+ }
- // Append the types.
- buffer->appendBuffer(std::move(typeBuffer));
+ private:
+ IDiagnostics* mDiag;
+ ResourceTablePackage* mPackage;
+ StringPool mTypePool;
+ StringPool mKeyPool;
- pkgWriter.finish();
- return true;
- }
-
-private:
- IDiagnostics* mDiag;
- ResourceTablePackage* mPackage;
- StringPool mTypePool;
- StringPool mKeyPool;
-
- template <typename T, bool IsItem>
- T* writeEntry(FlatEntry* entry, BigBuffer* buffer) {
- static_assert(std::is_same<ResTable_entry, T>::value ||
+ template <typename T, bool IsItem>
+ T* writeEntry(FlatEntry* entry, BigBuffer* buffer) {
+ static_assert(std::is_same<ResTable_entry, T>::value ||
std::is_same<ResTable_entry_ext, T>::value,
- "T must be ResTable_entry or ResTable_entry_ext");
+ "T must be ResTable_entry or ResTable_entry_ext");
- T* result = buffer->nextBlock<T>();
- ResTable_entry* outEntry = (ResTable_entry*)(result);
- if (entry->entry->symbolStatus.state == SymbolState::kPublic) {
- outEntry->flags |= ResTable_entry::FLAG_PUBLIC;
- }
-
- if (entry->value->isWeak()) {
- outEntry->flags |= ResTable_entry::FLAG_WEAK;
- }
-
- if (!IsItem) {
- outEntry->flags |= ResTable_entry::FLAG_COMPLEX;
- }
-
- outEntry->flags = util::hostToDevice16(outEntry->flags);
- outEntry->key.index = util::hostToDevice32(entry->entryKey);
- outEntry->size = util::hostToDevice16(sizeof(T));
- return result;
+ T* result = buffer->nextBlock<T>();
+ ResTable_entry* outEntry = (ResTable_entry*)(result);
+ if (entry->entry->symbolStatus.state == SymbolState::kPublic) {
+ outEntry->flags |= ResTable_entry::FLAG_PUBLIC;
}
- bool flattenValue(FlatEntry* entry, BigBuffer* buffer) {
- if (Item* item = valueCast<Item>(entry->value)) {
- writeEntry<ResTable_entry, true>(entry, buffer);
- Res_value* outValue = buffer->nextBlock<Res_value>();
- bool result = item->flatten(outValue);
- assert(result && "flatten failed");
- outValue->size = util::hostToDevice16(sizeof(*outValue));
- } else {
- ResTable_entry_ext* outEntry = writeEntry<ResTable_entry_ext, false>(entry, buffer);
- MapFlattenVisitor visitor(outEntry, buffer);
- entry->value->accept(&visitor);
- visitor.finish();
- }
- return true;
+ if (entry->value->isWeak()) {
+ outEntry->flags |= ResTable_entry::FLAG_WEAK;
}
- bool flattenConfig(const ResourceTableType* type, const ConfigDescription& config,
- std::vector<FlatEntry>* entries, BigBuffer* buffer) {
- ChunkWriter typeWriter(buffer);
- ResTable_type* typeHeader = typeWriter.startChunk<ResTable_type>(RES_TABLE_TYPE_TYPE);
- typeHeader->id = type->id.value();
- typeHeader->config = config;
- typeHeader->config.swapHtoD();
-
- auto maxAccum = [](uint32_t max, const std::unique_ptr<ResourceEntry>& a) -> uint32_t {
- return std::max(max, (uint32_t) a->id.value());
- };
-
- // Find the largest entry ID. That is how many entries we will have.
- const uint32_t entryCount =
- std::accumulate(type->entries.begin(), type->entries.end(), 0, maxAccum) + 1;
-
- typeHeader->entryCount = util::hostToDevice32(entryCount);
- uint32_t* indices = typeWriter.nextBlock<uint32_t>(entryCount);
-
- assert((size_t) entryCount <= std::numeric_limits<uint16_t>::max() + 1);
- memset(indices, 0xff, entryCount * sizeof(uint32_t));
-
- typeHeader->entriesStart = util::hostToDevice32(typeWriter.size());
-
- const size_t entryStart = typeWriter.getBuffer()->size();
- for (FlatEntry& flatEntry : *entries) {
- assert(flatEntry.entry->id.value() < entryCount);
- indices[flatEntry.entry->id.value()] = util::hostToDevice32(
- typeWriter.getBuffer()->size() - entryStart);
- if (!flattenValue(&flatEntry, typeWriter.getBuffer())) {
- mDiag->error(DiagMessage()
- << "failed to flatten resource '"
- << ResourceNameRef(mPackage->name, type->type, flatEntry.entry->name)
- << "' for configuration '" << config << "'");
- return false;
- }
- }
- typeWriter.finish();
- return true;
+ if (!IsItem) {
+ outEntry->flags |= ResTable_entry::FLAG_COMPLEX;
}
- std::vector<ResourceTableType*> collectAndSortTypes() {
- std::vector<ResourceTableType*> sortedTypes;
- for (auto& type : mPackage->types) {
- if (type->type == ResourceType::kStyleable) {
- // Styleables aren't real Resource Types, they are represented in the R.java
- // file.
- continue;
- }
+ outEntry->flags = util::hostToDevice16(outEntry->flags);
+ outEntry->key.index = util::hostToDevice32(entry->entryKey);
+ outEntry->size = util::hostToDevice16(sizeof(T));
+ return result;
+ }
- assert(type->id && "type must have an ID set");
+ bool flattenValue(FlatEntry* entry, BigBuffer* buffer) {
+ if (Item* item = valueCast<Item>(entry->value)) {
+ writeEntry<ResTable_entry, true>(entry, buffer);
+ Res_value* outValue = buffer->nextBlock<Res_value>();
+ bool result = item->flatten(outValue);
+ assert(result && "flatten failed");
+ outValue->size = util::hostToDevice16(sizeof(*outValue));
+ } else {
+ ResTable_entry_ext* outEntry =
+ writeEntry<ResTable_entry_ext, false>(entry, buffer);
+ MapFlattenVisitor visitor(outEntry, buffer);
+ entry->value->accept(&visitor);
+ visitor.finish();
+ }
+ return true;
+ }
- sortedTypes.push_back(type.get());
- }
- std::sort(sortedTypes.begin(), sortedTypes.end(), cmpIds<ResourceTableType>);
- return sortedTypes;
+ bool flattenConfig(const ResourceTableType* type,
+ const ConfigDescription& config,
+ std::vector<FlatEntry>* entries, BigBuffer* buffer) {
+ ChunkWriter typeWriter(buffer);
+ ResTable_type* typeHeader =
+ typeWriter.startChunk<ResTable_type>(RES_TABLE_TYPE_TYPE);
+ typeHeader->id = type->id.value();
+ typeHeader->config = config;
+ typeHeader->config.swapHtoD();
+
+ auto maxAccum = [](uint32_t max,
+ const std::unique_ptr<ResourceEntry>& a) -> uint32_t {
+ return std::max(max, (uint32_t)a->id.value());
+ };
+
+ // Find the largest entry ID. That is how many entries we will have.
+ const uint32_t entryCount =
+ std::accumulate(type->entries.begin(), type->entries.end(), 0,
+ maxAccum) +
+ 1;
+
+ typeHeader->entryCount = util::hostToDevice32(entryCount);
+ uint32_t* indices = typeWriter.nextBlock<uint32_t>(entryCount);
+
+ assert((size_t)entryCount <= std::numeric_limits<uint16_t>::max() + 1);
+ memset(indices, 0xff, entryCount * sizeof(uint32_t));
+
+ typeHeader->entriesStart = util::hostToDevice32(typeWriter.size());
+
+ const size_t entryStart = typeWriter.getBuffer()->size();
+ for (FlatEntry& flatEntry : *entries) {
+ assert(flatEntry.entry->id.value() < entryCount);
+ indices[flatEntry.entry->id.value()] =
+ util::hostToDevice32(typeWriter.getBuffer()->size() - entryStart);
+ if (!flattenValue(&flatEntry, typeWriter.getBuffer())) {
+ mDiag->error(DiagMessage()
+ << "failed to flatten resource '"
+ << ResourceNameRef(mPackage->name, type->type,
+ flatEntry.entry->name)
+ << "' for configuration '" << config << "'");
+ return false;
+ }
+ }
+ typeWriter.finish();
+ return true;
+ }
+
+ std::vector<ResourceTableType*> collectAndSortTypes() {
+ std::vector<ResourceTableType*> sortedTypes;
+ for (auto& type : mPackage->types) {
+ if (type->type == ResourceType::kStyleable) {
+ // Styleables aren't real Resource Types, they are represented in the
+ // R.java
+ // file.
+ continue;
+ }
+
+ assert(type->id && "type must have an ID set");
+
+ sortedTypes.push_back(type.get());
+ }
+ std::sort(sortedTypes.begin(), sortedTypes.end(),
+ cmpIds<ResourceTableType>);
+ return sortedTypes;
+ }
+
+ std::vector<ResourceEntry*> collectAndSortEntries(ResourceTableType* type) {
+ // Sort the entries by entry ID.
+ std::vector<ResourceEntry*> sortedEntries;
+ for (auto& entry : type->entries) {
+ assert(entry->id && "entry must have an ID set");
+ sortedEntries.push_back(entry.get());
+ }
+ std::sort(sortedEntries.begin(), sortedEntries.end(),
+ cmpIds<ResourceEntry>);
+ return sortedEntries;
+ }
+
+ bool flattenTypeSpec(ResourceTableType* type,
+ std::vector<ResourceEntry*>* sortedEntries,
+ BigBuffer* buffer) {
+ ChunkWriter typeSpecWriter(buffer);
+ ResTable_typeSpec* specHeader =
+ typeSpecWriter.startChunk<ResTable_typeSpec>(RES_TABLE_TYPE_SPEC_TYPE);
+ specHeader->id = type->id.value();
+
+ if (sortedEntries->empty()) {
+ typeSpecWriter.finish();
+ return true;
}
- std::vector<ResourceEntry*> collectAndSortEntries(ResourceTableType* type) {
- // Sort the entries by entry ID.
- std::vector<ResourceEntry*> sortedEntries;
- for (auto& entry : type->entries) {
- assert(entry->id && "entry must have an ID set");
- sortedEntries.push_back(entry.get());
+ // We can't just take the size of the vector. There may be holes in the
+ // entry ID space.
+ // Since the entries are sorted by ID, the last one will be the biggest.
+ const size_t numEntries = sortedEntries->back()->id.value() + 1;
+
+ specHeader->entryCount = util::hostToDevice32(numEntries);
+
+ // Reserve space for the masks of each resource in this type. These
+ // show for which configuration axis the resource changes.
+ uint32_t* configMasks = typeSpecWriter.nextBlock<uint32_t>(numEntries);
+
+ const size_t actualNumEntries = sortedEntries->size();
+ for (size_t entryIndex = 0; entryIndex < actualNumEntries; entryIndex++) {
+ ResourceEntry* entry = sortedEntries->at(entryIndex);
+
+ // Populate the config masks for this entry.
+
+ if (entry->symbolStatus.state == SymbolState::kPublic) {
+ configMasks[entry->id.value()] |=
+ util::hostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
+ }
+
+ const size_t configCount = entry->values.size();
+ for (size_t i = 0; i < configCount; i++) {
+ const ConfigDescription& config = entry->values[i]->config;
+ for (size_t j = i + 1; j < configCount; j++) {
+ configMasks[entry->id.value()] |=
+ util::hostToDevice32(config.diff(entry->values[j]->config));
}
- std::sort(sortedEntries.begin(), sortedEntries.end(), cmpIds<ResourceEntry>);
- return sortedEntries;
+ }
}
+ typeSpecWriter.finish();
+ return true;
+ }
- bool flattenTypeSpec(ResourceTableType* type, std::vector<ResourceEntry*>* sortedEntries,
- BigBuffer* buffer) {
- ChunkWriter typeSpecWriter(buffer);
- ResTable_typeSpec* specHeader = typeSpecWriter.startChunk<ResTable_typeSpec>(
- RES_TABLE_TYPE_SPEC_TYPE);
- specHeader->id = type->id.value();
+ bool flattenTypes(BigBuffer* buffer) {
+ // Sort the types by their IDs. They will be inserted into the StringPool in
+ // this order.
+ std::vector<ResourceTableType*> sortedTypes = collectAndSortTypes();
- if (sortedEntries->empty()) {
- typeSpecWriter.finish();
- return true;
+ size_t expectedTypeId = 1;
+ for (ResourceTableType* type : sortedTypes) {
+ // 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::stringstream typeName;
+ typeName << "?" << expectedTypeId;
+ mTypePool.makeRef(typeName.str());
+ expectedTypeId++;
+ }
+ expectedTypeId++;
+ mTypePool.makeRef(toString(type->type));
+
+ std::vector<ResourceEntry*> sortedEntries = collectAndSortEntries(type);
+
+ if (!flattenTypeSpec(type, &sortedEntries, buffer)) {
+ return false;
+ }
+
+ // The binary resource table lists resource entries for each
+ // configuration.
+ // We store them inverted, where a resource entry lists the values for
+ // each
+ // configuration available. Here we reverse this to match the binary
+ // table.
+ std::map<ConfigDescription, std::vector<FlatEntry>> configToEntryListMap;
+ for (ResourceEntry* entry : sortedEntries) {
+ const uint32_t keyIndex =
+ (uint32_t)mKeyPool.makeRef(entry->name).getIndex();
+
+ // Group values by configuration.
+ for (auto& configValue : entry->values) {
+ configToEntryListMap[configValue->config].push_back(
+ FlatEntry{entry, configValue->value.get(), keyIndex});
}
+ }
- // We can't just take the size of the vector. There may be holes in the entry ID space.
- // Since the entries are sorted by ID, the last one will be the biggest.
- const size_t numEntries = sortedEntries->back()->id.value() + 1;
-
- specHeader->entryCount = util::hostToDevice32(numEntries);
-
- // Reserve space for the masks of each resource in this type. These
- // show for which configuration axis the resource changes.
- uint32_t* configMasks = typeSpecWriter.nextBlock<uint32_t>(numEntries);
-
- const size_t actualNumEntries = sortedEntries->size();
- for (size_t entryIndex = 0; entryIndex < actualNumEntries; entryIndex++) {
- ResourceEntry* entry = sortedEntries->at(entryIndex);
-
- // Populate the config masks for this entry.
-
- if (entry->symbolStatus.state == SymbolState::kPublic) {
- configMasks[entry->id.value()] |=
- util::hostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
- }
-
- const size_t configCount = entry->values.size();
- for (size_t i = 0; i < configCount; i++) {
- const ConfigDescription& config = entry->values[i]->config;
- for (size_t j = i + 1; j < configCount; j++) {
- configMasks[entry->id.value()] |= util::hostToDevice32(
- config.diff(entry->values[j]->config));
- }
- }
+ // Flatten a configuration value.
+ for (auto& entry : configToEntryListMap) {
+ if (!flattenConfig(type, entry.first, &entry.second, buffer)) {
+ return false;
}
- typeSpecWriter.finish();
- return true;
+ }
}
-
- bool flattenTypes(BigBuffer* buffer) {
- // Sort the types by their IDs. They will be inserted into the StringPool in this order.
- std::vector<ResourceTableType*> sortedTypes = collectAndSortTypes();
-
- size_t expectedTypeId = 1;
- for (ResourceTableType* type : sortedTypes) {
- // 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::stringstream typeName;
- typeName << "?" << expectedTypeId;
- mTypePool.makeRef(typeName.str());
- expectedTypeId++;
- }
- expectedTypeId++;
- mTypePool.makeRef(toString(type->type));
-
- std::vector<ResourceEntry*> sortedEntries = collectAndSortEntries(type);
-
- if (!flattenTypeSpec(type, &sortedEntries, buffer)) {
- return false;
- }
-
- // The binary resource table lists resource entries for each configuration.
- // We store them inverted, where a resource entry lists the values for each
- // configuration available. Here we reverse this to match the binary table.
- std::map<ConfigDescription, std::vector<FlatEntry>> configToEntryListMap;
- for (ResourceEntry* entry : sortedEntries) {
- const uint32_t keyIndex = (uint32_t) mKeyPool.makeRef(entry->name).getIndex();
-
- // Group values by configuration.
- for (auto& configValue : entry->values) {
- configToEntryListMap[configValue->config].push_back(FlatEntry{
- entry, configValue->value.get(), keyIndex });
- }
- }
-
- // Flatten a configuration value.
- for (auto& entry : configToEntryListMap) {
- if (!flattenConfig(type, entry.first, &entry.second, buffer)) {
- return false;
- }
- }
- }
- return true;
- }
+ return true;
+ }
};
-} // namespace
+} // namespace
bool TableFlattener::consume(IAaptContext* context, ResourceTable* table) {
- // We must do this before writing the resources, since the string pool IDs may change.
- table->stringPool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
+ // We must do this before writing the resources, since the string pool IDs may
+ // change.
+ table->stringPool.sort(
+ [](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
int diff = a.context.priority - b.context.priority;
if (diff < 0) return true;
if (diff > 0) return false;
@@ -476,31 +492,32 @@
if (diff < 0) return true;
if (diff > 0) return false;
return a.value < b.value;
- });
- table->stringPool.prune();
+ });
+ table->stringPool.prune();
- // Write the ResTable header.
- ChunkWriter tableWriter(mBuffer);
- ResTable_header* tableHeader = tableWriter.startChunk<ResTable_header>(RES_TABLE_TYPE);
- tableHeader->packageCount = util::hostToDevice32(table->packages.size());
+ // Write the ResTable header.
+ ChunkWriter tableWriter(mBuffer);
+ ResTable_header* tableHeader =
+ tableWriter.startChunk<ResTable_header>(RES_TABLE_TYPE);
+ tableHeader->packageCount = util::hostToDevice32(table->packages.size());
- // Flatten the values string pool.
- StringPool::flattenUtf8(tableWriter.getBuffer(), table->stringPool);
+ // Flatten the values string pool.
+ StringPool::flattenUtf8(tableWriter.getBuffer(), table->stringPool);
- BigBuffer packageBuffer(1024);
+ BigBuffer packageBuffer(1024);
- // Flatten each package.
- for (auto& package : table->packages) {
- PackageFlattener flattener(context->getDiagnostics(), package.get());
- if (!flattener.flattenPackage(&packageBuffer)) {
- return false;
- }
+ // Flatten each package.
+ for (auto& package : table->packages) {
+ PackageFlattener flattener(context->getDiagnostics(), package.get());
+ if (!flattener.flattenPackage(&packageBuffer)) {
+ return false;
}
+ }
- // Finally merge all the packages into the main buffer.
- tableWriter.getBuffer()->appendBuffer(std::move(packageBuffer));
- tableWriter.finish();
- return true;
+ // Finally merge all the packages into the main buffer.
+ tableWriter.getBuffer()->appendBuffer(std::move(packageBuffer));
+ tableWriter.finish();
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/flatten/TableFlattener.h b/tools/aapt2/flatten/TableFlattener.h
index b416f20..91f9654 100644
--- a/tools/aapt2/flatten/TableFlattener.h
+++ b/tools/aapt2/flatten/TableFlattener.h
@@ -25,16 +25,15 @@
class ResourceTable;
class TableFlattener : public IResourceTableConsumer {
-public:
- explicit TableFlattener(BigBuffer* buffer) : mBuffer(buffer) {
- }
+ public:
+ explicit TableFlattener(BigBuffer* buffer) : mBuffer(buffer) {}
- bool consume(IAaptContext* context, ResourceTable* table) override;
+ bool consume(IAaptContext* context, ResourceTable* table) override;
-private:
- BigBuffer* mBuffer;
+ private:
+ BigBuffer* mBuffer;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_FLATTEN_TABLEFLATTENER_H */
diff --git a/tools/aapt2/flatten/TableFlattener_test.cpp b/tools/aapt2/flatten/TableFlattener_test.cpp
index b25bfa7..a7706bd 100644
--- a/tools/aapt2/flatten/TableFlattener_test.cpp
+++ b/tools/aapt2/flatten/TableFlattener_test.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "ResourceUtils.h"
#include "flatten/TableFlattener.h"
+#include "ResourceUtils.h"
#include "test/Test.h"
#include "unflatten/BinaryResourceParser.h"
#include "util/Util.h"
@@ -25,195 +25,207 @@
namespace aapt {
class TableFlattenerTest : public ::testing::Test {
-public:
- void SetUp() override {
- mContext = test::ContextBuilder()
- .setCompilationPackage("com.app.test")
- .setPackageId(0x7f)
- .build();
+ public:
+ void SetUp() override {
+ mContext = test::ContextBuilder()
+ .setCompilationPackage("com.app.test")
+ .setPackageId(0x7f)
+ .build();
+ }
+
+ ::testing::AssertionResult flatten(ResourceTable* table, ResTable* outTable) {
+ BigBuffer buffer(1024);
+ TableFlattener flattener(&buffer);
+ if (!flattener.consume(mContext.get(), table)) {
+ return ::testing::AssertionFailure() << "failed to flatten ResourceTable";
}
- ::testing::AssertionResult flatten(ResourceTable* table, ResTable* outTable) {
- BigBuffer buffer(1024);
- TableFlattener flattener(&buffer);
- if (!flattener.consume(mContext.get(), table)) {
- return ::testing::AssertionFailure() << "failed to flatten ResourceTable";
- }
+ std::unique_ptr<uint8_t[]> data = util::copy(buffer);
+ if (outTable->add(data.get(), buffer.size(), -1, true) != NO_ERROR) {
+ return ::testing::AssertionFailure() << "flattened ResTable is corrupt";
+ }
+ return ::testing::AssertionSuccess();
+ }
- std::unique_ptr<uint8_t[]> data = util::copy(buffer);
- if (outTable->add(data.get(), buffer.size(), -1, true) != NO_ERROR) {
- return ::testing::AssertionFailure() << "flattened ResTable is corrupt";
- }
- return ::testing::AssertionSuccess();
+ ::testing::AssertionResult flatten(ResourceTable* table,
+ ResourceTable* outTable) {
+ BigBuffer buffer(1024);
+ TableFlattener flattener(&buffer);
+ if (!flattener.consume(mContext.get(), table)) {
+ return ::testing::AssertionFailure() << "failed to flatten ResourceTable";
}
- ::testing::AssertionResult flatten(ResourceTable* table, ResourceTable* outTable) {
- BigBuffer buffer(1024);
- TableFlattener flattener(&buffer);
- if (!flattener.consume(mContext.get(), table)) {
- return ::testing::AssertionFailure() << "failed to flatten ResourceTable";
- }
+ std::unique_ptr<uint8_t[]> data = util::copy(buffer);
+ BinaryResourceParser parser(mContext.get(), outTable, {}, data.get(),
+ buffer.size());
+ if (!parser.parse()) {
+ return ::testing::AssertionFailure() << "flattened ResTable is corrupt";
+ }
+ return ::testing::AssertionSuccess();
+ }
- std::unique_ptr<uint8_t[]> data = util::copy(buffer);
- BinaryResourceParser parser(mContext.get(), outTable, {}, data.get(), buffer.size());
- if (!parser.parse()) {
- return ::testing::AssertionFailure() << "flattened ResTable is corrupt";
- }
- return ::testing::AssertionSuccess();
+ ::testing::AssertionResult exists(ResTable* table,
+ const StringPiece& expectedName,
+ const ResourceId& expectedId,
+ const ConfigDescription& expectedConfig,
+ const uint8_t expectedDataType,
+ const uint32_t expectedData,
+ const uint32_t expectedSpecFlags) {
+ const ResourceName expectedResName = test::parseNameOrDie(expectedName);
+
+ table->setParameters(&expectedConfig);
+
+ ResTable_config config;
+ Res_value val;
+ uint32_t specFlags;
+ if (table->getResource(expectedId.id, &val, false, 0, &specFlags, &config) <
+ 0) {
+ return ::testing::AssertionFailure() << "could not find resource with";
}
- ::testing::AssertionResult exists(ResTable* table,
- const StringPiece& expectedName,
- const ResourceId& expectedId,
- const ConfigDescription& expectedConfig,
- const uint8_t expectedDataType, const uint32_t expectedData,
- const uint32_t expectedSpecFlags) {
- const ResourceName expectedResName = test::parseNameOrDie(expectedName);
-
- table->setParameters(&expectedConfig);
-
- ResTable_config config;
- Res_value val;
- uint32_t specFlags;
- if (table->getResource(expectedId.id, &val, false, 0, &specFlags, &config) < 0) {
- return ::testing::AssertionFailure() << "could not find resource with";
- }
-
- if (expectedDataType != val.dataType) {
- return ::testing::AssertionFailure()
- << "expected data type "
- << std::hex << (int) expectedDataType << " but got data type "
- << (int) val.dataType << std::dec << " instead";
- }
-
- if (expectedData != val.data) {
- return ::testing::AssertionFailure()
- << "expected data "
- << std::hex << expectedData << " but got data "
- << val.data << std::dec << " instead";
- }
-
- if (expectedSpecFlags != specFlags) {
- return ::testing::AssertionFailure()
- << "expected specFlags "
- << std::hex << expectedSpecFlags << " but got specFlags "
- << specFlags << std::dec << " instead";
- }
-
- ResTable::resource_name actualName;
- if (!table->getResourceName(expectedId.id, false, &actualName)) {
- return ::testing::AssertionFailure() << "failed to find resource name";
- }
-
- Maybe<ResourceName> resName = ResourceUtils::toResourceName(actualName);
- if (!resName) {
- return ::testing::AssertionFailure()
- << "expected name '" << expectedResName << "' but got '"
- << StringPiece16(actualName.package, actualName.packageLen)
- << ":"
- << StringPiece16(actualName.type, actualName.typeLen)
- << "/"
- << StringPiece16(actualName.name, actualName.nameLen)
- << "'";
- }
-
- if (expectedConfig != config) {
- return ::testing::AssertionFailure()
- << "expected config '" << expectedConfig << "' but got '"
- << ConfigDescription(config) << "'";
- }
- return ::testing::AssertionSuccess();
+ if (expectedDataType != val.dataType) {
+ return ::testing::AssertionFailure()
+ << "expected data type " << std::hex << (int)expectedDataType
+ << " but got data type " << (int)val.dataType << std::dec
+ << " instead";
}
-private:
- std::unique_ptr<IAaptContext> mContext;
+ if (expectedData != val.data) {
+ return ::testing::AssertionFailure()
+ << "expected data " << std::hex << expectedData << " but got data "
+ << val.data << std::dec << " instead";
+ }
+
+ if (expectedSpecFlags != specFlags) {
+ return ::testing::AssertionFailure()
+ << "expected specFlags " << std::hex << expectedSpecFlags
+ << " but got specFlags " << specFlags << std::dec << " instead";
+ }
+
+ ResTable::resource_name actualName;
+ if (!table->getResourceName(expectedId.id, false, &actualName)) {
+ return ::testing::AssertionFailure() << "failed to find resource name";
+ }
+
+ Maybe<ResourceName> resName = ResourceUtils::toResourceName(actualName);
+ if (!resName) {
+ return ::testing::AssertionFailure()
+ << "expected name '" << expectedResName << "' but got '"
+ << StringPiece16(actualName.package, actualName.packageLen) << ":"
+ << StringPiece16(actualName.type, actualName.typeLen) << "/"
+ << StringPiece16(actualName.name, actualName.nameLen) << "'";
+ }
+
+ if (expectedConfig != config) {
+ return ::testing::AssertionFailure() << "expected config '"
+ << expectedConfig << "' but got '"
+ << ConfigDescription(config) << "'";
+ }
+ return ::testing::AssertionSuccess();
+ }
+
+ private:
+ std::unique_ptr<IAaptContext> mContext;
};
TEST_F(TableFlattenerTest, FlattenFullyLinkedTable) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .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("com.app.test:integer/one", test::parseConfigOrDie("v1"),
- ResourceId(0x7f030000),
- util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u))
- .addString("com.app.test:string/test", ResourceId(0x7f040000), "foo")
- .addString("com.app.test:layout/bar", ResourceId(0x7f050000), "res/layout/bar.xml")
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .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("com.app.test:integer/one", test::parseConfigOrDie("v1"),
+ ResourceId(0x7f030000),
+ util::make_unique<BinaryPrimitive>(
+ uint8_t(Res_value::TYPE_INT_DEC), 2u))
+ .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));
+ ResTable resTable;
+ ASSERT_TRUE(flatten(table.get(), &resTable));
- EXPECT_TRUE(exists(&resTable, "com.app.test:id/one", ResourceId(0x7f020000), {},
- Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+ EXPECT_TRUE(exists(&resTable, "com.app.test:id/one", ResourceId(0x7f020000),
+ {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
- EXPECT_TRUE(exists(&resTable, "com.app.test:id/two", ResourceId(0x7f020001), {},
- Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
+ EXPECT_TRUE(exists(&resTable, "com.app.test:id/two", ResourceId(0x7f020001),
+ {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
- EXPECT_TRUE(exists(&resTable, "com.app.test:id/three", ResourceId(0x7f020002), {},
- Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
+ EXPECT_TRUE(exists(&resTable, "com.app.test:id/three", ResourceId(0x7f020002),
+ {}, Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
- 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, "com.app.test:integer/one",
+ ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u,
+ ResTable_config::CONFIG_VERSION));
- EXPECT_TRUE(exists(&resTable, "com.app.test:integer/one", ResourceId(0x7f030000),
- test::parseConfigOrDie("v1"), Res_value::TYPE_INT_DEC, 2u,
- ResTable_config::CONFIG_VERSION));
+ EXPECT_TRUE(exists(&resTable, "com.app.test:integer/one",
+ ResourceId(0x7f030000), test::parseConfigOrDie("v1"),
+ Res_value::TYPE_INT_DEC, 2u,
+ ResTable_config::CONFIG_VERSION));
- std::u16string fooStr = u"foo";
- ssize_t idx = resTable.getTableStringBlock(0)->indexOfString(fooStr.data(), fooStr.size());
- ASSERT_GE(idx, 0);
- EXPECT_TRUE(exists(&resTable, "com.app.test:string/test", ResourceId(0x7f040000),
- {}, Res_value::TYPE_STRING, (uint32_t) idx, 0u));
+ std::u16string fooStr = u"foo";
+ ssize_t idx = resTable.getTableStringBlock(0)->indexOfString(fooStr.data(),
+ fooStr.size());
+ ASSERT_GE(idx, 0);
+ EXPECT_TRUE(exists(&resTable, "com.app.test:string/test",
+ ResourceId(0x7f040000), {}, Res_value::TYPE_STRING,
+ (uint32_t)idx, 0u));
- 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, "com.app.test:layout/bar", ResourceId(0x7f050000), {},
- Res_value::TYPE_STRING, (uint32_t) idx, 0u));
+ 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, "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("com.app.test", 0x7f)
- .addSimple("com.app.test:id/one", ResourceId(0x7f020001))
- .addSimple("com.app.test:id/three", ResourceId(0x7f020003))
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .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));
+ ResTable resTable;
+ ASSERT_TRUE(flatten(table.get(), &resTable));
- 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, "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));
}
TEST_F(TableFlattenerTest, FlattenMinMaxAttributes) {
- Attribute attr(false);
- attr.typeMask = android::ResTable_map::TYPE_INTEGER;
- attr.minInt = 10;
- attr.maxInt = 23;
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("android", 0x01)
- .addValue("android:attr/foo", ResourceId(0x01010000),
- util::make_unique<Attribute>(attr))
- .build();
+ Attribute attr(false);
+ attr.typeMask = android::ResTable_map::TYPE_INTEGER;
+ attr.minInt = 10;
+ attr.maxInt = 23;
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .setPackageId("android", 0x01)
+ .addValue("android:attr/foo", ResourceId(0x01010000),
+ util::make_unique<Attribute>(attr))
+ .build();
- ResourceTable result;
- ASSERT_TRUE(flatten(table.get(), &result));
+ ResourceTable result;
+ ASSERT_TRUE(flatten(table.get(), &result));
- 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);
- EXPECT_EQ(attr.minInt, actualAttr->minInt);
- EXPECT_EQ(attr.maxInt, actualAttr->maxInt);
+ 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);
+ EXPECT_EQ(attr.minInt, actualAttr->minInt);
+ EXPECT_EQ(attr.maxInt, actualAttr->maxInt);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/flatten/XmlFlattener.cpp
index ed5b60f..c296dde 100644
--- a/tools/aapt2/flatten/XmlFlattener.cpp
+++ b/tools/aapt2/flatten/XmlFlattener.cpp
@@ -14,16 +14,16 @@
* limitations under the License.
*/
+#include "flatten/XmlFlattener.h"
#include "SdkConstants.h"
#include "flatten/ChunkWriter.h"
#include "flatten/ResourceTypeExtensions.h"
-#include "flatten/XmlFlattener.h"
#include "xml/XmlDom.h"
#include <androidfw/ResourceTypes.h>
+#include <utils/misc.h>
#include <algorithm>
#include <map>
-#include <utils/misc.h>
#include <vector>
using namespace android;
@@ -35,300 +35,316 @@
constexpr uint32_t kLowPriority = 0xffffffffu;
struct XmlFlattenerVisitor : public xml::Visitor {
- using xml::Visitor::visit;
+ using xml::Visitor::visit;
- BigBuffer* mBuffer;
- XmlFlattenerOptions mOptions;
- StringPool mPool;
- std::map<uint8_t, StringPool> mPackagePools;
+ BigBuffer* mBuffer;
+ XmlFlattenerOptions mOptions;
+ StringPool mPool;
+ std::map<uint8_t, StringPool> mPackagePools;
- struct StringFlattenDest {
- StringPool::Ref ref;
- ResStringPool_ref* dest;
- };
- std::vector<StringFlattenDest> mStringRefs;
+ struct StringFlattenDest {
+ StringPool::Ref ref;
+ ResStringPool_ref* dest;
+ };
+ std::vector<StringFlattenDest> mStringRefs;
- // Scratch vector to filter attributes. We avoid allocations
- // making this a member.
- std::vector<xml::Attribute*> mFilteredAttrs;
+ // Scratch vector to filter attributes. We avoid allocations
+ // making this a member.
+ std::vector<xml::Attribute*> mFilteredAttrs;
+ XmlFlattenerVisitor(BigBuffer* buffer, XmlFlattenerOptions options)
+ : mBuffer(buffer), mOptions(options) {}
- XmlFlattenerVisitor(BigBuffer* buffer, XmlFlattenerOptions options) :
- mBuffer(buffer), mOptions(options) {
+ 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});
+ }
+ }
+
+ void addString(const StringPool::Ref& ref, android::ResStringPool_ref* dest) {
+ mStringRefs.push_back(StringFlattenDest{ref, dest});
+ }
+
+ void writeNamespace(xml::Namespace* node, uint16_t type) {
+ ChunkWriter writer(mBuffer);
+
+ ResXMLTree_node* flatNode = writer.startChunk<ResXMLTree_node>(type);
+ flatNode->lineNumber = util::hostToDevice32(node->lineNumber);
+ flatNode->comment.index = util::hostToDevice32(-1);
+
+ ResXMLTree_namespaceExt* flatNs =
+ writer.nextBlock<ResXMLTree_namespaceExt>();
+ addString(node->namespacePrefix, kLowPriority, &flatNs->prefix);
+ addString(node->namespaceUri, kLowPriority, &flatNs->uri);
+
+ writer.finish();
+ }
+
+ void visit(xml::Namespace* node) override {
+ if (node->namespaceUri == xml::kSchemaTools) {
+ // Skip dedicated tools namespace.
+ xml::Visitor::visit(node);
+ } else {
+ writeNamespace(node, android::RES_XML_START_NAMESPACE_TYPE);
+ xml::Visitor::visit(node);
+ writeNamespace(node, android::RES_XML_END_NAMESPACE_TYPE);
+ }
+ }
+
+ void visit(xml::Text* node) override {
+ if (util::trimWhitespace(node->text).empty()) {
+ // Skip whitespace only text nodes.
+ return;
}
- 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 });
- }
+ ChunkWriter writer(mBuffer);
+ ResXMLTree_node* flatNode =
+ writer.startChunk<ResXMLTree_node>(RES_XML_CDATA_TYPE);
+ flatNode->lineNumber = util::hostToDevice32(node->lineNumber);
+ flatNode->comment.index = util::hostToDevice32(-1);
+
+ ResXMLTree_cdataExt* flatText = writer.nextBlock<ResXMLTree_cdataExt>();
+ addString(node->text, kLowPriority, &flatText->data);
+
+ writer.finish();
+ }
+
+ void visit(xml::Element* node) override {
+ {
+ ChunkWriter startWriter(mBuffer);
+ ResXMLTree_node* flatNode =
+ startWriter.startChunk<ResXMLTree_node>(RES_XML_START_ELEMENT_TYPE);
+ flatNode->lineNumber = util::hostToDevice32(node->lineNumber);
+ flatNode->comment.index = util::hostToDevice32(-1);
+
+ ResXMLTree_attrExt* flatElem =
+ startWriter.nextBlock<ResXMLTree_attrExt>();
+
+ // 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));
+
+ writeAttributes(node, flatElem, &startWriter);
+
+ startWriter.finish();
}
- void addString(const StringPool::Ref& ref, android::ResStringPool_ref* dest) {
- mStringRefs.push_back(StringFlattenDest{ ref, dest });
- }
-
- void writeNamespace(xml::Namespace* node, uint16_t type) {
- ChunkWriter writer(mBuffer);
-
- ResXMLTree_node* flatNode = writer.startChunk<ResXMLTree_node>(type);
- flatNode->lineNumber = util::hostToDevice32(node->lineNumber);
- flatNode->comment.index = util::hostToDevice32(-1);
-
- ResXMLTree_namespaceExt* flatNs = writer.nextBlock<ResXMLTree_namespaceExt>();
- addString(node->namespacePrefix, kLowPriority, &flatNs->prefix);
- addString(node->namespaceUri, kLowPriority, &flatNs->uri);
-
- writer.finish();
- }
-
- void visit(xml::Namespace* node) override {
- if (node->namespaceUri == xml::kSchemaTools) {
- // Skip dedicated tools namespace.
- xml::Visitor::visit(node);
- } else {
- writeNamespace(node, android::RES_XML_START_NAMESPACE_TYPE);
- xml::Visitor::visit(node);
- writeNamespace(node, android::RES_XML_END_NAMESPACE_TYPE);
- }
- }
-
- void visit(xml::Text* node) override {
- if (util::trimWhitespace(node->text).empty()) {
- // Skip whitespace only text nodes.
- return;
- }
-
- ChunkWriter writer(mBuffer);
- ResXMLTree_node* flatNode = writer.startChunk<ResXMLTree_node>(RES_XML_CDATA_TYPE);
- flatNode->lineNumber = util::hostToDevice32(node->lineNumber);
- flatNode->comment.index = util::hostToDevice32(-1);
-
- ResXMLTree_cdataExt* flatText = writer.nextBlock<ResXMLTree_cdataExt>();
- addString(node->text, kLowPriority, &flatText->data);
-
- writer.finish();
- }
-
- void visit(xml::Element* node) override {
- {
- ChunkWriter startWriter(mBuffer);
- ResXMLTree_node* flatNode = startWriter.startChunk<ResXMLTree_node>(
- RES_XML_START_ELEMENT_TYPE);
- flatNode->lineNumber = util::hostToDevice32(node->lineNumber);
- flatNode->comment.index = util::hostToDevice32(-1);
-
- ResXMLTree_attrExt* flatElem = startWriter.nextBlock<ResXMLTree_attrExt>();
-
- // 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));
-
- writeAttributes(node, flatElem, &startWriter);
-
- startWriter.finish();
- }
-
- xml::Visitor::visit(node);
-
- {
- ChunkWriter endWriter(mBuffer);
- ResXMLTree_node* flatEndNode = endWriter.startChunk<ResXMLTree_node>(
- RES_XML_END_ELEMENT_TYPE);
- flatEndNode->lineNumber = util::hostToDevice32(node->lineNumber);
- flatEndNode->comment.index = util::hostToDevice32(-1);
-
- ResXMLTree_endElementExt* flatEndElem = endWriter.nextBlock<ResXMLTree_endElementExt>();
- addString(node->namespaceUri, kLowPriority, &flatEndElem->ns,
- true /* treatEmptyStringAsNull */);
- addString(node->name, kLowPriority, &flatEndElem->name);
-
- endWriter.finish();
- }
- }
-
- static bool cmpXmlAttributeById(const xml::Attribute* a, const xml::Attribute* b) {
- if (a->compiledAttribute && a->compiledAttribute.value().id) {
- if (b->compiledAttribute && b->compiledAttribute.value().id) {
- return a->compiledAttribute.value().id.value() < b->compiledAttribute.value().id.value();
- }
- return true;
- } else if (!b->compiledAttribute) {
- int diff = a->namespaceUri.compare(b->namespaceUri);
- if (diff < 0) {
- return true;
- } else if (diff > 0) {
- return false;
- }
- return a->name < b->name;
- }
- return false;
- }
-
- void writeAttributes(xml::Element* node, ResXMLTree_attrExt* flatElem, ChunkWriter* writer) {
- mFilteredAttrs.clear();
- mFilteredAttrs.reserve(node->attributes.size());
-
- // Filter the attributes.
- for (xml::Attribute& attr : node->attributes) {
- if (mOptions.maxSdkLevel && attr.compiledAttribute && attr.compiledAttribute.value().id) {
- size_t sdkLevel = findAttributeSdkLevel(attr.compiledAttribute.value().id.value());
- if (sdkLevel > mOptions.maxSdkLevel.value()) {
- continue;
- }
- }
- if (attr.namespaceUri == xml::kSchemaTools) {
- continue;
- }
- mFilteredAttrs.push_back(&attr);
- }
-
- if (mFilteredAttrs.empty()) {
- return;
- }
-
- const ResourceId kIdAttr(0x010100d0);
-
- std::sort(mFilteredAttrs.begin(), mFilteredAttrs.end(), cmpXmlAttributeById);
-
- flatElem->attributeCount = util::hostToDevice16(mFilteredAttrs.size());
-
- ResXMLTree_attribute* flatAttr = writer->nextBlock<ResXMLTree_attribute>(
- mFilteredAttrs.size());
- uint16_t attributeIndex = 1;
- for (const xml::Attribute* xmlAttr : mFilteredAttrs) {
- // Assign the indices for specific attributes.
- if (xmlAttr->compiledAttribute && xmlAttr->compiledAttribute.value().id &&
- xmlAttr->compiledAttribute.value().id.value() == kIdAttr) {
- flatElem->idIndex = util::hostToDevice16(attributeIndex);
- } else if (xmlAttr->namespaceUri.empty()) {
- if (xmlAttr->name == "class") {
- flatElem->classIndex = util::hostToDevice16(attributeIndex);
- } else if (xmlAttr->name == "style") {
- flatElem->styleIndex = util::hostToDevice16(attributeIndex);
- }
- }
- attributeIndex++;
-
- // 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);
-
- if (!xmlAttr->compiledAttribute || !xmlAttr->compiledAttribute.value().id) {
- // The attribute has no associated ResourceID, so the string order doesn't matter.
- addString(xmlAttr->name, kLowPriority, &flatAttr->name);
- } else {
- // Attribute names are stored without packages, but we use
- // their StringPool index to lookup their resource IDs.
- // This will cause collisions, so we can't dedupe
- // attribute names from different packages. We use separate
- // pools that we later combine.
- //
- // Lookup the StringPool for this package and make the reference there.
- const xml::AaptAttribute& aaptAttr = xmlAttr->compiledAttribute.value();
-
- StringPool::Ref nameRef = mPackagePools[aaptAttr.id.value().packageId()].makeRef(
- xmlAttr->name, StringPool::Context{ aaptAttr.id.value().id });
-
- // Add it to the list of strings to flatten.
- addString(nameRef, &flatAttr->name);
- }
-
- if (mOptions.keepRawValues || !xmlAttr->compiledValue) {
- // Keep raw values if the value is not compiled or
- // if we're building a static library (need symbols).
- addString(xmlAttr->value, kLowPriority, &flatAttr->rawValue);
- }
-
- if (xmlAttr->compiledValue) {
- bool result = xmlAttr->compiledValue->flatten(&flatAttr->typedValue);
- assert(result);
- } else {
- // Flatten as a regular string type.
- flatAttr->typedValue.dataType = android::Res_value::TYPE_STRING;
- addString(xmlAttr->value, kLowPriority,
- (ResStringPool_ref*) &flatAttr->typedValue.data);
- }
-
- flatAttr->typedValue.size = util::hostToDevice16(sizeof(flatAttr->typedValue));
- flatAttr++;
- }
- }
-};
-
-} // namespace
-
-bool XmlFlattener::flatten(IAaptContext* context, xml::Node* node) {
- BigBuffer nodeBuffer(1024);
- XmlFlattenerVisitor visitor(&nodeBuffer, mOptions);
- node->accept(&visitor);
-
- // Merge the package pools into the main pool.
- for (auto& packagePoolEntry : visitor.mPackagePools) {
- visitor.mPool.merge(std::move(packagePoolEntry.second));
- }
-
- // Sort the string pool so that attribute resource IDs show up first.
- visitor.mPool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
- return a.context.priority < b.context.priority;
- });
-
- // Now we flatten the string pool references into the correct places.
- for (const auto& refEntry : visitor.mStringRefs) {
- refEntry.dest->index = util::hostToDevice32(refEntry.ref.getIndex());
- }
-
- // Write the XML header.
- ChunkWriter xmlHeaderWriter(mBuffer);
- xmlHeaderWriter.startChunk<ResXMLTree_header>(RES_XML_TYPE);
-
- // Flatten the StringPool.
- StringPool::flattenUtf8(mBuffer, visitor.mPool);
+ xml::Visitor::visit(node);
{
- // Write the array of resource IDs, indexed by StringPool order.
- ChunkWriter resIdMapWriter(mBuffer);
- resIdMapWriter.startChunk<ResChunk_header>(RES_XML_RESOURCE_MAP_TYPE);
- for (const auto& str : visitor.mPool) {
- ResourceId id = { str->context.priority };
- if (id.id == kLowPriority || !id.isValid()) {
- // When we see the first non-resource ID,
- // we're done.
- break;
- }
+ ChunkWriter endWriter(mBuffer);
+ ResXMLTree_node* flatEndNode =
+ endWriter.startChunk<ResXMLTree_node>(RES_XML_END_ELEMENT_TYPE);
+ flatEndNode->lineNumber = util::hostToDevice32(node->lineNumber);
+ flatEndNode->comment.index = util::hostToDevice32(-1);
- *resIdMapWriter.nextBlock<uint32_t>() = id.id;
+ ResXMLTree_endElementExt* flatEndElem =
+ endWriter.nextBlock<ResXMLTree_endElementExt>();
+ addString(node->namespaceUri, kLowPriority, &flatEndElem->ns,
+ true /* treatEmptyStringAsNull */);
+ addString(node->name, kLowPriority, &flatEndElem->name);
+
+ endWriter.finish();
+ }
+ }
+
+ static bool cmpXmlAttributeById(const xml::Attribute* a,
+ const xml::Attribute* b) {
+ if (a->compiledAttribute && a->compiledAttribute.value().id) {
+ if (b->compiledAttribute && b->compiledAttribute.value().id) {
+ return a->compiledAttribute.value().id.value() <
+ b->compiledAttribute.value().id.value();
+ }
+ return true;
+ } else if (!b->compiledAttribute) {
+ int diff = a->namespaceUri.compare(b->namespaceUri);
+ if (diff < 0) {
+ return true;
+ } else if (diff > 0) {
+ return false;
+ }
+ return a->name < b->name;
+ }
+ return false;
+ }
+
+ void writeAttributes(xml::Element* node, ResXMLTree_attrExt* flatElem,
+ ChunkWriter* writer) {
+ mFilteredAttrs.clear();
+ mFilteredAttrs.reserve(node->attributes.size());
+
+ // Filter the attributes.
+ for (xml::Attribute& attr : node->attributes) {
+ if (mOptions.maxSdkLevel && attr.compiledAttribute &&
+ attr.compiledAttribute.value().id) {
+ size_t sdkLevel =
+ findAttributeSdkLevel(attr.compiledAttribute.value().id.value());
+ if (sdkLevel > mOptions.maxSdkLevel.value()) {
+ continue;
}
- resIdMapWriter.finish();
+ }
+ if (attr.namespaceUri == xml::kSchemaTools) {
+ continue;
+ }
+ mFilteredAttrs.push_back(&attr);
}
- // Move the nodeBuffer and append it to the out buffer.
- mBuffer->appendBuffer(std::move(nodeBuffer));
+ if (mFilteredAttrs.empty()) {
+ return;
+ }
- // Finish the xml header.
- xmlHeaderWriter.finish();
- return true;
+ const ResourceId kIdAttr(0x010100d0);
+
+ std::sort(mFilteredAttrs.begin(), mFilteredAttrs.end(),
+ cmpXmlAttributeById);
+
+ flatElem->attributeCount = util::hostToDevice16(mFilteredAttrs.size());
+
+ ResXMLTree_attribute* flatAttr =
+ writer->nextBlock<ResXMLTree_attribute>(mFilteredAttrs.size());
+ uint16_t attributeIndex = 1;
+ for (const xml::Attribute* xmlAttr : mFilteredAttrs) {
+ // Assign the indices for specific attributes.
+ if (xmlAttr->compiledAttribute && xmlAttr->compiledAttribute.value().id &&
+ xmlAttr->compiledAttribute.value().id.value() == kIdAttr) {
+ flatElem->idIndex = util::hostToDevice16(attributeIndex);
+ } else if (xmlAttr->namespaceUri.empty()) {
+ if (xmlAttr->name == "class") {
+ flatElem->classIndex = util::hostToDevice16(attributeIndex);
+ } else if (xmlAttr->name == "style") {
+ flatElem->styleIndex = util::hostToDevice16(attributeIndex);
+ }
+ }
+ attributeIndex++;
+
+ // 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);
+
+ if (!xmlAttr->compiledAttribute ||
+ !xmlAttr->compiledAttribute.value().id) {
+ // The attribute has no associated ResourceID, so the string order
+ // doesn't matter.
+ addString(xmlAttr->name, kLowPriority, &flatAttr->name);
+ } else {
+ // Attribute names are stored without packages, but we use
+ // their StringPool index to lookup their resource IDs.
+ // This will cause collisions, so we can't dedupe
+ // attribute names from different packages. We use separate
+ // pools that we later combine.
+ //
+ // Lookup the StringPool for this package and make the reference there.
+ const xml::AaptAttribute& aaptAttr = xmlAttr->compiledAttribute.value();
+
+ StringPool::Ref nameRef =
+ mPackagePools[aaptAttr.id.value().packageId()].makeRef(
+ xmlAttr->name, StringPool::Context{aaptAttr.id.value().id});
+
+ // Add it to the list of strings to flatten.
+ addString(nameRef, &flatAttr->name);
+ }
+
+ if (mOptions.keepRawValues || !xmlAttr->compiledValue) {
+ // Keep raw values if the value is not compiled or
+ // if we're building a static library (need symbols).
+ addString(xmlAttr->value, kLowPriority, &flatAttr->rawValue);
+ }
+
+ if (xmlAttr->compiledValue) {
+ bool result = xmlAttr->compiledValue->flatten(&flatAttr->typedValue);
+ assert(result);
+ } else {
+ // Flatten as a regular string type.
+ flatAttr->typedValue.dataType = android::Res_value::TYPE_STRING;
+ addString(xmlAttr->value, kLowPriority,
+ (ResStringPool_ref*)&flatAttr->typedValue.data);
+ }
+
+ flatAttr->typedValue.size =
+ util::hostToDevice16(sizeof(flatAttr->typedValue));
+ flatAttr++;
+ }
+ }
+};
+
+} // namespace
+
+bool XmlFlattener::flatten(IAaptContext* context, xml::Node* node) {
+ BigBuffer nodeBuffer(1024);
+ XmlFlattenerVisitor visitor(&nodeBuffer, mOptions);
+ node->accept(&visitor);
+
+ // Merge the package pools into the main pool.
+ for (auto& packagePoolEntry : visitor.mPackagePools) {
+ visitor.mPool.merge(std::move(packagePoolEntry.second));
+ }
+
+ // Sort the string pool so that attribute resource IDs show up first.
+ visitor.mPool.sort(
+ [](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
+ return a.context.priority < b.context.priority;
+ });
+
+ // Now we flatten the string pool references into the correct places.
+ for (const auto& refEntry : visitor.mStringRefs) {
+ refEntry.dest->index = util::hostToDevice32(refEntry.ref.getIndex());
+ }
+
+ // Write the XML header.
+ ChunkWriter xmlHeaderWriter(mBuffer);
+ xmlHeaderWriter.startChunk<ResXMLTree_header>(RES_XML_TYPE);
+
+ // Flatten the StringPool.
+ StringPool::flattenUtf8(mBuffer, visitor.mPool);
+
+ {
+ // Write the array of resource IDs, indexed by StringPool order.
+ ChunkWriter resIdMapWriter(mBuffer);
+ resIdMapWriter.startChunk<ResChunk_header>(RES_XML_RESOURCE_MAP_TYPE);
+ for (const auto& str : visitor.mPool) {
+ ResourceId id = {str->context.priority};
+ if (id.id == kLowPriority || !id.isValid()) {
+ // When we see the first non-resource ID,
+ // we're done.
+ break;
+ }
+
+ *resIdMapWriter.nextBlock<uint32_t>() = id.id;
+ }
+ resIdMapWriter.finish();
+ }
+
+ // Move the nodeBuffer and append it to the out buffer.
+ mBuffer->appendBuffer(std::move(nodeBuffer));
+
+ // Finish the xml header.
+ xmlHeaderWriter.finish();
+ return true;
}
bool XmlFlattener::consume(IAaptContext* context, xml::XmlResource* resource) {
- if (!resource->root) {
- return false;
- }
- return flatten(context, resource->root.get());
+ if (!resource->root) {
+ return false;
+ }
+ return flatten(context, resource->root.get());
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/flatten/XmlFlattener.h b/tools/aapt2/flatten/XmlFlattener.h
index a688ac9..d8d592b 100644
--- a/tools/aapt2/flatten/XmlFlattener.h
+++ b/tools/aapt2/flatten/XmlFlattener.h
@@ -24,32 +24,31 @@
namespace aapt {
struct XmlFlattenerOptions {
- /**
- * Keep attribute raw string values along with typed values.
- */
- bool keepRawValues = false;
+ /**
+ * Keep attribute raw string values along with typed values.
+ */
+ bool keepRawValues = false;
- /**
- * If set, the max SDK level of attribute to flatten. All others are ignored.
- */
- Maybe<size_t> maxSdkLevel;
+ /**
+ * If set, the max SDK level of attribute to flatten. All others are ignored.
+ */
+ Maybe<size_t> maxSdkLevel;
};
class XmlFlattener : public IXmlResourceConsumer {
-public:
- XmlFlattener(BigBuffer* buffer, XmlFlattenerOptions options) :
- mBuffer(buffer), mOptions(options) {
- }
+ public:
+ XmlFlattener(BigBuffer* buffer, XmlFlattenerOptions options)
+ : mBuffer(buffer), mOptions(options) {}
- bool consume(IAaptContext* context, xml::XmlResource* resource) override;
+ bool consume(IAaptContext* context, xml::XmlResource* resource) override;
-private:
- BigBuffer* mBuffer;
- XmlFlattenerOptions mOptions;
+ private:
+ BigBuffer* mBuffer;
+ XmlFlattenerOptions mOptions;
- bool flatten(IAaptContext* context, xml::Node* node);
+ bool flatten(IAaptContext* context, xml::Node* node);
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_FLATTEN_XMLFLATTENER_H */
diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/flatten/XmlFlattener_test.cpp
index 4d1e178..e0159cb 100644
--- a/tools/aapt2/flatten/XmlFlattener_test.cpp
+++ b/tools/aapt2/flatten/XmlFlattener_test.cpp
@@ -25,230 +25,240 @@
namespace aapt {
class XmlFlattenerTest : public ::testing::Test {
-public:
- void SetUp() override {
- mContext = test::ContextBuilder()
- .setCompilationPackage("com.app.test")
- .setNameManglerPolicy(NameManglerPolicy{ "com.app.test" })
- .addSymbolSource(test::StaticSymbolSourceBuilder()
- .addSymbol("android:attr/id", ResourceId(0x010100d0),
- test::AttributeBuilder().build())
- .addSymbol("com.app.test:id/id", ResourceId(0x7f020000))
- .addSymbol("android:attr/paddingStart", ResourceId(0x010103b3),
- test::AttributeBuilder().build())
- .addSymbol("android:attr/colorAccent", ResourceId(0x01010435),
- test::AttributeBuilder().build())
- .build())
- .build();
+ public:
+ void SetUp() override {
+ mContext =
+ test::ContextBuilder()
+ .setCompilationPackage("com.app.test")
+ .setNameManglerPolicy(NameManglerPolicy{"com.app.test"})
+ .addSymbolSource(
+ test::StaticSymbolSourceBuilder()
+ .addSymbol("android:attr/id", ResourceId(0x010100d0),
+ test::AttributeBuilder().build())
+ .addSymbol("com.app.test:id/id", ResourceId(0x7f020000))
+ .addSymbol("android:attr/paddingStart",
+ ResourceId(0x010103b3),
+ test::AttributeBuilder().build())
+ .addSymbol("android:attr/colorAccent",
+ ResourceId(0x01010435),
+ test::AttributeBuilder().build())
+ .build())
+ .build();
+ }
+
+ ::testing::AssertionResult flatten(xml::XmlResource* doc,
+ android::ResXMLTree* outTree,
+ const XmlFlattenerOptions& options = {}) {
+ using namespace android; // For NO_ERROR on windows because it is a macro.
+
+ BigBuffer buffer(1024);
+ XmlFlattener flattener(&buffer, options);
+ if (!flattener.consume(mContext.get(), doc)) {
+ return ::testing::AssertionFailure() << "failed to flatten XML Tree";
}
- ::testing::AssertionResult flatten(xml::XmlResource* doc, android::ResXMLTree* outTree,
- const XmlFlattenerOptions& options = {}) {
- using namespace android; // For NO_ERROR on windows because it is a macro.
-
- BigBuffer buffer(1024);
- XmlFlattener flattener(&buffer, options);
- if (!flattener.consume(mContext.get(), doc)) {
- return ::testing::AssertionFailure() << "failed to flatten XML Tree";
- }
-
- std::unique_ptr<uint8_t[]> data = util::copy(buffer);
- if (outTree->setTo(data.get(), buffer.size(), true) != NO_ERROR) {
- return ::testing::AssertionFailure() << "flattened XML is corrupt";
- }
- return ::testing::AssertionSuccess();
+ std::unique_ptr<uint8_t[]> data = util::copy(buffer);
+ if (outTree->setTo(data.get(), buffer.size(), true) != NO_ERROR) {
+ return ::testing::AssertionFailure() << "flattened XML is corrupt";
}
+ return ::testing::AssertionSuccess();
+ }
-protected:
- std::unique_ptr<IAaptContext> mContext;
+ protected:
+ std::unique_ptr<IAaptContext> mContext;
};
TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
<View xmlns:test="http://com.test"
attr="hey">
<Layout test:hello="hi" />
<Layout>Some text</Layout>
</View>)EOF");
+ android::ResXMLTree tree;
+ ASSERT_TRUE(flatten(doc.get(), &tree));
- android::ResXMLTree tree;
- ASSERT_TRUE(flatten(doc.get(), &tree));
+ ASSERT_EQ(tree.next(), android::ResXMLTree::START_NAMESPACE);
- ASSERT_EQ(tree.next(), android::ResXMLTree::START_NAMESPACE);
+ size_t len;
+ const char16_t* namespacePrefix = tree.getNamespacePrefix(&len);
+ EXPECT_EQ(StringPiece16(namespacePrefix, len), u"test");
- size_t len;
- const char16_t* namespacePrefix = tree.getNamespacePrefix(&len);
- EXPECT_EQ(StringPiece16(namespacePrefix, len), u"test");
+ const char16_t* namespaceUri = tree.getNamespaceUri(&len);
+ ASSERT_EQ(StringPiece16(namespaceUri, len), u"http://com.test");
- const char16_t* namespaceUri = tree.getNamespaceUri(&len);
- ASSERT_EQ(StringPiece16(namespaceUri, len), u"http://com.test");
+ ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
- ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
+ ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
+ const char16_t* tagName = tree.getElementName(&len);
+ EXPECT_EQ(StringPiece16(tagName, len), u"View");
- ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
- const char16_t* tagName = tree.getElementName(&len);
- EXPECT_EQ(StringPiece16(tagName, len), u"View");
+ ASSERT_EQ(1u, tree.getAttributeCount());
+ ASSERT_EQ(tree.getAttributeNamespace(0, &len), nullptr);
+ const char16_t* attrName = tree.getAttributeName(0, &len);
+ EXPECT_EQ(StringPiece16(attrName, len), u"attr");
- ASSERT_EQ(1u, tree.getAttributeCount());
- ASSERT_EQ(tree.getAttributeNamespace(0, &len), nullptr);
- const char16_t* attrName = tree.getAttributeName(0, &len);
- EXPECT_EQ(StringPiece16(attrName, len), u"attr");
+ EXPECT_EQ(0, tree.indexOfAttribute(nullptr, 0, u"attr",
+ StringPiece16(u"attr").size()));
- EXPECT_EQ(0, tree.indexOfAttribute(nullptr, 0, u"attr", StringPiece16(u"attr").size()));
+ ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
- ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
+ ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
+ tagName = tree.getElementName(&len);
+ EXPECT_EQ(StringPiece16(tagName, len), u"Layout");
- ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
- tagName = tree.getElementName(&len);
- EXPECT_EQ(StringPiece16(tagName, len), u"Layout");
+ ASSERT_EQ(1u, tree.getAttributeCount());
+ const char16_t* attrNamespace = tree.getAttributeNamespace(0, &len);
+ EXPECT_EQ(StringPiece16(attrNamespace, len), u"http://com.test");
- ASSERT_EQ(1u, tree.getAttributeCount());
- const char16_t* attrNamespace = tree.getAttributeNamespace(0, &len);
- EXPECT_EQ(StringPiece16(attrNamespace, len), u"http://com.test");
+ attrName = tree.getAttributeName(0, &len);
+ EXPECT_EQ(StringPiece16(attrName, len), u"hello");
- attrName = tree.getAttributeName(0, &len);
- EXPECT_EQ(StringPiece16(attrName, len), u"hello");
+ ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
+ ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
- ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
- ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
+ ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
+ tagName = tree.getElementName(&len);
+ EXPECT_EQ(StringPiece16(tagName, len), u"Layout");
+ ASSERT_EQ(0u, tree.getAttributeCount());
- ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
- tagName = tree.getElementName(&len);
- EXPECT_EQ(StringPiece16(tagName, len), u"Layout");
- ASSERT_EQ(0u, tree.getAttributeCount());
+ ASSERT_EQ(tree.next(), android::ResXMLTree::TEXT);
+ const char16_t* text = tree.getText(&len);
+ EXPECT_EQ(StringPiece16(text, len), u"Some text");
- ASSERT_EQ(tree.next(), android::ResXMLTree::TEXT);
- const char16_t* text = tree.getText(&len);
- EXPECT_EQ(StringPiece16(text, len), u"Some text");
+ ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
+ ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
+ tagName = tree.getElementName(&len);
+ EXPECT_EQ(StringPiece16(tagName, len), u"Layout");
- ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
- ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
- tagName = tree.getElementName(&len);
- EXPECT_EQ(StringPiece16(tagName, len), u"Layout");
+ ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
+ ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
+ tagName = tree.getElementName(&len);
+ EXPECT_EQ(StringPiece16(tagName, len), u"View");
- ASSERT_EQ(tree.next(), android::ResXMLTree::END_TAG);
- ASSERT_EQ(tree.getElementNamespace(&len), nullptr);
- tagName = tree.getElementName(&len);
- EXPECT_EQ(StringPiece16(tagName, len), u"View");
+ ASSERT_EQ(tree.next(), android::ResXMLTree::END_NAMESPACE);
+ namespacePrefix = tree.getNamespacePrefix(&len);
+ EXPECT_EQ(StringPiece16(namespacePrefix, len), u"test");
- ASSERT_EQ(tree.next(), android::ResXMLTree::END_NAMESPACE);
- namespacePrefix = tree.getNamespacePrefix(&len);
- EXPECT_EQ(StringPiece16(namespacePrefix, len), u"test");
+ namespaceUri = tree.getNamespaceUri(&len);
+ ASSERT_EQ(StringPiece16(namespaceUri, len), u"http://com.test");
- namespaceUri = tree.getNamespaceUri(&len);
- ASSERT_EQ(StringPiece16(namespaceUri, len), u"http://com.test");
-
- ASSERT_EQ(tree.next(), android::ResXMLTree::END_DOCUMENT);
+ ASSERT_EQ(tree.next(), android::ResXMLTree::END_DOCUMENT);
}
TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripSdk21) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingStart="1dp"
android:colorAccent="#ffffff"/>)EOF");
- XmlReferenceLinker linker;
- ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
- ASSERT_TRUE(linker.getSdkLevels().count(17) == 1);
- ASSERT_TRUE(linker.getSdkLevels().count(21) == 1);
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+ ASSERT_TRUE(linker.getSdkLevels().count(17) == 1);
+ ASSERT_TRUE(linker.getSdkLevels().count(21) == 1);
- android::ResXMLTree tree;
- XmlFlattenerOptions options;
- options.maxSdkLevel = 17;
- ASSERT_TRUE(flatten(doc.get(), &tree, options));
+ android::ResXMLTree tree;
+ XmlFlattenerOptions options;
+ options.maxSdkLevel = 17;
+ ASSERT_TRUE(flatten(doc.get(), &tree, options));
- while (tree.next() != android::ResXMLTree::START_TAG) {
- ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
- ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
- }
+ while (tree.next() != android::ResXMLTree::START_TAG) {
+ ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
+ ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
+ }
- ASSERT_EQ(1u, tree.getAttributeCount());
- EXPECT_EQ(uint32_t(0x010103b3), tree.getAttributeNameResID(0));
+ ASSERT_EQ(1u, tree.getAttributeCount());
+ EXPECT_EQ(uint32_t(0x010103b3), tree.getAttributeNameResID(0));
}
TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripOnlyTools) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
<View xmlns:tools="http://schemas.android.com/tools"
xmlns:foo="http://schemas.android.com/foo"
foo:bar="Foo"
tools:ignore="MissingTranslation"/>)EOF");
- android::ResXMLTree tree;
- ASSERT_TRUE(flatten(doc.get(), &tree));
+ android::ResXMLTree tree;
+ ASSERT_TRUE(flatten(doc.get(), &tree));
- ASSERT_EQ(tree.next(), android::ResXMLTree::START_NAMESPACE);
+ ASSERT_EQ(tree.next(), android::ResXMLTree::START_NAMESPACE);
- size_t len;
- const char16_t* namespacePrefix = tree.getNamespacePrefix(&len);
- EXPECT_EQ(StringPiece16(namespacePrefix, len), u"foo");
+ size_t len;
+ const char16_t* namespacePrefix = tree.getNamespacePrefix(&len);
+ EXPECT_EQ(StringPiece16(namespacePrefix, len), u"foo");
- const char16_t* namespaceUri = tree.getNamespaceUri(&len);
- ASSERT_EQ(StringPiece16(namespaceUri, len), u"http://schemas.android.com/foo");
+ const char16_t* namespaceUri = tree.getNamespaceUri(&len);
+ ASSERT_EQ(StringPiece16(namespaceUri, len),
+ u"http://schemas.android.com/foo");
- ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
+ ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
- EXPECT_EQ(
- tree.indexOfAttribute("http://schemas.android.com/tools", "ignore"),
+ EXPECT_EQ(tree.indexOfAttribute("http://schemas.android.com/tools", "ignore"),
android::NAME_NOT_FOUND);
- EXPECT_GE(tree.indexOfAttribute("http://schemas.android.com/foo", "bar"), 0);
+ EXPECT_GE(tree.indexOfAttribute("http://schemas.android.com/foo", "bar"), 0);
}
TEST_F(XmlFlattenerTest, AssignSpecialAttributeIndices) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@id/id"
class="str"
style="@id/id"/>)EOF");
- android::ResXMLTree tree;
- ASSERT_TRUE(flatten(doc.get(), &tree));
+ 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);
- }
+ while (tree.next() != android::ResXMLTree::START_TAG) {
+ ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
+ ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
+ }
- EXPECT_EQ(tree.indexOfClass(), 0);
- EXPECT_EQ(tree.indexOfStyle(), 1);
+ EXPECT_EQ(tree.indexOfClass(), 0);
+ EXPECT_EQ(tree.indexOfStyle(), 1);
}
/*
- * The device ResXMLParser in libandroidfw differentiates between empty namespace and null
+ * The device ResXMLParser in libandroidfw differentiates between empty
+ * namespace and null
* namespace.
*/
TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom("<View package=\"android\"/>");
+ std::unique_ptr<xml::XmlResource> doc =
+ test::buildXmlDom("<View package=\"android\"/>");
- android::ResXMLTree tree;
- ASSERT_TRUE(flatten(doc.get(), &tree));
+ 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);
- }
+ 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";
- EXPECT_GE(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), 0);
+ const StringPiece16 kPackage = u"package";
+ 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=\"\"/>");
+ std::unique_ptr<xml::XmlResource> doc =
+ test::buildXmlDom("<View package=\"\"/>");
- android::ResXMLTree tree;
- ASSERT_TRUE(flatten(doc.get(), &tree));
+ 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);
- }
+ 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);
+ 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));
+ size_t len;
+ EXPECT_NE(nullptr, tree.getAttributeStringValue(idx, &len));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/io/Data.h b/tools/aapt2/io/Data.h
index 34eed63..0479228b 100644
--- a/tools/aapt2/io/Data.h
+++ b/tools/aapt2/io/Data.h
@@ -18,105 +18,95 @@
#define AAPT_IO_DATA_H
#include <android-base/macros.h>
-#include <memory>
#include <utils/FileMap.h>
+#include <memory>
namespace aapt {
namespace io {
/**
- * Interface for a block of contiguous memory. An instance of this interface owns the data.
+ * Interface for a block of contiguous memory. An instance of this interface
+ * owns the data.
*/
class IData {
-public:
- virtual ~IData() = default;
+ public:
+ virtual ~IData() = default;
- virtual const void* data() const = 0;
- virtual size_t size() const = 0;
+ virtual const void* data() const = 0;
+ virtual size_t size() const = 0;
};
class DataSegment : public IData {
-public:
- explicit DataSegment(std::unique_ptr<IData> data, size_t offset, size_t len) :
- mData(std::move(data)), mOffset(offset), mLen(len) {
- }
+ public:
+ explicit DataSegment(std::unique_ptr<IData> data, size_t offset, size_t len)
+ : mData(std::move(data)), mOffset(offset), mLen(len) {}
- const void* data() const override {
- return static_cast<const uint8_t*>(mData->data()) + mOffset;
- }
+ const void* data() const override {
+ return static_cast<const uint8_t*>(mData->data()) + mOffset;
+ }
- size_t size() const override {
- return mLen;
- }
+ size_t size() const override { return mLen; }
-private:
- DISALLOW_COPY_AND_ASSIGN(DataSegment);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DataSegment);
- std::unique_ptr<IData> mData;
- size_t mOffset;
- size_t mLen;
+ std::unique_ptr<IData> mData;
+ size_t mOffset;
+ size_t mLen;
};
/**
- * Implementation of IData that exposes a memory mapped file. The mmapped file is owned by this
+ * Implementation of IData that exposes a memory mapped file. The mmapped file
+ * is owned by this
* object.
*/
class MmappedData : public IData {
-public:
- explicit MmappedData(android::FileMap&& map) : mMap(std::forward<android::FileMap>(map)) {
- }
+ public:
+ explicit MmappedData(android::FileMap&& map)
+ : mMap(std::forward<android::FileMap>(map)) {}
- const void* data() const override {
- return mMap.getDataPtr();
- }
+ const void* data() const override { return mMap.getDataPtr(); }
- size_t size() const override {
- return mMap.getDataLength();
- }
+ size_t size() const override { return mMap.getDataLength(); }
-private:
- android::FileMap mMap;
+ private:
+ android::FileMap mMap;
};
/**
- * Implementation of IData that exposes a block of memory that was malloc'ed (new'ed). The
+ * Implementation of IData that exposes a block of memory that was malloc'ed
+ * (new'ed). The
* memory is owned by this object.
*/
class MallocData : public IData {
-public:
- MallocData(std::unique_ptr<const uint8_t[]> data, size_t size) :
- mData(std::move(data)), mSize(size) {
- }
+ public:
+ MallocData(std::unique_ptr<const uint8_t[]> data, size_t size)
+ : mData(std::move(data)), mSize(size) {}
- const void* data() const override {
- return mData.get();
- }
+ const void* data() const override { return mData.get(); }
- size_t size() const override {
- return mSize;
- }
+ size_t size() const override { return mSize; }
-private:
- std::unique_ptr<const uint8_t[]> mData;
- size_t mSize;
+ private:
+ std::unique_ptr<const uint8_t[]> mData;
+ size_t mSize;
};
/**
- * When mmap fails because the file has length 0, we use the EmptyData to simulate data of length 0.
+ * When mmap fails because the file has length 0, we use the EmptyData to
+ * simulate data of length 0.
*/
class EmptyData : public IData {
-public:
- const void* data() const override {
- static const uint8_t d = 0;
- return &d;
- }
+ public:
+ const void* data() const override {
+ static const uint8_t d = 0;
+ return &d;
+ }
- size_t size() const override {
- return 0u;
- }
+ size_t size() const override { return 0u; }
};
-} // namespace io
-} // namespace aapt
+} // namespace io
+} // namespace aapt
#endif /* AAPT_IO_DATA_H */
diff --git a/tools/aapt2/io/File.h b/tools/aapt2/io/File.h
index 807981e..012f446 100644
--- a/tools/aapt2/io/File.h
+++ b/tools/aapt2/io/File.h
@@ -30,83 +30,90 @@
namespace io {
/**
- * Interface for a file, which could be a real file on the file system, or a file inside
+ * Interface for a file, which could be a real file on the file system, or a
+ * file inside
* a ZIP archive.
*/
class IFile {
-public:
- virtual ~IFile() = default;
+ public:
+ virtual ~IFile() = default;
- /**
- * Open the file and return it as a block of contiguous memory. How this occurs is
- * implementation dependent. For example, if this is a file on the file system, it may
- * simply mmap the contents. If this file represents a compressed file in a ZIP archive,
- * it may need to inflate it to memory, incurring a copy.
- *
- * Returns nullptr on failure.
- */
- virtual std::unique_ptr<IData> openAsData() = 0;
+ /**
+ * Open the file and return it as a block of contiguous memory. How this
+ * occurs is
+ * implementation dependent. For example, if this is a file on the file
+ * system, it may
+ * simply mmap the contents. If this file represents a compressed file in a
+ * ZIP archive,
+ * it may need to inflate it to memory, incurring a copy.
+ *
+ * Returns nullptr on failure.
+ */
+ virtual std::unique_ptr<IData> openAsData() = 0;
- /**
- * Returns the source of this file. This is for presentation to the user and may not be a
- * valid file system path (for example, it may contain a '@' sign to separate the files within
- * a ZIP archive from the path to the containing ZIP archive.
- */
- virtual const Source& getSource() const = 0;
+ /**
+ * Returns the source of this file. This is for presentation to the user and
+ * may not be a
+ * valid file system path (for example, it may contain a '@' sign to separate
+ * the files within
+ * a ZIP archive from the path to the containing ZIP archive.
+ */
+ virtual const Source& getSource() const = 0;
- IFile* createFileSegment(size_t offset, size_t len);
+ IFile* createFileSegment(size_t offset, size_t len);
-private:
- // Any segments created from this IFile need to be owned by this IFile, so keep them
- // in a list. This will never be read, so we prefer better insertion performance
- // than cache locality, hence the list.
- std::list<std::unique_ptr<IFile>> mSegments;
+ private:
+ // Any segments created from this IFile need to be owned by this IFile, so
+ // keep them
+ // in a list. This will never be read, so we prefer better insertion
+ // performance
+ // than cache locality, hence the list.
+ std::list<std::unique_ptr<IFile>> mSegments;
};
/**
- * An IFile that wraps an underlying IFile but limits it to a subsection of that file.
+ * An IFile that wraps an underlying IFile but limits it to a subsection of that
+ * file.
*/
class FileSegment : public IFile {
-public:
- explicit FileSegment(IFile* file, size_t offset, size_t len) :
- mFile(file), mOffset(offset), mLen(len) {
- }
+ public:
+ explicit FileSegment(IFile* file, size_t offset, size_t len)
+ : mFile(file), mOffset(offset), mLen(len) {}
- std::unique_ptr<IData> openAsData() override;
+ std::unique_ptr<IData> openAsData() override;
- const Source& getSource() const override {
- return mFile->getSource();
- }
+ const Source& getSource() const override { return mFile->getSource(); }
-private:
- DISALLOW_COPY_AND_ASSIGN(FileSegment);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FileSegment);
- IFile* mFile;
- size_t mOffset;
- size_t mLen;
+ IFile* mFile;
+ size_t mOffset;
+ size_t mLen;
};
class IFileCollectionIterator {
-public:
- virtual ~IFileCollectionIterator() = default;
+ public:
+ virtual ~IFileCollectionIterator() = default;
- virtual bool hasNext() = 0;
- virtual IFile* next() = 0;
+ virtual bool hasNext() = 0;
+ virtual IFile* next() = 0;
};
/**
- * Interface for a collection of files, all of which share a common source. That source may
+ * Interface for a collection of files, all of which share a common source. That
+ * source may
* simply be the filesystem, or a ZIP archive.
*/
class IFileCollection {
-public:
- virtual ~IFileCollection() = default;
+ public:
+ virtual ~IFileCollection() = default;
- virtual IFile* findFile(const StringPiece& path) = 0;
- virtual std::unique_ptr<IFileCollectionIterator> iterator() = 0;
+ virtual IFile* findFile(const StringPiece& path) = 0;
+ virtual std::unique_ptr<IFileCollectionIterator> iterator() = 0;
};
-} // namespace io
-} // namespace aapt
+} // namespace io
+} // namespace aapt
#endif /* AAPT_IO_FILE_H */
diff --git a/tools/aapt2/io/FileSystem.h b/tools/aapt2/io/FileSystem.h
index 72a932a..8584d48 100644
--- a/tools/aapt2/io/FileSystem.h
+++ b/tools/aapt2/io/FileSystem.h
@@ -28,47 +28,47 @@
* A regular file from the file system. Uses mmap to open the data.
*/
class RegularFile : public IFile {
-public:
- explicit RegularFile(const Source& source);
+ public:
+ explicit RegularFile(const Source& source);
- std::unique_ptr<IData> openAsData() override;
- const Source& getSource() const override;
+ std::unique_ptr<IData> openAsData() override;
+ const Source& getSource() const override;
-private:
- Source mSource;
+ private:
+ Source mSource;
};
class FileCollection;
class FileCollectionIterator : public IFileCollectionIterator {
-public:
- explicit FileCollectionIterator(FileCollection* collection);
+ public:
+ explicit FileCollectionIterator(FileCollection* collection);
- bool hasNext() override;
- io::IFile* next() override;
+ bool hasNext() override;
+ io::IFile* next() override;
-private:
- std::map<std::string, std::unique_ptr<IFile>>::const_iterator mCurrent, mEnd;
+ private:
+ std::map<std::string, std::unique_ptr<IFile>>::const_iterator mCurrent, mEnd;
};
/**
* An IFileCollection representing the file system.
*/
class FileCollection : public IFileCollection {
-public:
- /**
- * Adds a file located at path. Returns the IFile representation of that file.
- */
- IFile* insertFile(const StringPiece& path);
- IFile* findFile(const StringPiece& path) override;
- std::unique_ptr<IFileCollectionIterator> iterator() override;
+ public:
+ /**
+ * Adds a file located at path. Returns the IFile representation of that file.
+ */
+ IFile* insertFile(const StringPiece& path);
+ IFile* findFile(const StringPiece& path) override;
+ std::unique_ptr<IFileCollectionIterator> iterator() override;
-private:
- friend class FileCollectionIterator;
- std::map<std::string, std::unique_ptr<IFile>> mFiles;
+ private:
+ friend class FileCollectionIterator;
+ std::map<std::string, std::unique_ptr<IFile>> mFiles;
};
-} // namespace io
-} // namespace aapt
+} // namespace io
+} // namespace aapt
-#endif // AAPT_IO_FILESYSTEM_H
+#endif // AAPT_IO_FILESYSTEM_H
diff --git a/tools/aapt2/io/Io.h b/tools/aapt2/io/Io.h
index e1e9107..49b9fc3 100644
--- a/tools/aapt2/io/Io.h
+++ b/tools/aapt2/io/Io.h
@@ -30,12 +30,10 @@
* The code style here matches the protobuf style.
*/
class InputStream : public google::protobuf::io::ZeroCopyInputStream {
-public:
- virtual std::string GetError() const {
- return {};
- }
+ public:
+ virtual std::string GetError() const { return {}; }
- virtual bool HadError() const = 0;
+ virtual bool HadError() const = 0;
};
/**
@@ -45,12 +43,10 @@
* The code style here matches the protobuf style.
*/
class OutputStream : public google::protobuf::io::ZeroCopyOutputStream {
-public:
- virtual std::string GetError() const {
- return {};
- }
+ public:
+ virtual std::string GetError() const { return {}; }
- virtual bool HadError() const = 0;
+ virtual bool HadError() const = 0;
};
/**
@@ -60,7 +56,7 @@
*/
bool copy(OutputStream* out, InputStream* in);
-} // namespace io
-} // namespace aapt
+} // namespace io
+} // namespace aapt
#endif /* AAPT_IO_IO_H */
diff --git a/tools/aapt2/io/ZipArchive.h b/tools/aapt2/io/ZipArchive.h
index 565588e..e04525f 100644
--- a/tools/aapt2/io/ZipArchive.h
+++ b/tools/aapt2/io/ZipArchive.h
@@ -20,64 +20,66 @@
#include "io/File.h"
#include "util/StringPiece.h"
-#include <map>
#include <ziparchive/zip_archive.h>
+#include <map>
namespace aapt {
namespace io {
/**
- * An IFile representing a file within a ZIP archive. If the file is compressed, it is uncompressed
- * and copied into memory when opened. Otherwise it is mmapped from the ZIP archive.
+ * An IFile representing a file within a ZIP archive. If the file is compressed,
+ * it is uncompressed
+ * and copied into memory when opened. Otherwise it is mmapped from the ZIP
+ * archive.
*/
class ZipFile : public IFile {
-public:
- ZipFile(ZipArchiveHandle handle, const ZipEntry& entry, const Source& source);
+ public:
+ ZipFile(ZipArchiveHandle handle, const ZipEntry& entry, const Source& source);
- std::unique_ptr<IData> openAsData() override;
- const Source& getSource() const override;
+ std::unique_ptr<IData> openAsData() override;
+ const Source& getSource() const override;
-private:
- ZipArchiveHandle mZipHandle;
- ZipEntry mZipEntry;
- Source mSource;
+ private:
+ ZipArchiveHandle mZipHandle;
+ ZipEntry mZipEntry;
+ Source mSource;
};
class ZipFileCollection;
class ZipFileCollectionIterator : public IFileCollectionIterator {
-public:
- explicit ZipFileCollectionIterator(ZipFileCollection* collection);
+ public:
+ explicit ZipFileCollectionIterator(ZipFileCollection* collection);
- bool hasNext() override;
- io::IFile* next() override;
+ bool hasNext() override;
+ io::IFile* next() override;
-private:
- std::map<std::string, std::unique_ptr<IFile>>::const_iterator mCurrent, mEnd;
+ private:
+ std::map<std::string, std::unique_ptr<IFile>>::const_iterator mCurrent, mEnd;
};
/**
* An IFileCollection that represents a ZIP archive and the entries within it.
*/
class ZipFileCollection : public IFileCollection {
-public:
- static std::unique_ptr<ZipFileCollection> create(const StringPiece& path,
- std::string* outError);
+ public:
+ static std::unique_ptr<ZipFileCollection> create(const StringPiece& path,
+ std::string* outError);
- io::IFile* findFile(const StringPiece& path) override;
- std::unique_ptr<IFileCollectionIterator> iterator() override;
+ io::IFile* findFile(const StringPiece& path) override;
+ std::unique_ptr<IFileCollectionIterator> iterator() override;
- ~ZipFileCollection() override;
+ ~ZipFileCollection() override;
-private:
- friend class ZipFileCollectionIterator;
- ZipFileCollection();
+ private:
+ friend class ZipFileCollectionIterator;
+ ZipFileCollection();
- ZipArchiveHandle mHandle;
- std::map<std::string, std::unique_ptr<IFile>> mFiles;
+ ZipArchiveHandle mHandle;
+ std::map<std::string, std::unique_ptr<IFile>> mFiles;
};
-} // namespace io
-} // namespace aapt
+} // namespace io
+} // namespace aapt
#endif /* AAPT_IO_ZIPARCHIVE_H */
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
index cfc32f3..5419608 100644
--- a/tools/aapt2/java/AnnotationProcessor.h
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -52,34 +52,36 @@
*
*/
class AnnotationProcessor {
-public:
- /**
- * Adds more comments. Since resources can have various values with different configurations,
- * we need to collect all the comments.
- */
- void appendComment(const StringPiece& comment);
+ public:
+ /**
+ * Adds more comments. Since resources can have various values with different
+ * configurations,
+ * we need to collect all the comments.
+ */
+ void appendComment(const StringPiece& comment);
- void appendNewLine();
+ void appendNewLine();
- /**
- * Writes the comments and annotations to the stream, with the given prefix before each line.
- */
- void writeToStream(std::ostream* out, const StringPiece& prefix) const;
+ /**
+ * Writes the comments and annotations to the stream, with the given prefix
+ * before each line.
+ */
+ void writeToStream(std::ostream* out, const StringPiece& prefix) const;
-private:
- enum : uint32_t {
- kDeprecated = 0x01,
- kSystemApi = 0x02,
- };
+ private:
+ enum : uint32_t {
+ kDeprecated = 0x01,
+ kSystemApi = 0x02,
+ };
- std::stringstream mComment;
- std::stringstream mAnnotations;
- bool mHasComments = false;
- uint32_t mAnnotationBitMask = 0;
+ std::stringstream mComment;
+ std::stringstream mAnnotations;
+ bool mHasComments = false;
+ uint32_t mAnnotationBitMask = 0;
- void appendCommentLine(std::string& line);
+ void appendCommentLine(std::string& line);
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_JAVA_ANNOTATIONPROCESSOR_H */
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
index e84c274..bd7e7b2 100644
--- a/tools/aapt2/java/ClassDefinition.h
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -33,46 +33,43 @@
constexpr static const char* kIndent = " ";
class ClassMember {
-public:
- virtual ~ClassMember() = default;
+ public:
+ virtual ~ClassMember() = default;
- AnnotationProcessor* getCommentBuilder() {
- return &mProcessor;
- }
+ AnnotationProcessor* getCommentBuilder() { return &mProcessor; }
- virtual bool empty() const = 0;
+ virtual bool empty() const = 0;
- virtual void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const {
- mProcessor.writeToStream(out, prefix);
- }
+ virtual void writeToStream(const StringPiece& prefix, bool final,
+ std::ostream* out) const {
+ mProcessor.writeToStream(out, prefix);
+ }
-private:
- AnnotationProcessor mProcessor;
+ private:
+ AnnotationProcessor mProcessor;
};
template <typename T>
class PrimitiveMember : public ClassMember {
-public:
- PrimitiveMember(const StringPiece& name, const T& val) :
- mName(name.toString()), mVal(val) {
- }
+ public:
+ PrimitiveMember(const StringPiece& name, const T& val)
+ : mName(name.toString()), mVal(val) {}
- bool empty() const override {
- return false;
- }
+ bool empty() const override { return false; }
- void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override {
- ClassMember::writeToStream(prefix, final, out);
+ void writeToStream(const StringPiece& prefix, bool final,
+ std::ostream* out) const override {
+ ClassMember::writeToStream(prefix, final, out);
- *out << prefix << "public static " << (final ? "final " : "")
- << "int " << mName << "=" << mVal << ";";
- }
+ *out << prefix << "public static " << (final ? "final " : "") << "int "
+ << mName << "=" << mVal << ";";
+ }
-private:
- std::string mName;
- T mVal;
+ private:
+ std::string mName;
+ T mVal;
- DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
};
/**
@@ -80,27 +77,25 @@
*/
template <>
class PrimitiveMember<std::string> : public ClassMember {
-public:
- PrimitiveMember(const StringPiece& name, const std::string& val) :
- mName(name.toString()), mVal(val) {
- }
+ public:
+ PrimitiveMember(const StringPiece& name, const std::string& val)
+ : mName(name.toString()), mVal(val) {}
- bool empty() const override {
- return false;
- }
+ bool empty() const override { return false; }
- void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override {
- ClassMember::writeToStream(prefix, final, out);
+ void writeToStream(const StringPiece& prefix, bool final,
+ std::ostream* out) const override {
+ ClassMember::writeToStream(prefix, final, out);
- *out << prefix << "public static " << (final ? "final " : "")
- << "String " << mName << "=\"" << mVal << "\";";
- }
+ *out << prefix << "public static " << (final ? "final " : "") << "String "
+ << mName << "=\"" << mVal << "\";";
+ }
-private:
- std::string mName;
- std::string mVal;
+ private:
+ std::string mName;
+ std::string mVal;
- DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveMember);
};
using IntMember = PrimitiveMember<uint32_t>;
@@ -109,80 +104,75 @@
template <typename T>
class PrimitiveArrayMember : public ClassMember {
-public:
- explicit PrimitiveArrayMember(const StringPiece& name) :
- mName(name.toString()) {
+ public:
+ explicit PrimitiveArrayMember(const StringPiece& name)
+ : mName(name.toString()) {}
+
+ void addElement(const T& val) { mElements.push_back(val); }
+
+ bool empty() const override { return false; }
+
+ void writeToStream(const StringPiece& prefix, bool final,
+ std::ostream* out) const override {
+ ClassMember::writeToStream(prefix, final, out);
+
+ *out << prefix << "public static final int[] " << mName << "={";
+
+ const auto begin = mElements.begin();
+ const auto end = mElements.end();
+ for (auto current = begin; current != end; ++current) {
+ if (std::distance(begin, current) % kAttribsPerLine == 0) {
+ *out << "\n" << prefix << kIndent << kIndent;
+ }
+
+ *out << *current;
+ if (std::distance(current, end) > 1) {
+ *out << ", ";
+ }
}
+ *out << "\n" << prefix << kIndent << "};";
+ }
- void addElement(const T& val) {
- mElements.push_back(val);
- }
+ private:
+ std::string mName;
+ std::vector<T> mElements;
- bool empty() const override {
- return false;
- }
-
- void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override {
- ClassMember::writeToStream(prefix, final, out);
-
- *out << prefix << "public static final int[] " << mName << "={";
-
- const auto begin = mElements.begin();
- const auto end = mElements.end();
- for (auto current = begin; current != end; ++current) {
- if (std::distance(begin, current) % kAttribsPerLine == 0) {
- *out << "\n" << prefix << kIndent << kIndent;
- }
-
- *out << *current;
- if (std::distance(current, end) > 1) {
- *out << ", ";
- }
- }
- *out << "\n" << prefix << kIndent <<"};";
- }
-
-private:
- std::string mName;
- std::vector<T> mElements;
-
- DISALLOW_COPY_AND_ASSIGN(PrimitiveArrayMember);
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveArrayMember);
};
using ResourceArrayMember = PrimitiveArrayMember<ResourceId>;
-enum class ClassQualifier {
- None,
- Static
-};
+enum class ClassQualifier { None, Static };
class ClassDefinition : public ClassMember {
-public:
- static bool writeJavaFile(const ClassDefinition* def,
- const StringPiece& package,
- bool final,
- std::ostream* out);
+ public:
+ static bool writeJavaFile(const ClassDefinition* def,
+ const StringPiece& package, bool final,
+ std::ostream* out);
- ClassDefinition(const StringPiece& name, ClassQualifier qualifier, bool createIfEmpty) :
- mName(name.toString()), mQualifier(qualifier), mCreateIfEmpty(createIfEmpty) {
- }
+ ClassDefinition(const StringPiece& name, ClassQualifier qualifier,
+ bool createIfEmpty)
+ : mName(name.toString()),
+ mQualifier(qualifier),
+ mCreateIfEmpty(createIfEmpty) {}
- void addMember(std::unique_ptr<ClassMember> member) {
- mMembers.push_back(std::move(member));
- }
+ void addMember(std::unique_ptr<ClassMember> member) {
+ mMembers.push_back(std::move(member));
+ }
- bool empty() const override;
- void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override;
+ bool empty() const override;
+ void writeToStream(const StringPiece& prefix, bool final,
+ std::ostream* out) const override;
-private:
- std::string mName;
- ClassQualifier mQualifier;
- bool mCreateIfEmpty;
- std::vector<std::unique_ptr<ClassMember>> mMembers;
+ private:
+ std::string mName;
+ ClassQualifier mQualifier;
+ bool mCreateIfEmpty;
+ std::vector<std::unique_ptr<ClassMember>> mMembers;
- DISALLOW_COPY_AND_ASSIGN(ClassDefinition);
+ DISALLOW_COPY_AND_ASSIGN(ClassDefinition);
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_JAVA_CLASSDEFINITION_H */
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index 901a86e..2fdf268 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -31,72 +31,74 @@
class ClassDefinition;
struct JavaClassGeneratorOptions {
- /*
- * Specifies whether to use the 'final' modifier
- * on resource entries. Default is true.
- */
- bool useFinal = true;
+ /*
+ * Specifies whether to use the 'final' modifier
+ * on resource entries. Default is true.
+ */
+ bool useFinal = true;
- enum class SymbolTypes {
- kAll,
- kPublicPrivate,
- kPublic,
- };
+ enum class SymbolTypes {
+ kAll,
+ kPublicPrivate,
+ kPublic,
+ };
- SymbolTypes types = SymbolTypes::kAll;
+ SymbolTypes types = SymbolTypes::kAll;
- /**
- * A list of JavaDoc annotations to add to the comments of all generated classes.
- */
- std::vector<std::string> javadocAnnotations;
+ /**
+ * A list of JavaDoc annotations to add to the comments of all generated
+ * classes.
+ */
+ std::vector<std::string> javadocAnnotations;
};
/*
* Generates the R.java file for a resource table.
*/
class JavaClassGenerator {
-public:
- JavaClassGenerator(IAaptContext* context, ResourceTable* table,
- const JavaClassGeneratorOptions& options);
+ public:
+ JavaClassGenerator(IAaptContext* context, ResourceTable* table,
+ const JavaClassGeneratorOptions& options);
- /*
- * Writes the R.java file to `out`. Only symbols belonging to `package` are written.
- * All symbols technically belong to a single package, but linked libraries will
- * have their names mangled, denoting that they came from a different package.
- * We need to generate these symbols in a separate file.
- * Returns true on success.
- */
- bool generate(const StringPiece& packageNameToGenerate, std::ostream* out);
+ /*
+ * Writes the R.java file to `out`. Only symbols belonging to `package` are
+ * written.
+ * All symbols technically belong to a single package, but linked libraries
+ * will
+ * have their names mangled, denoting that they came from a different package.
+ * We need to generate these symbols in a separate file.
+ * Returns true on success.
+ */
+ bool generate(const StringPiece& packageNameToGenerate, std::ostream* out);
- bool generate(const StringPiece& packageNameToGenerate,
- const StringPiece& outputPackageName,
- std::ostream* out);
+ bool generate(const StringPiece& packageNameToGenerate,
+ const StringPiece& outputPackageName, std::ostream* out);
- const std::string& getError() const;
+ const std::string& getError() const;
-private:
- bool addMembersToTypeClass(const StringPiece& packageNameToGenerate,
- const ResourceTablePackage* package,
- const ResourceTableType* type,
- ClassDefinition* outTypeClassDef);
+ private:
+ bool addMembersToTypeClass(const StringPiece& packageNameToGenerate,
+ const ResourceTablePackage* package,
+ const ResourceTableType* type,
+ ClassDefinition* outTypeClassDef);
- void addMembersToStyleableClass(const StringPiece& packageNameToGenerate,
- const std::string& entryName,
- const Styleable* styleable,
- ClassDefinition* outStyleableClassDef);
+ void addMembersToStyleableClass(const StringPiece& packageNameToGenerate,
+ const std::string& entryName,
+ const Styleable* styleable,
+ ClassDefinition* outStyleableClassDef);
- bool skipSymbol(SymbolState state);
+ bool skipSymbol(SymbolState state);
- IAaptContext* mContext;
- ResourceTable* mTable;
- JavaClassGeneratorOptions mOptions;
- std::string mError;
+ IAaptContext* mContext;
+ ResourceTable* mTable;
+ JavaClassGeneratorOptions mOptions;
+ std::string mError;
};
inline const std::string& JavaClassGenerator::getError() const {
- return mError;
+ return mError;
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_JAVA_CLASS_GENERATOR_H
+#endif // AAPT_JAVA_CLASS_GENERATOR_H
diff --git a/tools/aapt2/java/ManifestClassGenerator.h b/tools/aapt2/java/ManifestClassGenerator.h
index f565289..1817648 100644
--- a/tools/aapt2/java/ManifestClassGenerator.h
+++ b/tools/aapt2/java/ManifestClassGenerator.h
@@ -26,8 +26,9 @@
namespace aapt {
-std::unique_ptr<ClassDefinition> generateManifestClass(IDiagnostics* diag, xml::XmlResource* res);
+std::unique_ptr<ClassDefinition> generateManifestClass(IDiagnostics* diag,
+ xml::XmlResource* res);
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_JAVA_MANIFESTCLASSGENERATOR_H */
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
index c2d2bd9..7578ec2 100644
--- a/tools/aapt2/java/ProguardRules.h
+++ b/tools/aapt2/java/ProguardRules.h
@@ -30,29 +30,31 @@
namespace proguard {
class KeepSet {
-public:
- inline void addClass(const Source& source, const std::string& className) {
- mKeepSet[className].insert(source);
- }
+ public:
+ inline void addClass(const Source& source, const std::string& className) {
+ mKeepSet[className].insert(source);
+ }
- inline void addMethod(const Source& source, const std::string& methodName) {
- mKeepMethodSet[methodName].insert(source);
- }
+ inline void addMethod(const Source& source, const std::string& methodName) {
+ mKeepMethodSet[methodName].insert(source);
+ }
-private:
- friend bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
+ private:
+ friend bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
- std::map<std::string, std::set<Source>> mKeepSet;
- std::map<std::string, 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 collectProguardRules(const Source& source, xml::XmlResource* res,
+ KeepSet* keepSet);
bool writeKeepSet(std::ostream* out, const KeepSet& keepSet);
-} // namespace proguard
-} // namespace aapt
+} // namespace proguard
+} // namespace aapt
-#endif // AAPT_PROGUARD_RULES_H
+#endif // AAPT_PROGUARD_RULES_H
diff --git a/tools/aapt2/link/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp
index 8ed27c3..5ba9819 100644
--- a/tools/aapt2/link/AutoVersioner.cpp
+++ b/tools/aapt2/link/AutoVersioner.cpp
@@ -25,117 +25,128 @@
namespace aapt {
-bool shouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
+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);
+ // 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) {
- if ((*iter)->config == config) {
- break;
- }
+ const auto endIter = entry->values.end();
+ auto iter = entry->values.begin();
+ for (; iter != endIter; ++iter) {
+ if ((*iter)->config == config) {
+ break;
}
+ }
- // The source config came from this list, so it should be here.
- assert(iter != entry->values.end());
- ++iter;
+ // The source config came from this list, so it should be here.
+ assert(iter != entry->values.end());
+ ++iter;
- // The next configuration either only varies in sdkVersion, or it is completely different
- // and therefore incompatible. If it is incompatible, we must generate the versioned resource.
+ // The next configuration either only varies in sdkVersion, or it is
+ // completely different
+ // and therefore incompatible. If it is incompatible, we must generate the
+ // versioned resource.
- // NOTE: The ordering of configurations takes sdkVersion as higher precedence than other
- // qualifiers, so we need to iterate through the entire list to be sure there
- // are no higher sdk level versions of this resource.
- ConfigDescription tempConfig(config);
- for (; iter != endIter; ++iter) {
- tempConfig.sdkVersion = (*iter)->config.sdkVersion;
- if (tempConfig == (*iter)->config) {
- // The two configs are the same, check the sdk version.
- return sdkVersionToGenerate < (*iter)->config.sdkVersion;
- }
+ // NOTE: The ordering of configurations takes sdkVersion as higher precedence
+ // than other
+ // qualifiers, so we need to iterate through the entire list to be sure there
+ // are no higher sdk level versions of this resource.
+ ConfigDescription tempConfig(config);
+ for (; iter != endIter; ++iter) {
+ tempConfig.sdkVersion = (*iter)->config.sdkVersion;
+ if (tempConfig == (*iter)->config) {
+ // The two configs are the same, check the sdk version.
+ return sdkVersionToGenerate < (*iter)->config.sdkVersion;
}
+ }
- // No match was found, so we should generate the versioned resource.
- return true;
+ // No match was found, so we should generate the versioned resource.
+ return true;
}
bool AutoVersioner::consume(IAaptContext* context, ResourceTable* table) {
- for (auto& package : table->packages) {
- for (auto& type : package->types) {
- if (type->type != ResourceType::kStyle) {
- continue;
- }
+ for (auto& package : table->packages) {
+ for (auto& type : package->types) {
+ if (type->type != ResourceType::kStyle) {
+ continue;
+ }
- for (auto& entry : type->entries) {
- for (size_t i = 0; i < entry->values.size(); i++) {
- ResourceConfigValue* configValue = entry->values[i].get();
- if (configValue->config.sdkVersion >= SDK_LOLLIPOP_MR1) {
- // If this configuration is only used on L-MR1 then we don't need
- // to do anything since we use private attributes since that version.
- continue;
- }
+ for (auto& entry : type->entries) {
+ for (size_t i = 0; i < entry->values.size(); i++) {
+ ResourceConfigValue* configValue = entry->values[i].get();
+ if (configValue->config.sdkVersion >= SDK_LOLLIPOP_MR1) {
+ // If this configuration is only used on L-MR1 then we don't need
+ // to do anything since we use private attributes since that
+ // version.
+ continue;
+ }
- if (Style* style = valueCast<Style>(configValue->value.get())) {
- Maybe<size_t> minSdkStripped;
- std::vector<Style::Entry> stripped;
+ if (Style* style = valueCast<Style>(configValue->value.get())) {
+ Maybe<size_t> minSdkStripped;
+ std::vector<Style::Entry> stripped;
- auto iter = style->entries.begin();
- while (iter != style->entries.end()) {
- assert(iter->key.id && "IDs must be assigned and linked");
+ auto iter = style->entries.begin();
+ while (iter != style->entries.end()) {
+ assert(iter->key.id && "IDs must be assigned and linked");
- // Find the SDK level that is higher than the configuration allows.
- const size_t sdkLevel = findAttributeSdkLevel(iter->key.id.value());
- if (sdkLevel > std::max<size_t>(configValue->config.sdkVersion, 1)) {
- // Record that we are about to strip this.
- stripped.emplace_back(std::move(*iter));
+ // Find the SDK level that is higher than the configuration
+ // allows.
+ const size_t sdkLevel =
+ findAttributeSdkLevel(iter->key.id.value());
+ if (sdkLevel >
+ std::max<size_t>(configValue->config.sdkVersion, 1)) {
+ // Record that we are about to strip this.
+ stripped.emplace_back(std::move(*iter));
- // We use the smallest SDK level to generate the new style.
- if (minSdkStripped) {
- minSdkStripped = std::min(minSdkStripped.value(), sdkLevel);
- } else {
- minSdkStripped = sdkLevel;
- }
-
- // Erase this from this style.
- iter = style->entries.erase(iter);
- continue;
- }
- ++iter;
- }
-
- if (minSdkStripped && !stripped.empty()) {
- // We found attributes from a higher SDK level. Check that
- // there is no other defined resource for the version we want to
- // generate.
- if (shouldGenerateVersionedResource(entry.get(),
- configValue->config,
- minSdkStripped.value())) {
- // Let's create a new Style for this versioned resource.
- ConfigDescription newConfig(configValue->config);
- newConfig.sdkVersion = minSdkStripped.value();
-
- std::unique_ptr<Style> newStyle(style->clone(&table->stringPool));
- newStyle->setComment(style->getComment());
- newStyle->setSource(style->getSource());
-
- // Move the previously stripped attributes into this style.
- newStyle->entries.insert(newStyle->entries.end(),
- std::make_move_iterator(stripped.begin()),
- std::make_move_iterator(stripped.end()));
-
- // Insert the new Resource into the correct place.
- entry->findOrCreateValue(newConfig, {})->value =
- std::move(newStyle);
- }
- }
- }
+ // We use the smallest SDK level to generate the new style.
+ if (minSdkStripped) {
+ minSdkStripped = std::min(minSdkStripped.value(), sdkLevel);
+ } else {
+ minSdkStripped = sdkLevel;
}
+
+ // Erase this from this style.
+ iter = style->entries.erase(iter);
+ continue;
+ }
+ ++iter;
}
+
+ if (minSdkStripped && !stripped.empty()) {
+ // We found attributes from a higher SDK level. Check that
+ // there is no other defined resource for the version we want to
+ // generate.
+ if (shouldGenerateVersionedResource(entry.get(),
+ configValue->config,
+ minSdkStripped.value())) {
+ // Let's create a new Style for this versioned resource.
+ ConfigDescription newConfig(configValue->config);
+ newConfig.sdkVersion = minSdkStripped.value();
+
+ std::unique_ptr<Style> newStyle(
+ style->clone(&table->stringPool));
+ newStyle->setComment(style->getComment());
+ newStyle->setSource(style->getSource());
+
+ // Move the previously stripped attributes into this style.
+ newStyle->entries.insert(
+ newStyle->entries.end(),
+ std::make_move_iterator(stripped.begin()),
+ std::make_move_iterator(stripped.end()));
+
+ // Insert the new Resource into the correct place.
+ entry->findOrCreateValue(newConfig, {})->value =
+ std::move(newStyle);
+ }
+ }
+ }
}
+ }
}
- return true;
+ }
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/AutoVersioner_test.cpp b/tools/aapt2/link/AutoVersioner_test.cpp
index 3a61da5..04bf9cd 100644
--- a/tools/aapt2/link/AutoVersioner_test.cpp
+++ b/tools/aapt2/link/AutoVersioner_test.cpp
@@ -21,102 +21,114 @@
namespace aapt {
TEST(AutoVersionerTest, GenerateVersionedResources) {
- const ConfigDescription defaultConfig = {};
- const ConfigDescription landConfig = test::parseConfigOrDie("land");
- const ConfigDescription sw600dpLandConfig = test::parseConfigOrDie("sw600dp-land");
+ const ConfigDescription defaultConfig = {};
+ const ConfigDescription landConfig = test::parseConfigOrDie("land");
+ const ConfigDescription sw600dpLandConfig =
+ test::parseConfigOrDie("sw600dp-land");
- 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, ""));
+ 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, ""));
- EXPECT_TRUE(shouldGenerateVersionedResource(&entry, defaultConfig, 17));
- EXPECT_TRUE(shouldGenerateVersionedResource(&entry, landConfig, 17));
+ EXPECT_TRUE(shouldGenerateVersionedResource(&entry, defaultConfig, 17));
+ EXPECT_TRUE(shouldGenerateVersionedResource(&entry, landConfig, 17));
}
TEST(AutoVersionerTest, GenerateVersionedResourceWhenHigherVersionExists) {
- const ConfigDescription defaultConfig = {};
- const ConfigDescription sw600dpV13Config = test::parseConfigOrDie("sw600dp-v13");
- const ConfigDescription v21Config = test::parseConfigOrDie("v21");
+ const ConfigDescription defaultConfig = {};
+ const ConfigDescription sw600dpV13Config =
+ test::parseConfigOrDie("sw600dp-v13");
+ const ConfigDescription v21Config = test::parseConfigOrDie("v21");
- 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, ""));
+ 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, ""));
- EXPECT_TRUE(shouldGenerateVersionedResource(&entry, defaultConfig, 17));
- EXPECT_FALSE(shouldGenerateVersionedResource(&entry, defaultConfig, 22));
+ EXPECT_TRUE(shouldGenerateVersionedResource(&entry, defaultConfig, 17));
+ EXPECT_FALSE(shouldGenerateVersionedResource(&entry, defaultConfig, 22));
}
TEST(AutoVersionerTest, VersionStylesForTable) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("app", 0x7f)
- .addValue("app:style/Foo", test::parseConfigOrDie("v4"), ResourceId(0x7f020000),
- test::StyleBuilder()
- .addItem("android:attr/onClick", ResourceId(0x0101026f),
- util::make_unique<Id>())
- .addItem("android:attr/paddingStart", ResourceId(0x010103b3),
- util::make_unique<Id>())
- .addItem("android:attr/requiresSmallestWidthDp",
- ResourceId(0x01010364), util::make_unique<Id>())
- .addItem("android:attr/colorAccent", ResourceId(0x01010435),
- util::make_unique<Id>())
- .build())
- .addValue("app:style/Foo", test::parseConfigOrDie("v21"), ResourceId(0x7f020000),
- test::StyleBuilder()
- .addItem("android:attr/paddingEnd", ResourceId(0x010103b4),
- util::make_unique<Id>())
- .build())
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .setPackageId("app", 0x7f)
+ .addValue(
+ "app:style/Foo", test::parseConfigOrDie("v4"),
+ ResourceId(0x7f020000),
+ test::StyleBuilder()
+ .addItem("android:attr/onClick", ResourceId(0x0101026f),
+ util::make_unique<Id>())
+ .addItem("android:attr/paddingStart", ResourceId(0x010103b3),
+ util::make_unique<Id>())
+ .addItem("android:attr/requiresSmallestWidthDp",
+ ResourceId(0x01010364), util::make_unique<Id>())
+ .addItem("android:attr/colorAccent", ResourceId(0x01010435),
+ util::make_unique<Id>())
+ .build())
+ .addValue(
+ "app:style/Foo", test::parseConfigOrDie("v21"),
+ ResourceId(0x7f020000),
+ test::StyleBuilder()
+ .addItem("android:attr/paddingEnd", ResourceId(0x010103b4),
+ util::make_unique<Id>())
+ .build())
+ .build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .setCompilationPackage("app")
- .setPackageId(0x7f)
- .build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .setCompilationPackage("app")
+ .setPackageId(0x7f)
+ .build();
- AutoVersioner versioner;
- ASSERT_TRUE(versioner.consume(context.get(), table.get()));
+ AutoVersioner versioner;
+ ASSERT_TRUE(versioner.consume(context.get(), table.get()));
- 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("android:attr/onClick"));
+ 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("android:attr/onClick"));
- 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("android:attr/onClick"));
- AAPT_ASSERT_TRUE(style->entries[1].key.name);
- EXPECT_EQ(style->entries[1].key.name.value(),
- test::parseNameOrDie("android:attr/requiresSmallestWidthDp"));
+ 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("android:attr/onClick"));
+ AAPT_ASSERT_TRUE(style->entries[1].key.name);
+ EXPECT_EQ(style->entries[1].key.name.value(),
+ test::parseNameOrDie("android:attr/requiresSmallestWidthDp"));
- 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("android:attr/onClick"));
- AAPT_ASSERT_TRUE(style->entries[1].key.name);
- EXPECT_EQ(style->entries[1].key.name.value(),
- test::parseNameOrDie("android:attr/requiresSmallestWidthDp"));
- AAPT_ASSERT_TRUE(style->entries[2].key.name);
- EXPECT_EQ(style->entries[2].key.name.value(),
- test::parseNameOrDie("android:attr/paddingStart"));
+ 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("android:attr/onClick"));
+ AAPT_ASSERT_TRUE(style->entries[1].key.name);
+ EXPECT_EQ(style->entries[1].key.name.value(),
+ test::parseNameOrDie("android:attr/requiresSmallestWidthDp"));
+ AAPT_ASSERT_TRUE(style->entries[2].key.name);
+ EXPECT_EQ(style->entries[2].key.name.value(),
+ test::parseNameOrDie("android:attr/paddingStart"));
- 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("android:attr/paddingEnd"));
+ 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("android:attr/paddingEnd"));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index b6b4b473..a42d868 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -31,9 +31,9 @@
#include "java/ManifestClassGenerator.h"
#include "java/ProguardRules.h"
#include "link/Linkers.h"
+#include "link/ManifestFixer.h"
#include "link/ProductFilter.h"
#include "link/ReferenceLinker.h"
-#include "link/ManifestFixer.h"
#include "link/TableMerger.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
@@ -47,9 +47,9 @@
#include <android-base/file.h>
#include <google/protobuf/io/coded_stream.h>
+#include <sys/stat.h>
#include <fstream>
#include <queue>
-#include <sys/stat.h>
#include <unordered_map>
#include <vector>
@@ -58,1908 +58,2043 @@
namespace aapt {
struct LinkOptions {
- std::string outputPath;
- std::string manifestPath;
- std::vector<std::string> includePaths;
- std::vector<std::string> overlayFiles;
+ std::string outputPath;
+ std::string manifestPath;
+ std::vector<std::string> includePaths;
+ std::vector<std::string> overlayFiles;
- // Java/Proguard options.
- Maybe<std::string> generateJavaClassPath;
- Maybe<std::string> customJavaPackage;
- std::set<std::string> extraJavaPackages;
- Maybe<std::string> generateProguardRulesPath;
- Maybe<std::string> generateMainDexProguardRulesPath;
+ // Java/Proguard options.
+ Maybe<std::string> generateJavaClassPath;
+ Maybe<std::string> customJavaPackage;
+ std::set<std::string> extraJavaPackages;
+ Maybe<std::string> generateProguardRulesPath;
+ Maybe<std::string> generateMainDexProguardRulesPath;
- bool noAutoVersion = false;
- bool noVersionVectors = false;
- bool noResourceDeduping = false;
- bool staticLib = false;
- bool noStaticLibPackages = false;
- bool generateNonFinalIds = false;
- std::vector<std::string> javadocAnnotations;
- bool outputToDirectory = false;
- bool noXmlNamespaces = false;
- bool autoAddOverlay = false;
- bool doNotCompressAnything = false;
- std::unordered_set<std::string> extensionsToNotCompress;
- Maybe<std::string> privateSymbols;
- ManifestFixerOptions manifestFixerOptions;
- std::unordered_set<std::string> products;
+ bool noAutoVersion = false;
+ bool noVersionVectors = false;
+ bool noResourceDeduping = false;
+ bool staticLib = false;
+ bool noStaticLibPackages = false;
+ bool generateNonFinalIds = false;
+ std::vector<std::string> javadocAnnotations;
+ bool outputToDirectory = false;
+ bool noXmlNamespaces = false;
+ bool autoAddOverlay = false;
+ bool doNotCompressAnything = false;
+ std::unordered_set<std::string> extensionsToNotCompress;
+ Maybe<std::string> privateSymbols;
+ ManifestFixerOptions manifestFixerOptions;
+ std::unordered_set<std::string> products;
- // Split APK options.
- TableSplitterOptions tableSplitterOptions;
- std::vector<SplitConstraints> splitConstraints;
- std::vector<std::string> splitPaths;
+ // Split APK options.
+ TableSplitterOptions tableSplitterOptions;
+ std::vector<SplitConstraints> splitConstraints;
+ std::vector<std::string> splitPaths;
- // Stable ID options.
- std::unordered_map<ResourceName, ResourceId> stableIdMap;
- Maybe<std::string> resourceIdMapPath;
+ // Stable ID options.
+ std::unordered_map<ResourceName, ResourceId> stableIdMap;
+ Maybe<std::string> resourceIdMapPath;
};
class LinkContext : public IAaptContext {
-public:
- LinkContext() : mNameMangler({}) {
- }
+ public:
+ LinkContext() : mNameMangler({}) {}
- IDiagnostics* getDiagnostics() override {
- return &mDiagnostics;
- }
+ IDiagnostics* getDiagnostics() override { return &mDiagnostics; }
- NameMangler* getNameMangler() override {
- return &mNameMangler;
- }
+ NameMangler* getNameMangler() override { return &mNameMangler; }
- void setNameManglerPolicy(const NameManglerPolicy& policy) {
- mNameMangler = NameMangler(policy);
- }
+ void setNameManglerPolicy(const NameManglerPolicy& policy) {
+ mNameMangler = NameMangler(policy);
+ }
- const std::string& getCompilationPackage() override {
- return mCompilationPackage;
- }
+ const std::string& getCompilationPackage() override {
+ return mCompilationPackage;
+ }
- void setCompilationPackage(const StringPiece& packageName) {
- mCompilationPackage = packageName.toString();
- }
+ void setCompilationPackage(const StringPiece& packageName) {
+ mCompilationPackage = packageName.toString();
+ }
- uint8_t getPackageId() override {
- return mPackageId;
- }
+ uint8_t getPackageId() override { return mPackageId; }
- void setPackageId(uint8_t id) {
- mPackageId = id;
- }
+ void setPackageId(uint8_t id) { mPackageId = id; }
- SymbolTable* getExternalSymbols() override {
- return &mSymbols;
- }
+ SymbolTable* getExternalSymbols() override { return &mSymbols; }
- bool verbose() override {
- return mVerbose;
- }
+ bool verbose() override { return mVerbose; }
- void setVerbose(bool val) {
- mVerbose = val;
- }
+ void setVerbose(bool val) { mVerbose = val; }
- int getMinSdkVersion() override {
- return mMinSdkVersion;
- }
+ int getMinSdkVersion() override { return mMinSdkVersion; }
- void setMinSdkVersion(int minSdk) {
- mMinSdkVersion = minSdk;
- }
+ void setMinSdkVersion(int minSdk) { mMinSdkVersion = minSdk; }
-private:
- StdErrDiagnostics mDiagnostics;
- NameMangler mNameMangler;
- std::string mCompilationPackage;
- uint8_t mPackageId = 0x0;
- SymbolTable mSymbols;
- bool mVerbose = false;
- int mMinSdkVersion = 0;
+ private:
+ StdErrDiagnostics mDiagnostics;
+ NameMangler mNameMangler;
+ 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,
- uint32_t compressionFlags,
- IArchiveWriter* writer, IAaptContext* context) {
- std::unique_ptr<io::IData> data = file->openAsData();
- if (!data) {
- context->getDiagnostics()->error(DiagMessage(file->getSource())
- << "failed to open file");
- return false;
- }
-
- const uint8_t* buffer = reinterpret_cast<const uint8_t*>(data->data());
- const size_t bufferSize = data->size();
-
- if (context->verbose()) {
- context->getDiagnostics()->note(DiagMessage() << "writing " << outPath << " to archive");
- }
-
- if (writer->startEntry(outPath, compressionFlags)) {
- if (writer->writeEntry(buffer, bufferSize)) {
- if (writer->finishEntry()) {
- return true;
- }
- }
- }
-
- context->getDiagnostics()->error(DiagMessage() << "failed to write file " << outPath);
+ uint32_t compressionFlags, IArchiveWriter* writer,
+ IAaptContext* context) {
+ std::unique_ptr<io::IData> data = file->openAsData();
+ if (!data) {
+ context->getDiagnostics()->error(DiagMessage(file->getSource())
+ << "failed to open file");
return false;
+ }
+
+ const uint8_t* buffer = reinterpret_cast<const uint8_t*>(data->data());
+ const size_t bufferSize = data->size();
+
+ if (context->verbose()) {
+ context->getDiagnostics()->note(DiagMessage() << "writing " << outPath
+ << " to archive");
+ }
+
+ if (writer->startEntry(outPath, compressionFlags)) {
+ if (writer->writeEntry(buffer, bufferSize)) {
+ if (writer->finishEntry()) {
+ return true;
+ }
+ }
+ }
+
+ context->getDiagnostics()->error(DiagMessage() << "failed to write file "
+ << outPath);
+ return false;
}
-static bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path, Maybe<size_t> maxSdkLevel,
- bool keepRawValues, IArchiveWriter* writer, IAaptContext* context) {
- BigBuffer buffer(1024);
- XmlFlattenerOptions options = {};
- options.keepRawValues = keepRawValues;
- options.maxSdkLevel = maxSdkLevel;
- XmlFlattener flattener(&buffer, options);
- if (!flattener.consume(context, xmlRes)) {
- return false;
- }
-
- if (context->verbose()) {
- DiagMessage msg;
- msg << "writing " << path << " to archive";
- if (maxSdkLevel) {
- msg << " maxSdkLevel=" << maxSdkLevel.value() << " keepRawValues=" << keepRawValues;
- }
- context->getDiagnostics()->note(msg);
- }
-
- if (writer->startEntry(path, ArchiveEntry::kCompress)) {
- if (writer->writeEntry(buffer)) {
- if (writer->finishEntry()) {
- return true;
- }
- }
- }
- context->getDiagnostics()->error(DiagMessage() << "failed to write " << path << " to archive");
+static bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path,
+ Maybe<size_t> maxSdkLevel, bool keepRawValues,
+ IArchiveWriter* writer, IAaptContext* context) {
+ BigBuffer buffer(1024);
+ XmlFlattenerOptions options = {};
+ options.keepRawValues = keepRawValues;
+ options.maxSdkLevel = maxSdkLevel;
+ XmlFlattener flattener(&buffer, options);
+ if (!flattener.consume(context, xmlRes)) {
return false;
+ }
+
+ if (context->verbose()) {
+ DiagMessage msg;
+ msg << "writing " << path << " to archive";
+ if (maxSdkLevel) {
+ msg << " maxSdkLevel=" << maxSdkLevel.value()
+ << " keepRawValues=" << keepRawValues;
+ }
+ context->getDiagnostics()->note(msg);
+ }
+
+ if (writer->startEntry(path, ArchiveEntry::kCompress)) {
+ if (writer->writeEntry(buffer)) {
+ if (writer->finishEntry()) {
+ return true;
+ }
+ }
+ }
+ context->getDiagnostics()->error(DiagMessage() << "failed to write " << path
+ << " to archive");
+ return false;
}
static std::unique_ptr<ResourceTable> loadTableFromPb(const Source& source,
- const void* data, size_t len,
+ const void* data,
+ size_t len,
IDiagnostics* diag) {
- pb::ResourceTable pbTable;
- if (!pbTable.ParseFromArray(data, len)) {
- diag->error(DiagMessage(source) << "invalid compiled table");
- return {};
- }
+ pb::ResourceTable pbTable;
+ if (!pbTable.ParseFromArray(data, len)) {
+ diag->error(DiagMessage(source) << "invalid compiled table");
+ return {};
+ }
- std::unique_ptr<ResourceTable> table = deserializeTableFromPb(pbTable, source, diag);
- if (!table) {
- return {};
- }
- return table;
+ std::unique_ptr<ResourceTable> table =
+ deserializeTableFromPb(pbTable, source, diag);
+ if (!table) {
+ return {};
+ }
+ return table;
}
/**
* Inflates an XML file from the source path.
*/
-static std::unique_ptr<xml::XmlResource> loadXml(const std::string& path, IDiagnostics* diag) {
- std::ifstream fin(path, std::ifstream::binary);
- if (!fin) {
- diag->error(DiagMessage(path) << strerror(errno));
- return {};
- }
- return xml::inflate(&fin, diag, Source(path));
+static std::unique_ptr<xml::XmlResource> loadXml(const std::string& path,
+ IDiagnostics* diag) {
+ std::ifstream fin(path, std::ifstream::binary);
+ if (!fin) {
+ diag->error(DiagMessage(path) << strerror(errno));
+ return {};
+ }
+ return xml::inflate(&fin, diag, Source(path));
}
struct ResourceFileFlattenerOptions {
- bool noAutoVersion = false;
- bool noVersionVectors = false;
- bool noXmlNamespaces = false;
- bool keepRawValues = false;
- bool doNotCompressAnything = false;
- bool updateProguardSpec = false;
- std::unordered_set<std::string> extensionsToNotCompress;
+ bool noAutoVersion = false;
+ bool noVersionVectors = false;
+ bool noXmlNamespaces = false;
+ bool keepRawValues = false;
+ bool doNotCompressAnything = false;
+ bool updateProguardSpec = false;
+ std::unordered_set<std::string> extensionsToNotCompress;
};
class ResourceFileFlattener {
-public:
- ResourceFileFlattener(const ResourceFileFlattenerOptions& options,
- IAaptContext* context, proguard::KeepSet* keepSet) :
- mOptions(options), mContext(context), mKeepSet(keepSet) {
- }
+ public:
+ ResourceFileFlattener(const ResourceFileFlattenerOptions& options,
+ IAaptContext* context, proguard::KeepSet* keepSet)
+ : mOptions(options), mContext(context), mKeepSet(keepSet) {}
- bool flatten(ResourceTable* table, IArchiveWriter* archiveWriter);
+ bool flatten(ResourceTable* table, IArchiveWriter* archiveWriter);
-private:
- struct FileOperation {
- ConfigDescription config;
+ private:
+ struct FileOperation {
+ ConfigDescription config;
- // The entry this file came from.
- const ResourceEntry* entry;
+ // The entry this file came from.
+ const ResourceEntry* entry;
- // The file to copy as-is.
- io::IFile* fileToCopy;
+ // The file to copy as-is.
+ io::IFile* fileToCopy;
- // The XML to process and flatten.
- std::unique_ptr<xml::XmlResource> xmlToFlatten;
+ // The XML to process and flatten.
+ std::unique_ptr<xml::XmlResource> xmlToFlatten;
- // The destination to write this file to.
- std::string dstPath;
- bool skipVersion = false;
- };
+ // The destination to write this file to.
+ std::string dstPath;
+ bool skipVersion = false;
+ };
- uint32_t getCompressionFlags(const StringPiece& str);
+ uint32_t getCompressionFlags(const StringPiece& str);
- bool linkAndVersionXmlFile(ResourceTable* table, FileOperation* fileOp,
- std::queue<FileOperation>* outFileOpQueue);
+ bool linkAndVersionXmlFile(ResourceTable* table, FileOperation* fileOp,
+ std::queue<FileOperation>* outFileOpQueue);
- ResourceFileFlattenerOptions mOptions;
- IAaptContext* mContext;
- proguard::KeepSet* mKeepSet;
+ ResourceFileFlattenerOptions mOptions;
+ IAaptContext* mContext;
+ proguard::KeepSet* mKeepSet;
};
uint32_t ResourceFileFlattener::getCompressionFlags(const StringPiece& str) {
- if (mOptions.doNotCompressAnything) {
- return 0;
- }
+ if (mOptions.doNotCompressAnything) {
+ return 0;
+ }
- for (const std::string& extension : mOptions.extensionsToNotCompress) {
- if (util::stringEndsWith(str, extension)) {
- return 0;
- }
+ for (const std::string& extension : mOptions.extensionsToNotCompress) {
+ if (util::stringEndsWith(str, extension)) {
+ return 0;
}
- return ArchiveEntry::kCompress;
+ }
+ return ArchiveEntry::kCompress;
}
-bool ResourceFileFlattener::linkAndVersionXmlFile(ResourceTable* table,
- FileOperation* fileOp,
- std::queue<FileOperation>* outFileOpQueue) {
- xml::XmlResource* doc = fileOp->xmlToFlatten.get();
- const Source& src = doc->file.source;
+bool ResourceFileFlattener::linkAndVersionXmlFile(
+ ResourceTable* table, FileOperation* fileOp,
+ std::queue<FileOperation>* outFileOpQueue) {
+ xml::XmlResource* doc = fileOp->xmlToFlatten.get();
+ const Source& src = doc->file.source;
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(DiagMessage() << "linking " << src.path);
+ if (mContext->verbose()) {
+ mContext->getDiagnostics()->note(DiagMessage() << "linking " << src.path);
+ }
+
+ XmlReferenceLinker xmlLinker;
+ if (!xmlLinker.consume(mContext, doc)) {
+ return false;
+ }
+
+ if (mOptions.updateProguardSpec &&
+ !proguard::collectProguardRules(src, doc, mKeepSet)) {
+ return false;
+ }
+
+ if (mOptions.noXmlNamespaces) {
+ XmlNamespaceRemover namespaceRemover;
+ if (!namespaceRemover.consume(mContext, doc)) {
+ return false;
}
+ }
- XmlReferenceLinker xmlLinker;
- if (!xmlLinker.consume(mContext, doc)) {
- return false;
- }
-
- if (mOptions.updateProguardSpec && !proguard::collectProguardRules(src, doc, mKeepSet)) {
- return false;
- }
-
- if (mOptions.noXmlNamespaces) {
- XmlNamespaceRemover namespaceRemover;
- if (!namespaceRemover.consume(mContext, doc)) {
- return false;
+ if (!mOptions.noAutoVersion) {
+ if (mOptions.noVersionVectors) {
+ // Skip this if it is a vector or animated-vector.
+ xml::Element* el = xml::findRootElement(doc);
+ if (el && el->namespaceUri.empty()) {
+ if (el->name == "vector" || el->name == "animated-vector") {
+ // We are NOT going to version this file.
+ fileOp->skipVersion = true;
+ return true;
}
+ }
}
- if (!mOptions.noAutoVersion) {
- if (mOptions.noVersionVectors) {
- // Skip this if it is a vector or animated-vector.
- xml::Element* el = xml::findRootElement(doc);
- if (el && el->namespaceUri.empty()) {
- if (el->name == "vector" || el->name == "animated-vector") {
- // We are NOT going to version this file.
- fileOp->skipVersion = true;
- return true;
- }
- }
+ const ConfigDescription& config = fileOp->config;
+
+ // Find the first SDK level used that is higher than this defined config and
+ // not superseded by a lower or equal SDK level resource.
+ const int minSdkVersion = mContext->getMinSdkVersion();
+ for (int sdkLevel : xmlLinker.getSdkLevels()) {
+ if (sdkLevel > minSdkVersion && sdkLevel > config.sdkVersion) {
+ if (!shouldGenerateVersionedResource(fileOp->entry, config, sdkLevel)) {
+ // If we shouldn't generate a versioned resource, stop checking.
+ break;
}
- const ConfigDescription& config = fileOp->config;
+ ResourceFile versionedFileDesc = doc->file;
+ versionedFileDesc.config.sdkVersion = (uint16_t)sdkLevel;
- // Find the first SDK level used that is higher than this defined config and
- // not superseded by a lower or equal SDK level resource.
- const int minSdkVersion = mContext->getMinSdkVersion();
- for (int sdkLevel : xmlLinker.getSdkLevels()) {
- if (sdkLevel > minSdkVersion && sdkLevel > config.sdkVersion) {
- if (!shouldGenerateVersionedResource(fileOp->entry, config, sdkLevel)) {
- // If we shouldn't generate a versioned resource, stop checking.
- break;
- }
+ FileOperation newFileOp;
+ newFileOp.xmlToFlatten = util::make_unique<xml::XmlResource>(
+ versionedFileDesc, doc->root->clone());
+ newFileOp.config = versionedFileDesc.config;
+ newFileOp.entry = fileOp->entry;
+ newFileOp.dstPath = ResourceUtils::buildResourceFileName(
+ versionedFileDesc, mContext->getNameMangler());
- ResourceFile versionedFileDesc = doc->file;
- versionedFileDesc.config.sdkVersion = (uint16_t) sdkLevel;
-
- FileOperation newFileOp;
- newFileOp.xmlToFlatten = util::make_unique<xml::XmlResource>(
- versionedFileDesc, doc->root->clone());
- newFileOp.config = versionedFileDesc.config;
- newFileOp.entry = fileOp->entry;
- newFileOp.dstPath = ResourceUtils::buildResourceFileName(
- versionedFileDesc, mContext->getNameMangler());
-
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(DiagMessage(versionedFileDesc.source)
- << "auto-versioning resource from config '"
- << config
- << "' -> '"
- << versionedFileDesc.config << "'");
- }
-
- bool added = table->addFileReferenceAllowMangled(versionedFileDesc.name,
- versionedFileDesc.config,
- versionedFileDesc.source,
- newFileOp.dstPath,
- nullptr,
- mContext->getDiagnostics());
- if (!added) {
- return false;
- }
-
- outFileOpQueue->push(std::move(newFileOp));
- break;
- }
+ if (mContext->verbose()) {
+ mContext->getDiagnostics()->note(
+ DiagMessage(versionedFileDesc.source)
+ << "auto-versioning resource from config '" << config << "' -> '"
+ << versionedFileDesc.config << "'");
}
+
+ bool added = table->addFileReferenceAllowMangled(
+ versionedFileDesc.name, versionedFileDesc.config,
+ versionedFileDesc.source, newFileOp.dstPath, nullptr,
+ mContext->getDiagnostics());
+ if (!added) {
+ return false;
+ }
+
+ outFileOpQueue->push(std::move(newFileOp));
+ break;
+ }
}
- return true;
+ }
+ return true;
}
/**
- * Do not insert or remove any resources while executing in this function. It will
+ * Do not insert or remove any resources while executing in this function. It
+ * will
* corrupt the iteration order.
*/
-bool ResourceFileFlattener::flatten(ResourceTable* table, IArchiveWriter* archiveWriter) {
- bool error = false;
- std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> configSortedFiles;
+bool ResourceFileFlattener::flatten(ResourceTable* table,
+ IArchiveWriter* archiveWriter) {
+ bool error = false;
+ std::map<std::pair<ConfigDescription, StringPiece>, FileOperation>
+ configSortedFiles;
- for (auto& pkg : table->packages) {
- for (auto& type : pkg->types) {
- // Sort by config and name, so that we get better locality in the zip file.
- configSortedFiles.clear();
- std::queue<FileOperation> fileOperations;
+ for (auto& pkg : table->packages) {
+ for (auto& type : pkg->types) {
+ // Sort by config and name, so that we get better locality in the zip
+ // file.
+ configSortedFiles.clear();
+ std::queue<FileOperation> fileOperations;
- // Populate the queue with all files in the ResourceTable.
- for (auto& entry : type->entries) {
- for (auto& configValue : entry->values) {
- FileReference* fileRef = valueCast<FileReference>(configValue->value.get());
- if (!fileRef) {
- continue;
- }
-
- io::IFile* file = fileRef->file;
- if (!file) {
- mContext->getDiagnostics()->error(DiagMessage(fileRef->getSource())
- << "file not found");
- return false;
- }
-
- FileOperation fileOp;
- fileOp.entry = entry.get();
- fileOp.dstPath = *fileRef->path;
- fileOp.config = configValue->config;
-
- const StringPiece srcPath = file->getSource().path;
- if (type->type != ResourceType::kRaw &&
- (util::stringEndsWith(srcPath, ".xml.flat") ||
- util::stringEndsWith(srcPath, ".xml"))) {
- std::unique_ptr<io::IData> data = file->openAsData();
- if (!data) {
- mContext->getDiagnostics()->error(DiagMessage(file->getSource())
- << "failed to open file");
- return false;
- }
-
- fileOp.xmlToFlatten = xml::inflate(data->data(), data->size(),
- mContext->getDiagnostics(),
- file->getSource());
-
- if (!fileOp.xmlToFlatten) {
- return false;
- }
-
- fileOp.xmlToFlatten->file.config = configValue->config;
- fileOp.xmlToFlatten->file.source = fileRef->getSource();
- fileOp.xmlToFlatten->file.name =
- ResourceName(pkg->name, type->type, entry->name);
-
- // Enqueue the XML files to be processed.
- fileOperations.push(std::move(fileOp));
- } else {
- fileOp.fileToCopy = file;
-
- // NOTE(adamlesinski): Explicitly construct a StringPiece here, or else
- // we end up copying the string in the std::make_pair() method, then
- // creating a StringPiece from the copy, which would cause us to end up
- // referencing garbage in the map.
- const StringPiece entryName(entry->name);
- configSortedFiles[std::make_pair(configValue->config, entryName)] =
- std::move(fileOp);
- }
- }
- }
-
- // Now process the XML queue
- for (; !fileOperations.empty(); fileOperations.pop()) {
- FileOperation& fileOp = fileOperations.front();
-
- if (!linkAndVersionXmlFile(table, &fileOp, &fileOperations)) {
- error = true;
- continue;
- }
-
- // NOTE(adamlesinski): Explicitly construct a StringPiece here, or else
- // we end up copying the string in the std::make_pair() method, then creating
- // a StringPiece from the copy, which would cause us to end up referencing
- // garbage in the map.
- const StringPiece entryName(fileOp.entry->name);
- configSortedFiles[std::make_pair(fileOp.config, entryName)] = std::move(fileOp);
- }
-
- if (error) {
- return false;
- }
-
- // Now flatten the sorted values.
- for (auto& mapEntry : configSortedFiles) {
- const ConfigDescription& config = mapEntry.first.first;
- const FileOperation& fileOp = mapEntry.second;
-
- if (fileOp.xmlToFlatten) {
- Maybe<size_t> maxSdkLevel;
- if (!mOptions.noAutoVersion && !fileOp.skipVersion) {
- maxSdkLevel = std::max<size_t>(std::max<size_t>(config.sdkVersion, 1u),
- mContext->getMinSdkVersion());
- }
-
- bool result = flattenXml(fileOp.xmlToFlatten.get(), fileOp.dstPath, maxSdkLevel,
- mOptions.keepRawValues,
- archiveWriter, mContext);
- if (!result) {
- error = true;
- }
- } else {
- bool result = copyFileToArchive(fileOp.fileToCopy, fileOp.dstPath,
- getCompressionFlags(fileOp.dstPath),
- archiveWriter, mContext);
- if (!result) {
- error = true;
- }
- }
- }
- }
- }
- return !error;
-}
-
-static bool writeStableIdMapToPath(IDiagnostics* diag,
- const std::unordered_map<ResourceName, ResourceId>& idMap,
- const std::string& idMapPath) {
- std::ofstream fout(idMapPath, std::ofstream::binary);
- if (!fout) {
- diag->error(DiagMessage(idMapPath) << strerror(errno));
- return false;
- }
-
- for (const auto& entry : idMap) {
- const ResourceName& name = entry.first;
- const ResourceId& id = entry.second;
- fout << name << " = " << id << "\n";
- }
-
- if (!fout) {
- diag->error(DiagMessage(idMapPath) << "failed writing to file: " << strerror(errno));
- return false;
- }
-
- return true;
-}
-
-static bool loadStableIdMap(IDiagnostics* diag, const std::string& path,
- std::unordered_map<ResourceName, ResourceId>* outIdMap) {
- std::string content;
- if (!android::base::ReadFileToString(path, &content)) {
- diag->error(DiagMessage(path) << "failed reading stable ID file");
- return false;
- }
-
- outIdMap->clear();
- size_t lineNo = 0;
- for (StringPiece line : util::tokenize(content, '\n')) {
- lineNo++;
- line = util::trimWhitespace(line);
- if (line.empty()) {
+ // Populate the queue with all files in the ResourceTable.
+ for (auto& entry : type->entries) {
+ for (auto& configValue : entry->values) {
+ FileReference* fileRef =
+ valueCast<FileReference>(configValue->value.get());
+ if (!fileRef) {
continue;
- }
+ }
- auto iter = std::find(line.begin(), line.end(), '=');
- if (iter == line.end()) {
- diag->error(DiagMessage(Source(path, lineNo)) << "missing '='");
+ io::IFile* file = fileRef->file;
+ if (!file) {
+ mContext->getDiagnostics()->error(DiagMessage(fileRef->getSource())
+ << "file not found");
return false;
+ }
+
+ FileOperation fileOp;
+ fileOp.entry = entry.get();
+ fileOp.dstPath = *fileRef->path;
+ fileOp.config = configValue->config;
+
+ const StringPiece srcPath = file->getSource().path;
+ if (type->type != ResourceType::kRaw &&
+ (util::stringEndsWith(srcPath, ".xml.flat") ||
+ util::stringEndsWith(srcPath, ".xml"))) {
+ std::unique_ptr<io::IData> data = file->openAsData();
+ if (!data) {
+ mContext->getDiagnostics()->error(DiagMessage(file->getSource())
+ << "failed to open file");
+ return false;
+ }
+
+ fileOp.xmlToFlatten =
+ xml::inflate(data->data(), data->size(),
+ mContext->getDiagnostics(), file->getSource());
+
+ if (!fileOp.xmlToFlatten) {
+ return false;
+ }
+
+ fileOp.xmlToFlatten->file.config = configValue->config;
+ fileOp.xmlToFlatten->file.source = fileRef->getSource();
+ fileOp.xmlToFlatten->file.name =
+ ResourceName(pkg->name, type->type, entry->name);
+
+ // Enqueue the XML files to be processed.
+ fileOperations.push(std::move(fileOp));
+ } else {
+ fileOp.fileToCopy = file;
+
+ // NOTE(adamlesinski): Explicitly construct a StringPiece here, or
+ // else
+ // we end up copying the string in the std::make_pair() method, then
+ // creating a StringPiece from the copy, which would cause us to end
+ // up
+ // referencing garbage in the map.
+ const StringPiece entryName(entry->name);
+ configSortedFiles[std::make_pair(configValue->config, entryName)] =
+ std::move(fileOp);
+ }
+ }
+ }
+
+ // Now process the XML queue
+ for (; !fileOperations.empty(); fileOperations.pop()) {
+ FileOperation& fileOp = fileOperations.front();
+
+ if (!linkAndVersionXmlFile(table, &fileOp, &fileOperations)) {
+ error = true;
+ continue;
}
- ResourceNameRef name;
- StringPiece resNameStr = util::trimWhitespace(
- line.substr(0, std::distance(line.begin(), iter)));
- if (!ResourceUtils::parseResourceName(resNameStr, &name)) {
- diag->error(DiagMessage(Source(path, lineNo))
- << "invalid resource name '" << resNameStr << "'");
- return false;
+ // NOTE(adamlesinski): Explicitly construct a StringPiece here, or else
+ // we end up copying the string in the std::make_pair() method, then
+ // creating
+ // a StringPiece from the copy, which would cause us to end up
+ // referencing
+ // garbage in the map.
+ const StringPiece entryName(fileOp.entry->name);
+ configSortedFiles[std::make_pair(fileOp.config, entryName)] =
+ std::move(fileOp);
+ }
+
+ if (error) {
+ return false;
+ }
+
+ // Now flatten the sorted values.
+ for (auto& mapEntry : configSortedFiles) {
+ const ConfigDescription& config = mapEntry.first.first;
+ const FileOperation& fileOp = mapEntry.second;
+
+ if (fileOp.xmlToFlatten) {
+ Maybe<size_t> maxSdkLevel;
+ if (!mOptions.noAutoVersion && !fileOp.skipVersion) {
+ maxSdkLevel =
+ std::max<size_t>(std::max<size_t>(config.sdkVersion, 1u),
+ mContext->getMinSdkVersion());
+ }
+
+ bool result =
+ flattenXml(fileOp.xmlToFlatten.get(), fileOp.dstPath, maxSdkLevel,
+ mOptions.keepRawValues, archiveWriter, mContext);
+ if (!result) {
+ error = true;
+ }
+ } else {
+ bool result = copyFileToArchive(fileOp.fileToCopy, fileOp.dstPath,
+ getCompressionFlags(fileOp.dstPath),
+ archiveWriter, mContext);
+ if (!result) {
+ error = true;
+ }
}
-
- const size_t resIdStartIdx = std::distance(line.begin(), iter) + 1;
- const size_t resIdStrLen = line.size() - resIdStartIdx;
- StringPiece resIdStr = util::trimWhitespace(line.substr(resIdStartIdx, resIdStrLen));
-
- Maybe<ResourceId> maybeId = ResourceUtils::parseResourceId(resIdStr);
- if (!maybeId) {
- diag->error(DiagMessage(Source(path, lineNo)) << "invalid resource ID '"
- << resIdStr << "'");
- return false;
- }
-
- (*outIdMap)[name.toResourceName()] = maybeId.value();
+ }
}
- return true;
+ }
+ return !error;
+}
+
+static bool writeStableIdMapToPath(
+ IDiagnostics* diag,
+ const std::unordered_map<ResourceName, ResourceId>& idMap,
+ const std::string& idMapPath) {
+ std::ofstream fout(idMapPath, std::ofstream::binary);
+ if (!fout) {
+ diag->error(DiagMessage(idMapPath) << strerror(errno));
+ return false;
+ }
+
+ for (const auto& entry : idMap) {
+ const ResourceName& name = entry.first;
+ const ResourceId& id = entry.second;
+ fout << name << " = " << id << "\n";
+ }
+
+ if (!fout) {
+ diag->error(DiagMessage(idMapPath) << "failed writing to file: "
+ << strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+static bool loadStableIdMap(
+ IDiagnostics* diag, const std::string& path,
+ std::unordered_map<ResourceName, ResourceId>* outIdMap) {
+ std::string content;
+ if (!android::base::ReadFileToString(path, &content)) {
+ diag->error(DiagMessage(path) << "failed reading stable ID file");
+ return false;
+ }
+
+ outIdMap->clear();
+ size_t lineNo = 0;
+ for (StringPiece line : util::tokenize(content, '\n')) {
+ lineNo++;
+ line = util::trimWhitespace(line);
+ if (line.empty()) {
+ continue;
+ }
+
+ auto iter = std::find(line.begin(), line.end(), '=');
+ if (iter == line.end()) {
+ diag->error(DiagMessage(Source(path, lineNo)) << "missing '='");
+ return false;
+ }
+
+ ResourceNameRef name;
+ StringPiece resNameStr =
+ util::trimWhitespace(line.substr(0, std::distance(line.begin(), iter)));
+ if (!ResourceUtils::parseResourceName(resNameStr, &name)) {
+ diag->error(DiagMessage(Source(path, lineNo)) << "invalid resource name '"
+ << resNameStr << "'");
+ return false;
+ }
+
+ const size_t resIdStartIdx = std::distance(line.begin(), iter) + 1;
+ const size_t resIdStrLen = line.size() - resIdStartIdx;
+ StringPiece resIdStr =
+ util::trimWhitespace(line.substr(resIdStartIdx, resIdStrLen));
+
+ Maybe<ResourceId> maybeId = ResourceUtils::parseResourceId(resIdStr);
+ if (!maybeId) {
+ diag->error(DiagMessage(Source(path, lineNo)) << "invalid resource ID '"
+ << resIdStr << "'");
+ return false;
+ }
+
+ (*outIdMap)[name.toResourceName()] = maybeId.value();
+ }
+ return true;
}
static bool parseSplitParameter(const StringPiece& arg, IDiagnostics* diag,
- std::string* outPath, SplitConstraints* outSplit) {
- std::vector<std::string> parts = util::split(arg, ':');
- if (parts.size() != 2) {
- diag->error(DiagMessage() << "invalid split parameter '" << arg << "'");
- diag->note(DiagMessage() << "should be --split path/to/output.apk:<config>[,<config>...]");
- return false;
+ std::string* outPath,
+ SplitConstraints* outSplit) {
+ std::vector<std::string> parts = util::split(arg, ':');
+ if (parts.size() != 2) {
+ diag->error(DiagMessage() << "invalid split parameter '" << arg << "'");
+ diag->note(
+ DiagMessage()
+ << "should be --split path/to/output.apk:<config>[,<config>...]");
+ return false;
+ }
+ *outPath = parts[0];
+ std::vector<ConfigDescription> configs;
+ for (const StringPiece& configStr : util::tokenize(parts[1], ',')) {
+ configs.push_back({});
+ if (!ConfigDescription::parse(configStr, &configs.back())) {
+ diag->error(DiagMessage() << "invalid config '" << configStr
+ << "' in split parameter '" << arg << "'");
+ return false;
}
- *outPath = parts[0];
- std::vector<ConfigDescription> configs;
- for (const StringPiece& configStr : util::tokenize(parts[1], ',')) {
- configs.push_back({});
- if (!ConfigDescription::parse(configStr, &configs.back())) {
- diag->error(DiagMessage() << "invalid config '" << configStr
- << "' in split parameter '" << arg << "'");
- return false;
- }
- }
- outSplit->configs.insert(configs.begin(), configs.end());
- return true;
+ }
+ outSplit->configs.insert(configs.begin(), configs.end());
+ return true;
}
class LinkCommand {
-public:
- LinkCommand(LinkContext* context, const LinkOptions& options) :
- mOptions(options), mContext(context), mFinalTable(),
- mFileCollection(util::make_unique<io::FileCollection>()) {
- }
+ public:
+ LinkCommand(LinkContext* context, const LinkOptions& options)
+ : mOptions(options),
+ mContext(context),
+ mFinalTable(),
+ mFileCollection(util::make_unique<io::FileCollection>()) {}
- /**
- * Creates a SymbolTable that loads symbols from the various APKs and caches the
- * results for faster lookup.
- */
- bool loadSymbolsFromIncludePaths() {
- std::unique_ptr<AssetManagerSymbolSource> assetSource =
- util::make_unique<AssetManagerSymbolSource>();
- for (const std::string& path : mOptions.includePaths) {
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(DiagMessage(path) << "loading include path");
- }
+ /**
+ * Creates a SymbolTable that loads symbols from the various APKs and caches
+ * the
+ * results for faster lookup.
+ */
+ bool loadSymbolsFromIncludePaths() {
+ std::unique_ptr<AssetManagerSymbolSource> assetSource =
+ util::make_unique<AssetManagerSymbolSource>();
+ for (const std::string& path : mOptions.includePaths) {
+ if (mContext->verbose()) {
+ mContext->getDiagnostics()->note(DiagMessage(path)
+ << "loading include path");
+ }
- // First try to load the file as a static lib.
- std::string errorStr;
- std::unique_ptr<ResourceTable> staticInclude = loadStaticLibrary(path, &errorStr);
- if (staticInclude) {
- if (!mOptions.staticLib) {
- // Can't include static libraries when not building a static library.
- mContext->getDiagnostics()->error(
- DiagMessage(path) << "can't include static library when building app");
- return false;
- }
-
- // If we are using --no-static-lib-packages, we need to rename the package of this
- // table to our compilation package.
- if (mOptions.noStaticLibPackages) {
- if (ResourceTablePackage* pkg = staticInclude->findPackageById(0x7f)) {
- pkg->name = mContext->getCompilationPackage();
- }
- }
-
- mContext->getExternalSymbols()->appendSource(
- util::make_unique<ResourceTableSymbolSource>(staticInclude.get()));
-
- mStaticTableIncludes.push_back(std::move(staticInclude));
-
- } else if (!errorStr.empty()) {
- // We had an error with reading, so fail.
- mContext->getDiagnostics()->error(DiagMessage(path) << errorStr);
- return false;
- }
-
- if (!assetSource->addAssetPath(path)) {
- mContext->getDiagnostics()->error(
- DiagMessage(path) << "failed to load include path");
- return false;
- }
- }
-
- mContext->getExternalSymbols()->appendSource(std::move(assetSource));
- return true;
- }
-
- 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())) {
- 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::Attribute* versionCodeAttr =
- manifestEl->findAttribute(xml::kSchemaAndroid, "versionCode")) {
- Maybe<uint32_t> maybeCode = ResourceUtils::parseInt(versionCodeAttr->value);
- if (!maybeCode) {
- diag->error(DiagMessage(xmlRes->file.source.withLine(manifestEl->lineNumber))
- << "invalid android:versionCode '"
- << versionCodeAttr->value << "'");
- return {};
- }
- appInfo.versionCode = maybeCode.value();
- }
-
- if (xml::Attribute* revisionCodeAttr =
- manifestEl->findAttribute(xml::kSchemaAndroid, "revisionCode")) {
- Maybe<uint32_t> maybeCode = ResourceUtils::parseInt(revisionCodeAttr->value);
- if (!maybeCode) {
- diag->error(DiagMessage(xmlRes->file.source.withLine(manifestEl->lineNumber))
- << "invalid android:revisionCode '"
- << revisionCodeAttr->value << "'");
- return {};
- }
- appInfo.revisionCode = maybeCode.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 {};
- }
-
- /**
- * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
- * Postcondition: ResourceTable has only one package left. All others are stripped, or there
- * is an error and false is returned.
- */
- bool verifyNoExternalPackages() {
- auto isExtPackageFunc = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
- return mContext->getCompilationPackage() != pkg->name ||
- !pkg->id ||
- pkg->id.value() != mContext->getPackageId();
- };
-
- bool error = false;
- for (const auto& package : mFinalTable.packages) {
- if (isExtPackageFunc(package)) {
- // We have a package that is not related to the one we're building!
- for (const auto& type : package->types) {
- for (const auto& entry : type->entries) {
- ResourceNameRef resName(package->name, type->type, entry->name);
-
- for (const auto& configValue : entry->values) {
- // 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 == "android") {
- mContext->getDiagnostics()->warn(
- DiagMessage(configValue->value->getSource())
- << "generated id '" << resName
- << "' for external package '" << package->name
- << "'");
- } else {
- mContext->getDiagnostics()->error(
- DiagMessage(configValue->value->getSource())
- << "defined resource '" << resName
- << "' for external package '" << package->name
- << "'");
- error = true;
- }
- }
- }
- }
- }
- }
-
- auto newEndIter = std::remove_if(mFinalTable.packages.begin(), mFinalTable.packages.end(),
- isExtPackageFunc);
- mFinalTable.packages.erase(newEndIter, mFinalTable.packages.end());
- return !error;
- }
-
- /**
- * Returns true if no IDs have been set, false otherwise.
- */
- bool verifyNoIdsSet() {
- for (const auto& package : mFinalTable.packages) {
- for (const auto& type : package->types) {
- if (type->id) {
- mContext->getDiagnostics()->error(DiagMessage() << "type " << type->type
- << " has ID " << std::hex
- << (int) type->id.value()
- << std::dec << " assigned");
- return false;
- }
-
- for (const auto& entry : type->entries) {
- if (entry->id) {
- ResourceNameRef resName(package->name, type->type, entry->name);
- mContext->getDiagnostics()->error(DiagMessage() << "entry " << resName
- << " has ID " << std::hex
- << (int) entry->id.value()
- << std::dec << " assigned");
- return false;
- }
- }
- }
- }
- return true;
- }
-
- std::unique_ptr<IArchiveWriter> makeArchiveWriter(const StringPiece& out) {
- if (mOptions.outputToDirectory) {
- return createDirectoryArchiveWriter(mContext->getDiagnostics(), out);
- } else {
- return createZipFileArchiveWriter(mContext->getDiagnostics(), out);
- }
- }
-
- bool flattenTable(ResourceTable* table, IArchiveWriter* writer) {
- BigBuffer buffer(1024);
- TableFlattener flattener(&buffer);
- if (!flattener.consume(mContext, table)) {
- return false;
- }
-
- if (writer->startEntry("resources.arsc", ArchiveEntry::kAlign)) {
- if (writer->writeEntry(buffer)) {
- if (writer->finishEntry()) {
- return true;
- }
- }
- }
-
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed to write resources.arsc to archive");
- return false;
- }
-
- bool flattenTableToPb(ResourceTable* table, IArchiveWriter* writer) {
- // Create the file/zip entry.
- if (!writer->startEntry("resources.arsc.flat", 0)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed to open");
- return false;
- }
-
- // Make sure CopyingOutputStreamAdaptor is deleted before we call writer->finishEntry().
- {
- // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
- // interface.
- CopyingOutputStreamAdaptor adaptor(writer);
-
- std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table);
- if (!pbTable->SerializeToZeroCopyStream(&adaptor)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed to write");
- return false;
- }
- }
-
- if (!writer->finishEntry()) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed to finish entry");
- return false;
- }
- return true;
- }
-
- bool writeJavaFile(ResourceTable* table, const StringPiece& packageNameToGenerate,
- const StringPiece& outPackage, const JavaClassGeneratorOptions& javaOptions) {
- if (!mOptions.generateJavaClassPath) {
- return true;
- }
-
- std::string outPath = mOptions.generateJavaClassPath.value();
- file::appendPath(&outPath, file::packageToPath(outPackage));
- if (!file::mkdirs(outPath)) {
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed to create directory '" << outPath << "'");
- return false;
- }
-
- file::appendPath(&outPath, "R.java");
-
- std::ofstream fout(outPath, std::ofstream::binary);
- if (!fout) {
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
- return false;
- }
-
- JavaClassGenerator generator(mContext, table, javaOptions);
- if (!generator.generate(packageNameToGenerate, outPackage, &fout)) {
- mContext->getDiagnostics()->error(DiagMessage(outPath) << generator.getError());
- return false;
- }
-
- if (!fout) {
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
- }
- return true;
- }
-
- bool writeManifestJavaFile(xml::XmlResource* manifestXml) {
- if (!mOptions.generateJavaClassPath) {
- return true;
- }
-
- std::unique_ptr<ClassDefinition> manifestClass = generateManifestClass(
- mContext->getDiagnostics(), manifestXml);
-
- if (!manifestClass) {
- // Something bad happened, but we already logged it, so exit.
- return false;
- }
-
- if (manifestClass->empty()) {
- // Empty Manifest class, no need to generate it.
- return true;
- }
-
- // Add any JavaDoc annotations to the generated class.
- for (const std::string& annotation : mOptions.javadocAnnotations) {
- std::string properAnnotation = "@";
- properAnnotation += annotation;
- manifestClass->getCommentBuilder()->appendComment(properAnnotation);
- }
-
- const std::string& packageUtf8 = mContext->getCompilationPackage();
-
- std::string outPath = mOptions.generateJavaClassPath.value();
- file::appendPath(&outPath, file::packageToPath(packageUtf8));
-
- if (!file::mkdirs(outPath)) {
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed to create directory '" << outPath << "'");
- return false;
- }
-
- file::appendPath(&outPath, "Manifest.java");
-
- std::ofstream fout(outPath, std::ofstream::binary);
- if (!fout) {
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
- return false;
- }
-
- if (!ClassDefinition::writeJavaFile(manifestClass.get(), packageUtf8, true, &fout)) {
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
- return false;
- }
- return true;
- }
-
- bool writeProguardFile(const Maybe<std::string>& out, const proguard::KeepSet& keepSet) {
- if (!out) {
- return true;
- }
-
- const std::string& outPath = out.value();
- std::ofstream fout(outPath, std::ofstream::binary);
- if (!fout) {
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed to open '" << outPath << "': " << strerror(errno));
- return false;
- }
-
- proguard::writeKeepSet(&fout, keepSet);
- if (!fout) {
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
- return false;
- }
- return true;
- }
-
- std::unique_ptr<ResourceTable> loadStaticLibrary(const std::string& input,
- std::string* outError) {
- std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create(
- input, outError);
- if (!collection) {
- return {};
- }
- return loadTablePbFromCollection(collection.get());
- }
-
- std::unique_ptr<ResourceTable> loadTablePbFromCollection(io::IFileCollection* collection) {
- io::IFile* file = collection->findFile("resources.arsc.flat");
- if (!file) {
- return {};
- }
-
- std::unique_ptr<io::IData> data = file->openAsData();
- return loadTableFromPb(file->getSource(), data->data(), data->size(),
- mContext->getDiagnostics());
- }
-
- bool mergeStaticLibrary(const std::string& input, bool override) {
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(DiagMessage() << "merging static library " << input);
- }
-
- std::string errorStr;
- std::unique_ptr<io::ZipFileCollection> collection =
- io::ZipFileCollection::create(input, &errorStr);
- if (!collection) {
- mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
- return false;
- }
-
- std::unique_ptr<ResourceTable> table = loadTablePbFromCollection(collection.get());
- if (!table) {
- mContext->getDiagnostics()->error(DiagMessage(input) << "invalid static library");
- return false;
- }
-
- ResourceTablePackage* pkg = table->findPackageById(0x7f);
- if (!pkg) {
- mContext->getDiagnostics()->error(DiagMessage(input)
- << "static library has no package");
- return false;
- }
-
- bool result;
- if (mOptions.noStaticLibPackages) {
- // Merge all resources as if they were in the compilation package. This is the old
- // behaviour of aapt.
-
- // Add the package to the set of --extra-packages so we emit an R.java for each
- // library package.
- if (!pkg->name.empty()) {
- mOptions.extraJavaPackages.insert(pkg->name);
- }
-
- pkg->name = "";
- if (override) {
- result = mTableMerger->mergeOverlay(Source(input), table.get(), collection.get());
- } else {
- result = mTableMerger->merge(Source(input), table.get(), collection.get());
- }
-
- } else {
- // This is the proper way to merge libraries, where the package name is preserved
- // and resource names are mangled.
- result = mTableMerger->mergeAndMangle(Source(input), pkg->name, table.get(),
- collection.get());
- }
-
- if (!result) {
- return false;
- }
-
- // Make sure to move the collection into the set of IFileCollections.
- mCollections.push_back(std::move(collection));
- return true;
- }
-
- bool mergeResourceTable(io::IFile* file, bool override) {
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(DiagMessage() << "merging resource table "
- << file->getSource());
- }
-
- std::unique_ptr<io::IData> data = file->openAsData();
- if (!data) {
- mContext->getDiagnostics()->error(DiagMessage(file->getSource())
- << "failed to open file");
- return false;
- }
-
- std::unique_ptr<ResourceTable> table = loadTableFromPb(file->getSource(),
- data->data(), data->size(),
- mContext->getDiagnostics());
- if (!table) {
- return false;
- }
-
- bool result = false;
- if (override) {
- result = mTableMerger->mergeOverlay(file->getSource(), table.get());
- } else {
- result = mTableMerger->merge(file->getSource(), table.get());
- }
- return result;
- }
-
- bool mergeCompiledFile(io::IFile* file, ResourceFile* fileDesc, bool override) {
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(DiagMessage()
- << "merging '" << fileDesc->name
- << "' from compiled file "
- << file->getSource());
- }
-
- bool result = false;
- if (override) {
- result = mTableMerger->mergeFileOverlay(*fileDesc, file);
- } else {
- result = mTableMerger->mergeFile(*fileDesc, file);
- }
-
- if (!result) {
- return false;
- }
-
- // Add the exports of this file to the table.
- for (SourcedResourceName& exportedSymbol : fileDesc->exportedSymbols) {
- if (exportedSymbol.name.package.empty()) {
- exportedSymbol.name.package = mContext->getCompilationPackage();
- }
-
- ResourceNameRef resName = exportedSymbol.name;
-
- Maybe<ResourceName> mangledName = mContext->getNameMangler()->mangleName(
- exportedSymbol.name);
- if (mangledName) {
- resName = mangledName.value();
- }
-
- std::unique_ptr<Id> id = util::make_unique<Id>();
- id->setSource(fileDesc->source.withLine(exportedSymbol.line));
- bool result = mFinalTable.addResourceAllowMangled(
- resName, ConfigDescription::defaultConfig(), std::string(), std::move(id),
- mContext->getDiagnostics());
- if (!result) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Takes a path to load as a ZIP file and merges the files within into the master ResourceTable.
- * If override is true, conflicting resources are allowed to override each other, in order of
- * last seen.
- *
- * An io::IFileCollection is created from the ZIP file and added to the set of
- * io::IFileCollections that are open.
- */
- bool mergeArchive(const std::string& input, bool override) {
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(DiagMessage() << "merging archive " << input);
- }
-
- std::string errorStr;
- std::unique_ptr<io::ZipFileCollection> collection =
- io::ZipFileCollection::create(input, &errorStr);
- if (!collection) {
- mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
- return false;
- }
-
- bool error = false;
- for (auto iter = collection->iterator(); iter->hasNext(); ) {
- if (!mergeFile(iter->next(), override)) {
- error = true;
- }
- }
-
- // Make sure to move the collection into the set of IFileCollections.
- mCollections.push_back(std::move(collection));
- return !error;
- }
-
- /**
- * Takes a path to load and merge into the master ResourceTable. If override is true,
- * conflicting resources are allowed to override each other, in order of last seen.
- *
- * If the file path ends with .flata, .jar, .jack, or .zip the file is treated as ZIP archive
- * and the files within are merged individually.
- *
- * Otherwise the files is processed on its own.
- */
- bool mergePath(const std::string& path, bool override) {
- 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(path, ".apk")) {
- return mergeStaticLibrary(path, override);
- }
-
- io::IFile* file = mFileCollection->insertFile(path);
- return mergeFile(file, override);
- }
-
- /**
- * Takes a file to load and merge into the master ResourceTable. If override is true,
- * conflicting resources are allowed to override each other, in order of last seen.
- *
- * If the file ends with .arsc.flat, then it is loaded as a ResourceTable and merged into the
- * master ResourceTable. If the file ends with .flat, then it is treated like a compiled file
- * and the header data is read and merged into the final ResourceTable.
- *
- * All other file types are ignored. This is because these files could be coming from a zip,
- * where we could have other files like classes.dex.
- */
- bool mergeFile(io::IFile* file, bool override) {
- const Source& src = file->getSource();
- if (util::stringEndsWith(src.path, ".arsc.flat")) {
- return mergeResourceTable(file, override);
-
- } 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) {
- mContext->getDiagnostics()->error(DiagMessage(src) << "failed to open");
- return false;
- }
-
- CompiledFileInputStream inputStream(data->data(), data->size());
- uint32_t numFiles = 0;
- if (!inputStream.ReadLittleEndian32(&numFiles)) {
- mContext->getDiagnostics()->error(DiagMessage(src) << "failed read num files");
- return false;
- }
-
- for (uint32_t i = 0; i < numFiles; i++) {
- pb::CompiledFile compiledFile;
- if (!inputStream.ReadCompiledFile(&compiledFile)) {
- mContext->getDiagnostics()->error(DiagMessage(src)
- << "failed to read compiled file header");
- return false;
- }
-
- uint64_t offset, len;
- if (!inputStream.ReadDataMetaData(&offset, &len)) {
- mContext->getDiagnostics()->error(DiagMessage(src)
- << "failed to read data meta data");
- return false;
- }
-
- std::unique_ptr<ResourceFile> resourceFile = deserializeCompiledFileFromPb(
- compiledFile, file->getSource(), mContext->getDiagnostics());
- if (!resourceFile) {
- return false;
- }
-
- if (!mergeCompiledFile(file->createFileSegment(offset, len), resourceFile.get(),
- override)) {
- return false;
- }
- }
- return true;
- }
-
- // Ignore non .flat files. This could be classes.dex or something else that happens
- // to be in an archive.
- return true;
- }
-
- std::unique_ptr<xml::XmlResource> generateSplitManifest(const AppInfo& appInfo,
- const SplitConstraints& constraints) {
- std::unique_ptr<xml::XmlResource> doc = util::make_unique<xml::XmlResource>();
-
- std::unique_ptr<xml::Namespace> namespaceAndroid = util::make_unique<xml::Namespace>();
- namespaceAndroid->namespaceUri = xml::kSchemaAndroid;
- namespaceAndroid->namespacePrefix = "android";
-
- std::unique_ptr<xml::Element> manifestEl = util::make_unique<xml::Element>();
- manifestEl->name = "manifest";
- manifestEl->attributes.push_back(
- xml::Attribute{ "", "package", appInfo.package });
-
- if (appInfo.versionCode) {
- manifestEl->attributes.push_back(xml::Attribute{
- xml::kSchemaAndroid,
- "versionCode",
- std::to_string(appInfo.versionCode.value()) });
- }
-
- if (appInfo.revisionCode) {
- manifestEl->attributes.push_back(xml::Attribute{
- xml::kSchemaAndroid,
- "revisionCode", std::to_string(appInfo.revisionCode.value()) });
- }
-
- std::stringstream splitName;
- splitName << "config." << util::joiner(constraints.configs, "_");
-
- manifestEl->attributes.push_back(
- xml::Attribute{ "", "split", splitName.str() });
-
- std::unique_ptr<xml::Element> applicationEl = util::make_unique<xml::Element>();
- applicationEl->name = "application";
- applicationEl->attributes.push_back(
- xml::Attribute{ xml::kSchemaAndroid, "hasCode", "false" });
-
- manifestEl->addChild(std::move(applicationEl));
- namespaceAndroid->addChild(std::move(manifestEl));
- doc->root = std::move(namespaceAndroid);
- return doc;
- }
-
- /**
- * Writes the AndroidManifest, ResourceTable, and all XML files referenced by the ResourceTable
- * to the IArchiveWriter.
- */
- bool writeApk(IArchiveWriter* writer, proguard::KeepSet* keepSet, xml::XmlResource* manifest,
- ResourceTable* table) {
- const bool keepRawValues = mOptions.staticLib;
- bool result = flattenXml(manifest, "AndroidManifest.xml", {}, keepRawValues, writer,
- mContext);
- if (!result) {
- return false;
- }
-
- ResourceFileFlattenerOptions fileFlattenerOptions;
- fileFlattenerOptions.keepRawValues = keepRawValues;
- fileFlattenerOptions.doNotCompressAnything = mOptions.doNotCompressAnything;
- fileFlattenerOptions.extensionsToNotCompress = mOptions.extensionsToNotCompress;
- fileFlattenerOptions.noAutoVersion = mOptions.noAutoVersion;
- fileFlattenerOptions.noVersionVectors = mOptions.noVersionVectors;
- fileFlattenerOptions.noXmlNamespaces = mOptions.noXmlNamespaces;
- fileFlattenerOptions.updateProguardSpec =
- static_cast<bool>(mOptions.generateProguardRulesPath);
-
- ResourceFileFlattener fileFlattener(fileFlattenerOptions, mContext, keepSet);
-
- if (!fileFlattener.flatten(table, writer)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed linking file resources");
- return false;
- }
-
- if (mOptions.staticLib) {
- if (!flattenTableToPb(table, writer)) {
- mContext->getDiagnostics()->error(DiagMessage()
- << "failed to write resources.arsc.flat");
- return false;
- }
- } else {
- if (!flattenTable(table, writer)) {
- mContext->getDiagnostics()->error(DiagMessage()
- << "failed to write resources.arsc");
- return false;
- }
- }
- return true;
- }
-
- int run(const std::vector<std::string>& inputFiles) {
- // Load the AndroidManifest.xml
- std::unique_ptr<xml::XmlResource> manifestXml = loadXml(mOptions.manifestPath,
- mContext->getDiagnostics());
- if (!manifestXml) {
- return 1;
- }
-
- // First extract the Package name without modifying it (via --rename-manifest-package).
- if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get(),
- mContext->getDiagnostics())) {
- const AppInfo& appInfo = maybeAppInfo.value();
- mContext->setCompilationPackage(appInfo.package);
- }
-
- ManifestFixer manifestFixer(mOptions.manifestFixerOptions);
- if (!manifestFixer.consume(mContext, manifestXml.get())) {
- return 1;
- }
-
- Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get(),
- mContext->getDiagnostics());
- if (!maybeAppInfo) {
- return 1;
- }
-
- const AppInfo& appInfo = maybeAppInfo.value();
- if (appInfo.minSdkVersion) {
- if (Maybe<int> maybeMinSdkVersion =
- ResourceUtils::parseSdkVersion(appInfo.minSdkVersion.value())) {
- mContext->setMinSdkVersion(maybeMinSdkVersion.value());
- }
- }
-
- mContext->setNameManglerPolicy(NameManglerPolicy{ mContext->getCompilationPackage() });
- if (mContext->getCompilationPackage() == "android") {
- mContext->setPackageId(0x01);
- } else {
- mContext->setPackageId(0x7f);
- }
-
- if (!loadSymbolsFromIncludePaths()) {
- return 1;
- }
-
- TableMergerOptions tableMergerOptions;
- tableMergerOptions.autoAddOverlay = mOptions.autoAddOverlay;
- mTableMerger = util::make_unique<TableMerger>(mContext, &mFinalTable, tableMergerOptions);
-
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(
- DiagMessage() << "linking package '" << mContext->getCompilationPackage()
- << "' with package ID " << std::hex
- << (int) mContext->getPackageId());
- }
-
-
- for (const std::string& input : inputFiles) {
- if (!mergePath(input, false)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed parsing input");
- return 1;
- }
- }
-
- for (const std::string& input : mOptions.overlayFiles) {
- if (!mergePath(input, true)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed parsing overlays");
- return 1;
- }
- }
-
- if (!verifyNoExternalPackages()) {
- return 1;
- }
-
+ // First try to load the file as a static lib.
+ std::string errorStr;
+ std::unique_ptr<ResourceTable> staticInclude =
+ loadStaticLibrary(path, &errorStr);
+ if (staticInclude) {
if (!mOptions.staticLib) {
- PrivateAttributeMover mover;
- if (!mover.consume(mContext, &mFinalTable)) {
+ // Can't include static libraries when not building a static library.
+ mContext->getDiagnostics()->error(
+ DiagMessage(path)
+ << "can't include static library when building app");
+ return false;
+ }
+
+ // If we are using --no-static-lib-packages, we need to rename the
+ // package of this
+ // table to our compilation package.
+ if (mOptions.noStaticLibPackages) {
+ if (ResourceTablePackage* pkg =
+ staticInclude->findPackageById(0x7f)) {
+ pkg->name = mContext->getCompilationPackage();
+ }
+ }
+
+ mContext->getExternalSymbols()->appendSource(
+ util::make_unique<ResourceTableSymbolSource>(staticInclude.get()));
+
+ mStaticTableIncludes.push_back(std::move(staticInclude));
+
+ } else if (!errorStr.empty()) {
+ // We had an error with reading, so fail.
+ mContext->getDiagnostics()->error(DiagMessage(path) << errorStr);
+ return false;
+ }
+
+ if (!assetSource->addAssetPath(path)) {
+ mContext->getDiagnostics()->error(DiagMessage(path)
+ << "failed to load include path");
+ return false;
+ }
+ }
+
+ mContext->getExternalSymbols()->appendSource(std::move(assetSource));
+ return true;
+ }
+
+ 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())) {
+ 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::Attribute* versionCodeAttr =
+ manifestEl->findAttribute(xml::kSchemaAndroid, "versionCode")) {
+ Maybe<uint32_t> maybeCode =
+ ResourceUtils::parseInt(versionCodeAttr->value);
+ if (!maybeCode) {
+ diag->error(
+ DiagMessage(xmlRes->file.source.withLine(manifestEl->lineNumber))
+ << "invalid android:versionCode '" << versionCodeAttr->value
+ << "'");
+ return {};
+ }
+ appInfo.versionCode = maybeCode.value();
+ }
+
+ if (xml::Attribute* revisionCodeAttr =
+ manifestEl->findAttribute(xml::kSchemaAndroid, "revisionCode")) {
+ Maybe<uint32_t> maybeCode =
+ ResourceUtils::parseInt(revisionCodeAttr->value);
+ if (!maybeCode) {
+ diag->error(
+ DiagMessage(xmlRes->file.source.withLine(manifestEl->lineNumber))
+ << "invalid android:revisionCode '" << revisionCodeAttr->value
+ << "'");
+ return {};
+ }
+ appInfo.revisionCode = maybeCode.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 {};
+ }
+
+ /**
+ * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it
+ * linked.
+ * Postcondition: ResourceTable has only one package left. All others are
+ * stripped, or there
+ * is an error and false is returned.
+ */
+ bool verifyNoExternalPackages() {
+ auto isExtPackageFunc =
+ [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
+ return mContext->getCompilationPackage() != pkg->name || !pkg->id ||
+ pkg->id.value() != mContext->getPackageId();
+ };
+
+ bool error = false;
+ for (const auto& package : mFinalTable.packages) {
+ if (isExtPackageFunc(package)) {
+ // We have a package that is not related to the one we're building!
+ for (const auto& type : package->types) {
+ for (const auto& entry : type->entries) {
+ ResourceNameRef resName(package->name, type->type, entry->name);
+
+ for (const auto& configValue : entry->values) {
+ // 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 == "android") {
+ mContext->getDiagnostics()->warn(
+ DiagMessage(configValue->value->getSource())
+ << "generated id '" << resName << "' for external package '"
+ << package->name << "'");
+ } else {
mContext->getDiagnostics()->error(
- DiagMessage() << "failed moving private attributes");
- return 1;
- }
-
- // Assign IDs if we are building a regular app.
- IdAssigner idAssigner(&mOptions.stableIdMap);
- if (!idAssigner.consume(mContext, &mFinalTable)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed assigning IDs");
- return 1;
- }
-
- // Now grab each ID and emit it as a file.
- if (mOptions.resourceIdMapPath) {
- for (auto& package : mFinalTable.packages) {
- for (auto& type : package->types) {
- for (auto& entry : type->entries) {
- ResourceName name(package->name, type->type, entry->name);
- // The IDs are guaranteed to exist.
- mOptions.stableIdMap[std::move(name)] = ResourceId(package->id.value(),
- type->id.value(),
- entry->id.value());
- }
- }
- }
-
- if (!writeStableIdMapToPath(mContext->getDiagnostics(),
- mOptions.stableIdMap,
- mOptions.resourceIdMapPath.value())) {
- return 1;
- }
- }
- } else {
- // Static libs are merged with other apps, and ID collisions are bad, so verify that
- // no IDs have been set.
- if (!verifyNoIdsSet()) {
- return 1;
- }
- }
-
- // Add the names to mangle based on our source merge earlier.
- mContext->setNameManglerPolicy(NameManglerPolicy{
- mContext->getCompilationPackage(), mTableMerger->getMergedPackages() });
-
- // Add our table to the symbol table.
- mContext->getExternalSymbols()->prependSource(
- util::make_unique<ResourceTableSymbolSource>(&mFinalTable));
-
- ReferenceLinker linker;
- if (!linker.consume(mContext, &mFinalTable)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed linking references");
- return 1;
- }
-
- if (mOptions.staticLib) {
- if (!mOptions.products.empty()) {
- mContext->getDiagnostics()->warn(
- DiagMessage() << "can't select products when building static library");
- }
- } else {
- ProductFilter productFilter(mOptions.products);
- if (!productFilter.consume(mContext, &mFinalTable)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products");
- return 1;
- }
- }
-
- if (!mOptions.noAutoVersion) {
- AutoVersioner versioner;
- if (!versioner.consume(mContext, &mFinalTable)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed versioning styles");
- return 1;
- }
- }
-
- 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.noResourceDeduping) {
- ResourceDeduper deduper;
- if (!deduper.consume(mContext, &mFinalTable)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed deduping resources");
- return 1;
- }
- }
-
- proguard::KeepSet proguardKeepSet;
- proguard::KeepSet proguardMainDexKeepSet;
-
- if (mOptions.staticLib) {
- if (mOptions.tableSplitterOptions.configFilter != nullptr ||
- mOptions.tableSplitterOptions.preferredDensity) {
- mContext->getDiagnostics()->warn(
- DiagMessage() << "can't strip resources when building static library");
- }
- } else {
- // Adjust the SplitConstraints so that their SDK version is stripped if it is less
- // than or equal to the minSdk. Otherwise the resources that have had their SDK version
- // stripped due to minSdk won't ever match.
- std::vector<SplitConstraints> adjustedConstraintsList;
- adjustedConstraintsList.reserve(mOptions.splitConstraints.size());
- for (const SplitConstraints& constraints : mOptions.splitConstraints) {
- SplitConstraints adjustedConstraints;
- for (const ConfigDescription& config : constraints.configs) {
- if (config.sdkVersion <= mContext->getMinSdkVersion()) {
- adjustedConstraints.configs.insert(config.copyWithoutSdkVersion());
- } else {
- adjustedConstraints.configs.insert(config);
- }
- }
- adjustedConstraintsList.push_back(std::move(adjustedConstraints));
- }
-
- TableSplitter tableSplitter(adjustedConstraintsList, mOptions.tableSplitterOptions);
- if (!tableSplitter.verifySplitConstraints(mContext)) {
- return 1;
- }
- tableSplitter.splitTable(&mFinalTable);
-
- // Now we need to write out the Split APKs.
- auto pathIter = mOptions.splitPaths.begin();
- auto splitConstraintsIter = adjustedConstraintsList.begin();
- for (std::unique_ptr<ResourceTable>& splitTable : tableSplitter.getSplits()) {
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(
- DiagMessage(*pathIter) << "generating split with configurations '"
- << util::joiner(splitConstraintsIter->configs, ", ") << "'");
- }
-
- std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter(*pathIter);
- if (!archiveWriter) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed to create archive");
- return 1;
- }
-
- // Generate an AndroidManifest.xml for each split.
- std::unique_ptr<xml::XmlResource> splitManifest =
- generateSplitManifest(appInfo, *splitConstraintsIter);
-
- XmlReferenceLinker linker;
- if (!linker.consume(mContext, splitManifest.get())) {
- mContext->getDiagnostics()->error(
- DiagMessage() << "failed to create Split AndroidManifest.xml");
- return 1;
- }
-
- if (!writeApk(archiveWriter.get(), &proguardKeepSet, splitManifest.get(),
- splitTable.get())) {
- return 1;
- }
-
- ++pathIter;
- ++splitConstraintsIter;
- }
- }
-
- // Start writing the base APK.
- std::unique_ptr<IArchiveWriter> archiveWriter = makeArchiveWriter(mOptions.outputPath);
- if (!archiveWriter) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed to create archive");
- return 1;
- }
-
- bool error = false;
- {
- // AndroidManifest.xml has no resource name, but the CallSite is built from the name
- // (aka, which package the AndroidManifest.xml is coming from).
- // So we give it a package name so it can see local resources.
- manifestXml->file.name.package = mContext->getCompilationPackage();
-
- XmlReferenceLinker manifestLinker;
- if (manifestLinker.consume(mContext, manifestXml.get())) {
- 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;
- }
-
- if (mOptions.generateJavaClassPath) {
- if (!writeManifestJavaFile(manifestXml.get())) {
- error = true;
- }
- }
-
- if (mOptions.noXmlNamespaces) {
- // PackageParser will fail if URIs are removed from AndroidManifest.xml.
- XmlNamespaceRemover namespaceRemover(true /* keepUris */);
- if (!namespaceRemover.consume(mContext, manifestXml.get())) {
- error = true;
- }
- }
- } else {
+ DiagMessage(configValue->value->getSource())
+ << "defined resource '" << resName
+ << "' for external package '" << package->name << "'");
error = true;
+ }
}
+ }
+ }
+ }
+ }
+
+ auto newEndIter =
+ std::remove_if(mFinalTable.packages.begin(), mFinalTable.packages.end(),
+ isExtPackageFunc);
+ mFinalTable.packages.erase(newEndIter, mFinalTable.packages.end());
+ return !error;
+ }
+
+ /**
+ * Returns true if no IDs have been set, false otherwise.
+ */
+ bool verifyNoIdsSet() {
+ for (const auto& package : mFinalTable.packages) {
+ for (const auto& type : package->types) {
+ if (type->id) {
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "type " << type->type << " has ID " << std::hex
+ << (int)type->id.value() << std::dec
+ << " assigned");
+ return false;
}
- if (error) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed processing manifest");
- return 1;
+ for (const auto& entry : type->entries) {
+ if (entry->id) {
+ ResourceNameRef resName(package->name, type->type, entry->name);
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "entry " << resName << " has ID " << std::hex
+ << (int)entry->id.value() << std::dec
+ << " assigned");
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ std::unique_ptr<IArchiveWriter> makeArchiveWriter(const StringPiece& out) {
+ if (mOptions.outputToDirectory) {
+ return createDirectoryArchiveWriter(mContext->getDiagnostics(), out);
+ } else {
+ return createZipFileArchiveWriter(mContext->getDiagnostics(), out);
+ }
+ }
+
+ bool flattenTable(ResourceTable* table, IArchiveWriter* writer) {
+ BigBuffer buffer(1024);
+ TableFlattener flattener(&buffer);
+ if (!flattener.consume(mContext, table)) {
+ return false;
+ }
+
+ if (writer->startEntry("resources.arsc", ArchiveEntry::kAlign)) {
+ if (writer->writeEntry(buffer)) {
+ if (writer->finishEntry()) {
+ return true;
+ }
+ }
+ }
+
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed to write resources.arsc to archive");
+ return false;
+ }
+
+ bool flattenTableToPb(ResourceTable* table, IArchiveWriter* writer) {
+ // Create the file/zip entry.
+ if (!writer->startEntry("resources.arsc.flat", 0)) {
+ mContext->getDiagnostics()->error(DiagMessage() << "failed to open");
+ return false;
+ }
+
+ // Make sure CopyingOutputStreamAdaptor is deleted before we call
+ // writer->finishEntry().
+ {
+ // Wrap our IArchiveWriter with an adaptor that implements the
+ // ZeroCopyOutputStream
+ // interface.
+ CopyingOutputStreamAdaptor adaptor(writer);
+
+ std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table);
+ if (!pbTable->SerializeToZeroCopyStream(&adaptor)) {
+ mContext->getDiagnostics()->error(DiagMessage() << "failed to write");
+ return false;
+ }
+ }
+
+ if (!writer->finishEntry()) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed to finish entry");
+ return false;
+ }
+ return true;
+ }
+
+ bool writeJavaFile(ResourceTable* table,
+ const StringPiece& packageNameToGenerate,
+ const StringPiece& outPackage,
+ const JavaClassGeneratorOptions& javaOptions) {
+ if (!mOptions.generateJavaClassPath) {
+ return true;
+ }
+
+ std::string outPath = mOptions.generateJavaClassPath.value();
+ file::appendPath(&outPath, file::packageToPath(outPackage));
+ if (!file::mkdirs(outPath)) {
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed to create directory '" << outPath << "'");
+ return false;
+ }
+
+ file::appendPath(&outPath, "R.java");
+
+ std::ofstream fout(outPath, std::ofstream::binary);
+ if (!fout) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed writing to '" << outPath
+ << "': " << strerror(errno));
+ return false;
+ }
+
+ JavaClassGenerator generator(mContext, table, javaOptions);
+ if (!generator.generate(packageNameToGenerate, outPackage, &fout)) {
+ mContext->getDiagnostics()->error(DiagMessage(outPath)
+ << generator.getError());
+ return false;
+ }
+
+ if (!fout) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed writing to '" << outPath
+ << "': " << strerror(errno));
+ }
+ return true;
+ }
+
+ bool writeManifestJavaFile(xml::XmlResource* manifestXml) {
+ if (!mOptions.generateJavaClassPath) {
+ return true;
+ }
+
+ std::unique_ptr<ClassDefinition> manifestClass =
+ generateManifestClass(mContext->getDiagnostics(), manifestXml);
+
+ if (!manifestClass) {
+ // Something bad happened, but we already logged it, so exit.
+ return false;
+ }
+
+ if (manifestClass->empty()) {
+ // Empty Manifest class, no need to generate it.
+ return true;
+ }
+
+ // Add any JavaDoc annotations to the generated class.
+ for (const std::string& annotation : mOptions.javadocAnnotations) {
+ std::string properAnnotation = "@";
+ properAnnotation += annotation;
+ manifestClass->getCommentBuilder()->appendComment(properAnnotation);
+ }
+
+ const std::string& packageUtf8 = mContext->getCompilationPackage();
+
+ std::string outPath = mOptions.generateJavaClassPath.value();
+ file::appendPath(&outPath, file::packageToPath(packageUtf8));
+
+ if (!file::mkdirs(outPath)) {
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed to create directory '" << outPath << "'");
+ return false;
+ }
+
+ file::appendPath(&outPath, "Manifest.java");
+
+ std::ofstream fout(outPath, std::ofstream::binary);
+ if (!fout) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed writing to '" << outPath
+ << "': " << strerror(errno));
+ return false;
+ }
+
+ if (!ClassDefinition::writeJavaFile(manifestClass.get(), packageUtf8, true,
+ &fout)) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed writing to '" << outPath
+ << "': " << strerror(errno));
+ return false;
+ }
+ return true;
+ }
+
+ bool writeProguardFile(const Maybe<std::string>& out,
+ const proguard::KeepSet& keepSet) {
+ if (!out) {
+ return true;
+ }
+
+ const std::string& outPath = out.value();
+ std::ofstream fout(outPath, std::ofstream::binary);
+ if (!fout) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed to open '" << outPath
+ << "': " << strerror(errno));
+ return false;
+ }
+
+ proguard::writeKeepSet(&fout, keepSet);
+ if (!fout) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed writing to '" << outPath
+ << "': " << strerror(errno));
+ return false;
+ }
+ return true;
+ }
+
+ std::unique_ptr<ResourceTable> loadStaticLibrary(const std::string& input,
+ std::string* outError) {
+ std::unique_ptr<io::ZipFileCollection> collection =
+ io::ZipFileCollection::create(input, outError);
+ if (!collection) {
+ return {};
+ }
+ return loadTablePbFromCollection(collection.get());
+ }
+
+ std::unique_ptr<ResourceTable> loadTablePbFromCollection(
+ io::IFileCollection* collection) {
+ io::IFile* file = collection->findFile("resources.arsc.flat");
+ if (!file) {
+ return {};
+ }
+
+ std::unique_ptr<io::IData> data = file->openAsData();
+ return loadTableFromPb(file->getSource(), data->data(), data->size(),
+ mContext->getDiagnostics());
+ }
+
+ bool mergeStaticLibrary(const std::string& input, bool override) {
+ if (mContext->verbose()) {
+ mContext->getDiagnostics()->note(DiagMessage()
+ << "merging static library " << input);
+ }
+
+ std::string errorStr;
+ std::unique_ptr<io::ZipFileCollection> collection =
+ io::ZipFileCollection::create(input, &errorStr);
+ if (!collection) {
+ mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
+ return false;
+ }
+
+ std::unique_ptr<ResourceTable> table =
+ loadTablePbFromCollection(collection.get());
+ if (!table) {
+ mContext->getDiagnostics()->error(DiagMessage(input)
+ << "invalid static library");
+ return false;
+ }
+
+ ResourceTablePackage* pkg = table->findPackageById(0x7f);
+ if (!pkg) {
+ mContext->getDiagnostics()->error(DiagMessage(input)
+ << "static library has no package");
+ return false;
+ }
+
+ bool result;
+ if (mOptions.noStaticLibPackages) {
+ // Merge all resources as if they were in the compilation package. This is
+ // the old
+ // behaviour of aapt.
+
+ // Add the package to the set of --extra-packages so we emit an R.java for
+ // each
+ // library package.
+ if (!pkg->name.empty()) {
+ mOptions.extraJavaPackages.insert(pkg->name);
+ }
+
+ pkg->name = "";
+ if (override) {
+ result = mTableMerger->mergeOverlay(Source(input), table.get(),
+ collection.get());
+ } else {
+ result =
+ mTableMerger->merge(Source(input), table.get(), collection.get());
+ }
+
+ } else {
+ // This is the proper way to merge libraries, where the package name is
+ // preserved
+ // and resource names are mangled.
+ result = mTableMerger->mergeAndMangle(Source(input), pkg->name,
+ table.get(), collection.get());
+ }
+
+ if (!result) {
+ return false;
+ }
+
+ // Make sure to move the collection into the set of IFileCollections.
+ mCollections.push_back(std::move(collection));
+ return true;
+ }
+
+ bool mergeResourceTable(io::IFile* file, bool override) {
+ if (mContext->verbose()) {
+ mContext->getDiagnostics()->note(
+ DiagMessage() << "merging resource table " << file->getSource());
+ }
+
+ std::unique_ptr<io::IData> data = file->openAsData();
+ if (!data) {
+ mContext->getDiagnostics()->error(DiagMessage(file->getSource())
+ << "failed to open file");
+ return false;
+ }
+
+ std::unique_ptr<ResourceTable> table =
+ loadTableFromPb(file->getSource(), data->data(), data->size(),
+ mContext->getDiagnostics());
+ if (!table) {
+ return false;
+ }
+
+ bool result = false;
+ if (override) {
+ result = mTableMerger->mergeOverlay(file->getSource(), table.get());
+ } else {
+ result = mTableMerger->merge(file->getSource(), table.get());
+ }
+ return result;
+ }
+
+ bool mergeCompiledFile(io::IFile* file, ResourceFile* fileDesc,
+ bool override) {
+ if (mContext->verbose()) {
+ mContext->getDiagnostics()->note(
+ DiagMessage() << "merging '" << fileDesc->name
+ << "' from compiled file " << file->getSource());
+ }
+
+ bool result = false;
+ if (override) {
+ result = mTableMerger->mergeFileOverlay(*fileDesc, file);
+ } else {
+ result = mTableMerger->mergeFile(*fileDesc, file);
+ }
+
+ if (!result) {
+ return false;
+ }
+
+ // Add the exports of this file to the table.
+ for (SourcedResourceName& exportedSymbol : fileDesc->exportedSymbols) {
+ if (exportedSymbol.name.package.empty()) {
+ exportedSymbol.name.package = mContext->getCompilationPackage();
+ }
+
+ ResourceNameRef resName = exportedSymbol.name;
+
+ Maybe<ResourceName> mangledName =
+ mContext->getNameMangler()->mangleName(exportedSymbol.name);
+ if (mangledName) {
+ resName = mangledName.value();
+ }
+
+ std::unique_ptr<Id> id = util::make_unique<Id>();
+ id->setSource(fileDesc->source.withLine(exportedSymbol.line));
+ bool result = mFinalTable.addResourceAllowMangled(
+ resName, ConfigDescription::defaultConfig(), std::string(),
+ std::move(id), mContext->getDiagnostics());
+ if (!result) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Takes a path to load as a ZIP file and merges the files within into the
+ * master ResourceTable.
+ * If override is true, conflicting resources are allowed to override each
+ * other, in order of
+ * last seen.
+ *
+ * An io::IFileCollection is created from the ZIP file and added to the set of
+ * io::IFileCollections that are open.
+ */
+ bool mergeArchive(const std::string& input, bool override) {
+ if (mContext->verbose()) {
+ mContext->getDiagnostics()->note(DiagMessage() << "merging archive "
+ << input);
+ }
+
+ std::string errorStr;
+ std::unique_ptr<io::ZipFileCollection> collection =
+ io::ZipFileCollection::create(input, &errorStr);
+ if (!collection) {
+ mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
+ return false;
+ }
+
+ bool error = false;
+ for (auto iter = collection->iterator(); iter->hasNext();) {
+ if (!mergeFile(iter->next(), override)) {
+ error = true;
+ }
+ }
+
+ // Make sure to move the collection into the set of IFileCollections.
+ mCollections.push_back(std::move(collection));
+ return !error;
+ }
+
+ /**
+ * Takes a path to load and merge into the master ResourceTable. If override
+ * is true,
+ * conflicting resources are allowed to override each other, in order of last
+ * seen.
+ *
+ * If the file path ends with .flata, .jar, .jack, or .zip the file is treated
+ * as ZIP archive
+ * and the files within are merged individually.
+ *
+ * Otherwise the files is processed on its own.
+ */
+ bool mergePath(const std::string& path, bool override) {
+ 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(path, ".apk")) {
+ return mergeStaticLibrary(path, override);
+ }
+
+ io::IFile* file = mFileCollection->insertFile(path);
+ return mergeFile(file, override);
+ }
+
+ /**
+ * Takes a file to load and merge into the master ResourceTable. If override
+ * is true,
+ * conflicting resources are allowed to override each other, in order of last
+ * seen.
+ *
+ * If the file ends with .arsc.flat, then it is loaded as a ResourceTable and
+ * merged into the
+ * master ResourceTable. If the file ends with .flat, then it is treated like
+ * a compiled file
+ * and the header data is read and merged into the final ResourceTable.
+ *
+ * All other file types are ignored. This is because these files could be
+ * coming from a zip,
+ * where we could have other files like classes.dex.
+ */
+ bool mergeFile(io::IFile* file, bool override) {
+ const Source& src = file->getSource();
+ if (util::stringEndsWith(src.path, ".arsc.flat")) {
+ return mergeResourceTable(file, override);
+
+ } 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) {
+ mContext->getDiagnostics()->error(DiagMessage(src) << "failed to open");
+ return false;
+ }
+
+ CompiledFileInputStream inputStream(data->data(), data->size());
+ uint32_t numFiles = 0;
+ if (!inputStream.ReadLittleEndian32(&numFiles)) {
+ mContext->getDiagnostics()->error(DiagMessage(src)
+ << "failed read num files");
+ return false;
+ }
+
+ for (uint32_t i = 0; i < numFiles; i++) {
+ pb::CompiledFile compiledFile;
+ if (!inputStream.ReadCompiledFile(&compiledFile)) {
+ mContext->getDiagnostics()->error(
+ DiagMessage(src) << "failed to read compiled file header");
+ return false;
}
- if (!writeApk(archiveWriter.get(), &proguardKeepSet, manifestXml.get(), &mFinalTable)) {
- return 1;
+ uint64_t offset, len;
+ if (!inputStream.ReadDataMetaData(&offset, &len)) {
+ mContext->getDiagnostics()->error(DiagMessage(src)
+ << "failed to read data meta data");
+ return false;
+ }
+
+ std::unique_ptr<ResourceFile> resourceFile =
+ deserializeCompiledFileFromPb(compiledFile, file->getSource(),
+ mContext->getDiagnostics());
+ if (!resourceFile) {
+ return false;
+ }
+
+ if (!mergeCompiledFile(file->createFileSegment(offset, len),
+ resourceFile.get(), override)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Ignore non .flat files. This could be classes.dex or something else that
+ // happens
+ // to be in an archive.
+ return true;
+ }
+
+ std::unique_ptr<xml::XmlResource> generateSplitManifest(
+ const AppInfo& appInfo, const SplitConstraints& constraints) {
+ std::unique_ptr<xml::XmlResource> doc =
+ util::make_unique<xml::XmlResource>();
+
+ std::unique_ptr<xml::Namespace> namespaceAndroid =
+ util::make_unique<xml::Namespace>();
+ namespaceAndroid->namespaceUri = xml::kSchemaAndroid;
+ namespaceAndroid->namespacePrefix = "android";
+
+ std::unique_ptr<xml::Element> manifestEl =
+ util::make_unique<xml::Element>();
+ manifestEl->name = "manifest";
+ manifestEl->attributes.push_back(
+ xml::Attribute{"", "package", appInfo.package});
+
+ if (appInfo.versionCode) {
+ manifestEl->attributes.push_back(
+ xml::Attribute{xml::kSchemaAndroid, "versionCode",
+ std::to_string(appInfo.versionCode.value())});
+ }
+
+ if (appInfo.revisionCode) {
+ manifestEl->attributes.push_back(
+ xml::Attribute{xml::kSchemaAndroid, "revisionCode",
+ std::to_string(appInfo.revisionCode.value())});
+ }
+
+ std::stringstream splitName;
+ splitName << "config." << util::joiner(constraints.configs, "_");
+
+ manifestEl->attributes.push_back(
+ xml::Attribute{"", "split", splitName.str()});
+
+ std::unique_ptr<xml::Element> applicationEl =
+ util::make_unique<xml::Element>();
+ applicationEl->name = "application";
+ applicationEl->attributes.push_back(
+ xml::Attribute{xml::kSchemaAndroid, "hasCode", "false"});
+
+ manifestEl->addChild(std::move(applicationEl));
+ namespaceAndroid->addChild(std::move(manifestEl));
+ doc->root = std::move(namespaceAndroid);
+ return doc;
+ }
+
+ /**
+ * Writes the AndroidManifest, ResourceTable, and all XML files referenced by
+ * the ResourceTable
+ * to the IArchiveWriter.
+ */
+ bool writeApk(IArchiveWriter* writer, proguard::KeepSet* keepSet,
+ xml::XmlResource* manifest, ResourceTable* table) {
+ const bool keepRawValues = mOptions.staticLib;
+ bool result = flattenXml(manifest, "AndroidManifest.xml", {}, keepRawValues,
+ writer, mContext);
+ if (!result) {
+ return false;
+ }
+
+ ResourceFileFlattenerOptions fileFlattenerOptions;
+ fileFlattenerOptions.keepRawValues = keepRawValues;
+ fileFlattenerOptions.doNotCompressAnything = mOptions.doNotCompressAnything;
+ fileFlattenerOptions.extensionsToNotCompress =
+ mOptions.extensionsToNotCompress;
+ fileFlattenerOptions.noAutoVersion = mOptions.noAutoVersion;
+ fileFlattenerOptions.noVersionVectors = mOptions.noVersionVectors;
+ fileFlattenerOptions.noXmlNamespaces = mOptions.noXmlNamespaces;
+ fileFlattenerOptions.updateProguardSpec =
+ static_cast<bool>(mOptions.generateProguardRulesPath);
+
+ ResourceFileFlattener fileFlattener(fileFlattenerOptions, mContext,
+ keepSet);
+
+ if (!fileFlattener.flatten(table, writer)) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed linking file resources");
+ return false;
+ }
+
+ if (mOptions.staticLib) {
+ if (!flattenTableToPb(table, writer)) {
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed to write resources.arsc.flat");
+ return false;
+ }
+ } else {
+ if (!flattenTable(table, writer)) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed to write resources.arsc");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ int run(const std::vector<std::string>& inputFiles) {
+ // Load the AndroidManifest.xml
+ std::unique_ptr<xml::XmlResource> manifestXml =
+ loadXml(mOptions.manifestPath, mContext->getDiagnostics());
+ if (!manifestXml) {
+ return 1;
+ }
+
+ // First extract the Package name without modifying it (via
+ // --rename-manifest-package).
+ if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(
+ manifestXml.get(), mContext->getDiagnostics())) {
+ const AppInfo& appInfo = maybeAppInfo.value();
+ mContext->setCompilationPackage(appInfo.package);
+ }
+
+ ManifestFixer manifestFixer(mOptions.manifestFixerOptions);
+ if (!manifestFixer.consume(mContext, manifestXml.get())) {
+ return 1;
+ }
+
+ Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(
+ manifestXml.get(), mContext->getDiagnostics());
+ if (!maybeAppInfo) {
+ return 1;
+ }
+
+ const AppInfo& appInfo = maybeAppInfo.value();
+ if (appInfo.minSdkVersion) {
+ if (Maybe<int> maybeMinSdkVersion =
+ ResourceUtils::parseSdkVersion(appInfo.minSdkVersion.value())) {
+ mContext->setMinSdkVersion(maybeMinSdkVersion.value());
+ }
+ }
+
+ mContext->setNameManglerPolicy(
+ NameManglerPolicy{mContext->getCompilationPackage()});
+ if (mContext->getCompilationPackage() == "android") {
+ mContext->setPackageId(0x01);
+ } else {
+ mContext->setPackageId(0x7f);
+ }
+
+ if (!loadSymbolsFromIncludePaths()) {
+ return 1;
+ }
+
+ TableMergerOptions tableMergerOptions;
+ tableMergerOptions.autoAddOverlay = mOptions.autoAddOverlay;
+ mTableMerger = util::make_unique<TableMerger>(mContext, &mFinalTable,
+ tableMergerOptions);
+
+ if (mContext->verbose()) {
+ mContext->getDiagnostics()->note(DiagMessage()
+ << "linking package '"
+ << mContext->getCompilationPackage()
+ << "' with package ID " << std::hex
+ << (int)mContext->getPackageId());
+ }
+
+ for (const std::string& input : inputFiles) {
+ if (!mergePath(input, false)) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed parsing input");
+ return 1;
+ }
+ }
+
+ for (const std::string& input : mOptions.overlayFiles) {
+ if (!mergePath(input, true)) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed parsing overlays");
+ return 1;
+ }
+ }
+
+ if (!verifyNoExternalPackages()) {
+ return 1;
+ }
+
+ if (!mOptions.staticLib) {
+ PrivateAttributeMover mover;
+ if (!mover.consume(mContext, &mFinalTable)) {
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed moving private attributes");
+ return 1;
+ }
+
+ // Assign IDs if we are building a regular app.
+ IdAssigner idAssigner(&mOptions.stableIdMap);
+ if (!idAssigner.consume(mContext, &mFinalTable)) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed assigning IDs");
+ return 1;
+ }
+
+ // Now grab each ID and emit it as a file.
+ if (mOptions.resourceIdMapPath) {
+ for (auto& package : mFinalTable.packages) {
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
+ ResourceName name(package->name, type->type, entry->name);
+ // The IDs are guaranteed to exist.
+ mOptions.stableIdMap[std::move(name)] = ResourceId(
+ package->id.value(), type->id.value(), entry->id.value());
+ }
+ }
+ }
+
+ if (!writeStableIdMapToPath(mContext->getDiagnostics(),
+ mOptions.stableIdMap,
+ mOptions.resourceIdMapPath.value())) {
+ return 1;
+ }
+ }
+ } else {
+ // Static libs are merged with other apps, and ID collisions are bad, so
+ // verify that
+ // no IDs have been set.
+ if (!verifyNoIdsSet()) {
+ return 1;
+ }
+ }
+
+ // Add the names to mangle based on our source merge earlier.
+ mContext->setNameManglerPolicy(NameManglerPolicy{
+ mContext->getCompilationPackage(), mTableMerger->getMergedPackages()});
+
+ // Add our table to the symbol table.
+ mContext->getExternalSymbols()->prependSource(
+ util::make_unique<ResourceTableSymbolSource>(&mFinalTable));
+
+ ReferenceLinker linker;
+ if (!linker.consume(mContext, &mFinalTable)) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed linking references");
+ return 1;
+ }
+
+ if (mOptions.staticLib) {
+ if (!mOptions.products.empty()) {
+ mContext->getDiagnostics()
+ ->warn(DiagMessage()
+ << "can't select products when building static library");
+ }
+ } else {
+ ProductFilter productFilter(mOptions.products);
+ if (!productFilter.consume(mContext, &mFinalTable)) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed stripping products");
+ return 1;
+ }
+ }
+
+ if (!mOptions.noAutoVersion) {
+ AutoVersioner versioner;
+ if (!versioner.consume(mContext, &mFinalTable)) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed versioning styles");
+ return 1;
+ }
+ }
+
+ 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.noResourceDeduping) {
+ ResourceDeduper deduper;
+ if (!deduper.consume(mContext, &mFinalTable)) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed deduping resources");
+ return 1;
+ }
+ }
+
+ proguard::KeepSet proguardKeepSet;
+ proguard::KeepSet proguardMainDexKeepSet;
+
+ if (mOptions.staticLib) {
+ if (mOptions.tableSplitterOptions.configFilter != nullptr ||
+ mOptions.tableSplitterOptions.preferredDensity) {
+ mContext->getDiagnostics()
+ ->warn(DiagMessage()
+ << "can't strip resources when building static library");
+ }
+ } else {
+ // Adjust the SplitConstraints so that their SDK version is stripped if it
+ // is less
+ // than or equal to the minSdk. Otherwise the resources that have had
+ // their SDK version
+ // stripped due to minSdk won't ever match.
+ std::vector<SplitConstraints> adjustedConstraintsList;
+ adjustedConstraintsList.reserve(mOptions.splitConstraints.size());
+ for (const SplitConstraints& constraints : mOptions.splitConstraints) {
+ SplitConstraints adjustedConstraints;
+ for (const ConfigDescription& config : constraints.configs) {
+ if (config.sdkVersion <= mContext->getMinSdkVersion()) {
+ adjustedConstraints.configs.insert(config.copyWithoutSdkVersion());
+ } else {
+ adjustedConstraints.configs.insert(config);
+ }
+ }
+ adjustedConstraintsList.push_back(std::move(adjustedConstraints));
+ }
+
+ TableSplitter tableSplitter(adjustedConstraintsList,
+ mOptions.tableSplitterOptions);
+ if (!tableSplitter.verifySplitConstraints(mContext)) {
+ return 1;
+ }
+ tableSplitter.splitTable(&mFinalTable);
+
+ // Now we need to write out the Split APKs.
+ auto pathIter = mOptions.splitPaths.begin();
+ auto splitConstraintsIter = adjustedConstraintsList.begin();
+ for (std::unique_ptr<ResourceTable>& splitTable :
+ tableSplitter.getSplits()) {
+ if (mContext->verbose()) {
+ mContext->getDiagnostics()->note(
+ DiagMessage(*pathIter)
+ << "generating split with configurations '"
+ << util::joiner(splitConstraintsIter->configs, ", ") << "'");
+ }
+
+ std::unique_ptr<IArchiveWriter> archiveWriter =
+ makeArchiveWriter(*pathIter);
+ if (!archiveWriter) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed to create archive");
+ return 1;
+ }
+
+ // Generate an AndroidManifest.xml for each split.
+ std::unique_ptr<xml::XmlResource> splitManifest =
+ generateSplitManifest(appInfo, *splitConstraintsIter);
+
+ XmlReferenceLinker linker;
+ if (!linker.consume(mContext, splitManifest.get())) {
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed to create Split AndroidManifest.xml");
+ return 1;
+ }
+
+ if (!writeApk(archiveWriter.get(), &proguardKeepSet,
+ splitManifest.get(), splitTable.get())) {
+ return 1;
+ }
+
+ ++pathIter;
+ ++splitConstraintsIter;
+ }
+ }
+
+ // Start writing the base APK.
+ std::unique_ptr<IArchiveWriter> archiveWriter =
+ makeArchiveWriter(mOptions.outputPath);
+ if (!archiveWriter) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed to create archive");
+ return 1;
+ }
+
+ bool error = false;
+ {
+ // AndroidManifest.xml has no resource name, but the CallSite is built
+ // from the name
+ // (aka, which package the AndroidManifest.xml is coming from).
+ // So we give it a package name so it can see local resources.
+ manifestXml->file.name.package = mContext->getCompilationPackage();
+
+ XmlReferenceLinker manifestLinker;
+ if (manifestLinker.consume(mContext, manifestXml.get())) {
+ 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;
}
if (mOptions.generateJavaClassPath) {
- JavaClassGeneratorOptions options;
- options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
- options.javadocAnnotations = mOptions.javadocAnnotations;
-
- if (mOptions.staticLib || mOptions.generateNonFinalIds) {
- options.useFinal = false;
- }
-
- 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();
- }
-
- if (mOptions.privateSymbols) {
- // If we defined a private symbols package, we only emit Public symbols
- // to the original package, and private and public symbols to the private package.
-
- options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
- if (!writeJavaFile(&mFinalTable, mContext->getCompilationPackage(),
- outputPackage, options)) {
- return 1;
- }
-
- options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
- outputPackage = mOptions.privateSymbols.value();
- }
-
- if (!writeJavaFile(&mFinalTable, actualPackage, outputPackage, options)) {
- return 1;
- }
-
- for (const std::string& extraPackage : mOptions.extraJavaPackages) {
- if (!writeJavaFile(&mFinalTable, actualPackage, extraPackage, options)) {
- return 1;
- }
- }
+ if (!writeManifestJavaFile(manifestXml.get())) {
+ error = true;
+ }
}
- if (!writeProguardFile(mOptions.generateProguardRulesPath, proguardKeepSet)) {
- return 1;
+ if (mOptions.noXmlNamespaces) {
+ // PackageParser will fail if URIs are removed from
+ // AndroidManifest.xml.
+ XmlNamespaceRemover namespaceRemover(true /* keepUris */);
+ if (!namespaceRemover.consume(mContext, manifestXml.get())) {
+ error = true;
+ }
}
-
- if (!writeProguardFile(mOptions.generateMainDexProguardRulesPath, proguardMainDexKeepSet)) {
- return 1;
- }
-
- if (mContext->verbose()) {
- DebugPrintTableOptions debugPrintTableOptions;
- debugPrintTableOptions.showSources = true;
- Debug::printTable(&mFinalTable, debugPrintTableOptions);
- }
- return 0;
+ } else {
+ error = true;
+ }
}
-private:
- LinkOptions mOptions;
- LinkContext* mContext;
- ResourceTable mFinalTable;
+ if (error) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed processing manifest");
+ return 1;
+ }
- std::unique_ptr<TableMerger> mTableMerger;
+ if (!writeApk(archiveWriter.get(), &proguardKeepSet, manifestXml.get(),
+ &mFinalTable)) {
+ return 1;
+ }
- // A pointer to the FileCollection representing the filesystem (not archives).
- std::unique_ptr<io::FileCollection> mFileCollection;
+ if (mOptions.generateJavaClassPath) {
+ JavaClassGeneratorOptions options;
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+ options.javadocAnnotations = mOptions.javadocAnnotations;
- // A vector of IFileCollections. This is mainly here to keep ownership of the collections.
- std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
+ if (mOptions.staticLib || mOptions.generateNonFinalIds) {
+ options.useFinal = false;
+ }
- // A vector of ResourceTables. This is here to retain ownership, so that the SymbolTable
- // can use these.
- std::vector<std::unique_ptr<ResourceTable>> mStaticTableIncludes;
+ 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();
+ }
+
+ if (mOptions.privateSymbols) {
+ // If we defined a private symbols package, we only emit Public symbols
+ // to the original package, and private and public symbols to the
+ // private package.
+
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
+ if (!writeJavaFile(&mFinalTable, mContext->getCompilationPackage(),
+ outputPackage, options)) {
+ return 1;
+ }
+
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
+ outputPackage = mOptions.privateSymbols.value();
+ }
+
+ if (!writeJavaFile(&mFinalTable, actualPackage, outputPackage, options)) {
+ return 1;
+ }
+
+ for (const std::string& extraPackage : mOptions.extraJavaPackages) {
+ if (!writeJavaFile(&mFinalTable, actualPackage, extraPackage,
+ options)) {
+ return 1;
+ }
+ }
+ }
+
+ if (!writeProguardFile(mOptions.generateProguardRulesPath,
+ proguardKeepSet)) {
+ return 1;
+ }
+
+ if (!writeProguardFile(mOptions.generateMainDexProguardRulesPath,
+ proguardMainDexKeepSet)) {
+ return 1;
+ }
+
+ if (mContext->verbose()) {
+ DebugPrintTableOptions debugPrintTableOptions;
+ debugPrintTableOptions.showSources = true;
+ Debug::printTable(&mFinalTable, debugPrintTableOptions);
+ }
+ return 0;
+ }
+
+ private:
+ LinkOptions mOptions;
+ LinkContext* mContext;
+ ResourceTable mFinalTable;
+
+ std::unique_ptr<TableMerger> mTableMerger;
+
+ // A pointer to the FileCollection representing the filesystem (not archives).
+ std::unique_ptr<io::FileCollection> mFileCollection;
+
+ // A vector of IFileCollections. This is mainly here to keep ownership of the
+ // collections.
+ std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
+
+ // A vector of ResourceTables. This is here to retain ownership, so that the
+ // SymbolTable
+ // can use these.
+ std::vector<std::unique_ptr<ResourceTable>> mStaticTableIncludes;
};
int link(const std::vector<StringPiece>& args) {
- LinkContext context;
- LinkOptions options;
- std::vector<std::string> overlayArgList;
- std::vector<std::string> extraJavaPackages;
- Maybe<std::string> configs;
- Maybe<std::string> preferredDensity;
- Maybe<std::string> productList;
- bool legacyXFlag = false;
- bool requireLocalization = false;
- bool verbose = false;
- Maybe<std::string> stableIdFilePath;
- std::vector<std::string> splitArgs;
- Flags flags = Flags()
- .requiredFlag("-o", "Output path", &options.outputPath)
- .requiredFlag("--manifest", "Path to the Android manifest to build",
- &options.manifestPath)
- .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.",
- &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)
- .optionalSwitch("--no-version-vectors",
- "Disables automatic versioning of vector drawables. Use this only\n"
- "when building with vector drawable support library",
- &options.noVersionVectors)
- .optionalSwitch("--no-resource-deduping", "Disables automatic deduping of resources with\n"
- "identical values across compatible configurations.",
- &options.noResourceDeduping)
- .optionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01",
- &legacyXFlag)
- .optionalSwitch("-z", "Require localization of strings marked 'suggested'",
- &requireLocalization)
- .optionalFlag("-c", "Comma separated list of configurations to include. The default\n"
- "is all configurations", &configs)
- .optionalFlag("--preferred-density",
- "Selects the closest matching density and strips out all others.",
- &preferredDensity)
- .optionalFlag("--product", "Comma separated list of product names to keep",
- &productList)
- .optionalSwitch("--output-to-dir", "Outputs the APK contents to a directory specified "
- "by -o",
- &options.outputToDirectory)
- .optionalSwitch("--no-xml-namespaces", "Removes XML namespace prefix and URI "
- "information from AndroidManifest.xml\nand XML binaries in res/*.",
- &options.noXmlNamespaces)
- .optionalFlag("--min-sdk-version", "Default minimum SDK version to use for "
- "AndroidManifest.xml",
- &options.manifestFixerOptions.minSdkVersionDefault)
- .optionalFlag("--target-sdk-version", "Default target SDK version to use for "
- "AndroidManifest.xml",
- &options.manifestFixerOptions.targetSdkVersionDefault)
- .optionalFlag("--version-code", "Version code (integer) to inject into the "
- "AndroidManifest.xml if none is present",
- &options.manifestFixerOptions.versionCodeDefault)
- .optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml "
- "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)
- .optionalSwitch("--non-final-ids", "Generates R.java without the final modifier.\n"
- "This is implied when --static-lib is specified.",
- &options.generateNonFinalIds)
- .optionalFlag("--stable-ids", "File containing a list of name to ID mapping.",
- &stableIdFilePath)
- .optionalFlag("--emit-ids", "Emit a file at the given path with a list of name to ID\n"
- "mappings, suitable for use with --stable-ids.",
- &options.resourceIdMapPath)
- .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",
- &options.privateSymbols)
- .optionalFlag("--custom-package", "Custom Java package under which to generate R.java",
- &options.customJavaPackage)
- .optionalFlagList("--extra-packages", "Generate the same R.java but with different "
- "package names",
- &extraJavaPackages)
- .optionalFlagList("--add-javadoc-annotation", "Adds a JavaDoc annotation to all "
+ LinkContext context;
+ LinkOptions options;
+ std::vector<std::string> overlayArgList;
+ std::vector<std::string> extraJavaPackages;
+ Maybe<std::string> configs;
+ Maybe<std::string> preferredDensity;
+ Maybe<std::string> productList;
+ bool legacyXFlag = false;
+ bool requireLocalization = false;
+ bool verbose = false;
+ Maybe<std::string> stableIdFilePath;
+ std::vector<std::string> splitArgs;
+ Flags flags =
+ Flags()
+ .requiredFlag("-o", "Output path", &options.outputPath)
+ .requiredFlag("--manifest", "Path to the Android manifest to build",
+ &options.manifestPath)
+ .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.",
+ &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)
+ .optionalSwitch("--no-version-vectors",
+ "Disables automatic versioning of vector drawables. "
+ "Use this only\n"
+ "when building with vector drawable support library",
+ &options.noVersionVectors)
+ .optionalSwitch("--no-resource-deduping",
+ "Disables automatic deduping of resources with\n"
+ "identical values across compatible configurations.",
+ &options.noResourceDeduping)
+ .optionalSwitch(
+ "-x",
+ "Legacy flag that specifies to use the package identifier 0x01",
+ &legacyXFlag)
+ .optionalSwitch("-z",
+ "Require localization of strings marked 'suggested'",
+ &requireLocalization)
+ .optionalFlag(
+ "-c",
+ "Comma separated list of configurations to include. The default\n"
+ "is all configurations",
+ &configs)
+ .optionalFlag(
+ "--preferred-density",
+ "Selects the closest matching density and strips out all others.",
+ &preferredDensity)
+ .optionalFlag("--product",
+ "Comma separated list of product names to keep",
+ &productList)
+ .optionalSwitch("--output-to-dir",
+ "Outputs the APK contents to a directory specified "
+ "by -o",
+ &options.outputToDirectory)
+ .optionalSwitch("--no-xml-namespaces",
+ "Removes XML namespace prefix and URI "
+ "information from AndroidManifest.xml\nand XML "
+ "binaries in res/*.",
+ &options.noXmlNamespaces)
+ .optionalFlag("--min-sdk-version",
+ "Default minimum SDK version to use for "
+ "AndroidManifest.xml",
+ &options.manifestFixerOptions.minSdkVersionDefault)
+ .optionalFlag("--target-sdk-version",
+ "Default target SDK version to use for "
+ "AndroidManifest.xml",
+ &options.manifestFixerOptions.targetSdkVersionDefault)
+ .optionalFlag("--version-code",
+ "Version code (integer) to inject into the "
+ "AndroidManifest.xml if none is present",
+ &options.manifestFixerOptions.versionCodeDefault)
+ .optionalFlag("--version-name",
+ "Version name to inject into the AndroidManifest.xml "
+ "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)
+ .optionalSwitch("--non-final-ids",
+ "Generates R.java without the final modifier.\n"
+ "This is implied when --static-lib is specified.",
+ &options.generateNonFinalIds)
+ .optionalFlag("--stable-ids",
+ "File containing a list of name to ID mapping.",
+ &stableIdFilePath)
+ .optionalFlag(
+ "--emit-ids",
+ "Emit a file at the given path with a list of name to ID\n"
+ "mappings, suitable for use with --stable-ids.",
+ &options.resourceIdMapPath)
+ .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",
+ &options.privateSymbols)
+ .optionalFlag("--custom-package",
+ "Custom Java package under which to generate R.java",
+ &options.customJavaPackage)
+ .optionalFlagList("--extra-packages",
+ "Generate the same R.java but with different "
+ "package names",
+ &extraJavaPackages)
+ .optionalFlagList("--add-javadoc-annotation",
+ "Adds a JavaDoc annotation to all "
"generated Java classes",
&options.javadocAnnotations)
- .optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
- "overlays without <add-resource> tags",
- &options.autoAddOverlay)
- .optionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml",
- &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",
- &options.manifestFixerOptions.renameInstrumentationTargetPackage)
- .optionalFlagList("-0", "File extensions not to compress",
- &options.extensionsToNotCompress)
- .optionalFlagList("--split", "Split resources matching a set of configs out to a "
- "Split APK.\nSyntax: path/to/output.apk:<config>[,<config>[...]]",
- &splitArgs)
- .optionalSwitch("-v", "Enables verbose logging",
- &verbose);
+ .optionalSwitch("--auto-add-overlay",
+ "Allows the addition of new resources in "
+ "overlays without <add-resource> tags",
+ &options.autoAddOverlay)
+ .optionalFlag("--rename-manifest-package",
+ "Renames the package in AndroidManifest.xml",
+ &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",
+ &options.manifestFixerOptions.renameInstrumentationTargetPackage)
+ .optionalFlagList("-0", "File extensions not to compress",
+ &options.extensionsToNotCompress)
+ .optionalFlagList(
+ "--split",
+ "Split resources matching a set of configs out to a "
+ "Split APK.\nSyntax: path/to/output.apk:<config>[,<config>[...]]",
+ &splitArgs)
+ .optionalSwitch("-v", "Enables verbose logging", &verbose);
- if (!flags.parse("aapt2 link", args, &std::cerr)) {
+ if (!flags.parse("aapt2 link", args, &std::cerr)) {
+ return 1;
+ }
+
+ // 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(arg, "@")) {
+ const std::string path = arg.substr(1, arg.size() - 1);
+ std::string error;
+ if (!file::appendArgsFromFile(path, &argList, &error)) {
+ context.getDiagnostics()->error(DiagMessage(path) << error);
return 1;
+ }
+ } else {
+ argList.push_back(arg);
+ }
+ }
+
+ // 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 (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(package.toString());
+ }
+ }
+
+ if (productList) {
+ for (StringPiece product : util::tokenize(productList.value(), ',')) {
+ if (product != "" && product != "default") {
+ options.products.insert(product.toString());
+ }
+ }
+ }
+
+ AxisConfigFilter filter;
+ if (configs) {
+ for (const StringPiece& configStr : util::tokenize(configs.value(), ',')) {
+ ConfigDescription config;
+ LocaleValue lv;
+ if (lv.initFromFilterString(configStr)) {
+ lv.writeTo(&config);
+ } else if (!ConfigDescription::parse(configStr, &config)) {
+ context.getDiagnostics()->error(DiagMessage() << "invalid config '"
+ << configStr
+ << "' for -c option");
+ return 1;
+ }
+
+ if (config.density != 0) {
+ context.getDiagnostics()->warn(DiagMessage() << "ignoring density '"
+ << config
+ << "' for -c option");
+ } else {
+ filter.addConfig(config);
+ }
}
- // 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(arg, "@")) {
- const std::string path = arg.substr(1, arg.size() - 1);
- std::string error;
- if (!file::appendArgsFromFile(path, &argList, &error)) {
- context.getDiagnostics()->error(DiagMessage(path) << error);
- return 1;
- }
- } else {
- argList.push_back(arg);
- }
+ options.tableSplitterOptions.configFilter = &filter;
+ }
+
+ if (preferredDensity) {
+ ConfigDescription preferredDensityConfig;
+ if (!ConfigDescription::parse(preferredDensity.value(),
+ &preferredDensityConfig)) {
+ context.getDiagnostics()->error(
+ DiagMessage() << "invalid density '" << preferredDensity.value()
+ << "' for --preferred-density option");
+ return 1;
}
- // 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);
- }
+ // Clear the version that can be automatically added.
+ preferredDensityConfig.sdkVersion = 0;
+
+ if (preferredDensityConfig.diff(ConfigDescription::defaultConfig()) !=
+ ConfigDescription::CONFIG_DENSITY) {
+ context.getDiagnostics()->error(
+ DiagMessage() << "invalid preferred density '"
+ << preferredDensity.value() << "'. "
+ << "Preferred density must only be a density value");
+ return 1;
}
+ options.tableSplitterOptions.preferredDensity =
+ preferredDensityConfig.density;
+ }
- if (verbose) {
- context.setVerbose(verbose);
+ if (!options.staticLib && stableIdFilePath) {
+ if (!loadStableIdMap(context.getDiagnostics(), stableIdFilePath.value(),
+ &options.stableIdMap)) {
+ return 1;
}
+ }
- // Populate the set of extra packages for which to generate R.java.
- for (std::string& extraPackage : extraJavaPackages) {
- // A given package can actually be a colon separated list of packages.
- for (StringPiece package : util::split(extraPackage, ':')) {
- options.extraJavaPackages.insert(package.toString());
- }
+ // Populate some default no-compress extensions that are already compressed.
+ options.extensionsToNotCompress.insert(
+ {".jpg", ".jpeg", ".png", ".gif", ".wav", ".mp2", ".mp3", ".ogg",
+ ".aac", ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", ".rtttl",
+ ".imy", ".xmf", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2",
+ ".3gpp2", ".amr", ".awb", ".wma", ".wmv", ".webm", ".mkv"});
+
+ // Parse the split parameters.
+ for (const std::string& splitArg : splitArgs) {
+ options.splitPaths.push_back({});
+ options.splitConstraints.push_back({});
+ if (!parseSplitParameter(splitArg, context.getDiagnostics(),
+ &options.splitPaths.back(),
+ &options.splitConstraints.back())) {
+ return 1;
}
+ }
- if (productList) {
- for (StringPiece product : util::tokenize(productList.value(), ',')) {
- if (product != "" && product != "default") {
- options.products.insert(product.toString());
- }
- }
- }
+ // Turn off auto versioning for static-libs.
+ if (options.staticLib) {
+ options.noAutoVersion = true;
+ options.noVersionVectors = true;
+ }
- AxisConfigFilter filter;
- if (configs) {
- for (const StringPiece& configStr : util::tokenize(configs.value(), ',')) {
- ConfigDescription config;
- LocaleValue lv;
- if (lv.initFromFilterString(configStr)) {
- lv.writeTo(&config);
- } else if (!ConfigDescription::parse(configStr, &config)) {
- context.getDiagnostics()->error(
- DiagMessage() << "invalid config '" << configStr << "' for -c option");
- return 1;
- }
-
- if (config.density != 0) {
- context.getDiagnostics()->warn(
- DiagMessage() << "ignoring density '" << config << "' for -c option");
- } else {
- filter.addConfig(config);
- }
- }
-
- options.tableSplitterOptions.configFilter = &filter;
- }
-
- if (preferredDensity) {
- ConfigDescription preferredDensityConfig;
- if (!ConfigDescription::parse(preferredDensity.value(), &preferredDensityConfig)) {
- context.getDiagnostics()->error(DiagMessage() << "invalid density '"
- << preferredDensity.value()
- << "' for --preferred-density option");
- return 1;
- }
-
- // Clear the version that can be automatically added.
- preferredDensityConfig.sdkVersion = 0;
-
- if (preferredDensityConfig.diff(ConfigDescription::defaultConfig())
- != ConfigDescription::CONFIG_DENSITY) {
- context.getDiagnostics()->error(DiagMessage() << "invalid preferred density '"
- << preferredDensity.value() << "'. "
- << "Preferred density must only be a density value");
- return 1;
- }
- options.tableSplitterOptions.preferredDensity = preferredDensityConfig.density;
- }
-
- if (!options.staticLib && stableIdFilePath) {
- if (!loadStableIdMap(context.getDiagnostics(), stableIdFilePath.value(),
- &options.stableIdMap)) {
- return 1;
- }
- }
-
- // Populate some default no-compress extensions that are already compressed.
- options.extensionsToNotCompress.insert({
- ".jpg", ".jpeg", ".png", ".gif",
- ".wav", ".mp2", ".mp3", ".ogg", ".aac",
- ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet",
- ".rtttl", ".imy", ".xmf", ".mp4", ".m4a",
- ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2",
- ".amr", ".awb", ".wma", ".wmv", ".webm", ".mkv"});
-
- // Parse the split parameters.
- for (const std::string& splitArg : splitArgs) {
- options.splitPaths.push_back({});
- options.splitConstraints.push_back({});
- if (!parseSplitParameter(splitArg, context.getDiagnostics(), &options.splitPaths.back(),
- &options.splitConstraints.back())) {
- return 1;
- }
- }
-
- // Turn off auto versioning for static-libs.
- if (options.staticLib) {
- options.noAutoVersion = true;
- options.noVersionVectors = true;
- }
-
- LinkCommand cmd(&context, options);
- return cmd.run(argList);
+ LinkCommand cmd(&context, options);
+ return cmd.run(argList);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index ce455da..f40c0e8 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -30,106 +30,121 @@
struct ConfigDescription;
/**
- * Defines the location in which a value exists. This determines visibility of other
+ * Defines the location in which a value exists. This determines visibility of
+ * other
* package's private symbols.
*/
struct CallSite {
- ResourceNameRef resource;
+ ResourceNameRef resource;
};
/**
- * Determines whether a versioned resource should be created. If a versioned resource already
+ * Determines whether a versioned resource should be created. If a versioned
+ * resource already
* exists, it takes precedence.
*/
-bool shouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
+bool shouldGenerateVersionedResource(const ResourceEntry* entry,
+ const ConfigDescription& config,
const int sdkVersionToGenerate);
class AutoVersioner : public IResourceTableConsumer {
-public:
- bool consume(IAaptContext* context, ResourceTable* table) override;
+ public:
+ bool consume(IAaptContext* context, ResourceTable* table) override;
};
class XmlAutoVersioner : public IXmlResourceConsumer {
-public:
- bool consume(IAaptContext* context, xml::XmlResource* resource) override;
+ public:
+ bool consume(IAaptContext* context, xml::XmlResource* resource) override;
};
class VersionCollapser : public IResourceTableConsumer {
-public:
- bool consume(IAaptContext* context, ResourceTable* table) override;
+ public:
+ bool consume(IAaptContext* context, ResourceTable* table) override;
};
/**
* Removes duplicated key-value entries from dominated resources.
*/
class ResourceDeduper : public IResourceTableConsumer {
-public:
- bool consume(IAaptContext* context, ResourceTable* table) override;
+ 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
+ * 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
* issues with new apps running on old platforms.
*
- * The Android platform ignores resource attributes it doesn't recognize, so an app developer can
- * use new attributes in their layout XML files without worrying about versioning. This assumption
- * actually breaks on older platforms. OEMs may add private attributes that are used internally.
- * AAPT originally assigned all private attributes IDs immediately proceeding the public attributes'
+ * The Android platform ignores resource attributes it doesn't recognize, so an
+ * app developer can
+ * use new attributes in their layout XML files without worrying about
+ * versioning. This assumption
+ * actually breaks on older platforms. OEMs may add private attributes that are
+ * used internally.
+ * AAPT originally assigned all private attributes IDs immediately proceeding
+ * the public attributes'
* IDs.
*
- * This means that on a newer Android platform, an ID previously assigned to a private attribute
+ * This means that on a newer Android platform, an ID previously assigned to a
+ * private attribute
* may end up assigned to a public attribute.
*
- * App developers assume using the newer attribute is safe on older platforms because it will
- * be ignored. Instead, the platform thinks the new attribute is an older, private attribute and
- * will interpret it as such. This leads to unintended styling and exceptions thrown due to
+ * App developers assume using the newer attribute is safe on older platforms
+ * because it will
+ * be ignored. Instead, the platform thinks the new attribute is an older,
+ * private attribute and
+ * will interpret it as such. This leads to unintended styling and exceptions
+ * thrown due to
* unexpected types.
*
- * By moving the private attributes to a completely different type, this ID conflict will never
+ * By moving the private attributes to a completely different type, this ID
+ * conflict will never
* occur.
*/
struct PrivateAttributeMover : public IResourceTableConsumer {
- bool consume(IAaptContext* context, ResourceTable* table) override;
+ bool consume(IAaptContext* context, ResourceTable* table) override;
};
/**
* Removes namespace nodes and URI information from the XmlResource.
*
- * Once an XmlResource is processed by this consumer, it is no longer able to have its attributes
- * parsed. As such, this XmlResource must have already been processed by XmlReferenceLinker.
+ * Once an XmlResource is processed by this consumer, it is no longer able to
+ * have its attributes
+ * parsed. As such, this XmlResource must have already been processed by
+ * XmlReferenceLinker.
*/
class XmlNamespaceRemover : public IXmlResourceConsumer {
-private:
- bool mKeepUris;
+ private:
+ bool mKeepUris;
-public:
- XmlNamespaceRemover(bool keepUris = false) : mKeepUris(keepUris) {
- };
+ public:
+ XmlNamespaceRemover(bool keepUris = false) : mKeepUris(keepUris){};
- bool consume(IAaptContext* context, xml::XmlResource* resource) override;
+ bool consume(IAaptContext* context, xml::XmlResource* resource) override;
};
/**
- * Resolves attributes in the XmlResource and compiles string values to resource values.
+ * Resolves attributes in the XmlResource and compiles string values to resource
+ * values.
* Once an XmlResource is processed by this linker, it is ready to be flattened.
*/
class XmlReferenceLinker : public IXmlResourceConsumer {
-private:
- std::set<int> mSdkLevelsFound;
+ private:
+ std::set<int> mSdkLevelsFound;
-public:
- bool consume(IAaptContext* context, xml::XmlResource* resource) override;
+ public:
+ bool consume(IAaptContext* context, xml::XmlResource* resource) override;
- /**
- * Once the XmlResource has been consumed, this returns the various SDK levels in which
- * framework attributes used within the XML document were defined.
- */
- inline const std::set<int>& getSdkLevels() const {
- return mSdkLevelsFound;
- }
+ /**
+ * Once the XmlResource has been consumed, this returns the various SDK levels
+ * in which
+ * framework attributes used within the XML document were defined.
+ */
+ inline const std::set<int>& getSdkLevels() const { return mSdkLevelsFound; }
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_LINKER_LINKERS_H */
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 45f5acd..3c9298b 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "ResourceUtils.h"
#include "link/ManifestFixer.h"
+#include "ResourceUtils.h"
#include "util/Util.h"
#include "xml/XmlActionExecutor.h"
#include "xml/XmlDom.h"
@@ -25,295 +25,310 @@
namespace aapt {
/**
- * This is how PackageManager builds class names from AndroidManifest.xml entries.
+ * This is how PackageManager builds class names from AndroidManifest.xml
+ * entries.
*/
static bool nameIsJavaClassName(xml::Element* el, xml::Attribute* attr,
SourcePathDiagnostics* diag) {
- // 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::string> fullyQualifiedClassName =
- util::getFullyQualifiedClassName("a", 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::string> fullyQualifiedClassName =
+ util::getFullyQualifiedClassName("a", attr->value);
- StringPiece qualifiedClassName = fullyQualifiedClassName
- ? fullyQualifiedClassName.value() : attr->value;
+ StringPiece qualifiedClassName =
+ fullyQualifiedClassName ? fullyQualifiedClassName.value() : attr->value;
- if (!util::isJavaClassName(qualifiedClassName)) {
- diag->error(DiagMessage(el->lineNumber)
- << "attribute 'android:name' in <"
- << el->name << "> tag must be a valid Java class name");
- return false;
- }
- return true;
-}
-
-static bool optionalNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) {
- 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, "name")) {
- return nameIsJavaClassName(el, attr, diag);
- }
+ if (!util::isJavaClassName(qualifiedClassName)) {
diag->error(DiagMessage(el->lineNumber)
- << "<" << el->name << "> is missing attribute 'android:name'");
+ << "attribute 'android:name' in <" << el->name
+ << "> tag must be a valid Java class name");
return false;
+ }
+ return true;
+}
+
+static bool optionalNameIsJavaClassName(xml::Element* el,
+ SourcePathDiagnostics* diag) {
+ 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, "name")) {
+ return nameIsJavaClassName(el, attr, diag);
+ }
+ diag->error(DiagMessage(el->lineNumber)
+ << "<" << el->name << "> is missing attribute 'android:name'");
+ return false;
}
static bool verifyManifest(xml::Element* el, SourcePathDiagnostics* diag) {
- xml::Attribute* attr = el->findAttribute({}, "package");
- if (!attr) {
- diag->error(DiagMessage(el->lineNumber) << "<manifest> tag is missing 'package' attribute");
- return false;
- } else if (ResourceUtils::isReference(attr->value)) {
- diag->error(DiagMessage(el->lineNumber)
- << "attribute 'package' in <manifest> tag must not be a reference");
- return false;
- } else if (!util::isJavaPackageName(attr->value)) {
- diag->error(DiagMessage(el->lineNumber)
- << "attribute 'package' in <manifest> tag is not a valid Java package name: '"
- << attr->value << "'");
- return false;
- }
- return true;
+ xml::Attribute* attr = el->findAttribute({}, "package");
+ if (!attr) {
+ diag->error(DiagMessage(el->lineNumber)
+ << "<manifest> tag is missing 'package' attribute");
+ return false;
+ } else if (ResourceUtils::isReference(attr->value)) {
+ diag->error(
+ DiagMessage(el->lineNumber)
+ << "attribute 'package' in <manifest> tag must not be a reference");
+ return false;
+ } else if (!util::isJavaPackageName(attr->value)) {
+ diag->error(DiagMessage(el->lineNumber)
+ << "attribute 'package' in <manifest> tag is not a valid Java "
+ "package name: '"
+ << attr->value << "'");
+ return false;
+ }
+ return true;
}
/**
- * The coreApp attribute in <manifest> is not a regular AAPT attribute, so type checking on it
+ * The coreApp attribute in <manifest> is not a regular AAPT attribute, so type
+ * checking on it
* is manual.
*/
static bool fixCoreAppAttribute(xml::Element* el, SourcePathDiagnostics* diag) {
- if (xml::Attribute* attr = el->findAttribute("", "coreApp")) {
- std::unique_ptr<BinaryPrimitive> result = ResourceUtils::tryParseBool(attr->value);
- if (!result) {
- diag->error(DiagMessage(el->lineNumber) << "attribute coreApp must be a boolean");
- return false;
- }
- attr->compiledValue = std::move(result);
+ if (xml::Attribute* attr = el->findAttribute("", "coreApp")) {
+ std::unique_ptr<BinaryPrimitive> result =
+ ResourceUtils::tryParseBool(attr->value);
+ if (!result) {
+ diag->error(DiagMessage(el->lineNumber)
+ << "attribute coreApp must be a boolean");
+ return false;
}
- return true;
+ attr->compiledValue = std::move(result);
+ }
+ return true;
}
-bool ManifestFixer::buildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag) {
- // First verify some options.
- if (mOptions.renameManifestPackage) {
- if (!util::isJavaPackageName(mOptions.renameManifestPackage.value())) {
- diag->error(DiagMessage() << "invalid manifest package override '"
- << mOptions.renameManifestPackage.value() << "'");
- return false;
- }
+bool ManifestFixer::buildRules(xml::XmlActionExecutor* executor,
+ IDiagnostics* diag) {
+ // First verify some options.
+ if (mOptions.renameManifestPackage) {
+ if (!util::isJavaPackageName(mOptions.renameManifestPackage.value())) {
+ diag->error(DiagMessage() << "invalid manifest package override '"
+ << mOptions.renameManifestPackage.value()
+ << "'");
+ return false;
+ }
+ }
+
+ if (mOptions.renameInstrumentationTargetPackage) {
+ if (!util::isJavaPackageName(
+ mOptions.renameInstrumentationTargetPackage.value())) {
+ diag->error(DiagMessage()
+ << "invalid instrumentation target package override '"
+ << mOptions.renameInstrumentationTargetPackage.value()
+ << "'");
+ return false;
+ }
+ }
+
+ // Common intent-filter actions.
+ xml::XmlNodeAction intentFilterAction;
+ intentFilterAction["action"];
+ intentFilterAction["category"];
+ intentFilterAction["data"];
+
+ // Common meta-data actions.
+ xml::XmlNodeAction metaDataAction;
+
+ // Manifest actions.
+ xml::XmlNodeAction& manifestAction = (*executor)["manifest"];
+ manifestAction.action(verifyManifest);
+ manifestAction.action(fixCoreAppAttribute);
+ manifestAction.action([&](xml::Element* el) -> bool {
+ if (mOptions.versionNameDefault) {
+ if (el->findAttribute(xml::kSchemaAndroid, "versionName") == nullptr) {
+ el->attributes.push_back(
+ xml::Attribute{xml::kSchemaAndroid, "versionName",
+ mOptions.versionNameDefault.value()});
+ }
}
- if (mOptions.renameInstrumentationTargetPackage) {
- if (!util::isJavaPackageName(mOptions.renameInstrumentationTargetPackage.value())) {
- diag->error(DiagMessage() << "invalid instrumentation target package override '"
- << mOptions.renameInstrumentationTargetPackage.value() << "'");
- return false;
- }
+ if (mOptions.versionCodeDefault) {
+ if (el->findAttribute(xml::kSchemaAndroid, "versionCode") == nullptr) {
+ el->attributes.push_back(
+ xml::Attribute{xml::kSchemaAndroid, "versionCode",
+ mOptions.versionCodeDefault.value()});
+ }
}
-
- // Common intent-filter actions.
- xml::XmlNodeAction intentFilterAction;
- intentFilterAction["action"];
- intentFilterAction["category"];
- intentFilterAction["data"];
-
- // Common meta-data actions.
- xml::XmlNodeAction metaDataAction;
-
- // Manifest actions.
- xml::XmlNodeAction& manifestAction = (*executor)["manifest"];
- manifestAction.action(verifyManifest);
- manifestAction.action(fixCoreAppAttribute);
- manifestAction.action([&](xml::Element* el) -> bool {
- if (mOptions.versionNameDefault) {
- if (el->findAttribute(xml::kSchemaAndroid, "versionName") == nullptr) {
- el->attributes.push_back(xml::Attribute{
- xml::kSchemaAndroid,
- "versionName",
- mOptions.versionNameDefault.value() });
- }
- }
-
- if (mOptions.versionCodeDefault) {
- if (el->findAttribute(xml::kSchemaAndroid, "versionCode") == nullptr) {
- el->attributes.push_back(xml::Attribute{
- xml::kSchemaAndroid,
- "versionCode",
- mOptions.versionCodeDefault.value() });
- }
- }
- return true;
- });
-
- // Meta tags.
- manifestAction["eat-comment"];
-
- // Uses-sdk actions.
- manifestAction["uses-sdk"].action([&](xml::Element* el) -> bool {
- if (mOptions.minSdkVersionDefault &&
- 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, "minSdkVersion",
- mOptions.minSdkVersionDefault.value() });
- }
-
- if (mOptions.targetSdkVersionDefault &&
- 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, "targetSdkVersion",
- mOptions.targetSdkVersionDefault.value() });
- }
- return true;
- });
-
- // Instrumentation actions.
- manifestAction["instrumentation"].action([&](xml::Element* el) -> bool {
- if (!mOptions.renameInstrumentationTargetPackage) {
- return true;
- }
-
- if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, "targetPackage")) {
- attr->value = mOptions.renameInstrumentationTargetPackage.value();
- }
- return true;
- });
-
- manifestAction["original-package"];
- manifestAction["protected-broadcast"];
- manifestAction["uses-permission"];
- manifestAction["permission"];
- manifestAction["permission-tree"];
- manifestAction["permission-group"];
-
- manifestAction["uses-configuration"];
- manifestAction["uses-feature"];
- manifestAction["supports-screens"];
-
- manifestAction["compatible-screens"];
- manifestAction["compatible-screens"]["screen"];
-
- manifestAction["supports-gl-texture"];
-
- // Application actions.
- xml::XmlNodeAction& applicationAction = manifestAction["application"];
- applicationAction.action(optionalNameIsJavaClassName);
-
- // Uses library actions.
- applicationAction["uses-library"];
-
- // Meta-data.
- applicationAction["meta-data"] = metaDataAction;
-
- // Activity actions.
- applicationAction["activity"].action(requiredNameIsJavaClassName);
- applicationAction["activity"]["intent-filter"] = intentFilterAction;
- applicationAction["activity"]["meta-data"] = metaDataAction;
-
- // Activity alias actions.
- applicationAction["activity-alias"]["intent-filter"] = intentFilterAction;
- applicationAction["activity-alias"]["meta-data"] = metaDataAction;
-
- // Service actions.
- applicationAction["service"].action(requiredNameIsJavaClassName);
- applicationAction["service"]["intent-filter"] = intentFilterAction;
- applicationAction["service"]["meta-data"] = metaDataAction;
-
- // Receiver actions.
- applicationAction["receiver"].action(requiredNameIsJavaClassName);
- applicationAction["receiver"]["intent-filter"] = intentFilterAction;
- applicationAction["receiver"]["meta-data"] = metaDataAction;
-
- // Provider actions.
- applicationAction["provider"].action(requiredNameIsJavaClassName);
- applicationAction["provider"]["intent-filter"] = intentFilterAction;
- applicationAction["provider"]["meta-data"] = metaDataAction;
- applicationAction["provider"]["grant-uri-permissions"];
- applicationAction["provider"]["path-permissions"];
-
return true;
+ });
+
+ // Meta tags.
+ manifestAction["eat-comment"];
+
+ // Uses-sdk actions.
+ manifestAction["uses-sdk"].action([&](xml::Element* el) -> bool {
+ if (mOptions.minSdkVersionDefault &&
+ 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, "minSdkVersion",
+ mOptions.minSdkVersionDefault.value()});
+ }
+
+ if (mOptions.targetSdkVersionDefault &&
+ 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, "targetSdkVersion",
+ mOptions.targetSdkVersionDefault.value()});
+ }
+ return true;
+ });
+
+ // Instrumentation actions.
+ manifestAction["instrumentation"].action([&](xml::Element* el) -> bool {
+ if (!mOptions.renameInstrumentationTargetPackage) {
+ return true;
+ }
+
+ if (xml::Attribute* attr =
+ el->findAttribute(xml::kSchemaAndroid, "targetPackage")) {
+ attr->value = mOptions.renameInstrumentationTargetPackage.value();
+ }
+ return true;
+ });
+
+ manifestAction["original-package"];
+ manifestAction["protected-broadcast"];
+ manifestAction["uses-permission"];
+ manifestAction["permission"];
+ manifestAction["permission-tree"];
+ manifestAction["permission-group"];
+
+ manifestAction["uses-configuration"];
+ manifestAction["uses-feature"];
+ manifestAction["supports-screens"];
+
+ manifestAction["compatible-screens"];
+ manifestAction["compatible-screens"]["screen"];
+
+ manifestAction["supports-gl-texture"];
+
+ // Application actions.
+ xml::XmlNodeAction& applicationAction = manifestAction["application"];
+ applicationAction.action(optionalNameIsJavaClassName);
+
+ // Uses library actions.
+ applicationAction["uses-library"];
+
+ // Meta-data.
+ applicationAction["meta-data"] = metaDataAction;
+
+ // Activity actions.
+ applicationAction["activity"].action(requiredNameIsJavaClassName);
+ applicationAction["activity"]["intent-filter"] = intentFilterAction;
+ applicationAction["activity"]["meta-data"] = metaDataAction;
+
+ // Activity alias actions.
+ applicationAction["activity-alias"]["intent-filter"] = intentFilterAction;
+ applicationAction["activity-alias"]["meta-data"] = metaDataAction;
+
+ // Service actions.
+ applicationAction["service"].action(requiredNameIsJavaClassName);
+ applicationAction["service"]["intent-filter"] = intentFilterAction;
+ applicationAction["service"]["meta-data"] = metaDataAction;
+
+ // Receiver actions.
+ applicationAction["receiver"].action(requiredNameIsJavaClassName);
+ applicationAction["receiver"]["intent-filter"] = intentFilterAction;
+ applicationAction["receiver"]["meta-data"] = metaDataAction;
+
+ // Provider actions.
+ applicationAction["provider"].action(requiredNameIsJavaClassName);
+ applicationAction["provider"]["intent-filter"] = intentFilterAction;
+ applicationAction["provider"]["meta-data"] = metaDataAction;
+ applicationAction["provider"]["grant-uri-permissions"];
+ applicationAction["provider"]["path-permissions"];
+
+ return true;
}
class FullyQualifiedClassNameVisitor : public xml::Visitor {
-public:
- using xml::Visitor::visit;
+ public:
+ using xml::Visitor::visit;
- explicit FullyQualifiedClassNameVisitor(const StringPiece& package) : mPackage(package) {
- }
+ explicit FullyQualifiedClassNameVisitor(const StringPiece& package)
+ : mPackage(package) {}
- void visit(xml::Element* el) override {
- for (xml::Attribute& attr : el->attributes) {
- 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());
- }
- }
+ void visit(xml::Element* el) override {
+ for (xml::Attribute& attr : el->attributes) {
+ 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());
}
-
- // Super implementation to iterate over the children.
- xml::Visitor::visit(el);
+ }
}
-private:
- StringPiece mPackage;
- std::unordered_set<StringPiece> mClassAttributes = { "name" };
+ // Super implementation to iterate over the children.
+ xml::Visitor::visit(el);
+ }
+
+ private:
+ StringPiece mPackage;
+ std::unordered_set<StringPiece> mClassAttributes = {"name"};
};
-static bool renameManifestPackage(const StringPiece& packageOverride, xml::Element* manifestEl) {
- xml::Attribute* attr = manifestEl->findAttribute({}, "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);
+ // We've already verified that the manifest element is present, with a package
+ // name specified.
+ assert(attr);
- std::string originalPackage = std::move(attr->value);
- attr->value = packageOverride.toString();
+ std::string originalPackage = std::move(attr->value);
+ attr->value = packageOverride.toString();
- FullyQualifiedClassNameVisitor visitor(originalPackage);
- manifestEl->accept(&visitor);
- return true;
+ FullyQualifiedClassNameVisitor visitor(originalPackage);
+ manifestEl->accept(&visitor);
+ return true;
}
bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) {
- xml::Element* root = xml::findRootElement(doc->root.get());
- if (!root || !root->namespaceUri.empty() || root->name != "manifest") {
- context->getDiagnostics()->error(DiagMessage(doc->file.source)
- << "root tag must be <manifest>");
- return false;
- }
+ xml::Element* root = xml::findRootElement(doc->root.get());
+ 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({}, "uses-sdk") == nullptr) {
- // Auto insert a <uses-sdk> element.
- std::unique_ptr<xml::Element> usesSdk = util::make_unique<xml::Element>();
- usesSdk->name = "uses-sdk";
- root->addChild(std::move(usesSdk));
- }
+ if ((mOptions.minSdkVersionDefault || mOptions.targetSdkVersionDefault) &&
+ root->findChild({}, "uses-sdk") == nullptr) {
+ // Auto insert a <uses-sdk> element.
+ std::unique_ptr<xml::Element> usesSdk = util::make_unique<xml::Element>();
+ usesSdk->name = "uses-sdk";
+ root->addChild(std::move(usesSdk));
+ }
- xml::XmlActionExecutor executor;
- if (!buildRules(&executor, context->getDiagnostics())) {
- return false;
- }
+ xml::XmlActionExecutor executor;
+ if (!buildRules(&executor, context->getDiagnostics())) {
+ return false;
+ }
- if (!executor.execute(xml::XmlActionExecutorPolicy::Whitelist, context->getDiagnostics(),
- doc)) {
- return false;
- }
+ if (!executor.execute(xml::XmlActionExecutorPolicy::Whitelist,
+ context->getDiagnostics(), doc)) {
+ return false;
+ }
- if (mOptions.renameManifestPackage) {
- // Rename manifest package outside of the XmlActionExecutor.
- // We need to extract the old package name and FullyQualify all class names.
- if (!renameManifestPackage(mOptions.renameManifestPackage.value(), root)) {
- return false;
- }
+ if (mOptions.renameManifestPackage) {
+ // Rename manifest package outside of the XmlActionExecutor.
+ // We need to extract the old package name and FullyQualify all class names.
+ if (!renameManifestPackage(mOptions.renameManifestPackage.value(), root)) {
+ return false;
}
- return true;
+ }
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index 2e81266..c3a114b 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -27,12 +27,12 @@
namespace aapt {
struct ManifestFixerOptions {
- Maybe<std::string> minSdkVersionDefault;
- Maybe<std::string> targetSdkVersionDefault;
- Maybe<std::string> renameManifestPackage;
- Maybe<std::string> renameInstrumentationTargetPackage;
- Maybe<std::string> versionNameDefault;
- Maybe<std::string> 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;
};
/**
@@ -40,18 +40,18 @@
* where specified with ManifestFixerOptions.
*/
class ManifestFixer : public IXmlResourceConsumer {
-public:
- explicit ManifestFixer(const ManifestFixerOptions& options) : mOptions(options) {
- }
+ public:
+ explicit ManifestFixer(const ManifestFixerOptions& options)
+ : mOptions(options) {}
- bool consume(IAaptContext* context, xml::XmlResource* doc) override;
+ bool consume(IAaptContext* context, xml::XmlResource* doc) override;
-private:
- bool buildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag);
+ private:
+ bool buildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag);
- ManifestFixerOptions mOptions;
+ ManifestFixerOptions mOptions;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_LINK_MANIFESTFIXER_H */
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 16ab9ab..dc78d98 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -23,254 +23,274 @@
namespace aapt {
struct ManifestFixerTest : public ::testing::Test {
- std::unique_ptr<IAaptContext> mContext;
+ std::unique_ptr<IAaptContext> mContext;
- void SetUp() override {
- mContext = test::ContextBuilder()
- .setCompilationPackage("android")
- .setPackageId(0x01)
- .setNameManglerPolicy(NameManglerPolicy{ "android" })
- .addSymbolSource(test::StaticSymbolSourceBuilder()
- .addSymbol("android:attr/package", ResourceId(0x01010000),
- test::AttributeBuilder()
- .setTypeMask(android::ResTable_map::TYPE_STRING)
- .build())
- .addSymbol("android:attr/minSdkVersion", ResourceId(0x01010001),
- test::AttributeBuilder()
- .setTypeMask(android::ResTable_map::TYPE_STRING |
- android::ResTable_map::TYPE_INTEGER)
- .build())
- .addSymbol("android:attr/targetSdkVersion", ResourceId(0x01010002),
- test::AttributeBuilder()
- .setTypeMask(android::ResTable_map::TYPE_STRING |
- android::ResTable_map::TYPE_INTEGER)
- .build())
- .addSymbol("android:string/str", ResourceId(0x01060000))
- .build())
- .build();
- }
+ void SetUp() override {
+ mContext =
+ test::ContextBuilder()
+ .setCompilationPackage("android")
+ .setPackageId(0x01)
+ .setNameManglerPolicy(NameManglerPolicy{"android"})
+ .addSymbolSource(
+ test::StaticSymbolSourceBuilder()
+ .addSymbol(
+ "android:attr/package", ResourceId(0x01010000),
+ test::AttributeBuilder()
+ .setTypeMask(android::ResTable_map::TYPE_STRING)
+ .build())
+ .addSymbol(
+ "android:attr/minSdkVersion", ResourceId(0x01010001),
+ test::AttributeBuilder()
+ .setTypeMask(android::ResTable_map::TYPE_STRING |
+ android::ResTable_map::TYPE_INTEGER)
+ .build())
+ .addSymbol(
+ "android:attr/targetSdkVersion", ResourceId(0x01010002),
+ test::AttributeBuilder()
+ .setTypeMask(android::ResTable_map::TYPE_STRING |
+ android::ResTable_map::TYPE_INTEGER)
+ .build())
+ .addSymbol("android:string/str", ResourceId(0x01060000))
+ .build())
+ .build();
+ }
- std::unique_ptr<xml::XmlResource> verify(const StringPiece& str) {
- return verifyWithOptions(str, {});
- }
+ std::unique_ptr<xml::XmlResource> verify(const StringPiece& str) {
+ return verifyWithOptions(str, {});
+ }
- std::unique_ptr<xml::XmlResource> verifyWithOptions(const StringPiece& str,
- const ManifestFixerOptions& options) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(str);
- ManifestFixer fixer(options);
- if (fixer.consume(mContext.get(), doc.get())) {
- return doc;
- }
- return {};
+ std::unique_ptr<xml::XmlResource> verifyWithOptions(
+ const StringPiece& str, const ManifestFixerOptions& options) {
+ std::unique_ptr<xml::XmlResource> doc = test::buildXmlDom(str);
+ ManifestFixer fixer(options);
+ if (fixer.consume(mContext.get(), doc.get())) {
+ return doc;
}
+ return {};
+ }
};
TEST_F(ManifestFixerTest, EnsureManifestIsRootTag) {
- EXPECT_EQ(nullptr, verify("<other-tag />"));
- EXPECT_EQ(nullptr, verify("<ns:manifest xmlns:ns=\"com\" />"));
- EXPECT_NE(nullptr, verify("<manifest package=\"android\"></manifest>"));
+ EXPECT_EQ(nullptr, verify("<other-tag />"));
+ EXPECT_EQ(nullptr, verify("<ns:manifest xmlns:ns=\"com\" />"));
+ EXPECT_NE(nullptr, verify("<manifest package=\"android\"></manifest>"));
}
TEST_F(ManifestFixerTest, EnsureManifestHasPackage) {
- EXPECT_NE(nullptr, verify("<manifest package=\"android\" />"));
- EXPECT_NE(nullptr, verify("<manifest package=\"com.android\" />"));
- EXPECT_NE(nullptr, verify("<manifest package=\"com.android.google\" />"));
- EXPECT_EQ(nullptr, verify("<manifest package=\"com.android.google.Class$1\" />"));
- EXPECT_EQ(nullptr,
- verify("<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" "
- "android:package=\"com.android\" />"));
- EXPECT_EQ(nullptr, verify("<manifest package=\"@string/str\" />"));
+ EXPECT_NE(nullptr, verify("<manifest package=\"android\" />"));
+ EXPECT_NE(nullptr, verify("<manifest package=\"com.android\" />"));
+ EXPECT_NE(nullptr, verify("<manifest package=\"com.android.google\" />"));
+ EXPECT_EQ(nullptr,
+ verify("<manifest package=\"com.android.google.Class$1\" />"));
+ EXPECT_EQ(nullptr, verify("<manifest "
+ "xmlns:android=\"http://schemas.android.com/apk/"
+ "res/android\" "
+ "android:package=\"com.android\" />"));
+ EXPECT_EQ(nullptr, verify("<manifest package=\"@string/str\" />"));
}
TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
- ManifestFixerOptions options = { std::string("8"), std::string("22") };
+ ManifestFixerOptions options = {std::string("8"), std::string("22")};
- std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="21" />
- </manifest>)EOF", options);
- ASSERT_NE(nullptr, doc);
+ </manifest>)EOF",
+ options);
+ ASSERT_NE(nullptr, doc);
- xml::Element* el;
- xml::Attribute* attr;
+ xml::Element* el;
+ xml::Attribute* attr;
- el = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, el);
- el = el->findChild({}, "uses-sdk");
- ASSERT_NE(nullptr, el);
- attr = el->findAttribute(xml::kSchemaAndroid, "minSdkVersion");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ("7", attr->value);
- attr = el->findAttribute(xml::kSchemaAndroid, "targetSdkVersion");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ("21", attr->value);
+ el = xml::findRootElement(doc.get());
+ ASSERT_NE(nullptr, el);
+ el = el->findChild({}, "uses-sdk");
+ ASSERT_NE(nullptr, el);
+ attr = el->findAttribute(xml::kSchemaAndroid, "minSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ("7", attr->value);
+ attr = el->findAttribute(xml::kSchemaAndroid, "targetSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ("21", attr->value);
- doc = verifyWithOptions(R"EOF(
+ doc = verifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<uses-sdk android:targetSdkVersion="21" />
- </manifest>)EOF", options);
- ASSERT_NE(nullptr, doc);
+ </manifest>)EOF",
+ options);
+ ASSERT_NE(nullptr, doc);
- el = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, el);
- el = el->findChild({}, "uses-sdk");
- ASSERT_NE(nullptr, el);
- attr = el->findAttribute(xml::kSchemaAndroid, "minSdkVersion");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ("8", attr->value);
- attr = el->findAttribute(xml::kSchemaAndroid, "targetSdkVersion");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ("21", attr->value);
+ el = xml::findRootElement(doc.get());
+ ASSERT_NE(nullptr, el);
+ el = el->findChild({}, "uses-sdk");
+ ASSERT_NE(nullptr, el);
+ attr = el->findAttribute(xml::kSchemaAndroid, "minSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ("8", attr->value);
+ attr = el->findAttribute(xml::kSchemaAndroid, "targetSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ("21", attr->value);
- doc = verifyWithOptions(R"EOF(
+ doc = verifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<uses-sdk />
- </manifest>)EOF", options);
- ASSERT_NE(nullptr, doc);
+ </manifest>)EOF",
+ options);
+ ASSERT_NE(nullptr, doc);
- el = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, el);
- el = el->findChild({}, "uses-sdk");
- ASSERT_NE(nullptr, el);
- attr = el->findAttribute(xml::kSchemaAndroid, "minSdkVersion");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ("8", attr->value);
- attr = el->findAttribute(xml::kSchemaAndroid, "targetSdkVersion");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ("22", attr->value);
+ el = xml::findRootElement(doc.get());
+ ASSERT_NE(nullptr, el);
+ el = el->findChild({}, "uses-sdk");
+ ASSERT_NE(nullptr, el);
+ attr = el->findAttribute(xml::kSchemaAndroid, "minSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ("8", attr->value);
+ attr = el->findAttribute(xml::kSchemaAndroid, "targetSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ("22", attr->value);
- doc = verifyWithOptions(R"EOF(
+ doc = verifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android" />)EOF", options);
- ASSERT_NE(nullptr, doc);
+ package="android" />)EOF",
+ options);
+ ASSERT_NE(nullptr, doc);
- el = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, el);
- el = el->findChild({}, "uses-sdk");
- ASSERT_NE(nullptr, el);
- attr = el->findAttribute(xml::kSchemaAndroid, "minSdkVersion");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ("8", attr->value);
- attr = el->findAttribute(xml::kSchemaAndroid, "targetSdkVersion");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ("22", attr->value);
+ el = xml::findRootElement(doc.get());
+ ASSERT_NE(nullptr, el);
+ el = el->findChild({}, "uses-sdk");
+ ASSERT_NE(nullptr, el);
+ attr = el->findAttribute(xml::kSchemaAndroid, "minSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ("8", attr->value);
+ attr = el->findAttribute(xml::kSchemaAndroid, "targetSdkVersion");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ("22", attr->value);
}
TEST_F(ManifestFixerTest, RenameManifestPackageAndFullyQualifyClasses) {
- ManifestFixerOptions options;
- options.renameManifestPackage = std::string("com.android");
+ ManifestFixerOptions options;
+ options.renameManifestPackage = std::string("com.android");
- std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<application android:name=".MainApplication" text="hello">
<activity android:name=".activity.Start" />
<receiver android:name="com.google.android.Receiver" />
</application>
- </manifest>)EOF", options);
- ASSERT_NE(nullptr, doc);
+ </manifest>)EOF",
+ options);
+ ASSERT_NE(nullptr, doc);
- xml::Element* manifestEl = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, manifestEl);
+ xml::Element* manifestEl = xml::findRootElement(doc.get());
+ ASSERT_NE(nullptr, manifestEl);
- xml::Attribute* attr = nullptr;
+ xml::Attribute* attr = nullptr;
- attr = manifestEl->findAttribute({},"package");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(std::string("com.android"), attr->value);
+ attr = manifestEl->findAttribute({}, "package");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::string("com.android"), attr->value);
- xml::Element* applicationEl = manifestEl->findChild({}, "application");
- ASSERT_NE(nullptr, applicationEl);
+ xml::Element* applicationEl = manifestEl->findChild({}, "application");
+ ASSERT_NE(nullptr, applicationEl);
- attr = applicationEl->findAttribute(xml::kSchemaAndroid, "name");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(std::string("android.MainApplication"), attr->value);
+ attr = applicationEl->findAttribute(xml::kSchemaAndroid, "name");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::string("android.MainApplication"), attr->value);
- attr = applicationEl->findAttribute({}, "text");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(std::string("hello"), attr->value);
+ attr = applicationEl->findAttribute({}, "text");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::string("hello"), attr->value);
- xml::Element* el;
- el = applicationEl->findChild({}, "activity");
- ASSERT_NE(nullptr, el);
+ xml::Element* el;
+ el = applicationEl->findChild({}, "activity");
+ ASSERT_NE(nullptr, el);
- attr = el->findAttribute(xml::kSchemaAndroid, "name");
- ASSERT_NE(nullptr, el);
- EXPECT_EQ(std::string("android.activity.Start"), attr->value);
+ attr = el->findAttribute(xml::kSchemaAndroid, "name");
+ ASSERT_NE(nullptr, el);
+ EXPECT_EQ(std::string("android.activity.Start"), attr->value);
- el = applicationEl->findChild({}, "receiver");
- ASSERT_NE(nullptr, el);
+ el = applicationEl->findChild({}, "receiver");
+ ASSERT_NE(nullptr, el);
- attr = el->findAttribute(xml::kSchemaAndroid, "name");
- ASSERT_NE(nullptr, el);
- EXPECT_EQ(std::string("com.google.android.Receiver"), attr->value);
+ attr = el->findAttribute(xml::kSchemaAndroid, "name");
+ ASSERT_NE(nullptr, el);
+ EXPECT_EQ(std::string("com.google.android.Receiver"), attr->value);
}
-TEST_F(ManifestFixerTest, RenameManifestInstrumentationPackageAndFullyQualifyTarget) {
- ManifestFixerOptions options;
- options.renameInstrumentationTargetPackage = std::string("com.android");
+TEST_F(ManifestFixerTest,
+ RenameManifestInstrumentationPackageAndFullyQualifyTarget) {
+ ManifestFixerOptions options;
+ options.renameInstrumentationTargetPackage = std::string("com.android");
- std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<instrumentation android:targetPackage="android" />
- </manifest>)EOF", options);
- ASSERT_NE(nullptr, doc);
+ </manifest>)EOF",
+ options);
+ ASSERT_NE(nullptr, doc);
- xml::Element* manifestEl = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, manifestEl);
+ xml::Element* manifestEl = xml::findRootElement(doc.get());
+ ASSERT_NE(nullptr, manifestEl);
- xml::Element* instrumentationEl = manifestEl->findChild({}, "instrumentation");
- ASSERT_NE(nullptr, instrumentationEl);
+ xml::Element* instrumentationEl =
+ manifestEl->findChild({}, "instrumentation");
+ ASSERT_NE(nullptr, instrumentationEl);
- xml::Attribute* attr = instrumentationEl->findAttribute(xml::kSchemaAndroid, "targetPackage");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(std::string("com.android"), attr->value);
+ xml::Attribute* attr =
+ instrumentationEl->findAttribute(xml::kSchemaAndroid, "targetPackage");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::string("com.android"), attr->value);
}
TEST_F(ManifestFixerTest, UseDefaultVersionNameAndCode) {
- ManifestFixerOptions options;
- options.versionNameDefault = std::string("Beta");
- options.versionCodeDefault = std::string("0x10000000");
+ ManifestFixerOptions options;
+ options.versionNameDefault = std::string("Beta");
+ options.versionCodeDefault = std::string("0x10000000");
- std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android" />)EOF", options);
- ASSERT_NE(nullptr, doc);
+ package="android" />)EOF",
+ options);
+ ASSERT_NE(nullptr, doc);
- xml::Element* manifestEl = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, manifestEl);
+ xml::Element* manifestEl = xml::findRootElement(doc.get());
+ ASSERT_NE(nullptr, manifestEl);
- xml::Attribute* attr = manifestEl->findAttribute(xml::kSchemaAndroid, "versionName");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(std::string("Beta"), attr->value);
+ xml::Attribute* attr =
+ manifestEl->findAttribute(xml::kSchemaAndroid, "versionName");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::string("Beta"), attr->value);
- attr = manifestEl->findAttribute(xml::kSchemaAndroid, "versionCode");
- ASSERT_NE(nullptr, attr);
- EXPECT_EQ(std::string("0x10000000"), attr->value);
+ attr = manifestEl->findAttribute(xml::kSchemaAndroid, "versionCode");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::string("0x10000000"), attr->value);
}
TEST_F(ManifestFixerTest, EnsureManifestAttributesAreTyped) {
- EXPECT_EQ(nullptr, verify("<manifest package=\"android\" coreApp=\"hello\" />"));
- EXPECT_EQ(nullptr, verify("<manifest package=\"android\" coreApp=\"1dp\" />"));
+ EXPECT_EQ(nullptr,
+ verify("<manifest package=\"android\" coreApp=\"hello\" />"));
+ EXPECT_EQ(nullptr,
+ verify("<manifest package=\"android\" coreApp=\"1dp\" />"));
- std::unique_ptr<xml::XmlResource> doc =
- verify("<manifest package=\"android\" coreApp=\"true\" />");
- ASSERT_NE(nullptr, doc);
+ std::unique_ptr<xml::XmlResource> doc =
+ verify("<manifest package=\"android\" coreApp=\"true\" />");
+ ASSERT_NE(nullptr, doc);
- xml::Element* el = xml::findRootElement(doc.get());
- ASSERT_NE(nullptr, el);
+ xml::Element* el = xml::findRootElement(doc.get());
+ ASSERT_NE(nullptr, el);
- EXPECT_EQ("manifest", el->name);
+ EXPECT_EQ("manifest", el->name);
- xml::Attribute* attr = el->findAttribute("", "coreApp");
- ASSERT_NE(nullptr, attr);
+ xml::Attribute* attr = el->findAttribute("", "coreApp");
+ ASSERT_NE(nullptr, attr);
- EXPECT_NE(nullptr, attr->compiledValue);
- EXPECT_NE(nullptr, valueCast<BinaryPrimitive>(attr->compiledValue.get()));
+ EXPECT_NE(nullptr, attr->compiledValue);
+ EXPECT_NE(nullptr, valueCast<BinaryPrimitive>(attr->compiledValue.get()));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/PrivateAttributeMover.cpp b/tools/aapt2/link/PrivateAttributeMover.cpp
index 3c8af4f..174b41f 100644
--- a/tools/aapt2/link/PrivateAttributeMover.cpp
+++ b/tools/aapt2/link/PrivateAttributeMover.cpp
@@ -25,56 +25,61 @@
template <typename InputContainer, typename OutputIterator, typename Predicate>
OutputIterator moveIf(InputContainer& inputContainer, OutputIterator result,
Predicate pred) {
- const auto last = inputContainer.end();
- auto newEnd = std::find_if(inputContainer.begin(), inputContainer.end(), pred);
- if (newEnd == last) {
- return result;
- }
-
- *result = std::move(*newEnd);
-
- auto first = newEnd;
- ++first;
-
- for (; first != last; ++first) {
- if (bool(pred(*first))) {
- // We want to move this guy
- *result = std::move(*first);
- ++result;
- } else {
- // We want to keep this guy, but we will need to move it up the list to replace
- // missing items.
- *newEnd = std::move(*first);
- ++newEnd;
- }
- }
-
- inputContainer.erase(newEnd, last);
+ const auto last = inputContainer.end();
+ auto newEnd =
+ std::find_if(inputContainer.begin(), inputContainer.end(), pred);
+ if (newEnd == last) {
return result;
-}
+ }
-bool PrivateAttributeMover::consume(IAaptContext* context, ResourceTable* table) {
- for (auto& package : table->packages) {
- ResourceTableType* type = package->findType(ResourceType::kAttr);
- if (!type) {
- continue;
- }
+ *result = std::move(*newEnd);
- if (type->symbolStatus.state != SymbolState::kPublic) {
- // No public attributes, so we can safely leave these private attributes where they are.
- return true;
- }
+ auto first = newEnd;
+ ++first;
- ResourceTableType* privAttrType = package->findOrCreateType(ResourceType::kAttrPrivate);
- assert(privAttrType->entries.empty());
-
- moveIf(type->entries, std::back_inserter(privAttrType->entries),
- [](const std::unique_ptr<ResourceEntry>& entry) -> bool {
- return entry->symbolStatus.state != SymbolState::kPublic;
- });
- break;
+ for (; first != last; ++first) {
+ if (bool(pred(*first))) {
+ // We want to move this guy
+ *result = std::move(*first);
+ ++result;
+ } else {
+ // We want to keep this guy, but we will need to move it up the list to
+ // replace
+ // missing items.
+ *newEnd = std::move(*first);
+ ++newEnd;
}
- return true;
+ }
+
+ inputContainer.erase(newEnd, last);
+ return result;
}
-} // namespace aapt
+bool PrivateAttributeMover::consume(IAaptContext* context,
+ ResourceTable* table) {
+ for (auto& package : table->packages) {
+ ResourceTableType* type = package->findType(ResourceType::kAttr);
+ if (!type) {
+ continue;
+ }
+
+ if (type->symbolStatus.state != SymbolState::kPublic) {
+ // No public attributes, so we can safely leave these private attributes
+ // where they are.
+ return true;
+ }
+
+ ResourceTableType* privAttrType =
+ package->findOrCreateType(ResourceType::kAttrPrivate);
+ assert(privAttrType->entries.empty());
+
+ moveIf(type->entries, std::back_inserter(privAttrType->entries),
+ [](const std::unique_ptr<ResourceEntry>& entry) -> bool {
+ return entry->symbolStatus.state != SymbolState::kPublic;
+ });
+ break;
+ }
+ return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/PrivateAttributeMover_test.cpp b/tools/aapt2/link/PrivateAttributeMover_test.cpp
index c9d1a08..a7a1013 100644
--- a/tools/aapt2/link/PrivateAttributeMover_test.cpp
+++ b/tools/aapt2/link/PrivateAttributeMover_test.cpp
@@ -20,56 +20,60 @@
namespace aapt {
TEST(PrivateAttributeMoverTest, MovePrivateAttributes) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .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();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .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()));
+ PrivateAttributeMover mover;
+ ASSERT_TRUE(mover.consume(context.get(), table.get()));
- ResourceTablePackage* package = table->findPackage("android");
- ASSERT_NE(package, nullptr);
+ 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("publicA"), nullptr);
- EXPECT_NE(type->findEntry("publicB"), nullptr);
+ ResourceTableType* type = package->findType(ResourceType::kAttr);
+ ASSERT_NE(type, nullptr);
+ ASSERT_EQ(type->entries.size(), 2u);
+ 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("privateA"), nullptr);
- EXPECT_NE(type->findEntry("privateB"), nullptr);
+ type = package->findType(ResourceType::kAttrPrivate);
+ ASSERT_NE(type, nullptr);
+ ASSERT_EQ(type->entries.size(), 2u);
+ EXPECT_NE(type->findEntry("privateA"), nullptr);
+ EXPECT_NE(type->findEntry("privateB"), nullptr);
}
-TEST(PrivateAttributeMoverTest, LeavePrivateAttributesWhenNoPublicAttributesDefined) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+TEST(PrivateAttributeMoverTest,
+ LeavePrivateAttributesWhenNoPublicAttributesDefined) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addSimple("android:attr/privateA")
- .addSimple("android:attr/privateB")
- .build();
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .addSimple("android:attr/privateA")
+ .addSimple("android:attr/privateB")
+ .build();
- PrivateAttributeMover mover;
- ASSERT_TRUE(mover.consume(context.get(), table.get()));
+ PrivateAttributeMover mover;
+ ASSERT_TRUE(mover.consume(context.get(), table.get()));
- ResourceTablePackage* package = table->findPackage("android");
- ASSERT_NE(package, nullptr);
+ 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);
+ ResourceTableType* type = package->findType(ResourceType::kAttr);
+ ASSERT_NE(type, nullptr);
+ ASSERT_EQ(type->entries.size(), 2u);
- type = package->findType(ResourceType::kAttrPrivate);
- ASSERT_EQ(type, nullptr);
+ type = package->findType(ResourceType::kAttrPrivate);
+ ASSERT_EQ(type, nullptr);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/ProductFilter.cpp b/tools/aapt2/link/ProductFilter.cpp
index 8784e89..d59b682 100644
--- a/tools/aapt2/link/ProductFilter.cpp
+++ b/tools/aapt2/link/ProductFilter.cpp
@@ -18,101 +18,103 @@
namespace aapt {
-ProductFilter::ResourceConfigValueIter
-ProductFilter::selectProductToKeep(const ResourceNameRef& name,
- const ResourceConfigValueIter begin,
- const ResourceConfigValueIter end,
- IDiagnostics* diag) {
- ResourceConfigValueIter defaultProductIter = end;
- ResourceConfigValueIter selectedProductIter = end;
+ProductFilter::ResourceConfigValueIter ProductFilter::selectProductToKeep(
+ const ResourceNameRef& name, const ResourceConfigValueIter begin,
+ const ResourceConfigValueIter end, IDiagnostics* diag) {
+ ResourceConfigValueIter defaultProductIter = end;
+ ResourceConfigValueIter selectedProductIter = end;
- for (ResourceConfigValueIter iter = begin; iter != end; ++iter) {
- ResourceConfigValue* configValue = iter->get();
- if (mProducts.find(configValue->product) != mProducts.end()) {
- if (selectedProductIter != end) {
- // We have two possible values for this product!
- diag->error(DiagMessage(configValue->value->getSource())
- << "selection of product '" << configValue->product
- << "' for resource " << name << " is ambiguous");
+ for (ResourceConfigValueIter iter = begin; iter != end; ++iter) {
+ ResourceConfigValue* configValue = iter->get();
+ if (mProducts.find(configValue->product) != mProducts.end()) {
+ if (selectedProductIter != end) {
+ // We have two possible values for this product!
+ diag->error(DiagMessage(configValue->value->getSource())
+ << "selection of product '" << configValue->product
+ << "' for resource " << name << " is ambiguous");
- ResourceConfigValue* previouslySelectedConfigValue = selectedProductIter->get();
- diag->note(DiagMessage(previouslySelectedConfigValue->value->getSource())
- << "product '" << previouslySelectedConfigValue->product
- << "' is also a candidate");
- return end;
- }
-
- // Select this product.
- selectedProductIter = iter;
- }
-
- if (configValue->product.empty() || configValue->product == "default") {
- if (defaultProductIter != end) {
- // We have two possible default values.
- diag->error(DiagMessage(configValue->value->getSource())
- << "multiple default products defined for resource " << name);
-
- ResourceConfigValue* previouslyDefaultConfigValue = defaultProductIter->get();
- diag->note(DiagMessage(previouslyDefaultConfigValue->value->getSource())
- << "default product also defined here");
- return end;
- }
-
- // Mark the default.
- defaultProductIter = iter;
- }
- }
-
- if (defaultProductIter == end) {
- diag->error(DiagMessage() << "no default product defined for resource " << name);
+ ResourceConfigValue* previouslySelectedConfigValue =
+ selectedProductIter->get();
+ diag->note(
+ DiagMessage(previouslySelectedConfigValue->value->getSource())
+ << "product '" << previouslySelectedConfigValue->product
+ << "' is also a candidate");
return end;
+ }
+
+ // Select this product.
+ selectedProductIter = iter;
}
- if (selectedProductIter == end) {
- selectedProductIter = defaultProductIter;
+ if (configValue->product.empty() || configValue->product == "default") {
+ if (defaultProductIter != end) {
+ // We have two possible default values.
+ diag->error(DiagMessage(configValue->value->getSource())
+ << "multiple default products defined for resource "
+ << name);
+
+ ResourceConfigValue* previouslyDefaultConfigValue =
+ defaultProductIter->get();
+ diag->note(DiagMessage(previouslyDefaultConfigValue->value->getSource())
+ << "default product also defined here");
+ return end;
+ }
+
+ // Mark the default.
+ defaultProductIter = iter;
}
- return selectedProductIter;
+ }
+
+ if (defaultProductIter == end) {
+ diag->error(DiagMessage() << "no default product defined for resource "
+ << name);
+ return end;
+ }
+
+ if (selectedProductIter == end) {
+ selectedProductIter = defaultProductIter;
+ }
+ return selectedProductIter;
}
bool ProductFilter::consume(IAaptContext* context, ResourceTable* table) {
- bool error = false;
- for (auto& pkg : table->packages) {
- for (auto& type : pkg->types) {
- for (auto& entry : type->entries) {
- std::vector<std::unique_ptr<ResourceConfigValue>> newValues;
+ bool error = false;
+ for (auto& pkg : table->packages) {
+ for (auto& type : pkg->types) {
+ for (auto& entry : type->entries) {
+ std::vector<std::unique_ptr<ResourceConfigValue>> newValues;
- ResourceConfigValueIter iter = entry->values.begin();
- ResourceConfigValueIter startRangeIter = iter;
- while (iter != entry->values.end()) {
- ++iter;
- if (iter == entry->values.end() ||
- (*iter)->config != (*startRangeIter)->config) {
-
- // End of the array, or we saw a different config,
- // so this must be the end of a range of products.
- // Select the product to keep from the set of products defined.
- ResourceNameRef name(pkg->name, type->type, entry->name);
- auto valueToKeep = selectProductToKeep(name, startRangeIter, iter,
- context->getDiagnostics());
- if (valueToKeep == iter) {
- // An error occurred, we could not pick a product.
- error = true;
- } else {
- // We selected a product to keep. Move it to the new array.
- newValues.push_back(std::move(*valueToKeep));
- }
-
- // Start the next range of products.
- startRangeIter = iter;
- }
- }
-
- // Now move the new values in to place.
- entry->values = std::move(newValues);
+ ResourceConfigValueIter iter = entry->values.begin();
+ ResourceConfigValueIter startRangeIter = iter;
+ while (iter != entry->values.end()) {
+ ++iter;
+ if (iter == entry->values.end() ||
+ (*iter)->config != (*startRangeIter)->config) {
+ // End of the array, or we saw a different config,
+ // so this must be the end of a range of products.
+ // Select the product to keep from the set of products defined.
+ ResourceNameRef name(pkg->name, type->type, entry->name);
+ auto valueToKeep = selectProductToKeep(name, startRangeIter, iter,
+ context->getDiagnostics());
+ if (valueToKeep == iter) {
+ // An error occurred, we could not pick a product.
+ error = true;
+ } else {
+ // We selected a product to keep. Move it to the new array.
+ newValues.push_back(std::move(*valueToKeep));
}
+
+ // Start the next range of products.
+ startRangeIter = iter;
+ }
}
+
+ // Now move the new values in to place.
+ entry->values = std::move(newValues);
+ }
}
- return !error;
+ }
+ return !error;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/ProductFilter.h b/tools/aapt2/link/ProductFilter.h
index 7724e14..cc8b8c2 100644
--- a/tools/aapt2/link/ProductFilter.h
+++ b/tools/aapt2/link/ProductFilter.h
@@ -26,24 +26,25 @@
namespace aapt {
class ProductFilter {
-public:
- using ResourceConfigValueIter = std::vector<std::unique_ptr<ResourceConfigValue>>::iterator;
+ public:
+ using ResourceConfigValueIter =
+ std::vector<std::unique_ptr<ResourceConfigValue>>::iterator;
- explicit ProductFilter(std::unordered_set<std::string> products) : mProducts(products) { }
+ explicit ProductFilter(std::unordered_set<std::string> products)
+ : mProducts(products) {}
- ResourceConfigValueIter selectProductToKeep(const ResourceNameRef& name,
- const ResourceConfigValueIter begin,
- const ResourceConfigValueIter end,
- IDiagnostics* diag);
+ ResourceConfigValueIter selectProductToKeep(
+ const ResourceNameRef& name, const ResourceConfigValueIter begin,
+ const ResourceConfigValueIter end, IDiagnostics* diag);
- bool consume(IAaptContext* context, ResourceTable* table);
+ bool consume(IAaptContext* context, ResourceTable* table);
-private:
- std::unordered_set<std::string> mProducts;
+ private:
+ std::unordered_set<std::string> mProducts;
- DISALLOW_COPY_AND_ASSIGN(ProductFilter);
+ DISALLOW_COPY_AND_ASSIGN(ProductFilter);
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_LINK_PRODUCTFILTER_H */
diff --git a/tools/aapt2/link/ProductFilter_test.cpp b/tools/aapt2/link/ProductFilter_test.cpp
index a3376ac..7f78f8b 100644
--- a/tools/aapt2/link/ProductFilter_test.cpp
+++ b/tools/aapt2/link/ProductFilter_test.cpp
@@ -20,114 +20,110 @@
namespace aapt {
TEST(ProductFilterTest, SelectTwoProducts) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- const ConfigDescription land = test::parseConfigOrDie("land");
- const ConfigDescription port = test::parseConfigOrDie("port");
+ const ConfigDescription land = test::parseConfigOrDie("land");
+ const ConfigDescription port = test::parseConfigOrDie("port");
- ResourceTable table;
- 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("android:string/one"),
- land, "tablet",
- test::ValueBuilder<Id>()
- .setSource(Source("land/tablet.xml")).build(),
- context->getDiagnostics()));
+ ResourceTable table;
+ 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("android:string/one"), land, "tablet",
+ test::ValueBuilder<Id>().setSource(Source("land/tablet.xml")).build(),
+ context->getDiagnostics()));
- 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("android:string/one"),
- port, "tablet",
- test::ValueBuilder<Id>()
- .setSource(Source("port/tablet.xml")).build(),
- context->getDiagnostics()));
+ 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("android:string/one"), port, "tablet",
+ test::ValueBuilder<Id>().setSource(Source("port/tablet.xml")).build(),
+ context->getDiagnostics()));
- ProductFilter filter({ "tablet" });
- ASSERT_TRUE(filter.consume(context.get(), &table));
+ ProductFilter filter({"tablet"});
+ ASSERT_TRUE(filter.consume(context.get(), &table));
- EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, "android:string/one",
- land, ""));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, "android:string/one",
- land, "tablet"));
- EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, "android:string/one",
- port, ""));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, "android:string/one",
- port, "tablet"));
+ EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(
+ &table, "android:string/one", land, ""));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(
+ &table, "android:string/one", land, "tablet"));
+ EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(
+ &table, "android:string/one", port, ""));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(
+ &table, "android:string/one", port, "tablet"));
}
TEST(ProductFilterTest, SelectDefaultProduct) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- ResourceTable table;
- 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("android:string/one"),
- ConfigDescription::defaultConfig(), "tablet",
- test::ValueBuilder<Id>()
- .setSource(Source("tablet.xml")).build(),
- context->getDiagnostics()));
+ ResourceTable table;
+ 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("android:string/one"),
+ ConfigDescription::defaultConfig(), "tablet",
+ test::ValueBuilder<Id>().setSource(Source("tablet.xml")).build(),
+ context->getDiagnostics()));
- ProductFilter filter({});
- ASSERT_TRUE(filter.consume(context.get(), &table));
+ ProductFilter filter({});
+ ASSERT_TRUE(filter.consume(context.get(), &table));
- EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, "android:string/one",
- ConfigDescription::defaultConfig(),
- ""));
- EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(&table, "android:string/one",
- ConfigDescription::defaultConfig(),
- "tablet"));
+ EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(
+ &table, "android:string/one",
+ ConfigDescription::defaultConfig(), ""));
+ EXPECT_EQ(nullptr, test::getValueForConfigAndProduct<Id>(
+ &table, "android:string/one",
+ ConfigDescription::defaultConfig(), "tablet"));
}
TEST(ProductFilterTest, FailOnAmbiguousProduct) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- ResourceTable table;
- 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("android:string/one"),
- ConfigDescription::defaultConfig(), "tablet",
- test::ValueBuilder<Id>()
- .setSource(Source("tablet.xml")).build(),
- context->getDiagnostics()));
- ASSERT_TRUE(table.addResource(test::parseNameOrDie("android:string/one"),
- ConfigDescription::defaultConfig(), "no-sdcard",
- test::ValueBuilder<Id>()
- .setSource(Source("no-sdcard.xml")).build(),
- context->getDiagnostics()));
+ ResourceTable table;
+ 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("android:string/one"),
+ ConfigDescription::defaultConfig(), "tablet",
+ test::ValueBuilder<Id>().setSource(Source("tablet.xml")).build(),
+ context->getDiagnostics()));
+ ASSERT_TRUE(table.addResource(
+ test::parseNameOrDie("android:string/one"),
+ ConfigDescription::defaultConfig(), "no-sdcard",
+ test::ValueBuilder<Id>().setSource(Source("no-sdcard.xml")).build(),
+ context->getDiagnostics()));
- ProductFilter filter({ "tablet", "no-sdcard" });
- ASSERT_FALSE(filter.consume(context.get(), &table));
+ ProductFilter filter({"tablet", "no-sdcard"});
+ ASSERT_FALSE(filter.consume(context.get(), &table));
}
TEST(ProductFilterTest, FailOnMultipleDefaults) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- ResourceTable table;
- 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("android:string/one"),
- ConfigDescription::defaultConfig(), "default",
- test::ValueBuilder<Id>()
- .setSource(Source("default.xml")).build(),
- context->getDiagnostics()));
+ ResourceTable table;
+ 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("android:string/one"),
+ ConfigDescription::defaultConfig(), "default",
+ test::ValueBuilder<Id>().setSource(Source("default.xml")).build(),
+ context->getDiagnostics()));
- ProductFilter filter({});
- ASSERT_FALSE(filter.consume(context.get(), &table));
+ ProductFilter filter({});
+ ASSERT_FALSE(filter.consume(context.get(), &table));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index be7aca3..7fe0956 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include "Diagnostics.h"
#include "ReferenceLinker.h"
+#include "Diagnostics.h"
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
@@ -34,302 +34,335 @@
namespace {
/**
- * The ReferenceLinkerVisitor will follow all references and make sure they point
- * to resources that actually exist, either in the local resource table, or as external
- * symbols. Once the target resource has been found, the ID of the resource will be assigned
+ * The ReferenceLinkerVisitor will follow all references and make sure they
+ * point
+ * to resources that actually exist, either in the local resource table, or as
+ * external
+ * symbols. Once the target resource has been found, the ID of the resource will
+ * be assigned
* to the reference object.
*
* NOTE: All of the entries in the ResourceTable must be assigned IDs.
*/
class ReferenceLinkerVisitor : public ValueVisitor {
-public:
- using ValueVisitor::visit;
+ public:
+ using ValueVisitor::visit;
- ReferenceLinkerVisitor(IAaptContext* context, SymbolTable* symbols, StringPool* stringPool,
- xml::IPackageDeclStack* decl,CallSite* callSite) :
- mContext(context), mSymbols(symbols), mPackageDecls(decl), mStringPool(stringPool),
- mCallSite(callSite) {
+ ReferenceLinkerVisitor(IAaptContext* context, SymbolTable* symbols,
+ StringPool* stringPool, xml::IPackageDeclStack* decl,
+ CallSite* callSite)
+ : mContext(context),
+ mSymbols(symbols),
+ mPackageDecls(decl),
+ mStringPool(stringPool),
+ mCallSite(callSite) {}
+
+ void visit(Reference* ref) override {
+ if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mPackageDecls,
+ mCallSite)) {
+ mError = true;
+ }
+ }
+
+ /**
+ * We visit the Style specially because during this phase, values of
+ * attributes are
+ * all RawString values. Now that we are expected to resolve all symbols, we
+ * can
+ * lookup the attributes to find out which types are allowed for the
+ * attributes' values.
+ */
+ void visit(Style* style) override {
+ if (style->parent) {
+ visit(&style->parent.value());
}
- void visit(Reference* ref) override {
- if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mPackageDecls, mCallSite)) {
- mError = true;
- }
- }
+ for (Style::Entry& entry : style->entries) {
+ std::string errStr;
- /**
- * We visit the Style specially because during this phase, values of attributes are
- * all RawString values. Now that we are expected to resolve all symbols, we can
- * lookup the attributes to find out which types are allowed for the attributes' values.
- */
- void visit(Style* style) override {
- if (style->parent) {
- visit(&style->parent.value());
+ // Transform the attribute reference so that it is using the fully
+ // qualified package
+ // name. This will also mark the reference as being able to see private
+ // resources if
+ // there was a '*' in the reference or if the package came from the
+ // private namespace.
+ Reference transformedReference = entry.key;
+ transformReferenceFromNamespace(mPackageDecls,
+ mContext->getCompilationPackage(),
+ &transformedReference);
+
+ // Find the attribute in the symbol table and check if it is visible from
+ // this callsite.
+ const SymbolTable::Symbol* symbol =
+ ReferenceLinker::resolveAttributeCheckVisibility(
+ transformedReference, mContext->getNameMangler(), mSymbols,
+ mCallSite, &errStr);
+ if (symbol) {
+ // Assign our style key the correct ID.
+ // The ID may not exist.
+ entry.key.id = symbol->id;
+
+ // Try to convert the value to a more specific, typed value based on the
+ // attribute it is set to.
+ entry.value = parseValueWithAttribute(std::move(entry.value),
+ symbol->attribute.get());
+
+ // Link/resolve the final value (mostly if it's a reference).
+ entry.value->accept(this);
+
+ // Now verify that the type of this item is compatible with the
+ // attribute it
+ // is defined for. We pass `nullptr` as the DiagMessage so that this
+ // check is
+ // fast and we avoid creating a DiagMessage when the match is
+ // successful.
+ if (!symbol->attribute->matches(entry.value.get(), nullptr)) {
+ // The actual type of this item is incompatible with the attribute.
+ DiagMessage msg(entry.key.getSource());
+
+ // Call the matches method again, this time with a DiagMessage so we
+ // fill
+ // in the actual error message.
+ symbol->attribute->matches(entry.value.get(), &msg);
+ mContext->getDiagnostics()->error(msg);
+ mError = true;
}
- for (Style::Entry& entry : style->entries) {
- std::string errStr;
+ } else {
+ DiagMessage msg(entry.key.getSource());
+ msg << "style attribute '";
+ ReferenceLinker::writeResourceName(&msg, entry.key,
+ transformedReference);
+ msg << "' " << errStr;
+ mContext->getDiagnostics()->error(msg);
+ mError = true;
+ }
+ }
+ }
- // Transform the attribute reference so that it is using the fully qualified package
- // name. This will also mark the reference as being able to see private resources if
- // there was a '*' in the reference or if the package came from the private namespace.
- Reference transformedReference = entry.key;
- transformReferenceFromNamespace(mPackageDecls, mContext->getCompilationPackage(),
- &transformedReference);
+ bool hasError() { return mError; }
- // Find the attribute in the symbol table and check if it is visible from this callsite.
- const SymbolTable::Symbol* symbol = ReferenceLinker::resolveAttributeCheckVisibility(
- transformedReference, mContext->getNameMangler(), mSymbols, mCallSite, &errStr);
- if (symbol) {
- // Assign our style key the correct ID.
- // The ID may not exist.
- entry.key.id = symbol->id;
+ private:
+ IAaptContext* mContext;
+ SymbolTable* mSymbols;
+ xml::IPackageDeclStack* mPackageDecls;
+ StringPool* mStringPool;
+ CallSite* mCallSite;
+ bool mError = false;
- // Try to convert the value to a more specific, typed value based on the
- // attribute it is set to.
- entry.value = parseValueWithAttribute(std::move(entry.value),
- symbol->attribute.get());
+ /**
+ * Transform a RawString value into a more specific, appropriate value, based
+ * on the
+ * Attribute. If a non RawString value is passed in, this is an identity
+ * transform.
+ */
+ std::unique_ptr<Item> parseValueWithAttribute(std::unique_ptr<Item> value,
+ const Attribute* attr) {
+ if (RawString* rawString = valueCast<RawString>(value.get())) {
+ std::unique_ptr<Item> transformed =
+ ResourceUtils::tryParseItemForAttribute(*rawString->value, attr);
- // Link/resolve the final value (mostly if it's a reference).
- entry.value->accept(this);
-
- // Now verify that the type of this item is compatible with the attribute it
- // is defined for. We pass `nullptr` as the DiagMessage so that this check is
- // fast and we avoid creating a DiagMessage when the match is successful.
- if (!symbol->attribute->matches(entry.value.get(), nullptr)) {
- // The actual type of this item is incompatible with the attribute.
- DiagMessage msg(entry.key.getSource());
-
- // Call the matches method again, this time with a DiagMessage so we fill
- // in the actual error message.
- symbol->attribute->matches(entry.value.get(), &msg);
- mContext->getDiagnostics()->error(msg);
- mError = true;
- }
-
- } else {
- DiagMessage msg(entry.key.getSource());
- msg << "style attribute '";
- ReferenceLinker::writeResourceName(&msg, entry.key, transformedReference);
- msg << "' " << errStr;
- mContext->getDiagnostics()->error(msg);
- mError = true;
- }
+ // If we could not parse as any specific type, try a basic STRING.
+ if (!transformed &&
+ (attr->typeMask & android::ResTable_map::TYPE_STRING)) {
+ util::StringBuilder stringBuilder;
+ stringBuilder.append(*rawString->value);
+ if (stringBuilder) {
+ transformed = util::make_unique<String>(
+ mStringPool->makeRef(stringBuilder.str()));
}
- }
+ }
- bool hasError() {
- return mError;
- }
-
-private:
- IAaptContext* mContext;
- SymbolTable* mSymbols;
- xml::IPackageDeclStack* mPackageDecls;
- StringPool* mStringPool;
- CallSite* mCallSite;
- bool mError = false;
-
- /**
- * Transform a RawString value into a more specific, appropriate value, based on the
- * Attribute. If a non RawString value is passed in, this is an identity transform.
- */
- std::unique_ptr<Item> parseValueWithAttribute(std::unique_ptr<Item> value,
- const Attribute* attr) {
- if (RawString* rawString = valueCast<RawString>(value.get())) {
- std::unique_ptr<Item> transformed =
- ResourceUtils::tryParseItemForAttribute(*rawString->value, attr);
-
- // If we could not parse as any specific type, try a basic STRING.
- if (!transformed && (attr->typeMask & android::ResTable_map::TYPE_STRING)) {
- util::StringBuilder stringBuilder;
- stringBuilder.append(*rawString->value);
- if (stringBuilder) {
- transformed = util::make_unique<String>(
- mStringPool->makeRef(stringBuilder.str()));
- }
- }
-
- if (transformed) {
- return transformed;
- }
- };
- return value;
- }
+ if (transformed) {
+ return transformed;
+ }
+ };
+ return value;
+ }
};
-} // namespace
+} // namespace
/**
- * The symbol is visible if it is public, or if the reference to it is requesting private access
+ * The symbol is visible if it is public, or if the reference to it is
+ * requesting private access
* or if the callsite comes from the same package.
*/
-bool ReferenceLinker::isSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
+bool ReferenceLinker::isSymbolVisible(const SymbolTable::Symbol& symbol,
+ const Reference& ref,
const CallSite& callSite) {
- if (!symbol.isPublic && !ref.privateReference) {
- if (ref.name) {
- return callSite.resource.package == ref.name.value().package;
- } else if (ref.id && symbol.id) {
- return ref.id.value().packageId() == symbol.id.value().packageId();
- } else {
- return false;
- }
+ if (!symbol.isPublic && !ref.privateReference) {
+ if (ref.name) {
+ return callSite.resource.package == ref.name.value().package;
+ } else if (ref.id && symbol.id) {
+ return ref.id.value().packageId() == symbol.id.value().packageId();
+ } else {
+ return false;
}
- return true;
+ }
+ return true;
}
-const SymbolTable::Symbol* ReferenceLinker::resolveSymbol(const Reference& reference,
- NameMangler* mangler,
- SymbolTable* symbols) {
- if (reference.name) {
- Maybe<ResourceName> mangled = mangler->mangleName(reference.name.value());
- return symbols->findByName(mangled ? mangled.value() : reference.name.value());
- } else if (reference.id) {
- return symbols->findById(reference.id.value());
- } else {
- return nullptr;
- }
+const SymbolTable::Symbol* ReferenceLinker::resolveSymbol(
+ const Reference& reference, NameMangler* mangler, SymbolTable* symbols) {
+ if (reference.name) {
+ Maybe<ResourceName> mangled = mangler->mangleName(reference.name.value());
+ return symbols->findByName(mangled ? mangled.value()
+ : reference.name.value());
+ } else if (reference.id) {
+ return symbols->findById(reference.id.value());
+ } else {
+ return nullptr;
+ }
}
const SymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility(
- const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
- CallSite* callSite, std::string* outError) {
- const SymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
- if (!symbol) {
- if (outError) *outError = "not found";
- return nullptr;
- }
+ const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
+ CallSite* callSite, std::string* outError) {
+ const SymbolTable::Symbol* symbol =
+ resolveSymbol(reference, nameMangler, symbols);
+ if (!symbol) {
+ if (outError) *outError = "not found";
+ return nullptr;
+ }
- if (!isSymbolVisible(*symbol, reference, *callSite)) {
- if (outError) *outError = "is private";
- return nullptr;
- }
- return symbol;
+ if (!isSymbolVisible(*symbol, reference, *callSite)) {
+ if (outError) *outError = "is private";
+ return nullptr;
+ }
+ return symbol;
}
const SymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility(
- const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
- CallSite* callSite, std::string* outError) {
- const SymbolTable::Symbol* symbol = resolveSymbolCheckVisibility(reference, nameMangler,
- symbols, callSite,
- outError);
- if (!symbol) {
- return nullptr;
- }
+ const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
+ CallSite* callSite, std::string* outError) {
+ const SymbolTable::Symbol* symbol = resolveSymbolCheckVisibility(
+ reference, nameMangler, symbols, callSite, outError);
+ if (!symbol) {
+ return nullptr;
+ }
- if (!symbol->attribute) {
- if (outError) *outError = "is not an attribute";
- return nullptr;
- }
- return symbol;
+ if (!symbol->attribute) {
+ if (outError) *outError = "is not an attribute";
+ return nullptr;
+ }
+ return symbol;
}
-Maybe<xml::AaptAttribute> ReferenceLinker::compileXmlAttribute(const Reference& reference,
- NameMangler* nameMangler,
- SymbolTable* symbols,
- CallSite* callSite,
- std::string* outError) {
- const SymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
- if (!symbol) {
- if (outError) *outError = "not found";
- return {};
- }
+Maybe<xml::AaptAttribute> ReferenceLinker::compileXmlAttribute(
+ const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
+ CallSite* callSite, std::string* outError) {
+ const SymbolTable::Symbol* symbol =
+ resolveSymbol(reference, nameMangler, symbols);
+ if (!symbol) {
+ if (outError) *outError = "not found";
+ return {};
+ }
- if (!symbol->attribute) {
- if (outError) *outError = "is not an attribute";
- return {};
- }
- return xml::AaptAttribute{ symbol->id, *symbol->attribute };
+ if (!symbol->attribute) {
+ if (outError) *outError = "is not an attribute";
+ return {};
+ }
+ return xml::AaptAttribute{symbol->id, *symbol->attribute};
}
-void ReferenceLinker::writeResourceName(DiagMessage* outMsg, const Reference& orig,
+void ReferenceLinker::writeResourceName(DiagMessage* outMsg,
+ const Reference& orig,
const Reference& transformed) {
- assert(outMsg);
+ assert(outMsg);
- if (orig.name) {
- *outMsg << orig.name.value();
- if (transformed.name.value() != orig.name.value()) {
- *outMsg << " (aka " << transformed.name.value() << ")";
- }
- } else {
- *outMsg << orig.id.value();
+ if (orig.name) {
+ *outMsg << orig.name.value();
+ if (transformed.name.value() != orig.name.value()) {
+ *outMsg << " (aka " << transformed.name.value() << ")";
}
+ } else {
+ *outMsg << orig.id.value();
+ }
}
bool ReferenceLinker::linkReference(Reference* reference, IAaptContext* context,
- SymbolTable* symbols, xml::IPackageDeclStack* decls,
+ SymbolTable* symbols,
+ xml::IPackageDeclStack* decls,
CallSite* callSite) {
- assert(reference);
- assert(reference->name || reference->id);
+ assert(reference);
+ assert(reference->name || reference->id);
- Reference transformedReference = *reference;
- transformReferenceFromNamespace(decls, context->getCompilationPackage(),
- &transformedReference);
+ Reference transformedReference = *reference;
+ transformReferenceFromNamespace(decls, context->getCompilationPackage(),
+ &transformedReference);
- std::string errStr;
- const SymbolTable::Symbol* s = resolveSymbolCheckVisibility(
- transformedReference, context->getNameMangler(), symbols, callSite, &errStr);
- if (s) {
- // The ID may not exist. This is fine because of the possibility of building against
- // libraries without assigned IDs.
- // Ex: Linking against own resources when building a static library.
- reference->id = s->id;
- return true;
- }
+ std::string errStr;
+ const SymbolTable::Symbol* s = resolveSymbolCheckVisibility(
+ transformedReference, context->getNameMangler(), symbols, callSite,
+ &errStr);
+ if (s) {
+ // The ID may not exist. This is fine because of the possibility of building
+ // against
+ // libraries without assigned IDs.
+ // Ex: Linking against own resources when building a static library.
+ reference->id = s->id;
+ return true;
+ }
- DiagMessage errorMsg(reference->getSource());
- errorMsg << "resource ";
- writeResourceName(&errorMsg, *reference, transformedReference);
- errorMsg << " " << errStr;
- context->getDiagnostics()->error(errorMsg);
- return false;
+ DiagMessage errorMsg(reference->getSource());
+ errorMsg << "resource ";
+ writeResourceName(&errorMsg, *reference, transformedReference);
+ errorMsg << " " << errStr;
+ context->getDiagnostics()->error(errorMsg);
+ return false;
}
namespace {
struct EmptyDeclStack : public xml::IPackageDeclStack {
- Maybe<xml::ExtractedPackage> transformPackageAlias(
- const StringPiece& alias, const StringPiece& localPackage) const override {
- if (alias.empty()) {
- return xml::ExtractedPackage{ localPackage.toString(), true /* private */ };
- }
- return {};
+ Maybe<xml::ExtractedPackage> transformPackageAlias(
+ const StringPiece& alias,
+ const StringPiece& localPackage) const override {
+ if (alias.empty()) {
+ return xml::ExtractedPackage{localPackage.toString(), true /* private */};
}
+ return {};
+ }
};
-} // namespace
+} // namespace
bool ReferenceLinker::consume(IAaptContext* context, ResourceTable* table) {
- EmptyDeclStack declStack;
- bool error = false;
- for (auto& package : table->packages) {
- for (auto& type : package->types) {
- for (auto& entry : type->entries) {
- // Symbol state information may be lost if there is no value for the resource.
- if (entry->symbolStatus.state != SymbolState::kUndefined && entry->values.empty()) {
- context->getDiagnostics()->error(
- DiagMessage(entry->symbolStatus.source)
- << "no definition for declared symbol '"
- << ResourceNameRef(package->name, type->type, entry->name)
- << "'");
- error = true;
- }
-
- CallSite callSite = { ResourceNameRef(package->name, type->type, entry->name) };
- ReferenceLinkerVisitor visitor(context, context->getExternalSymbols(),
- &table->stringPool, &declStack, &callSite);
-
- for (auto& configValue : entry->values) {
- configValue->value->accept(&visitor);
- }
-
- if (visitor.hasError()) {
- error = true;
- }
- }
+ EmptyDeclStack declStack;
+ bool error = false;
+ for (auto& package : table->packages) {
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
+ // Symbol state information may be lost if there is no value for the
+ // resource.
+ if (entry->symbolStatus.state != SymbolState::kUndefined &&
+ entry->values.empty()) {
+ context->getDiagnostics()->error(
+ DiagMessage(entry->symbolStatus.source)
+ << "no definition for declared symbol '"
+ << ResourceNameRef(package->name, type->type, entry->name)
+ << "'");
+ error = true;
}
+
+ CallSite callSite = {
+ ResourceNameRef(package->name, type->type, entry->name)};
+ ReferenceLinkerVisitor visitor(context, context->getExternalSymbols(),
+ &table->stringPool, &declStack,
+ &callSite);
+
+ for (auto& configValue : entry->values) {
+ configValue->value->accept(&visitor);
+ }
+
+ if (visitor.hasError()) {
+ error = true;
+ }
+ }
}
- return !error;
+ }
+ return !error;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index 7993aaf..8f6604f 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -30,77 +30,86 @@
namespace aapt {
/**
- * Resolves all references to resources in the ResourceTable and assigns them IDs.
+ * Resolves all references to resources in the ResourceTable and assigns them
+ * IDs.
* The ResourceTable must already have IDs assigned to each resource.
- * Once the ResourceTable is processed by this linker, it is ready to be flattened.
+ * Once the ResourceTable is processed by this linker, it is ready to be
+ * flattened.
*/
struct ReferenceLinker : public IResourceTableConsumer {
- /**
- * Returns true if the symbol is visible by the reference and from the callsite.
- */
- static bool isSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
- const CallSite& callSite);
+ /**
+ * Returns true if the symbol is visible by the reference and from the
+ * callsite.
+ */
+ static bool isSymbolVisible(const SymbolTable::Symbol& symbol,
+ const Reference& ref, const CallSite& callSite);
- /**
- * Performs name mangling and looks up the resource in the symbol table. Returns nullptr
- * if the symbol was not found.
- */
- static const SymbolTable::Symbol* resolveSymbol(const Reference& reference,
- NameMangler* mangler, SymbolTable* symbols);
+ /**
+ * Performs name mangling and looks up the resource in the symbol table.
+ * Returns nullptr
+ * if the symbol was not found.
+ */
+ static const SymbolTable::Symbol* resolveSymbol(const Reference& reference,
+ NameMangler* mangler,
+ SymbolTable* symbols);
- /**
- * Performs name mangling and looks up the resource in the symbol table. If the symbol is
- * not visible by the reference at the callsite, nullptr is returned. outError holds
- * the error message.
- */
- static const SymbolTable::Symbol* resolveSymbolCheckVisibility(const Reference& reference,
- NameMangler* nameMangler,
- SymbolTable* symbols,
- CallSite* callSite,
- std::string* outError);
+ /**
+ * Performs name mangling and looks up the resource in the symbol table. If
+ * the symbol is
+ * not visible by the reference at the callsite, nullptr is returned. outError
+ * holds
+ * the error message.
+ */
+ static const SymbolTable::Symbol* resolveSymbolCheckVisibility(
+ const Reference& reference, NameMangler* nameMangler,
+ SymbolTable* symbols, CallSite* callSite, std::string* outError);
- /**
- * Same as resolveSymbolCheckVisibility(), but also makes sure the symbol is an attribute.
- * That is, the return value will have a non-null value for ISymbolTable::Symbol::attribute.
- */
- static const SymbolTable::Symbol* resolveAttributeCheckVisibility(const Reference& reference,
- NameMangler* nameMangler,
- SymbolTable* symbols,
- CallSite* callSite,
- std::string* outError);
+ /**
+ * Same as resolveSymbolCheckVisibility(), but also makes sure the symbol is
+ * an attribute.
+ * That is, the return value will have a non-null value for
+ * ISymbolTable::Symbol::attribute.
+ */
+ static const SymbolTable::Symbol* resolveAttributeCheckVisibility(
+ const Reference& reference, NameMangler* nameMangler,
+ SymbolTable* symbols, CallSite* callSite, std::string* outError);
- /**
- * Resolves the attribute reference and returns an xml::AaptAttribute if successful.
- * If resolution fails, outError holds the error message.
- */
- static Maybe<xml::AaptAttribute> compileXmlAttribute(const Reference& reference,
- NameMangler* nameMangler,
- SymbolTable* symbols,
- CallSite* callSite,
- std::string* outError);
+ /**
+ * Resolves the attribute reference and returns an xml::AaptAttribute if
+ * successful.
+ * If resolution fails, outError holds the error message.
+ */
+ static Maybe<xml::AaptAttribute> compileXmlAttribute(
+ const Reference& reference, NameMangler* nameMangler,
+ SymbolTable* symbols, CallSite* callSite, std::string* outError);
- /**
- * Writes the resource name to the DiagMessage, using the "orig_name (aka <transformed_name>)"
- * syntax.
- */
- static void writeResourceName(DiagMessage* outMsg, const Reference& orig,
- const Reference& transformed);
+ /**
+ * Writes the resource name to the DiagMessage, using the "orig_name (aka
+ * <transformed_name>)"
+ * syntax.
+ */
+ static void writeResourceName(DiagMessage* outMsg, const Reference& orig,
+ const Reference& transformed);
- /**
- * Transforms the package name of the reference to the fully qualified package name using
- * the xml::IPackageDeclStack, then mangles and looks up the symbol. If the symbol is visible
- * to the reference at the callsite, the reference is updated with an ID.
- * Returns false on failure, and an error message is logged to the IDiagnostics in the context.
- */
- static bool linkReference(Reference* reference, IAaptContext* context, SymbolTable* symbols,
- xml::IPackageDeclStack* decls, CallSite* callSite);
+ /**
+ * Transforms the package name of the reference to the fully qualified package
+ * name using
+ * the xml::IPackageDeclStack, then mangles and looks up the symbol. If the
+ * symbol is visible
+ * to the reference at the callsite, the reference is updated with an ID.
+ * Returns false on failure, and an error message is logged to the
+ * IDiagnostics in the context.
+ */
+ static bool linkReference(Reference* reference, IAaptContext* context,
+ SymbolTable* symbols, xml::IPackageDeclStack* decls,
+ CallSite* callSite);
- /**
- * Links all references in the ResourceTable.
- */
- bool consume(IAaptContext* context, ResourceTable* table) override;
+ /**
+ * Links all references in the ResourceTable.
+ */
+ bool consume(IAaptContext* context, ResourceTable* table) override;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_LINKER_REFERENCELINKER_H */
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index 5c1511f..8aa3616 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -22,206 +22,238 @@
namespace aapt {
TEST(ReferenceLinkerTest, LinkSimpleReferences) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("com.app.test", 0x7f)
- .addReference("com.app.test:string/foo", ResourceId(0x7f020000),
- "com.app.test:string/bar")
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .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("com.app.test:string/bar", ResourceId(0x7f020001), "string/baz")
+ // Test use of local reference (w/o package name).
+ .addReference("com.app.test:string/bar", ResourceId(0x7f020001),
+ "string/baz")
- .addReference("com.app.test:string/baz", ResourceId(0x7f020002),
- "android:string/ok")
- .build();
+ .addReference("com.app.test:string/baz", ResourceId(0x7f020002),
+ "android:string/ok")
+ .build();
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .setCompilationPackage("com.app.test")
- .setPackageId(0x7f)
- .setNameManglerPolicy(NameManglerPolicy{ "com.app.test" })
- .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
- .addSymbolSource(test::StaticSymbolSourceBuilder()
- .addPublicSymbol("android:string/ok", ResourceId(0x01040034))
- .build())
- .build();
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .setCompilationPackage("com.app.test")
+ .setPackageId(0x7f)
+ .setNameManglerPolicy(NameManglerPolicy{"com.app.test"})
+ .addSymbolSource(
+ util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .addSymbolSource(
+ test::StaticSymbolSourceBuilder()
+ .addPublicSymbol("android:string/ok", ResourceId(0x01040034))
+ .build())
+ .build();
- ReferenceLinker linker;
- ASSERT_TRUE(linker.consume(context.get(), table.get()));
+ ReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(context.get(), table.get()));
- 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));
+ 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(), "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(), "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(), "com.app.test:string/baz");
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x01040034));
+ 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));
}
TEST(ReferenceLinkerTest, LinkStyleAttributes) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .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();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .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(), "com.app.test:style/Theme");
- ASSERT_NE(style, nullptr);
- style->entries.back().value = util::make_unique<RawString>(
- table->stringPool.makeRef("one|two"));
- }
-
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .setCompilationPackage("com.app.test")
- .setPackageId(0x7f)
- .setNameManglerPolicy(NameManglerPolicy{ "com.app.test" })
- .addSymbolSource(test::StaticSymbolSourceBuilder()
- .addPublicSymbol("android:style/Theme.Material",
- ResourceId(0x01060000))
- .addPublicSymbol("android:attr/foo", ResourceId(0x01010001),
- test::AttributeBuilder()
- .setTypeMask(ResTable_map::TYPE_COLOR)
- .build())
- .addPublicSymbol("android:attr/bar", ResourceId(0x01010002),
- test::AttributeBuilder()
- .setTypeMask(ResTable_map::TYPE_FLAGS)
- .addItem("one", 0x01)
- .addItem("two", 0x02)
- .build())
- .build())
- .build();
-
- ReferenceLinker linker;
- ASSERT_TRUE(linker.consume(context.get(), table.get()));
-
- Style* style = test::getValue<Style>(table.get(), "com.app.test:style/Theme");
+ {
+ // 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(), "com.app.test:style/Theme");
ASSERT_NE(style, nullptr);
- AAPT_ASSERT_TRUE(style->parent);
- AAPT_ASSERT_TRUE(style->parent.value().id);
- EXPECT_EQ(style->parent.value().id.value(), ResourceId(0x01060000));
+ style->entries.back().value =
+ util::make_unique<RawString>(table->stringPool.makeRef("one|two"));
+ }
- ASSERT_EQ(2u, style->entries.size());
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .setCompilationPackage("com.app.test")
+ .setPackageId(0x7f)
+ .setNameManglerPolicy(NameManglerPolicy{"com.app.test"})
+ .addSymbolSource(
+ test::StaticSymbolSourceBuilder()
+ .addPublicSymbol("android:style/Theme.Material",
+ ResourceId(0x01060000))
+ .addPublicSymbol("android:attr/foo", ResourceId(0x01010001),
+ test::AttributeBuilder()
+ .setTypeMask(ResTable_map::TYPE_COLOR)
+ .build())
+ .addPublicSymbol("android:attr/bar", ResourceId(0x01010002),
+ test::AttributeBuilder()
+ .setTypeMask(ResTable_map::TYPE_FLAGS)
+ .addItem("one", 0x01)
+ .addItem("two", 0x02)
+ .build())
+ .build())
+ .build();
- AAPT_ASSERT_TRUE(style->entries[0].key.id);
- EXPECT_EQ(style->entries[0].key.id.value(), ResourceId(0x01010001));
- ASSERT_NE(valueCast<BinaryPrimitive>(style->entries[0].value.get()), nullptr);
+ ReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(context.get(), table.get()));
- AAPT_ASSERT_TRUE(style->entries[1].key.id);
- EXPECT_EQ(style->entries[1].key.id.value(), ResourceId(0x01010002));
- ASSERT_NE(valueCast<BinaryPrimitive>(style->entries[1].value.get()), nullptr);
+ 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);
+ EXPECT_EQ(style->parent.value().id.value(), ResourceId(0x01060000));
+
+ ASSERT_EQ(2u, style->entries.size());
+
+ AAPT_ASSERT_TRUE(style->entries[0].key.id);
+ EXPECT_EQ(style->entries[0].key.id.value(), ResourceId(0x01010001));
+ ASSERT_NE(valueCast<BinaryPrimitive>(style->entries[0].value.get()), nullptr);
+
+ AAPT_ASSERT_TRUE(style->entries[1].key.id);
+ EXPECT_EQ(style->entries[1].key.id.value(), ResourceId(0x01010002));
+ ASSERT_NE(valueCast<BinaryPrimitive>(style->entries[1].value.get()), nullptr);
}
TEST(ReferenceLinkerTest, LinkMangledReferencesAndAttributes) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder()
- .setCompilationPackage("com.app.test")
- .setPackageId(0x7f)
- .setNameManglerPolicy(NameManglerPolicy{ "com.app.test", { "com.android.support" } })
- .addSymbolSource(test::StaticSymbolSourceBuilder()
- .addPublicSymbol("com.app.test:attr/com.android.support$foo",
- ResourceId(0x7f010000),
- test::AttributeBuilder()
- .setTypeMask(ResTable_map::TYPE_COLOR)
- .build())
- .build())
- .build();
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .setCompilationPackage("com.app.test")
+ .setPackageId(0x7f)
+ .setNameManglerPolicy(
+ NameManglerPolicy{"com.app.test", {"com.android.support"}})
+ .addSymbolSource(
+ test::StaticSymbolSourceBuilder()
+ .addPublicSymbol("com.app.test:attr/com.android.support$foo",
+ ResourceId(0x7f010000),
+ test::AttributeBuilder()
+ .setTypeMask(ResTable_map::TYPE_COLOR)
+ .build())
+ .build())
+ .build();
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .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();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .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()));
+ ReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(context.get(), table.get()));
- 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);
- EXPECT_EQ(style->entries.front().key.id.value(), ResourceId(0x7f010000));
+ 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);
+ EXPECT_EQ(style->entries.front().key.id.value(), ResourceId(0x7f010000));
}
TEST(ReferenceLinkerTest, FailToLinkPrivateSymbols) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("com.app.test", 0x7f)
- .addReference("com.app.test:string/foo", ResourceId(0x7f020000),
- "android:string/hidden")
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .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("com.app.test")
- .setPackageId(0x7f)
- .setNameManglerPolicy(NameManglerPolicy{ "com.app.test" })
- .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
- .addSymbolSource(test::StaticSymbolSourceBuilder()
- .addSymbol("android:string/hidden", ResourceId(0x01040034))
- .build())
- .build();
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .setCompilationPackage("com.app.test")
+ .setPackageId(0x7f)
+ .setNameManglerPolicy(NameManglerPolicy{"com.app.test"})
+ .addSymbolSource(
+ util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .addSymbolSource(
+ test::StaticSymbolSourceBuilder()
+ .addSymbol("android:string/hidden", ResourceId(0x01040034))
+ .build())
+ .build();
- ReferenceLinker linker;
- ASSERT_FALSE(linker.consume(context.get(), table.get()));
+ ReferenceLinker linker;
+ ASSERT_FALSE(linker.consume(context.get(), table.get()));
}
TEST(ReferenceLinkerTest, FailToLinkPrivateMangledSymbols) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .setPackageId("com.app.test", 0x7f)
- .addReference("com.app.test:string/foo", ResourceId(0x7f020000),
- "com.app.lib:string/hidden")
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .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("com.app.test")
- .setPackageId(0x7f)
- .setNameManglerPolicy(NameManglerPolicy{ "com.app.test", { "com.app.lib" } })
- .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
- .addSymbolSource(test::StaticSymbolSourceBuilder()
- .addSymbol("com.app.test:string/com.app.lib$hidden",
- ResourceId(0x7f040034))
- .build())
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .setCompilationPackage("com.app.test")
+ .setPackageId(0x7f)
+ .setNameManglerPolicy(
+ NameManglerPolicy{"com.app.test", {"com.app.lib"}})
+ .addSymbolSource(
+ util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .addSymbolSource(
+ test::StaticSymbolSourceBuilder()
+ .addSymbol("com.app.test:string/com.app.lib$hidden",
+ ResourceId(0x7f040034))
+ .build())
- .build();
+ .build();
- ReferenceLinker linker;
- ASSERT_FALSE(linker.consume(context.get(), table.get()));
+ ReferenceLinker linker;
+ ASSERT_FALSE(linker.consume(context.get(), table.get()));
}
TEST(ReferenceLinkerTest, FailToLinkPrivateStyleAttributes) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .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<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .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("com.app.test")
- .setPackageId(0x7f)
- .setNameManglerPolicy(NameManglerPolicy{ "com.app.test" })
- .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
- .addSymbolSource(test::StaticSymbolSourceBuilder()
- .addSymbol("android:attr/hidden", ResourceId(0x01010001),
- test::AttributeBuilder()
- .setTypeMask(
- android::ResTable_map::TYPE_COLOR)
- .build())
- .build())
- .build();
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .setCompilationPackage("com.app.test")
+ .setPackageId(0x7f)
+ .setNameManglerPolicy(NameManglerPolicy{"com.app.test"})
+ .addSymbolSource(
+ util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .addSymbolSource(
+ test::StaticSymbolSourceBuilder()
+ .addSymbol("android:attr/hidden", ResourceId(0x01010001),
+ test::AttributeBuilder()
+ .setTypeMask(android::ResTable_map::TYPE_COLOR)
+ .build())
+ .build())
+ .build();
- ReferenceLinker linker;
- ASSERT_FALSE(linker.consume(context.get(), table.get()));
+ ReferenceLinker linker;
+ ASSERT_FALSE(linker.consume(context.get(), table.get()));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/ResourceDeduper.cpp b/tools/aapt2/link/ResourceDeduper.cpp
index 0276261..f565359 100644
--- a/tools/aapt2/link/ResourceDeduper.cpp
+++ b/tools/aapt2/link/ResourceDeduper.cpp
@@ -36,79 +36,81 @@
* an equivalent entry value.
*/
class DominatedKeyValueRemover : public DominatorTree::BottomUpVisitor {
-public:
- using Node = DominatorTree::Node;
+ public:
+ using Node = DominatorTree::Node;
- explicit DominatedKeyValueRemover(IAaptContext* context, ResourceEntry* entry) :
- mContext(context), mEntry(entry) {
+ explicit DominatedKeyValueRemover(IAaptContext* context, ResourceEntry* entry)
+ : mContext(context), mEntry(entry) {}
+
+ void visitConfig(Node* node) {
+ Node* parent = node->parent();
+ if (!parent) {
+ return;
+ }
+ ResourceConfigValue* nodeValue = node->value();
+ ResourceConfigValue* parentValue = parent->value();
+ if (!nodeValue || !parentValue) {
+ return;
+ }
+ if (!nodeValue->value->equals(parentValue->value.get())) {
+ return;
}
- void visitConfig(Node* node) {
- Node* parent = node->parent();
- if (!parent) {
- return;
- }
- ResourceConfigValue* nodeValue = node->value();
- ResourceConfigValue* parentValue = parent->value();
- if (!nodeValue || !parentValue) {
- return;
- }
- if (!nodeValue->value->equals(parentValue->value.get())) {
- return;
- }
-
- // Compare compatible configs for this entry and ensure the values are
- // equivalent.
- const ConfigDescription& nodeConfiguration = nodeValue->config;
- for (const auto& sibling : mEntry->values) {
- if (!sibling->value) {
- // Sibling was already removed.
- continue;
- }
- if (nodeConfiguration.isCompatibleWith(sibling->config)
- && !nodeValue->value->equals(sibling->value.get())) {
- // The configurations are compatible, but the value is
- // different, so we can't remove this value.
- return;
- }
- }
- if (mContext->verbose()) {
- mContext->getDiagnostics()->note(
- DiagMessage(nodeValue->value->getSource())
- << "removing dominated duplicate resource with name \""
- << mEntry->name << "\"");
- }
- nodeValue->value = {};
+ // Compare compatible configs for this entry and ensure the values are
+ // equivalent.
+ const ConfigDescription& nodeConfiguration = nodeValue->config;
+ for (const auto& sibling : mEntry->values) {
+ if (!sibling->value) {
+ // Sibling was already removed.
+ continue;
+ }
+ if (nodeConfiguration.isCompatibleWith(sibling->config) &&
+ !nodeValue->value->equals(sibling->value.get())) {
+ // The configurations are compatible, but the value is
+ // different, so we can't remove this value.
+ return;
+ }
}
+ if (mContext->verbose()) {
+ mContext->getDiagnostics()->note(
+ DiagMessage(nodeValue->value->getSource())
+ << "removing dominated duplicate resource with name \""
+ << mEntry->name << "\"");
+ }
+ nodeValue->value = {};
+ }
-private:
- IAaptContext* mContext;
- ResourceEntry* mEntry;
+ private:
+ IAaptContext* mContext;
+ ResourceEntry* mEntry;
};
static void dedupeEntry(IAaptContext* context, ResourceEntry* entry) {
- DominatorTree tree(entry->values);
- DominatedKeyValueRemover remover(context, entry);
- tree.accept(&remover);
+ DominatorTree tree(entry->values);
+ DominatedKeyValueRemover remover(context, entry);
+ tree.accept(&remover);
- // Erase the values that were removed.
- entry->values.erase(std::remove_if(entry->values.begin(), entry->values.end(),
- [](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
- return val == nullptr || val->value == nullptr;
- }), entry->values.end());
+ // Erase the values that were removed.
+ entry->values.erase(
+ std::remove_if(
+ entry->values.begin(), entry->values.end(),
+ [](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
+ return val == nullptr || val->value == nullptr;
+ }),
+ entry->values.end());
}
-} // namespace
+} // namespace
bool ResourceDeduper::consume(IAaptContext* context, ResourceTable* table) {
- for (auto& package : table->packages) {
- for (auto& type : package->types) {
- for (auto& entry : type->entries) {
- dedupeEntry(context, entry.get());
- }
- }
+ for (auto& package : table->packages) {
+ for (auto& type : package->types) {
+ for (auto& entry : type->entries) {
+ dedupeEntry(context, entry.get());
+ }
}
- return true;
+ }
+ return true;
}
-} // aapt
+} // aapt
diff --git a/tools/aapt2/link/ResourceDeduper_test.cpp b/tools/aapt2/link/ResourceDeduper_test.cpp
index 47071a51..7e2d476 100644
--- a/tools/aapt2/link/ResourceDeduper_test.cpp
+++ b/tools/aapt2/link/ResourceDeduper_test.cpp
@@ -21,63 +21,63 @@
namespace aapt {
TEST(ResourceDeduperTest, SameValuesAreDeduped) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- const ConfigDescription defaultConfig = {};
- const ConfigDescription enConfig = test::parseConfigOrDie("en");
- const ConfigDescription enV21Config = test::parseConfigOrDie("en-v21");
- // Chosen because this configuration is compatible with en.
- const ConfigDescription landConfig = test::parseConfigOrDie("land");
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ const ConfigDescription defaultConfig = {};
+ const ConfigDescription enConfig = test::parseConfigOrDie("en");
+ const ConfigDescription enV21Config = test::parseConfigOrDie("en-v21");
+ // Chosen because this configuration is compatible with en.
+ const ConfigDescription landConfig = test::parseConfigOrDie("land");
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addString("android:string/dedupe", ResourceId{}, defaultConfig, "dedupe")
- .addString("android:string/dedupe", ResourceId{}, enConfig, "dedupe")
- .addString("android:string/dedupe", ResourceId{}, landConfig, "dedupe")
- .addString("android:string/dedupe2", ResourceId{}, defaultConfig, "dedupe")
- .addString("android:string/dedupe2", ResourceId{}, enConfig, "dedupe")
- .addString("android:string/dedupe2", ResourceId{}, enV21Config, "keep")
- .addString("android:string/dedupe2", ResourceId{}, landConfig, "dedupe")
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .addString("android:string/dedupe", ResourceId{}, defaultConfig,
+ "dedupe")
+ .addString("android:string/dedupe", ResourceId{}, enConfig, "dedupe")
+ .addString("android:string/dedupe", ResourceId{}, landConfig,
+ "dedupe")
+ .addString("android:string/dedupe2", ResourceId{}, defaultConfig,
+ "dedupe")
+ .addString("android:string/dedupe2", ResourceId{}, enConfig, "dedupe")
+ .addString("android:string/dedupe2", ResourceId{}, enV21Config,
+ "keep")
+ .addString("android:string/dedupe2", ResourceId{}, landConfig,
+ "dedupe")
+ .build();
- ASSERT_TRUE(ResourceDeduper().consume(context.get(), table.get()));
- EXPECT_EQ(
- nullptr,
- test::getValueForConfig<String>(table.get(), "android:string/dedupe", enConfig));
- EXPECT_EQ(
- nullptr,
- test::getValueForConfig<String>(table.get(), "android:string/dedupe", landConfig));
- EXPECT_EQ(
- nullptr,
- test::getValueForConfig<String>(table.get(), "android:string/dedupe2", enConfig));
- EXPECT_NE(
- nullptr,
- test::getValueForConfig<String>(table.get(), "android:string/dedupe2", enV21Config));
+ ASSERT_TRUE(ResourceDeduper().consume(context.get(), table.get()));
+ EXPECT_EQ(nullptr, test::getValueForConfig<String>(
+ table.get(), "android:string/dedupe", enConfig));
+ EXPECT_EQ(nullptr, test::getValueForConfig<String>(
+ table.get(), "android:string/dedupe", landConfig));
+ EXPECT_EQ(nullptr, test::getValueForConfig<String>(
+ table.get(), "android:string/dedupe2", enConfig));
+ EXPECT_NE(nullptr, test::getValueForConfig<String>(
+ table.get(), "android:string/dedupe2", enV21Config));
}
TEST(ResourceDeduperTest, DifferentValuesAreKept) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
- const ConfigDescription defaultConfig = {};
- const ConfigDescription enConfig = test::parseConfigOrDie("en");
- const ConfigDescription enV21Config = test::parseConfigOrDie("en-v21");
- // Chosen because this configuration is compatible with en.
- const ConfigDescription landConfig = test::parseConfigOrDie("land");
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+ const ConfigDescription defaultConfig = {};
+ const ConfigDescription enConfig = test::parseConfigOrDie("en");
+ const ConfigDescription enV21Config = test::parseConfigOrDie("en-v21");
+ // Chosen because this configuration is compatible with en.
+ const ConfigDescription landConfig = test::parseConfigOrDie("land");
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addString("android:string/keep", ResourceId{}, defaultConfig, "keep")
- .addString("android:string/keep", ResourceId{}, enConfig, "keep")
- .addString("android:string/keep", ResourceId{}, enV21Config, "keep2")
- .addString("android:string/keep", ResourceId{}, landConfig, "keep2")
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .addString("android:string/keep", ResourceId{}, defaultConfig, "keep")
+ .addString("android:string/keep", ResourceId{}, enConfig, "keep")
+ .addString("android:string/keep", ResourceId{}, enV21Config, "keep2")
+ .addString("android:string/keep", ResourceId{}, landConfig, "keep2")
+ .build();
- ASSERT_TRUE(ResourceDeduper().consume(context.get(), table.get()));
- EXPECT_NE(
- nullptr,
- test::getValueForConfig<String>(table.get(), "android:string/keep", enConfig));
- EXPECT_NE(
- nullptr,
- test::getValueForConfig<String>(table.get(), "android:string/keep", enV21Config));
- EXPECT_NE(
- nullptr,
- test::getValueForConfig<String>(table.get(), "android:string/keep", landConfig));
+ ASSERT_TRUE(ResourceDeduper().consume(context.get(), table.get()));
+ EXPECT_NE(nullptr, test::getValueForConfig<String>(
+ table.get(), "android:string/keep", enConfig));
+ EXPECT_NE(nullptr, test::getValueForConfig<String>(
+ table.get(), "android:string/keep", enV21Config));
+ EXPECT_NE(nullptr, test::getValueForConfig<String>(
+ table.get(), "android:string/keep", landConfig));
}
} // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index eea4306..adf83a4 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -14,11 +14,11 @@
* limitations under the License.
*/
+#include "link/TableMerger.h"
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
#include "ValueVisitor.h"
-#include "link/TableMerger.h"
#include "util/Util.h"
#include <cassert>
@@ -26,348 +26,365 @@
namespace aapt {
TableMerger::TableMerger(IAaptContext* context, ResourceTable* outTable,
- const TableMergerOptions& options) :
- mContext(context), mMasterTable(outTable), mOptions(options) {
- // Create the desired package that all tables will be merged into.
- mMasterPackage = mMasterTable->createPackage(
- mContext->getCompilationPackage(), mContext->getPackageId());
- assert(mMasterPackage && "package name or ID already taken");
+ const TableMergerOptions& options)
+ : mContext(context), mMasterTable(outTable), mOptions(options) {
+ // Create the desired package that all tables will be merged into.
+ mMasterPackage = mMasterTable->createPackage(
+ mContext->getCompilationPackage(), mContext->getPackageId());
+ assert(mMasterPackage && "package name or ID already taken");
}
bool TableMerger::merge(const Source& src, ResourceTable* table,
io::IFileCollection* collection) {
- return mergeImpl(src, table, collection, false /* overlay */, true /* allow new */);
+ return mergeImpl(src, table, collection, false /* overlay */,
+ true /* allow new */);
}
bool TableMerger::mergeOverlay(const Source& src, ResourceTable* table,
io::IFileCollection* collection) {
- return mergeImpl(src, table, collection, true /* overlay */, mOptions.autoAddOverlay);
+ return mergeImpl(src, table, collection, true /* overlay */,
+ mOptions.autoAddOverlay);
}
/**
* This will merge packages with the same package name (or no package name).
*/
bool TableMerger::mergeImpl(const Source& src, ResourceTable* table,
- io::IFileCollection* collection,
- bool overlay, bool allowNew) {
- const uint8_t desiredPackageId = mContext->getPackageId();
+ io::IFileCollection* collection, bool overlay,
+ bool allowNew) {
+ const uint8_t desiredPackageId = mContext->getPackageId();
- bool error = false;
- for (auto& package : table->packages) {
- // Warn of packages with an unrelated ID.
- const Maybe<ResourceId>& id = package->id;
- if (id && id.value() != 0x0 && id.value() != desiredPackageId) {
- mContext->getDiagnostics()->warn(DiagMessage(src)
- << "ignoring package " << package->name);
- continue;
- }
-
- // Only merge an empty package or the package we're building.
- // Other packages may exist, which likely contain attribute definitions.
- // This is because at compile time it is unknown if the attributes are simply
- // uses of the attribute or definitions.
- if (package->name.empty() || mContext->getCompilationPackage() == package->name) {
- FileMergeCallback callback;
- if (collection) {
- 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(*oldFile->path);
- if (!f) {
- mContext->getDiagnostics()->error(DiagMessage(src) << "file '"
- << *oldFile->path
- << "' not found");
- return false;
- }
-
- newFile->file = f;
- return true;
- };
- }
-
- // Merge here. Once the entries are merged and mangled, any references to
- // them are still valid. This is because un-mangled references are
- // mangled, then looked up at resolution time.
- // Also, when linking, we convert references with no package name to use
- // the compilation package name.
- error |= !doMerge(src, table, package.get(), false /* mangle */, overlay, allowNew,
- callback);
- }
+ bool error = false;
+ for (auto& package : table->packages) {
+ // Warn of packages with an unrelated ID.
+ const Maybe<ResourceId>& id = package->id;
+ if (id && id.value() != 0x0 && id.value() != desiredPackageId) {
+ mContext->getDiagnostics()->warn(DiagMessage(src) << "ignoring package "
+ << package->name);
+ continue;
}
- return !error;
+
+ // Only merge an empty package or the package we're building.
+ // Other packages may exist, which likely contain attribute definitions.
+ // This is because at compile time it is unknown if the attributes are
+ // simply
+ // uses of the attribute or definitions.
+ if (package->name.empty() ||
+ mContext->getCompilationPackage() == package->name) {
+ FileMergeCallback callback;
+ if (collection) {
+ 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(*oldFile->path);
+ if (!f) {
+ mContext->getDiagnostics()->error(DiagMessage(src)
+ << "file '" << *oldFile->path
+ << "' not found");
+ return false;
+ }
+
+ newFile->file = f;
+ return true;
+ };
+ }
+
+ // Merge here. Once the entries are merged and mangled, any references to
+ // them are still valid. This is because un-mangled references are
+ // mangled, then looked up at resolution time.
+ // Also, when linking, we convert references with no package name to use
+ // the compilation package name.
+ error |= !doMerge(src, table, package.get(), false /* mangle */, overlay,
+ allowNew, callback);
+ }
+ }
+ return !error;
}
/**
* This will merge and mangle resources from a static library.
*/
-bool TableMerger::mergeAndMangle(const Source& src, const StringPiece& packageName,
- ResourceTable* table, io::IFileCollection* collection) {
- bool error = false;
- for (auto& package : table->packages) {
- // Warn of packages with an unrelated ID.
- if (packageName != package->name) {
- mContext->getDiagnostics()->warn(DiagMessage(src)
- << "ignoring package " << package->name);
- continue;
- }
-
- bool mangle = packageName != mContext->getCompilationPackage();
- mMergedPackages.insert(package->name);
-
- 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(*oldFile->path);
- if (!f) {
- mContext->getDiagnostics()->error(DiagMessage(src) << "file '" << *oldFile->path
- << "' not found");
- return false;
- }
-
- newFile->file = f;
- return true;
- };
-
- error |= !doMerge(src, table, package.get(),
- mangle, false /* overlay */, true /* allow new */, callback);
+bool TableMerger::mergeAndMangle(const Source& src,
+ const StringPiece& packageName,
+ ResourceTable* table,
+ io::IFileCollection* collection) {
+ bool error = false;
+ for (auto& package : table->packages) {
+ // Warn of packages with an unrelated ID.
+ if (packageName != package->name) {
+ mContext->getDiagnostics()->warn(DiagMessage(src) << "ignoring package "
+ << package->name);
+ continue;
}
- return !error;
+
+ bool mangle = packageName != mContext->getCompilationPackage();
+ mMergedPackages.insert(package->name);
+
+ 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(*oldFile->path);
+ if (!f) {
+ mContext->getDiagnostics()->error(
+ DiagMessage(src) << "file '" << *oldFile->path << "' not found");
+ return false;
+ }
+
+ newFile->file = f;
+ return true;
+ };
+
+ error |= !doMerge(src, table, package.get(), mangle, false /* overlay */,
+ true /* allow new */, callback);
+ }
+ return !error;
}
-static bool mergeType(IAaptContext* context, const Source& src, ResourceTableType* dstType,
- ResourceTableType* srcType) {
- if (dstType->symbolStatus.state < srcType->symbolStatus.state) {
- // The incoming type's visibility is stronger, so we should override
- // the visibility.
- if (srcType->symbolStatus.state == SymbolState::kPublic) {
- // Only copy the ID if the source is public, or else the ID is meaningless.
- dstType->id = srcType->id;
- }
- dstType->symbolStatus = std::move(srcType->symbolStatus);
- } else if (dstType->symbolStatus.state == SymbolState::kPublic
- && srcType->symbolStatus.state == SymbolState::kPublic
- && dstType->id && srcType->id
- && dstType->id.value() != srcType->id.value()) {
- // Both types are public and have different IDs.
- context->getDiagnostics()->error(DiagMessage(src)
- << "cannot merge type '" << srcType->type
- << "': conflicting public IDs");
- return false;
+static bool mergeType(IAaptContext* context, const Source& src,
+ ResourceTableType* dstType, ResourceTableType* srcType) {
+ if (dstType->symbolStatus.state < srcType->symbolStatus.state) {
+ // The incoming type's visibility is stronger, so we should override
+ // the visibility.
+ if (srcType->symbolStatus.state == SymbolState::kPublic) {
+ // Only copy the ID if the source is public, or else the ID is
+ // meaningless.
+ dstType->id = srcType->id;
}
- return true;
+ dstType->symbolStatus = std::move(srcType->symbolStatus);
+ } else if (dstType->symbolStatus.state == SymbolState::kPublic &&
+ srcType->symbolStatus.state == SymbolState::kPublic &&
+ dstType->id && srcType->id &&
+ dstType->id.value() != srcType->id.value()) {
+ // Both types are public and have different IDs.
+ context->getDiagnostics()->error(DiagMessage(src)
+ << "cannot merge type '" << srcType->type
+ << "': conflicting public IDs");
+ return false;
+ }
+ return true;
}
-static bool mergeEntry(IAaptContext* context, const Source& src, ResourceEntry* dstEntry,
- ResourceEntry* srcEntry) {
- if (dstEntry->symbolStatus.state < srcEntry->symbolStatus.state) {
- // The incoming type's visibility is stronger, so we should override
- // the visibility.
- if (srcEntry->symbolStatus.state == SymbolState::kPublic) {
- // Only copy the ID if the source is public, or else the ID is meaningless.
- dstEntry->id = srcEntry->id;
- }
- dstEntry->symbolStatus = std::move(srcEntry->symbolStatus);
- } else if (srcEntry->symbolStatus.state == SymbolState::kPublic
- && dstEntry->symbolStatus.state == SymbolState::kPublic
- && dstEntry->id && srcEntry->id
- && dstEntry->id.value() != srcEntry->id.value()) {
- // Both entries are public and have different IDs.
- context->getDiagnostics()->error(DiagMessage(src)
- << "cannot merge entry '" << srcEntry->name
- << "': conflicting public IDs");
- return false;
+static bool mergeEntry(IAaptContext* context, const Source& src,
+ ResourceEntry* dstEntry, ResourceEntry* srcEntry) {
+ if (dstEntry->symbolStatus.state < srcEntry->symbolStatus.state) {
+ // The incoming type's visibility is stronger, so we should override
+ // the visibility.
+ if (srcEntry->symbolStatus.state == SymbolState::kPublic) {
+ // Only copy the ID if the source is public, or else the ID is
+ // meaningless.
+ dstEntry->id = srcEntry->id;
}
- return true;
+ dstEntry->symbolStatus = std::move(srcEntry->symbolStatus);
+ } else if (srcEntry->symbolStatus.state == SymbolState::kPublic &&
+ dstEntry->symbolStatus.state == SymbolState::kPublic &&
+ dstEntry->id && srcEntry->id &&
+ dstEntry->id.value() != srcEntry->id.value()) {
+ // Both entries are public and have different IDs.
+ context->getDiagnostics()->error(DiagMessage(src)
+ << "cannot merge entry '" << srcEntry->name
+ << "': conflicting public IDs");
+ return false;
+ }
+ return true;
}
/**
* Modified CollisionResolver which will merge Styleables. Used with overlays.
*
* Styleables are not actual resources, but they are treated as such during the
- * compilation phase. Styleables don't simply overlay each other, their definitions merge
- * and accumulate. If both values are Styleables, we just merge them into the existing value.
+ * compilation phase. Styleables don't simply overlay each other, their
+ * definitions merge
+ * and accumulate. If both values are Styleables, we just merge them into the
+ * existing value.
*/
-static ResourceTable::CollisionResult resolveMergeCollision(Value* existing, Value* incoming) {
- if (Styleable* existingStyleable = valueCast<Styleable>(existing)) {
- if (Styleable* incomingStyleable = valueCast<Styleable>(incoming)) {
- // Styleables get merged.
- existingStyleable->mergeWith(incomingStyleable);
- return ResourceTable::CollisionResult::kKeepOriginal;
- }
+static ResourceTable::CollisionResult resolveMergeCollision(Value* existing,
+ Value* incoming) {
+ if (Styleable* existingStyleable = valueCast<Styleable>(existing)) {
+ if (Styleable* incomingStyleable = valueCast<Styleable>(incoming)) {
+ // Styleables get merged.
+ existingStyleable->mergeWith(incomingStyleable);
+ return ResourceTable::CollisionResult::kKeepOriginal;
}
- // Delegate to the default handler.
- return ResourceTable::resolveValueCollision(existing, incoming);
+ }
+ // Delegate to the default handler.
+ return ResourceTable::resolveValueCollision(existing, incoming);
}
-static ResourceTable::CollisionResult mergeConfigValue(IAaptContext* context,
- const ResourceNameRef& resName,
- const bool overlay,
- ResourceConfigValue* dstConfigValue,
- ResourceConfigValue* srcConfigValue) {
- using CollisionResult = ResourceTable::CollisionResult;
+static ResourceTable::CollisionResult mergeConfigValue(
+ IAaptContext* context, const ResourceNameRef& resName, const bool overlay,
+ ResourceConfigValue* dstConfigValue, ResourceConfigValue* srcConfigValue) {
+ using CollisionResult = ResourceTable::CollisionResult;
- Value* dstValue = dstConfigValue->value.get();
- Value* srcValue = srcConfigValue->value.get();
+ Value* dstValue = dstConfigValue->value.get();
+ Value* srcValue = srcConfigValue->value.get();
- CollisionResult collisionResult;
+ CollisionResult collisionResult;
+ if (overlay) {
+ collisionResult = resolveMergeCollision(dstValue, srcValue);
+ } else {
+ collisionResult = ResourceTable::resolveValueCollision(dstValue, srcValue);
+ }
+
+ if (collisionResult == CollisionResult::kConflict) {
if (overlay) {
- collisionResult = resolveMergeCollision(dstValue, srcValue);
- } else {
- collisionResult = ResourceTable::resolveValueCollision(dstValue, srcValue);
+ return CollisionResult::kTakeNew;
}
- if (collisionResult == CollisionResult::kConflict) {
- if (overlay) {
- return CollisionResult::kTakeNew;
- }
-
- // Error!
- context->getDiagnostics()->error(DiagMessage(srcValue->getSource())
- << "resource '" << resName
- << "' has a conflicting value for "
- << "configuration ("
- << srcConfigValue->config << ")");
- context->getDiagnostics()->note(DiagMessage(dstValue->getSource())
- << "originally defined here");
- return CollisionResult::kConflict;
- }
- return collisionResult;
+ // Error!
+ context->getDiagnostics()->error(
+ DiagMessage(srcValue->getSource())
+ << "resource '" << resName << "' has a conflicting value for "
+ << "configuration (" << srcConfigValue->config << ")");
+ context->getDiagnostics()->note(DiagMessage(dstValue->getSource())
+ << "originally defined here");
+ return CollisionResult::kConflict;
+ }
+ return collisionResult;
}
-bool TableMerger::doMerge(const Source& src,
- ResourceTable* srcTable,
+bool TableMerger::doMerge(const Source& src, ResourceTable* srcTable,
ResourceTablePackage* srcPackage,
- const bool manglePackage,
- const bool overlay,
+ const bool manglePackage, const bool overlay,
const bool allowNewResources,
const FileMergeCallback& callback) {
- bool error = false;
+ bool error = false;
- for (auto& srcType : srcPackage->types) {
- ResourceTableType* dstType = mMasterPackage->findOrCreateType(srcType->type);
- if (!mergeType(mContext, src, dstType, srcType.get())) {
+ for (auto& srcType : srcPackage->types) {
+ ResourceTableType* dstType =
+ mMasterPackage->findOrCreateType(srcType->type);
+ if (!mergeType(mContext, src, dstType, srcType.get())) {
+ error = true;
+ continue;
+ }
+
+ for (auto& srcEntry : srcType->entries) {
+ std::string entryName = srcEntry->name;
+ if (manglePackage) {
+ entryName = NameMangler::mangleEntry(srcPackage->name, srcEntry->name);
+ }
+
+ ResourceEntry* dstEntry;
+ if (allowNewResources) {
+ dstEntry = dstType->findOrCreateEntry(entryName);
+ } else {
+ dstEntry = dstType->findEntry(entryName);
+ }
+
+ const ResourceNameRef resName(srcPackage->name, srcType->type,
+ srcEntry->name);
+
+ if (!dstEntry) {
+ mContext->getDiagnostics()->error(
+ DiagMessage(src) << "resource " << resName
+ << " does not override an existing resource");
+ mContext->getDiagnostics()->note(
+ DiagMessage(src) << "define an <add-resource> tag or use "
+ << "--auto-add-overlay");
+ error = true;
+ continue;
+ }
+
+ if (!mergeEntry(mContext, src, dstEntry, srcEntry.get())) {
+ error = true;
+ continue;
+ }
+
+ for (auto& srcConfigValue : srcEntry->values) {
+ using CollisionResult = ResourceTable::CollisionResult;
+
+ ResourceConfigValue* dstConfigValue = dstEntry->findValue(
+ srcConfigValue->config, srcConfigValue->product);
+ if (dstConfigValue) {
+ CollisionResult collisionResult = mergeConfigValue(
+ mContext, resName, overlay, dstConfigValue, srcConfigValue.get());
+ if (collisionResult == CollisionResult::kConflict) {
error = true;
continue;
+ } else if (collisionResult == CollisionResult::kKeepOriginal) {
+ continue;
+ }
+ } else {
+ dstConfigValue = dstEntry->findOrCreateValue(srcConfigValue->config,
+ srcConfigValue->product);
}
- for (auto& srcEntry : srcType->entries) {
- std::string entryName = srcEntry->name;
- if (manglePackage) {
- entryName = NameMangler::mangleEntry(srcPackage->name, srcEntry->name);
+ // Continue if we're taking the new resource.
+
+ if (FileReference* f =
+ valueCast<FileReference>(srcConfigValue->value.get())) {
+ std::unique_ptr<FileReference> newFileRef;
+ if (manglePackage) {
+ newFileRef = cloneAndMangleFile(srcPackage->name, *f);
+ } else {
+ newFileRef = std::unique_ptr<FileReference>(
+ f->clone(&mMasterTable->stringPool));
+ }
+
+ if (callback) {
+ if (!callback(resName, srcConfigValue->config, newFileRef.get(),
+ f)) {
+ error = true;
+ continue;
}
+ }
+ dstConfigValue->value = std::move(newFileRef);
- ResourceEntry* dstEntry;
- if (allowNewResources) {
- dstEntry = dstType->findOrCreateEntry(entryName);
- } else {
- dstEntry = dstType->findEntry(entryName);
- }
-
- const ResourceNameRef resName(srcPackage->name, srcType->type, srcEntry->name);
-
- if (!dstEntry) {
- mContext->getDiagnostics()->error(DiagMessage(src)
- << "resource " << resName
- << " does not override an existing resource");
- mContext->getDiagnostics()->note(DiagMessage(src)
- << "define an <add-resource> tag or use "
- << "--auto-add-overlay");
- error = true;
- continue;
- }
-
- if (!mergeEntry(mContext, src, dstEntry, srcEntry.get())) {
- error = true;
- continue;
- }
-
- for (auto& srcConfigValue : srcEntry->values) {
- using CollisionResult = ResourceTable::CollisionResult;
-
- ResourceConfigValue* dstConfigValue = dstEntry->findValue(srcConfigValue->config,
- srcConfigValue->product);
- if (dstConfigValue) {
- CollisionResult collisionResult = mergeConfigValue(
- mContext, resName, overlay, dstConfigValue, srcConfigValue.get());
- if (collisionResult == CollisionResult::kConflict) {
- error = true;
- continue;
- } else if (collisionResult == CollisionResult::kKeepOriginal) {
- continue;
- }
- } else {
- dstConfigValue = dstEntry->findOrCreateValue(srcConfigValue->config,
- srcConfigValue->product);
- }
-
- // Continue if we're taking the new resource.
-
- if (FileReference* f = valueCast<FileReference>(srcConfigValue->value.get())) {
- std::unique_ptr<FileReference> newFileRef;
- if (manglePackage) {
- newFileRef = cloneAndMangleFile(srcPackage->name, *f);
- } else {
- newFileRef = std::unique_ptr<FileReference>(f->clone(
- &mMasterTable->stringPool));
- }
-
- if (callback) {
- if (!callback(resName, srcConfigValue->config, newFileRef.get(), f)) {
- error = true;
- continue;
- }
- }
- dstConfigValue->value = std::move(newFileRef);
-
- } else {
- dstConfigValue->value = std::unique_ptr<Value>(srcConfigValue->value->clone(
- &mMasterTable->stringPool));
- }
- }
+ } else {
+ dstConfigValue->value = std::unique_ptr<Value>(
+ srcConfigValue->value->clone(&mMasterTable->stringPool));
}
+ }
}
- return !error;
+ }
+ return !error;
}
-std::unique_ptr<FileReference> TableMerger::cloneAndMangleFile(const std::string& package,
- const FileReference& fileRef) {
- StringPiece prefix, entry, suffix;
- if (util::extractResFilePathParts(*fileRef.path, &prefix, &entry, &suffix)) {
- 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());
- newFileRef->setSource(fileRef.getSource());
- return newFileRef;
- }
- return std::unique_ptr<FileReference>(fileRef.clone(&mMasterTable->stringPool));
+std::unique_ptr<FileReference> TableMerger::cloneAndMangleFile(
+ const std::string& package, const FileReference& fileRef) {
+ StringPiece prefix, entry, suffix;
+ if (util::extractResFilePathParts(*fileRef.path, &prefix, &entry, &suffix)) {
+ 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());
+ newFileRef->setSource(fileRef.getSource());
+ return newFileRef;
+ }
+ return std::unique_ptr<FileReference>(
+ fileRef.clone(&mMasterTable->stringPool));
}
-bool TableMerger::mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file, bool overlay) {
- ResourceTable table;
- std::string path = ResourceUtils::buildResourceFileName(fileDesc, nullptr);
- std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
- table.stringPool.makeRef(path));
- fileRef->setSource(fileDesc.source);
- fileRef->file = file;
+bool TableMerger::mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file,
+ bool overlay) {
+ ResourceTable table;
+ std::string path = ResourceUtils::buildResourceFileName(fileDesc, nullptr);
+ std::unique_ptr<FileReference> fileRef =
+ util::make_unique<FileReference>(table.stringPool.makeRef(path));
+ fileRef->setSource(fileDesc.source);
+ fileRef->file = file;
- ResourceTablePackage* pkg = table.createPackage(fileDesc.name.package, 0x0);
- pkg->findOrCreateType(fileDesc.name.type)
- ->findOrCreateEntry(fileDesc.name.entry)
- ->findOrCreateValue(fileDesc.config, {})
- ->value = std::move(fileRef);
+ ResourceTablePackage* pkg = table.createPackage(fileDesc.name.package, 0x0);
+ pkg->findOrCreateType(fileDesc.name.type)
+ ->findOrCreateEntry(fileDesc.name.entry)
+ ->findOrCreateValue(fileDesc.config, {})
+ ->value = std::move(fileRef);
- return doMerge(file->getSource(), &table, pkg,
- false /* mangle */, overlay /* overlay */, true /* allow new */, {});
+ return doMerge(file->getSource(), &table, pkg, false /* mangle */,
+ overlay /* overlay */, true /* allow new */, {});
}
bool TableMerger::mergeFile(const ResourceFile& fileDesc, io::IFile* file) {
- return mergeFileImpl(fileDesc, file, false /* overlay */);
+ return mergeFileImpl(fileDesc, file, false /* overlay */);
}
-bool TableMerger::mergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file) {
- return mergeFileImpl(fileDesc, file, true /* overlay */);
+bool TableMerger::mergeFileOverlay(const ResourceFile& fileDesc,
+ io::IFile* file) {
+ return mergeFileImpl(fileDesc, file, true /* overlay */);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h
index 3473a27..c2e7181 100644
--- a/tools/aapt2/link/TableMerger.h
+++ b/tools/aapt2/link/TableMerger.h
@@ -31,96 +31,111 @@
namespace aapt {
struct TableMergerOptions {
- /**
- * If true, resources in overlays can be added without previously having existed.
- */
- bool autoAddOverlay = false;
+ /**
+ * If true, resources in overlays can be added without previously having
+ * existed.
+ */
+ bool autoAddOverlay = false;
};
/**
- * TableMerger takes resource tables and merges all packages within the tables that have the same
+ * TableMerger takes resource tables and merges all packages within the tables
+ * that have the same
* package ID.
*
- * If a package has a different name, all the entries in that table have their names mangled
- * to include the package name. This way there are no collisions. In order to do this correctly,
- * the TableMerger needs to also mangle any FileReference paths. Once these are mangled,
- * the original source path of the file, along with the new destination path is recorded in the
+ * If a package has a different name, all the entries in that table have their
+ * names mangled
+ * to include the package name. This way there are no collisions. In order to do
+ * this correctly,
+ * the TableMerger needs to also mangle any FileReference paths. Once these are
+ * mangled,
+ * the original source path of the file, along with the new destination path is
+ * recorded in the
* queue returned from getFileMergeQueue().
*
- * Once the merging is complete, a separate process can go collect the files from the various
- * source APKs and either copy or process their XML and put them in the correct location in
+ * Once the merging is complete, a separate process can go collect the files
+ * from the various
+ * source APKs and either copy or process their XML and put them in the correct
+ * location in
* the final APK.
*/
class TableMerger {
-public:
- /**
- * Note: The outTable ResourceTable must live longer than this TableMerger. References
- * are made to this ResourceTable for efficiency reasons.
- */
- TableMerger(IAaptContext* context, ResourceTable* outTable, const TableMergerOptions& options);
+ public:
+ /**
+ * Note: The outTable ResourceTable must live longer than this TableMerger.
+ * References
+ * are made to this ResourceTable for efficiency reasons.
+ */
+ TableMerger(IAaptContext* context, ResourceTable* outTable,
+ const TableMergerOptions& options);
- const std::set<std::string>& getMergedPackages() const {
- return mMergedPackages;
- }
+ const std::set<std::string>& getMergedPackages() const {
+ return mMergedPackages;
+ }
- /**
- * Merges resources from the same or empty package. This is for local sources.
- * An io::IFileCollection is optional and used to find the referenced Files and process them.
- */
- bool merge(const Source& src, ResourceTable* table,
- io::IFileCollection* collection = nullptr);
+ /**
+ * Merges resources from the same or empty package. This is for local sources.
+ * An io::IFileCollection is optional and used to find the referenced Files
+ * and process them.
+ */
+ bool merge(const Source& src, ResourceTable* table,
+ io::IFileCollection* collection = nullptr);
- /**
- * Merges resources from an overlay ResourceTable.
- * An io::IFileCollection is optional and used to find the referenced Files and process them.
- */
- bool mergeOverlay(const Source& src, ResourceTable* table,
- io::IFileCollection* collection = nullptr);
+ /**
+ * Merges resources from an overlay ResourceTable.
+ * An io::IFileCollection is optional and used to find the referenced Files
+ * and process them.
+ */
+ bool mergeOverlay(const Source& src, ResourceTable* table,
+ io::IFileCollection* collection = nullptr);
- /**
- * 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 StringPiece& package, ResourceTable* table,
- io::IFileCollection* collection);
+ /**
+ * 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 StringPiece& package,
+ ResourceTable* table, io::IFileCollection* collection);
- /**
- * Merges a compiled file that belongs to this same or empty package. This is for local sources.
- */
- bool mergeFile(const ResourceFile& fileDesc, io::IFile* file);
+ /**
+ * Merges a compiled file that belongs to this same or empty package. This is
+ * for local sources.
+ */
+ bool mergeFile(const ResourceFile& fileDesc, io::IFile* file);
- /**
- * Merges a compiled file from an overlay, overriding an existing definition.
- */
- bool mergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file);
+ /**
+ * Merges a compiled file from an overlay, overriding an existing definition.
+ */
+ bool mergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file);
-private:
- using FileMergeCallback = std::function<bool(const ResourceNameRef&,
- const ConfigDescription& config,
- FileReference*, FileReference*)>;
+ private:
+ using FileMergeCallback = std::function<bool(const ResourceNameRef&,
+ const ConfigDescription& config,
+ FileReference*, FileReference*)>;
- IAaptContext* mContext;
- ResourceTable* mMasterTable;
- TableMergerOptions mOptions;
- ResourceTablePackage* mMasterPackage;
+ IAaptContext* mContext;
+ ResourceTable* mMasterTable;
+ TableMergerOptions mOptions;
+ ResourceTablePackage* mMasterPackage;
- std::set<std::string> mMergedPackages;
+ std::set<std::string> mMergedPackages;
- bool mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file, bool overlay);
+ bool mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file,
+ bool overlay);
- bool mergeImpl(const Source& src, ResourceTable* srcTable, io::IFileCollection* collection,
- bool overlay, bool allowNew);
+ bool mergeImpl(const Source& src, ResourceTable* srcTable,
+ io::IFileCollection* collection, bool overlay, bool allowNew);
- bool doMerge(const Source& src, ResourceTable* srcTable, ResourceTablePackage* srcPackage,
- const bool manglePackage,
- const bool overlay,
- const bool allowNewResources,
- const FileMergeCallback& callback);
+ bool doMerge(const Source& src, ResourceTable* srcTable,
+ ResourceTablePackage* srcPackage, const bool manglePackage,
+ const bool overlay, const bool allowNewResources,
+ const FileMergeCallback& callback);
- std::unique_ptr<FileReference> cloneAndMangleFile(const std::string& package,
- const FileReference& value);
+ std::unique_ptr<FileReference> cloneAndMangleFile(const std::string& package,
+ const FileReference& value);
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_TABLEMERGER_H */
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index fb1cb21..e0b2b66 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
+#include "link/TableMerger.h"
#include "filter/ConfigFilter.h"
#include "io/FileSystem.h"
-#include "link/TableMerger.h"
#include "test/Builders.h"
#include "test/Context.h"
@@ -25,292 +25,327 @@
namespace aapt {
struct TableMergerTest : public ::testing::Test {
- std::unique_ptr<IAaptContext> mContext;
+ std::unique_ptr<IAaptContext> mContext;
- void SetUp() override {
- mContext = test::ContextBuilder()
- // We are compiling this package.
- .setCompilationPackage("com.app.a")
+ void SetUp() override {
+ mContext =
+ test::ContextBuilder()
+ // We are compiling this package.
+ .setCompilationPackage("com.app.a")
- // Merge all packages that have this package ID.
- .setPackageId(0x7f)
+ // Merge all packages that have this package ID.
+ .setPackageId(0x7f)
- // Mangle all packages that do not have this package name.
- .setNameManglerPolicy(NameManglerPolicy{ "com.app.a", { "com.app.b" } })
+ // Mangle all packages that do not have this package name.
+ .setNameManglerPolicy(NameManglerPolicy{"com.app.a", {"com.app.b"}})
- .build();
- }
+ .build();
+ }
};
TEST_F(TableMergerTest, SimpleMerge) {
- std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
- .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> tableA =
+ test::ResourceTableBuilder()
+ .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("com.app.b", 0x7f)
- .addSimple("com.app.b:id/foo")
- .build();
+ std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
+ .setPackageId("com.app.b", 0x7f)
+ .addSimple("com.app.b:id/foo")
+ .build();
- ResourceTable finalTable;
- TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
- io::FileCollection collection;
+ ResourceTable finalTable;
+ TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
+ io::FileCollection collection;
- ASSERT_TRUE(merger.merge({}, tableA.get()));
- ASSERT_TRUE(merger.mergeAndMangle({}, "com.app.b", tableB.get(), &collection));
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(
+ merger.mergeAndMangle({}, "com.app.b", tableB.get(), &collection));
- EXPECT_TRUE(merger.getMergedPackages().count("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("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")));
+ // Entries from com.app.a should not be mangled.
+ 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("com.app.b:id/foo")));
+ // The unmangled name should not be present.
+ AAPT_EXPECT_FALSE(
+ finalTable.findResource(test::parseNameOrDie("com.app.b:id/foo")));
- // Look for the mangled name.
- AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie("com.app.a:id/com.app.b$foo")));
+ // Look for the mangled name.
+ AAPT_EXPECT_TRUE(finalTable.findResource(
+ test::parseNameOrDie("com.app.a:id/com.app.b$foo")));
}
TEST_F(TableMergerTest, MergeFile) {
- ResourceTable finalTable;
- TableMergerOptions options;
- options.autoAddOverlay = false;
- TableMerger merger(mContext.get(), &finalTable, options);
+ ResourceTable finalTable;
+ TableMergerOptions options;
+ options.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, options);
- ResourceFile fileDesc;
- fileDesc.config = test::parseConfigOrDie("hdpi-v4");
- 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");
+ ResourceFile fileDesc;
+ fileDesc.config = test::parseConfigOrDie("hdpi-v4");
+ 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));
+ ASSERT_TRUE(merger.mergeFile(fileDesc, &testFile));
- FileReference* file = test::getValueForConfig<FileReference>(&finalTable,
- "com.app.a:layout/main",
- test::parseConfigOrDie("hdpi-v4"));
- ASSERT_NE(nullptr, file);
- EXPECT_EQ(std::string("res/layout-hdpi-v4/main.xml"), *file->path);
+ FileReference* file = test::getValueForConfig<FileReference>(
+ &finalTable, "com.app.a:layout/main", test::parseConfigOrDie("hdpi-v4"));
+ ASSERT_NE(nullptr, file);
+ EXPECT_EQ(std::string("res/layout-hdpi-v4/main.xml"), *file->path);
}
TEST_F(TableMergerTest, MergeFileOverlay) {
- ResourceTable finalTable;
- TableMergerOptions tableMergerOptions;
- tableMergerOptions.autoAddOverlay = false;
- TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
+ ResourceTable finalTable;
+ TableMergerOptions tableMergerOptions;
+ tableMergerOptions.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
- ResourceFile fileDesc;
- fileDesc.name = test::parseNameOrDie("xml/foo");
- test::TestFile fileA("path/to/fileA.xml.flat");
- test::TestFile fileB("path/to/fileB.xml.flat");
+ ResourceFile fileDesc;
+ fileDesc.name = test::parseNameOrDie("xml/foo");
+ test::TestFile fileA("path/to/fileA.xml.flat");
+ test::TestFile fileB("path/to/fileB.xml.flat");
- ASSERT_TRUE(merger.mergeFile(fileDesc, &fileA));
- ASSERT_TRUE(merger.mergeFileOverlay(fileDesc, &fileB));
+ ASSERT_TRUE(merger.mergeFile(fileDesc, &fileA));
+ ASSERT_TRUE(merger.mergeFileOverlay(fileDesc, &fileB));
}
TEST_F(TableMergerTest, MergeFileReferences) {
- std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
- .setPackageId("com.app.a", 0x7f)
- .addFileReference("com.app.a:xml/file", "res/xml/file.xml")
- .build();
- std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
- .setPackageId("com.app.b", 0x7f)
- .addFileReference("com.app.b:xml/file", "res/xml/file.xml")
- .build();
+ std::unique_ptr<ResourceTable> tableA =
+ test::ResourceTableBuilder()
+ .setPackageId("com.app.a", 0x7f)
+ .addFileReference("com.app.a:xml/file", "res/xml/file.xml")
+ .build();
+ std::unique_ptr<ResourceTable> tableB =
+ test::ResourceTableBuilder()
+ .setPackageId("com.app.b", 0x7f)
+ .addFileReference("com.app.b:xml/file", "res/xml/file.xml")
+ .build();
- ResourceTable finalTable;
- TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
- io::FileCollection collection;
- collection.insertFile("res/xml/file.xml");
+ ResourceTable finalTable;
+ TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
+ io::FileCollection collection;
+ collection.insertFile("res/xml/file.xml");
- ASSERT_TRUE(merger.merge({}, tableA.get()));
- ASSERT_TRUE(merger.mergeAndMangle({}, "com.app.b", tableB.get(), &collection));
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(
+ merger.mergeAndMangle({}, "com.app.b", tableB.get(), &collection));
- FileReference* f = test::getValue<FileReference>(&finalTable, "com.app.a:xml/file");
- ASSERT_NE(f, nullptr);
- EXPECT_EQ(std::string("res/xml/file.xml"), *f->path);
+ FileReference* f =
+ test::getValue<FileReference>(&finalTable, "com.app.a:xml/file");
+ ASSERT_NE(f, nullptr);
+ EXPECT_EQ(std::string("res/xml/file.xml"), *f->path);
- f = test::getValue<FileReference>(&finalTable, "com.app.a:xml/com.app.b$file");
- ASSERT_NE(f, nullptr);
- EXPECT_EQ(std::string("res/xml/com.app.b$file.xml"), *f->path);
+ f = test::getValue<FileReference>(&finalTable,
+ "com.app.a:xml/com.app.b$file");
+ ASSERT_NE(f, nullptr);
+ 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("", 0x00)
- .addValue("bool/foo", ResourceUtils::tryParseBool("true"))
- .build();
- std::unique_ptr<ResourceTable> overlay = test::ResourceTableBuilder()
- .setPackageId("", 0x00)
- .addValue("bool/foo", ResourceUtils::tryParseBool("false"))
- .build();
+ std::unique_ptr<ResourceTable> base =
+ test::ResourceTableBuilder()
+ .setPackageId("", 0x00)
+ .addValue("bool/foo", ResourceUtils::tryParseBool("true"))
+ .build();
+ std::unique_ptr<ResourceTable> overlay =
+ test::ResourceTableBuilder()
+ .setPackageId("", 0x00)
+ .addValue("bool/foo", ResourceUtils::tryParseBool("false"))
+ .build();
- ResourceTable finalTable;
- TableMergerOptions tableMergerOptions;
- tableMergerOptions.autoAddOverlay = false;
- TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
+ ResourceTable finalTable;
+ TableMergerOptions tableMergerOptions;
+ tableMergerOptions.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
- ASSERT_TRUE(merger.merge({}, base.get()));
- ASSERT_TRUE(merger.mergeOverlay({}, overlay.get()));
+ ASSERT_TRUE(merger.merge({}, base.get()));
+ ASSERT_TRUE(merger.mergeOverlay({}, overlay.get()));
- BinaryPrimitive* foo = test::getValue<BinaryPrimitive>(&finalTable, "com.app.a:bool/foo");
- ASSERT_NE(nullptr, foo);
- EXPECT_EQ(0x0u, foo->value.data);
+ BinaryPrimitive* foo =
+ test::getValue<BinaryPrimitive>(&finalTable, "com.app.a:bool/foo");
+ ASSERT_NE(nullptr, foo);
+ EXPECT_EQ(0x0u, foo->value.data);
}
TEST_F(TableMergerTest, OverrideSameResourceIdsWithOverlay) {
- std::unique_ptr<ResourceTable> base = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), SymbolState::kPublic)
- .build();
- std::unique_ptr<ResourceTable> overlay = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), SymbolState::kPublic)
- .build();
+ std::unique_ptr<ResourceTable> base =
+ test::ResourceTableBuilder()
+ .setPackageId("", 0x7f)
+ .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001),
+ SymbolState::kPublic)
+ .build();
+ std::unique_ptr<ResourceTable> overlay =
+ test::ResourceTableBuilder()
+ .setPackageId("", 0x7f)
+ .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001),
+ SymbolState::kPublic)
+ .build();
- ResourceTable finalTable;
- TableMergerOptions tableMergerOptions;
- tableMergerOptions.autoAddOverlay = false;
- TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
+ ResourceTable finalTable;
+ TableMergerOptions tableMergerOptions;
+ tableMergerOptions.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
- ASSERT_TRUE(merger.merge({}, base.get()));
- ASSERT_TRUE(merger.mergeOverlay({}, overlay.get()));
+ ASSERT_TRUE(merger.merge({}, base.get()));
+ ASSERT_TRUE(merger.mergeOverlay({}, overlay.get()));
}
TEST_F(TableMergerTest, FailToOverrideConflictingTypeIdsWithOverlay) {
- std::unique_ptr<ResourceTable> base = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), SymbolState::kPublic)
- .build();
- std::unique_ptr<ResourceTable> overlay = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .setSymbolState("bool/foo", ResourceId(0x7f, 0x02, 0x0001), SymbolState::kPublic)
- .build();
+ std::unique_ptr<ResourceTable> base =
+ test::ResourceTableBuilder()
+ .setPackageId("", 0x7f)
+ .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001),
+ SymbolState::kPublic)
+ .build();
+ std::unique_ptr<ResourceTable> overlay =
+ test::ResourceTableBuilder()
+ .setPackageId("", 0x7f)
+ .setSymbolState("bool/foo", ResourceId(0x7f, 0x02, 0x0001),
+ SymbolState::kPublic)
+ .build();
- ResourceTable finalTable;
- TableMergerOptions tableMergerOptions;
- tableMergerOptions.autoAddOverlay = false;
- TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
+ ResourceTable finalTable;
+ TableMergerOptions tableMergerOptions;
+ tableMergerOptions.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
- ASSERT_TRUE(merger.merge({}, base.get()));
- ASSERT_FALSE(merger.mergeOverlay({}, overlay.get()));
+ ASSERT_TRUE(merger.merge({}, base.get()));
+ ASSERT_FALSE(merger.mergeOverlay({}, overlay.get()));
}
TEST_F(TableMergerTest, FailToOverrideConflictingEntryIdsWithOverlay) {
- std::unique_ptr<ResourceTable> base = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001), SymbolState::kPublic)
- .build();
- std::unique_ptr<ResourceTable> overlay = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0002), SymbolState::kPublic)
- .build();
+ std::unique_ptr<ResourceTable> base =
+ test::ResourceTableBuilder()
+ .setPackageId("", 0x7f)
+ .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0001),
+ SymbolState::kPublic)
+ .build();
+ std::unique_ptr<ResourceTable> overlay =
+ test::ResourceTableBuilder()
+ .setPackageId("", 0x7f)
+ .setSymbolState("bool/foo", ResourceId(0x7f, 0x01, 0x0002),
+ SymbolState::kPublic)
+ .build();
- ResourceTable finalTable;
- TableMergerOptions tableMergerOptions;
- tableMergerOptions.autoAddOverlay = false;
- TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
+ ResourceTable finalTable;
+ TableMergerOptions tableMergerOptions;
+ tableMergerOptions.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
- ASSERT_TRUE(merger.merge({}, base.get()));
- ASSERT_FALSE(merger.mergeOverlay({}, overlay.get()));
+ ASSERT_TRUE(merger.merge({}, base.get()));
+ ASSERT_FALSE(merger.mergeOverlay({}, overlay.get()));
}
TEST_F(TableMergerTest, MergeAddResourceFromOverlay) {
- std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .setSymbolState("bool/foo", {}, SymbolState::kUndefined)
- .build();
- std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .addValue("bool/foo", ResourceUtils::tryParseBool("true"))
- .build();
+ std::unique_ptr<ResourceTable> tableA =
+ test::ResourceTableBuilder()
+ .setPackageId("", 0x7f)
+ .setSymbolState("bool/foo", {}, SymbolState::kUndefined)
+ .build();
+ std::unique_ptr<ResourceTable> tableB =
+ test::ResourceTableBuilder()
+ .setPackageId("", 0x7f)
+ .addValue("bool/foo", ResourceUtils::tryParseBool("true"))
+ .build();
- ResourceTable finalTable;
- TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
+ ResourceTable finalTable;
+ TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
- ASSERT_TRUE(merger.merge({}, tableA.get()));
- ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
}
TEST_F(TableMergerTest, MergeAddResourceFromOverlayWithAutoAddOverlay) {
- std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .build();
- std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .addValue("bool/foo", ResourceUtils::tryParseBool("true"))
- .build();
+ std::unique_ptr<ResourceTable> tableA =
+ test::ResourceTableBuilder().setPackageId("", 0x7f).build();
+ std::unique_ptr<ResourceTable> tableB =
+ test::ResourceTableBuilder()
+ .setPackageId("", 0x7f)
+ .addValue("bool/foo", ResourceUtils::tryParseBool("true"))
+ .build();
- ResourceTable finalTable;
- TableMergerOptions options;
- options.autoAddOverlay = true;
- TableMerger merger(mContext.get(), &finalTable, options);
+ ResourceTable finalTable;
+ TableMergerOptions options;
+ options.autoAddOverlay = true;
+ TableMerger merger(mContext.get(), &finalTable, options);
- ASSERT_TRUE(merger.merge({}, tableA.get()));
- ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
}
TEST_F(TableMergerTest, FailToMergeNewResourceWithoutAutoAddOverlay) {
- std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .build();
- std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
- .setPackageId("", 0x7f)
- .addValue("bool/foo", ResourceUtils::tryParseBool("true"))
- .build();
+ std::unique_ptr<ResourceTable> tableA =
+ test::ResourceTableBuilder().setPackageId("", 0x7f).build();
+ std::unique_ptr<ResourceTable> tableB =
+ test::ResourceTableBuilder()
+ .setPackageId("", 0x7f)
+ .addValue("bool/foo", ResourceUtils::tryParseBool("true"))
+ .build();
- ResourceTable finalTable;
- TableMergerOptions options;
- options.autoAddOverlay = false;
- TableMerger merger(mContext.get(), &finalTable, options);
+ ResourceTable finalTable;
+ TableMergerOptions options;
+ options.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, options);
- ASSERT_TRUE(merger.merge({}, tableA.get()));
- ASSERT_FALSE(merger.mergeOverlay({}, tableB.get()));
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_FALSE(merger.mergeOverlay({}, tableB.get()));
}
TEST_F(TableMergerTest, OverlaidStyleablesShouldBeMerged) {
- std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
- .setPackageId("com.app.a", 0x7f)
- .addValue("com.app.a:styleable/Foo", test::StyleableBuilder()
- .addItem("com.app.a:attr/bar")
- .addItem("com.app.a:attr/foo", ResourceId(0x01010000))
- .build())
- .build();
+ std::unique_ptr<ResourceTable> tableA =
+ test::ResourceTableBuilder()
+ .setPackageId("com.app.a", 0x7f)
+ .addValue("com.app.a:styleable/Foo",
+ test::StyleableBuilder()
+ .addItem("com.app.a:attr/bar")
+ .addItem("com.app.a:attr/foo", ResourceId(0x01010000))
+ .build())
+ .build();
- std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
- .setPackageId("com.app.a", 0x7f)
- .addValue("com.app.a:styleable/Foo", test::StyleableBuilder()
- .addItem("com.app.a:attr/bat")
- .addItem("com.app.a:attr/foo")
- .build())
- .build();
+ std::unique_ptr<ResourceTable> tableB =
+ test::ResourceTableBuilder()
+ .setPackageId("com.app.a", 0x7f)
+ .addValue("com.app.a:styleable/Foo",
+ test::StyleableBuilder()
+ .addItem("com.app.a:attr/bat")
+ .addItem("com.app.a:attr/foo")
+ .build())
+ .build();
- ResourceTable finalTable;
- TableMergerOptions options;
- options.autoAddOverlay = true;
- TableMerger merger(mContext.get(), &finalTable, options);
+ ResourceTable finalTable;
+ TableMergerOptions options;
+ options.autoAddOverlay = true;
+ TableMerger merger(mContext.get(), &finalTable, options);
- ASSERT_TRUE(merger.merge({}, tableA.get()));
- ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
- Debug::printTable(&finalTable, {});
+ Debug::printTable(&finalTable, {});
- Styleable* styleable = test::getValue<Styleable>(&finalTable, "com.app.a:styleable/Foo");
- ASSERT_NE(nullptr, styleable);
+ Styleable* styleable =
+ test::getValue<Styleable>(&finalTable, "com.app.a:styleable/Foo");
+ ASSERT_NE(nullptr, styleable);
- std::vector<Reference> expectedRefs = {
- Reference(test::parseNameOrDie("com.app.a:attr/bar")),
- Reference(test::parseNameOrDie("com.app.a:attr/bat")),
- Reference(test::parseNameOrDie("com.app.a:attr/foo"), ResourceId(0x01010000)),
- };
+ std::vector<Reference> expectedRefs = {
+ Reference(test::parseNameOrDie("com.app.a:attr/bar")),
+ Reference(test::parseNameOrDie("com.app.a:attr/bat")),
+ Reference(test::parseNameOrDie("com.app.a:attr/foo"),
+ ResourceId(0x01010000)),
+ };
- EXPECT_EQ(expectedRefs, styleable->entries);
+ EXPECT_EQ(expectedRefs, styleable->entries);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/VersionCollapser.cpp b/tools/aapt2/link/VersionCollapser.cpp
index 949d656..61a1f86 100644
--- a/tools/aapt2/link/VersionCollapser.cpp
+++ b/tools/aapt2/link/VersionCollapser.cpp
@@ -24,129 +24,140 @@
template <typename Iterator, typename Pred>
class FilterIterator {
-public:
- FilterIterator(Iterator begin, Iterator end, Pred pred=Pred()) :
- mCurrent(begin), mEnd(end), mPred(pred) {
- advance();
- }
+ public:
+ FilterIterator(Iterator begin, Iterator end, Pred pred = Pred())
+ : mCurrent(begin), mEnd(end), mPred(pred) {
+ advance();
+ }
- bool hasNext() {
- return mCurrent != mEnd;
- }
+ bool hasNext() { return mCurrent != mEnd; }
- Iterator nextIter() {
- Iterator iter = mCurrent;
- ++mCurrent;
- advance();
- return iter;
- }
+ Iterator nextIter() {
+ Iterator iter = mCurrent;
+ ++mCurrent;
+ advance();
+ return iter;
+ }
- typename Iterator::reference next() {
- return *nextIter();
- }
+ typename Iterator::reference next() { return *nextIter(); }
-private:
- void advance() {
- for (; mCurrent != mEnd; ++mCurrent) {
- if (mPred(*mCurrent)) {
- return;
- }
- }
+ private:
+ void advance() {
+ for (; mCurrent != mEnd; ++mCurrent) {
+ if (mPred(*mCurrent)) {
+ return;
+ }
}
+ }
- Iterator mCurrent, mEnd;
- Pred mPred;
+ 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);
+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
+ * 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() = {};
- }
- }
+ // 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;
}
- // 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());
+ 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.
- // 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;
+ 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;
}
- }
- 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;
- });
+ // 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());
- }
- }
+ 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;
+ }
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/VersionCollapser_test.cpp b/tools/aapt2/link/VersionCollapser_test.cpp
index dd5f1d1..c0e0ddb 100644
--- a/tools/aapt2/link/VersionCollapser_test.cpp
+++ b/tools/aapt2/link/VersionCollapser_test.cpp
@@ -22,82 +22,96 @@
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();
+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();
+ uptr<IAaptContext> context =
+ test::ContextBuilder().setMinSdkVersion(7).build();
- const StringPiece resName = "@android:string/foo";
+ const StringPiece resName = "@android:string/foo";
- uptr<ResourceTable> table =
- buildTableWithConfigs(resName,
- { "land-v4", "land-v5", "sw600dp", "land-v6",
- "land-v14", "land-v21" });
+ 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()));
+ 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 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")));
+ // 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")));
+ // '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();
+ uptr<IAaptContext> context =
+ test::ContextBuilder().setMinSdkVersion(21).build();
- const StringPiece resName = "@android:string/foo";
+ 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()));
+ 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 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()));
+ // 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")));
+ // 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
+} // namespace aapt
diff --git a/tools/aapt2/link/XmlNamespaceRemover.cpp b/tools/aapt2/link/XmlNamespaceRemover.cpp
index 9f95177..6e8d80d 100644
--- a/tools/aapt2/link/XmlNamespaceRemover.cpp
+++ b/tools/aapt2/link/XmlNamespaceRemover.cpp
@@ -27,57 +27,59 @@
* Visits each xml Node, removing URI references and nested namespaces.
*/
class XmlVisitor : public xml::Visitor {
-public:
- XmlVisitor(bool keepUris) : mKeepUris(keepUris) {
- }
+ public:
+ XmlVisitor(bool keepUris) : mKeepUris(keepUris) {}
- void visit(xml::Element* el) override {
- // Strip namespaces
- for (auto& child : el->children) {
- while (child && xml::nodeCast<xml::Namespace>(child.get())) {
- if (child->children.empty()) {
- child = {};
- } else {
- child = std::move(child->children.front());
- child->parent = el;
- }
- }
+ void visit(xml::Element* el) override {
+ // Strip namespaces
+ for (auto& child : el->children) {
+ while (child && xml::nodeCast<xml::Namespace>(child.get())) {
+ if (child->children.empty()) {
+ child = {};
+ } else {
+ child = std::move(child->children.front());
+ child->parent = el;
}
- el->children.erase(std::remove_if(el->children.begin(), el->children.end(),
- [](const std::unique_ptr<xml::Node>& child) -> bool {
- return child == nullptr;
- }), el->children.end());
-
- if (!mKeepUris) {
- for (xml::Attribute& attr : el->attributes) {
- attr.namespaceUri = std::string();
- }
- el->namespaceUri = std::string();
- }
- xml::Visitor::visit(el);
+ }
}
+ el->children.erase(
+ std::remove_if(el->children.begin(), el->children.end(),
+ [](const std::unique_ptr<xml::Node>& child) -> bool {
+ return child == nullptr;
+ }),
+ el->children.end());
-private:
- bool mKeepUris;
+ if (!mKeepUris) {
+ for (xml::Attribute& attr : el->attributes) {
+ attr.namespaceUri = std::string();
+ }
+ el->namespaceUri = std::string();
+ }
+ xml::Visitor::visit(el);
+ }
+
+ private:
+ bool mKeepUris;
};
-} // namespace
+} // namespace
-bool XmlNamespaceRemover::consume(IAaptContext* context, xml::XmlResource* resource) {
- if (!resource->root) {
- return false;
+bool XmlNamespaceRemover::consume(IAaptContext* context,
+ xml::XmlResource* resource) {
+ if (!resource->root) {
+ return false;
+ }
+ // Replace any root namespaces until the root is a non-namespace node
+ while (xml::nodeCast<xml::Namespace>(resource->root.get())) {
+ if (resource->root->children.empty()) {
+ break;
}
- // Replace any root namespaces until the root is a non-namespace node
- while (xml::nodeCast<xml::Namespace>(resource->root.get())) {
- if (resource->root->children.empty()) {
- break;
- }
- resource->root = std::move(resource->root->children.front());
- resource->root->parent = nullptr;
- }
- XmlVisitor visitor(mKeepUris);
- resource->root->accept(&visitor);
- return true;
+ resource->root = std::move(resource->root->children.front());
+ resource->root->parent = nullptr;
+ }
+ XmlVisitor visitor(mKeepUris);
+ resource->root->accept(&visitor);
+ return true;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/XmlNamespaceRemover_test.cpp b/tools/aapt2/link/XmlNamespaceRemover_test.cpp
index e72ea439..d2daaee 100644
--- a/tools/aapt2/link/XmlNamespaceRemover_test.cpp
+++ b/tools/aapt2/link/XmlNamespaceRemover_test.cpp
@@ -20,90 +20,92 @@
namespace aapt {
class XmlUriTestVisitor : public xml::Visitor {
-public:
- void visit(xml::Element* el) override {
- for (const auto& attr : el->attributes) {
- EXPECT_EQ(std::string(), attr.namespaceUri);
- }
- EXPECT_EQ(std::string(), el->namespaceUri);
- xml::Visitor::visit(el);
+ public:
+ void visit(xml::Element* el) override {
+ for (const auto& attr : el->attributes) {
+ EXPECT_EQ(std::string(), attr.namespaceUri);
}
+ EXPECT_EQ(std::string(), el->namespaceUri);
+ xml::Visitor::visit(el);
+ }
- void visit(xml::Namespace* ns) override {
- EXPECT_EQ(std::string(), ns->namespaceUri);
- xml::Visitor::visit(ns);
- }
+ void visit(xml::Namespace* ns) override {
+ EXPECT_EQ(std::string(), ns->namespaceUri);
+ xml::Visitor::visit(ns);
+ }
};
class XmlNamespaceTestVisitor : public xml::Visitor {
-public:
- void visit(xml::Namespace* ns) override {
- ADD_FAILURE() << "Detected namespace: "
- << ns->namespacePrefix << "=\"" << ns->namespaceUri << "\"";
- xml::Visitor::visit(ns);
- }
+ public:
+ void visit(xml::Namespace* ns) override {
+ ADD_FAILURE() << "Detected namespace: " << ns->namespacePrefix << "=\""
+ << ns->namespaceUri << "\"";
+ xml::Visitor::visit(ns);
+ }
};
class XmlNamespaceRemoverTest : public ::testing::Test {
-public:
- void SetUp() override {
- mContext = test::ContextBuilder()
- .setCompilationPackage("com.app.test")
- .build();
- }
+ public:
+ void SetUp() override {
+ mContext =
+ test::ContextBuilder().setCompilationPackage("com.app.test").build();
+ }
-protected:
- std::unique_ptr<IAaptContext> mContext;
+ protected:
+ std::unique_ptr<IAaptContext> mContext;
};
TEST_F(XmlNamespaceRemoverTest, RemoveUris) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::buildXmlDomForPackageName(mContext.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:text="hello" />)EOF");
- XmlNamespaceRemover remover;
- ASSERT_TRUE(remover.consume(mContext.get(), doc.get()));
+ XmlNamespaceRemover remover;
+ ASSERT_TRUE(remover.consume(mContext.get(), doc.get()));
- xml::Node* root = doc.get()->root.get();
- ASSERT_NE(root, nullptr);
+ xml::Node* root = doc.get()->root.get();
+ ASSERT_NE(root, nullptr);
- XmlUriTestVisitor visitor;
- root->accept(&visitor);
+ XmlUriTestVisitor visitor;
+ root->accept(&visitor);
}
TEST_F(XmlNamespaceRemoverTest, RemoveNamespaces) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::buildXmlDomForPackageName(mContext.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:foo="http://schemas.android.com/apk/res/foo"
foo:bar="foobar"
android:text="hello" />)EOF");
- XmlNamespaceRemover remover;
- ASSERT_TRUE(remover.consume(mContext.get(), doc.get()));
+ XmlNamespaceRemover remover;
+ ASSERT_TRUE(remover.consume(mContext.get(), doc.get()));
- xml::Node* root = doc.get()->root.get();
- ASSERT_NE(root, nullptr);
+ xml::Node* root = doc.get()->root.get();
+ ASSERT_NE(root, nullptr);
- XmlNamespaceTestVisitor visitor;
- root->accept(&visitor);
+ XmlNamespaceTestVisitor visitor;
+ root->accept(&visitor);
}
TEST_F(XmlNamespaceRemoverTest, RemoveNestedNamespaces) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::buildXmlDomForPackageName(mContext.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:text="hello">
<View xmlns:foo="http://schemas.example.com/foo"
android:text="foo"/>
</View>)EOF");
- XmlNamespaceRemover remover;
- ASSERT_TRUE(remover.consume(mContext.get(), doc.get()));
+ XmlNamespaceRemover remover;
+ ASSERT_TRUE(remover.consume(mContext.get(), doc.get()));
- xml::Node* root = doc.get()->root.get();
- ASSERT_NE(root, nullptr);
+ xml::Node* root = doc.get()->root.get();
+ ASSERT_NE(root, nullptr);
- XmlNamespaceTestVisitor visitor;
- root->accept(&visitor);
+ XmlNamespaceTestVisitor visitor;
+ root->accept(&visitor);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index 59ffe15..945f98a 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -29,146 +29,156 @@
namespace {
/**
- * Visits all references (including parents of styles, references in styles, arrays, etc) and
- * links their symbolic name to their Resource ID, performing mangling and package aliasing
+ * Visits all references (including parents of styles, references in styles,
+ * arrays, etc) and
+ * links their symbolic name to their Resource ID, performing mangling and
+ * package aliasing
* as needed.
*/
class ReferenceVisitor : public ValueVisitor {
-public:
- using ValueVisitor::visit;
+ public:
+ using ValueVisitor::visit;
- ReferenceVisitor(IAaptContext* context, SymbolTable* symbols, xml::IPackageDeclStack* decls,
- CallSite* callSite) :
- mContext(context), mSymbols(symbols), mDecls(decls), mCallSite(callSite),
- mError(false) {
+ ReferenceVisitor(IAaptContext* context, SymbolTable* symbols,
+ xml::IPackageDeclStack* decls, CallSite* callSite)
+ : mContext(context),
+ mSymbols(symbols),
+ mDecls(decls),
+ mCallSite(callSite),
+ mError(false) {}
+
+ void visit(Reference* ref) override {
+ if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mDecls,
+ mCallSite)) {
+ mError = true;
}
+ }
- void visit(Reference* ref) override {
- if (!ReferenceLinker::linkReference(ref, mContext, mSymbols, mDecls, mCallSite)) {
- mError = true;
- }
- }
+ bool hasError() const { return mError; }
- bool hasError() const {
- return mError;
- }
-
-private:
- IAaptContext* mContext;
- SymbolTable* mSymbols;
- xml::IPackageDeclStack* mDecls;
- CallSite* mCallSite;
- bool mError;
+ private:
+ IAaptContext* mContext;
+ SymbolTable* mSymbols;
+ xml::IPackageDeclStack* mDecls;
+ CallSite* mCallSite;
+ bool mError;
};
/**
* Visits each xml Element and compiles the attributes within.
*/
class XmlVisitor : public xml::PackageAwareVisitor {
-public:
- using xml::PackageAwareVisitor::visit;
+ public:
+ using xml::PackageAwareVisitor::visit;
- XmlVisitor(IAaptContext* context, SymbolTable* symbols, const Source& source,
- std::set<int>* sdkLevelsFound, CallSite* callSite) :
- mContext(context), mSymbols(symbols), mSource(source), mSdkLevelsFound(sdkLevelsFound),
- mCallSite(callSite), mReferenceVisitor(context, symbols, this, callSite) {
- }
+ XmlVisitor(IAaptContext* context, SymbolTable* symbols, const Source& source,
+ std::set<int>* sdkLevelsFound, CallSite* callSite)
+ : mContext(context),
+ mSymbols(symbols),
+ mSource(source),
+ mSdkLevelsFound(sdkLevelsFound),
+ mCallSite(callSite),
+ mReferenceVisitor(context, symbols, this, callSite) {}
- void visit(xml::Element* el) override {
- const Source source = mSource.withLine(el->lineNumber);
- for (xml::Attribute& attr : el->attributes) {
- Maybe<xml::ExtractedPackage> maybePackage =
- xml::extractPackageFromNamespace(attr.namespaceUri);
- if (maybePackage) {
- // There is a valid package name for this attribute. We will look this up.
- StringPiece package = maybePackage.value().package;
- if (package.empty()) {
- // Empty package means the 'current' or 'local' package.
- package = mContext->getCompilationPackage();
- }
-
- Reference attrRef(ResourceNameRef(package, ResourceType::kAttr, attr.name));
- attrRef.privateReference = maybePackage.value().privateNamespace;
-
- std::string errStr;
- attr.compiledAttribute = ReferenceLinker::compileXmlAttribute(
- attrRef, mContext->getNameMangler(), mSymbols, mCallSite, &errStr);
-
- // Convert the string value into a compiled Value if this is a valid attribute.
- if (attr.compiledAttribute) {
- if (attr.compiledAttribute.value().id) {
- // Record all SDK levels from which the attributes were defined.
- const size_t sdkLevel = findAttributeSdkLevel(
- attr.compiledAttribute.value().id.value());
- if (sdkLevel > 1) {
- mSdkLevelsFound->insert(sdkLevel);
- }
- }
-
- const Attribute* attribute = &attr.compiledAttribute.value().attribute;
- attr.compiledValue = ResourceUtils::tryParseItemForAttribute(attr.value,
- attribute);
- if (!attr.compiledValue &&
- !(attribute->typeMask & android::ResTable_map::TYPE_STRING)) {
- // We won't be able to encode this as a string.
- mContext->getDiagnostics()->error(
- DiagMessage(source) << "'" << attr.value << "' "
- << "is incompatible with attribute "
- << package << ":" << attr.name << " "
- << *attribute);
- mError = true;
- }
-
- } else {
- mContext->getDiagnostics()->error(DiagMessage(source)
- << "attribute '" << package << ":"
- << attr.name << "' " << errStr);
- mError = true;
-
- }
- } else if (!attr.compiledValue) {
- // We still encode references, but only if we haven't manually set this to
- // another compiled value.
- attr.compiledValue = ResourceUtils::tryParseReference(attr.value);
- }
-
- if (attr.compiledValue) {
- // With a compiledValue, we must resolve the reference and assign it an ID.
- attr.compiledValue->setSource(source);
- attr.compiledValue->accept(&mReferenceVisitor);
- }
+ void visit(xml::Element* el) override {
+ const Source source = mSource.withLine(el->lineNumber);
+ for (xml::Attribute& attr : el->attributes) {
+ Maybe<xml::ExtractedPackage> maybePackage =
+ xml::extractPackageFromNamespace(attr.namespaceUri);
+ if (maybePackage) {
+ // There is a valid package name for this attribute. We will look this
+ // up.
+ StringPiece package = maybePackage.value().package;
+ if (package.empty()) {
+ // Empty package means the 'current' or 'local' package.
+ package = mContext->getCompilationPackage();
}
- // Call the super implementation.
- xml::PackageAwareVisitor::visit(el);
+ Reference attrRef(
+ ResourceNameRef(package, ResourceType::kAttr, attr.name));
+ attrRef.privateReference = maybePackage.value().privateNamespace;
+
+ std::string errStr;
+ attr.compiledAttribute = ReferenceLinker::compileXmlAttribute(
+ attrRef, mContext->getNameMangler(), mSymbols, mCallSite, &errStr);
+
+ // Convert the string value into a compiled Value if this is a valid
+ // attribute.
+ if (attr.compiledAttribute) {
+ if (attr.compiledAttribute.value().id) {
+ // Record all SDK levels from which the attributes were defined.
+ const size_t sdkLevel = findAttributeSdkLevel(
+ attr.compiledAttribute.value().id.value());
+ if (sdkLevel > 1) {
+ mSdkLevelsFound->insert(sdkLevel);
+ }
+ }
+
+ const Attribute* attribute =
+ &attr.compiledAttribute.value().attribute;
+ attr.compiledValue =
+ ResourceUtils::tryParseItemForAttribute(attr.value, attribute);
+ if (!attr.compiledValue &&
+ !(attribute->typeMask & android::ResTable_map::TYPE_STRING)) {
+ // We won't be able to encode this as a string.
+ mContext->getDiagnostics()->error(
+ DiagMessage(source) << "'" << attr.value << "' "
+ << "is incompatible with attribute "
+ << package << ":" << attr.name << " "
+ << *attribute);
+ mError = true;
+ }
+
+ } else {
+ mContext->getDiagnostics()->error(DiagMessage(source)
+ << "attribute '" << package << ":"
+ << attr.name << "' " << errStr);
+ mError = true;
+ }
+ } else if (!attr.compiledValue) {
+ // We still encode references, but only if we haven't manually set this
+ // to
+ // another compiled value.
+ attr.compiledValue = ResourceUtils::tryParseReference(attr.value);
+ }
+
+ if (attr.compiledValue) {
+ // With a compiledValue, we must resolve the reference and assign it an
+ // ID.
+ attr.compiledValue->setSource(source);
+ attr.compiledValue->accept(&mReferenceVisitor);
+ }
}
- bool hasError() {
- return mError || mReferenceVisitor.hasError();
- }
+ // Call the super implementation.
+ xml::PackageAwareVisitor::visit(el);
+ }
-private:
- IAaptContext* mContext;
- SymbolTable* mSymbols;
- Source mSource;
- std::set<int>* mSdkLevelsFound;
- CallSite* mCallSite;
- ReferenceVisitor mReferenceVisitor;
- bool mError = false;
+ bool hasError() { return mError || mReferenceVisitor.hasError(); }
+
+ private:
+ IAaptContext* mContext;
+ SymbolTable* mSymbols;
+ Source mSource;
+ std::set<int>* mSdkLevelsFound;
+ CallSite* mCallSite;
+ ReferenceVisitor mReferenceVisitor;
+ bool mError = false;
};
-} // namespace
+} // namespace
-bool XmlReferenceLinker::consume(IAaptContext* context, xml::XmlResource* resource) {
- mSdkLevelsFound.clear();
- CallSite callSite = { resource->file.name };
- XmlVisitor visitor(context, context->getExternalSymbols(), resource->file.source,
- &mSdkLevelsFound, &callSite);
- if (resource->root) {
- resource->root->accept(&visitor);
- return !visitor.hasError();
- }
- return false;
+bool XmlReferenceLinker::consume(IAaptContext* context,
+ xml::XmlResource* resource) {
+ mSdkLevelsFound.clear();
+ CallSite callSite = {resource->file.name};
+ XmlVisitor visitor(context, context->getExternalSymbols(),
+ resource->file.source, &mSdkLevelsFound, &callSite);
+ if (resource->root) {
+ resource->root->accept(&visitor);
+ return !visitor.hasError();
+ }
+ return false;
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index 51eb62c..35d479f 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -20,237 +20,276 @@
namespace aapt {
class XmlReferenceLinkerTest : public ::testing::Test {
-public:
- void SetUp() override {
- mContext = test::ContextBuilder()
- .setCompilationPackage("com.app.test")
- .setNameManglerPolicy(
- NameManglerPolicy{ "com.app.test", { "com.android.support" } })
- .addSymbolSource(test::StaticSymbolSourceBuilder()
- .addPublicSymbol("android:attr/layout_width", ResourceId(0x01010000),
- test::AttributeBuilder()
- .setTypeMask(android::ResTable_map::TYPE_ENUM |
- android::ResTable_map::TYPE_DIMENSION)
- .addItem("match_parent", 0xffffffff)
- .build())
- .addPublicSymbol("android:attr/background", ResourceId(0x01010001),
- test::AttributeBuilder()
- .setTypeMask(android::ResTable_map::TYPE_COLOR).build())
- .addPublicSymbol("android:attr/attr", ResourceId(0x01010002),
- test::AttributeBuilder().build())
- .addPublicSymbol("android:attr/text", ResourceId(0x01010003),
- test::AttributeBuilder()
- .setTypeMask(android::ResTable_map::TYPE_STRING)
- .build())
+ public:
+ void SetUp() override {
+ mContext =
+ test::ContextBuilder()
+ .setCompilationPackage("com.app.test")
+ .setNameManglerPolicy(
+ NameManglerPolicy{"com.app.test", {"com.android.support"}})
+ .addSymbolSource(
+ test::StaticSymbolSourceBuilder()
+ .addPublicSymbol(
+ "android:attr/layout_width", ResourceId(0x01010000),
+ test::AttributeBuilder()
+ .setTypeMask(android::ResTable_map::TYPE_ENUM |
+ android::ResTable_map::TYPE_DIMENSION)
+ .addItem("match_parent", 0xffffffff)
+ .build())
+ .addPublicSymbol(
+ "android:attr/background", ResourceId(0x01010001),
+ test::AttributeBuilder()
+ .setTypeMask(android::ResTable_map::TYPE_COLOR)
+ .build())
+ .addPublicSymbol("android:attr/attr",
+ ResourceId(0x01010002),
+ test::AttributeBuilder().build())
+ .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("android:attr/colorAccent", ResourceId(0x01010435),
- test::AttributeBuilder().build())
+ // Add one real symbol that was introduces in v21
+ .addPublicSymbol("android:attr/colorAccent",
+ ResourceId(0x01010435),
+ test::AttributeBuilder().build())
- // Private symbol.
- .addSymbol("android:color/hidden", ResourceId(0x01020001))
+ // Private symbol.
+ .addSymbol("android:color/hidden", ResourceId(0x01020001))
- .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("com.app.test:attr/com.android.support$colorAccent",
- ResourceId(0x7f010001), test::AttributeBuilder()
- .setTypeMask(android::ResTable_map::TYPE_COLOR).build())
- .addPublicSymbol("com.app.test:attr/attr", ResourceId(0x7f010002),
- test::AttributeBuilder().build())
- .build())
- .build();
- }
+ .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(
+ "com.app.test:attr/com.android.support$colorAccent",
+ ResourceId(0x7f010001),
+ test::AttributeBuilder()
+ .setTypeMask(android::ResTable_map::TYPE_COLOR)
+ .build())
+ .addPublicSymbol("com.app.test:attr/attr",
+ ResourceId(0x7f010002),
+ test::AttributeBuilder().build())
+ .build())
+ .build();
+ }
-protected:
- std::unique_ptr<IAaptContext> mContext;
+ protected:
+ std::unique_ptr<IAaptContext> mContext;
};
TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::buildXmlDomForPackageName(mContext.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="@color/green"
android:text="hello"
class="hello" />)EOF");
- XmlReferenceLinker linker;
- ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
- xml::Element* viewEl = xml::findRootElement(doc.get());
- ASSERT_NE(viewEl, nullptr);
+ xml::Element* viewEl = xml::findRootElement(doc.get());
+ ASSERT_NE(viewEl, nullptr);
- 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);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x01010000));
- ASSERT_NE(xmlAttr->compiledValue, nullptr);
- ASSERT_NE(valueCast<BinaryPrimitive>(xmlAttr->compiledValue.get()), nullptr);
+ 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);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(),
+ ResourceId(0x01010000));
+ ASSERT_NE(xmlAttr->compiledValue, nullptr);
+ ASSERT_NE(valueCast<BinaryPrimitive>(xmlAttr->compiledValue.get()), nullptr);
- xmlAttr = viewEl->findAttribute(xml::kSchemaAndroid, "background");
- ASSERT_NE(xmlAttr, nullptr);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x01010001));
- ASSERT_NE(xmlAttr->compiledValue, nullptr);
- Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->name);
- 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(xml::kSchemaAndroid, "background");
+ ASSERT_NE(xmlAttr, nullptr);
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(),
+ ResourceId(0x01010001));
+ ASSERT_NE(xmlAttr->compiledValue, nullptr);
+ Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->name);
+ 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(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(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("", "class");
- ASSERT_NE(xmlAttr, nullptr);
- AAPT_ASSERT_FALSE(xmlAttr->compiledAttribute);
- ASSERT_EQ(xmlAttr->compiledValue, nullptr);
+ xmlAttr = viewEl->findAttribute("", "class");
+ ASSERT_NE(xmlAttr, nullptr);
+ AAPT_ASSERT_FALSE(xmlAttr->compiledAttribute);
+ ASSERT_EQ(xmlAttr->compiledValue, nullptr);
}
TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreNotLinked) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::buildXmlDomForPackageName(mContext.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:colorAccent="@android:color/hidden" />)EOF");
- XmlReferenceLinker linker;
- ASSERT_FALSE(linker.consume(mContext.get(), doc.get()));
+ XmlReferenceLinker linker;
+ ASSERT_FALSE(linker.consume(mContext.get(), doc.get()));
}
-TEST_F(XmlReferenceLinkerTest, PrivateSymbolsAreLinkedWhenReferenceHasStarPrefix) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+TEST_F(XmlReferenceLinkerTest,
+ PrivateSymbolsAreLinkedWhenReferenceHasStarPrefix) {
+ std::unique_ptr<xml::XmlResource> doc =
+ test::buildXmlDomForPackageName(mContext.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:colorAccent="@*android:color/hidden" />)EOF");
- XmlReferenceLinker linker;
- ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
}
TEST_F(XmlReferenceLinkerTest, SdkLevelsAreRecorded) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::buildXmlDomForPackageName(mContext.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:colorAccent="#ffffff" />)EOF");
- XmlReferenceLinker linker;
- ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
- EXPECT_TRUE(linker.getSdkLevels().count(21) == 1);
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+ EXPECT_TRUE(linker.getSdkLevels().count(21) == 1);
}
TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::buildXmlDomForPackageName(mContext.get(), R"EOF(
<View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
support:colorAccent="#ff0000" />)EOF");
- XmlReferenceLinker linker;
- ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
- xml::Element* viewEl = xml::findRootElement(doc.get());
- ASSERT_NE(viewEl, nullptr);
+ xml::Element* viewEl = xml::findRootElement(doc.get());
+ ASSERT_NE(viewEl, nullptr);
- xml::Attribute* xmlAttr = viewEl->findAttribute(
- xml::buildPackageNamespace("com.android.support"), "colorAccent");
- ASSERT_NE(xmlAttr, nullptr);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010001));
- ASSERT_NE(valueCast<BinaryPrimitive>(xmlAttr->compiledValue.get()), nullptr);
+ xml::Attribute* xmlAttr = viewEl->findAttribute(
+ xml::buildPackageNamespace("com.android.support"), "colorAccent");
+ ASSERT_NE(xmlAttr, nullptr);
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(),
+ ResourceId(0x7f010001));
+ ASSERT_NE(valueCast<BinaryPrimitive>(xmlAttr->compiledValue.get()), nullptr);
}
TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::buildXmlDomForPackageName(mContext.get(), R"EOF(
<View xmlns:app="http://schemas.android.com/apk/res-auto"
app:colorAccent="@app:color/red" />)EOF");
- XmlReferenceLinker linker;
- ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
- xml::Element* viewEl = xml::findRootElement(doc.get());
- ASSERT_NE(viewEl, nullptr);
+ xml::Element* viewEl = xml::findRootElement(doc.get());
+ ASSERT_NE(viewEl, nullptr);
- xml::Attribute* xmlAttr = viewEl->findAttribute(xml::kSchemaAuto, "colorAccent");
- ASSERT_NE(xmlAttr, nullptr);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010000));
- Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->name);
- AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x7f020001));
+ xml::Attribute* xmlAttr =
+ viewEl->findAttribute(xml::kSchemaAuto, "colorAccent");
+ ASSERT_NE(xmlAttr, nullptr);
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(),
+ ResourceId(0x7f010000));
+ Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->name);
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x7f020001));
}
TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::buildXmlDomForPackageName(mContext.get(), R"EOF(
<View xmlns:app="http://schemas.android.com/apk/res/android"
app:attr="@app:id/id">
<View xmlns:app="http://schemas.android.com/apk/res/com.app.test"
app:attr="@app:id/id"/>
</View>)EOF");
- XmlReferenceLinker linker;
- ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
- xml::Element* viewEl = xml::findRootElement(doc.get());
- ASSERT_NE(viewEl, nullptr);
+ xml::Element* viewEl = xml::findRootElement(doc.get());
+ ASSERT_NE(viewEl, nullptr);
- // All attributes and references in this element should be referring to "android" (0x01).
- xml::Attribute* xmlAttr = viewEl->findAttribute(xml::kSchemaAndroid, "attr");
- ASSERT_NE(xmlAttr, nullptr);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
- AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x01010002));
- Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x01030000));
+ // All attributes and references in this element should be referring to
+ // "android" (0x01).
+ xml::Attribute* xmlAttr = viewEl->findAttribute(xml::kSchemaAndroid, "attr");
+ ASSERT_NE(xmlAttr, nullptr);
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(),
+ ResourceId(0x01010002));
+ Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x01030000));
- ASSERT_FALSE(viewEl->getChildElements().empty());
- viewEl = viewEl->getChildElements().front();
- ASSERT_NE(viewEl, nullptr);
+ ASSERT_FALSE(viewEl->getChildElements().empty());
+ viewEl = viewEl->getChildElements().front();
+ ASSERT_NE(viewEl, nullptr);
- // All attributes and references in this element should be referring to "com.app.test" (0x7f).
- 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);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010002));
- ref = valueCast<Reference>(xmlAttr->compiledValue.get());
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x7f030000));
+ // All attributes and references in this element should be referring to
+ // "com.app.test" (0x7f).
+ 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);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(),
+ ResourceId(0x7f010002));
+ ref = valueCast<Reference>(xmlAttr->compiledValue.get());
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x7f030000));
}
TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
- std::unique_ptr<xml::XmlResource> doc = test::buildXmlDomForPackageName(mContext.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc =
+ test::buildXmlDomForPackageName(mContext.get(), R"EOF(
<View xmlns:android="http://schemas.android.com/apk/res/com.app.test"
android:attr="@id/id"/>)EOF");
- XmlReferenceLinker linker;
- ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.consume(mContext.get(), doc.get()));
- xml::Element* viewEl = xml::findRootElement(doc.get());
- ASSERT_NE(viewEl, nullptr);
+ xml::Element* viewEl = xml::findRootElement(doc.get());
+ ASSERT_NE(viewEl, nullptr);
- // All attributes and references in this element should be referring to "com.app.test" (0x7f).
- 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);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010002));
- Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
- ASSERT_NE(ref, nullptr);
- AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x7f030000));
+ // All attributes and references in this element should be referring to
+ // "com.app.test" (0x7f).
+ 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);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(),
+ ResourceId(0x7f010002));
+ Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
+ ASSERT_NE(ref, nullptr);
+ AAPT_ASSERT_TRUE(ref->id);
+ EXPECT_EQ(ref->id.value(), ResourceId(0x7f030000));
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/process/IResourceTableConsumer.h b/tools/aapt2/process/IResourceTableConsumer.h
index a7e752a..a9bde39 100644
--- a/tools/aapt2/process/IResourceTableConsumer.h
+++ b/tools/aapt2/process/IResourceTableConsumer.h
@@ -33,21 +33,21 @@
class SymbolTable;
struct IAaptContext {
- virtual ~IAaptContext() = default;
+ virtual ~IAaptContext() = default;
- virtual SymbolTable* getExternalSymbols() = 0;
- virtual IDiagnostics* getDiagnostics() = 0;
- virtual const std::string& getCompilationPackage() = 0;
- virtual uint8_t getPackageId() = 0;
- virtual NameMangler* getNameMangler() = 0;
- virtual bool verbose() = 0;
- virtual int getMinSdkVersion() = 0;
+ virtual SymbolTable* getExternalSymbols() = 0;
+ virtual IDiagnostics* getDiagnostics() = 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 {
- virtual ~IResourceTableConsumer() = default;
+ virtual ~IResourceTableConsumer() = default;
- virtual bool consume(IAaptContext* context, ResourceTable* table) = 0;
+ virtual bool consume(IAaptContext* context, ResourceTable* table) = 0;
};
namespace xml {
@@ -55,11 +55,11 @@
}
struct IXmlResourceConsumer {
- virtual ~IXmlResourceConsumer() = default;
+ virtual ~IXmlResourceConsumer() = default;
- virtual bool consume(IAaptContext* context, xml::XmlResource* resource) = 0;
+ virtual bool consume(IAaptContext* context, xml::XmlResource* resource) = 0;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_PROCESS_IRESOURCETABLECONSUMER_H */
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 0c92718..00ffeb2 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -14,11 +14,11 @@
* limitations under the License.
*/
+#include "process/SymbolTable.h"
#include "ConfigDescription.h"
#include "Resource.h"
#include "ResourceUtils.h"
#include "ValueVisitor.h"
-#include "process/SymbolTable.h"
#include "util/Util.h"
#include <androidfw/AssetManager.h>
@@ -27,254 +27,273 @@
namespace aapt {
void SymbolTable::appendSource(std::unique_ptr<ISymbolSource> source) {
- mSources.push_back(std::move(source));
+ mSources.push_back(std::move(source));
- // We do not clear the cache, because sources earlier in the list take precedent.
+ // We do not clear the cache, because sources earlier in the list take
+ // precedent.
}
void SymbolTable::prependSource(std::unique_ptr<ISymbolSource> source) {
- mSources.insert(mSources.begin(), std::move(source));
+ mSources.insert(mSources.begin(), std::move(source));
- // We must clear the cache in case we did a lookup before adding this resource.
- mCache.clear();
+ // We must clear the cache in case we did a lookup before adding this
+ // resource.
+ mCache.clear();
}
const SymbolTable::Symbol* SymbolTable::findByName(const ResourceName& name) {
- if (const std::shared_ptr<Symbol>& s = mCache.get(name)) {
- return s.get();
- }
+ if (const std::shared_ptr<Symbol>& s = mCache.get(name)) {
+ return s.get();
+ }
- // We did not find it in the cache, so look through the sources.
- for (auto& symbolSource : mSources) {
- std::unique_ptr<Symbol> symbol = symbolSource->findByName(name);
- if (symbol) {
- // Take ownership of the symbol into a shared_ptr. We do this because LruCache
- // doesn't support unique_ptr.
- std::shared_ptr<Symbol> sharedSymbol = std::shared_ptr<Symbol>(symbol.release());
- mCache.put(name, sharedSymbol);
+ // We did not find it in the cache, so look through the sources.
+ for (auto& symbolSource : mSources) {
+ std::unique_ptr<Symbol> symbol = symbolSource->findByName(name);
+ if (symbol) {
+ // Take ownership of the symbol into a shared_ptr. We do this because
+ // LruCache
+ // doesn't support unique_ptr.
+ std::shared_ptr<Symbol> sharedSymbol =
+ std::shared_ptr<Symbol>(symbol.release());
+ mCache.put(name, sharedSymbol);
- if (sharedSymbol->id) {
- // The symbol has an ID, so we can also cache this!
- mIdCache.put(sharedSymbol->id.value(), sharedSymbol);
- }
- return sharedSymbol.get();
- }
+ if (sharedSymbol->id) {
+ // The symbol has an ID, so we can also cache this!
+ mIdCache.put(sharedSymbol->id.value(), sharedSymbol);
+ }
+ return sharedSymbol.get();
}
- return nullptr;
+ }
+ return nullptr;
}
const SymbolTable::Symbol* SymbolTable::findById(const ResourceId& id) {
- if (const std::shared_ptr<Symbol>& s = mIdCache.get(id)) {
- return s.get();
- }
+ if (const std::shared_ptr<Symbol>& s = mIdCache.get(id)) {
+ return s.get();
+ }
- // We did not find it in the cache, so look through the sources.
- for (auto& symbolSource : mSources) {
- std::unique_ptr<Symbol> symbol = symbolSource->findById(id);
- if (symbol) {
- // Take ownership of the symbol into a shared_ptr. We do this because LruCache
- // doesn't support unique_ptr.
- std::shared_ptr<Symbol> sharedSymbol = std::shared_ptr<Symbol>(symbol.release());
- mIdCache.put(id, sharedSymbol);
- return sharedSymbol.get();
- }
+ // We did not find it in the cache, so look through the sources.
+ for (auto& symbolSource : mSources) {
+ std::unique_ptr<Symbol> symbol = symbolSource->findById(id);
+ if (symbol) {
+ // Take ownership of the symbol into a shared_ptr. We do this because
+ // LruCache
+ // doesn't support unique_ptr.
+ std::shared_ptr<Symbol> sharedSymbol =
+ std::shared_ptr<Symbol>(symbol.release());
+ mIdCache.put(id, sharedSymbol);
+ return sharedSymbol.get();
}
- return nullptr;
+ }
+ return nullptr;
}
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 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
- // succeeded to lookup by ID. Subsequent lookups will miss then hit.
- const SymbolTable::Symbol* symbol = nullptr;
- if (ref.id) {
- symbol = findById(ref.id.value());
- }
+ // 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 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
+ // succeeded to lookup by ID. Subsequent lookups will miss then hit.
+ const SymbolTable::Symbol* symbol = nullptr;
+ if (ref.id) {
+ symbol = findById(ref.id.value());
+ }
- if (ref.name && !symbol) {
- symbol = findByName(ref.name.value());
- }
- return symbol;
+ if (ref.name && !symbol) {
+ symbol = findByName(ref.name.value());
+ }
+ return symbol;
}
std::unique_ptr<SymbolTable::Symbol> ResourceTableSymbolSource::findByName(
- const ResourceName& name) {
- Maybe<ResourceTable::SearchResult> result = mTable->findResource(name);
- if (!result) {
- if (name.type == ResourceType::kAttr) {
- // Recurse and try looking up a private attribute.
- return findByName(ResourceName(name.package, ResourceType::kAttrPrivate, name.entry));
- }
+ const ResourceName& name) {
+ Maybe<ResourceTable::SearchResult> result = mTable->findResource(name);
+ if (!result) {
+ if (name.type == ResourceType::kAttr) {
+ // Recurse and try looking up a private attribute.
+ return findByName(
+ ResourceName(name.package, ResourceType::kAttrPrivate, name.entry));
+ }
+ return {};
+ }
+
+ ResourceTable::SearchResult sr = result.value();
+
+ std::unique_ptr<SymbolTable::Symbol> symbol =
+ util::make_unique<SymbolTable::Symbol>();
+ symbol->isPublic = (sr.entry->symbolStatus.state == SymbolState::kPublic);
+
+ if (sr.package->id && sr.type->id && sr.entry->id) {
+ symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(),
+ sr.entry->id.value());
+ }
+
+ if (name.type == ResourceType::kAttr ||
+ name.type == ResourceType::kAttrPrivate) {
+ const ConfigDescription kDefaultConfig;
+ ResourceConfigValue* configValue = sr.entry->findValue(kDefaultConfig);
+ if (configValue) {
+ // This resource has an Attribute.
+ if (Attribute* attr = valueCast<Attribute>(configValue->value.get())) {
+ symbol->attribute = std::make_shared<Attribute>(*attr);
+ } else {
return {};
+ }
}
-
- ResourceTable::SearchResult sr = result.value();
-
- std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>();
- symbol->isPublic = (sr.entry->symbolStatus.state == SymbolState::kPublic);
-
- if (sr.package->id && sr.type->id && sr.entry->id) {
- symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value());
- }
-
- if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
- const ConfigDescription kDefaultConfig;
- ResourceConfigValue* configValue = sr.entry->findValue(kDefaultConfig);
- if (configValue) {
- // This resource has an Attribute.
- if (Attribute* attr = valueCast<Attribute>(configValue->value.get())) {
- symbol->attribute = std::make_shared<Attribute>(*attr);
- } else {
- return {};
- }
- }
- }
- return symbol;
+ }
+ return symbol;
}
bool AssetManagerSymbolSource::addAssetPath(const StringPiece& path) {
- int32_t cookie = 0;
- return mAssets.addAssetPath(android::String8(path.data(), path.size()), &cookie);
+ int32_t cookie = 0;
+ return mAssets.addAssetPath(android::String8(path.data(), path.size()),
+ &cookie);
}
-static std::unique_ptr<SymbolTable::Symbol> lookupAttributeInTable(const android::ResTable& table,
- ResourceId id) {
- // Try as a bag.
- const android::ResTable::bag_entry* entry;
- ssize_t count = table.lockBag(id.id, &entry);
- if (count < 0) {
- table.unlockBag(entry);
- return nullptr;
+static std::unique_ptr<SymbolTable::Symbol> lookupAttributeInTable(
+ const android::ResTable& table, ResourceId id) {
+ // Try as a bag.
+ const android::ResTable::bag_entry* entry;
+ ssize_t count = table.lockBag(id.id, &entry);
+ if (count < 0) {
+ table.unlockBag(entry);
+ return nullptr;
+ }
+
+ // We found a resource.
+ std::unique_ptr<SymbolTable::Symbol> s =
+ util::make_unique<SymbolTable::Symbol>();
+ s->id = id;
+
+ // Check to see if it is an attribute.
+ for (size_t i = 0; i < (size_t)count; i++) {
+ if (entry[i].map.name.ident == android::ResTable_map::ATTR_TYPE) {
+ s->attribute = std::make_shared<Attribute>(false);
+ s->attribute->typeMask = entry[i].map.value.data;
+ break;
}
+ }
- // We found a resource.
- std::unique_ptr<SymbolTable::Symbol> s = util::make_unique<SymbolTable::Symbol>();
- s->id = id;
-
- // Check to see if it is an attribute.
- for (size_t i = 0; i < (size_t) count; i++) {
- if (entry[i].map.name.ident == android::ResTable_map::ATTR_TYPE) {
- s->attribute = std::make_shared<Attribute>(false);
- s->attribute->typeMask = entry[i].map.value.data;
+ if (s->attribute) {
+ for (size_t i = 0; i < (size_t)count; i++) {
+ const android::ResTable_map& mapEntry = entry[i].map;
+ if (Res_INTERNALID(mapEntry.name.ident)) {
+ switch (mapEntry.name.ident) {
+ case android::ResTable_map::ATTR_MIN:
+ s->attribute->minInt = static_cast<int32_t>(mapEntry.value.data);
+ break;
+ case android::ResTable_map::ATTR_MAX:
+ s->attribute->maxInt = static_cast<int32_t>(mapEntry.value.data);
break;
}
+ continue;
+ }
+
+ android::ResTable::resource_name entryName;
+ if (!table.getResourceName(mapEntry.name.ident, false, &entryName)) {
+ table.unlockBag(entry);
+ return nullptr;
+ }
+
+ Maybe<ResourceName> parsedName = ResourceUtils::toResourceName(entryName);
+ if (!parsedName) {
+ return nullptr;
+ }
+
+ Attribute::Symbol symbol;
+ 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));
}
-
- if (s->attribute) {
- for (size_t i = 0; i < (size_t) count; i++) {
- const android::ResTable_map& mapEntry = entry[i].map;
- if (Res_INTERNALID(mapEntry.name.ident)) {
- switch (mapEntry.name.ident) {
- case android::ResTable_map::ATTR_MIN:
- s->attribute->minInt = static_cast<int32_t>(mapEntry.value.data);
- break;
- case android::ResTable_map::ATTR_MAX:
- s->attribute->maxInt = static_cast<int32_t>(mapEntry.value.data);
- break;
- }
- continue;
- }
-
- android::ResTable::resource_name entryName;
- if (!table.getResourceName(mapEntry.name.ident, false, &entryName)) {
- table.unlockBag(entry);
- return nullptr;
- }
-
- Maybe<ResourceName> parsedName = ResourceUtils::toResourceName(entryName);
- if (!parsedName) {
- return nullptr;
- }
-
- Attribute::Symbol symbol;
- 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));
- }
- }
- table.unlockBag(entry);
- return s;
+ }
+ table.unlockBag(entry);
+ return s;
}
std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findByName(
- const ResourceName& name) {
- const android::ResTable& table = mAssets.getResources(false);
+ const ResourceName& name) {
+ const android::ResTable& table = mAssets.getResources(false);
- 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);
+ 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(entry16.data(), entry16.size(),
- type16.data(), type16.size(),
- package16.data(), package16.size(),
- &typeSpecFlags);
- if (!resId.isValid()) {
- return {};
- }
-
- std::unique_ptr<SymbolTable::Symbol> s;
- if (name.type == ResourceType::kAttr) {
- s = lookupAttributeInTable(table, resId);
- } else {
- s = util::make_unique<SymbolTable::Symbol>();
- s->id = resId;
- }
-
- if (s) {
- s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
- return s;
- }
+ uint32_t typeSpecFlags = 0;
+ ResourceId resId = table.identifierForName(
+ entry16.data(), entry16.size(), type16.data(), type16.size(),
+ package16.data(), package16.size(), &typeSpecFlags);
+ if (!resId.isValid()) {
return {};
+ }
+
+ std::unique_ptr<SymbolTable::Symbol> s;
+ if (name.type == ResourceType::kAttr) {
+ s = lookupAttributeInTable(table, resId);
+ } else {
+ s = util::make_unique<SymbolTable::Symbol>();
+ s->id = resId;
+ }
+
+ if (s) {
+ s->isPublic =
+ (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
+ return s;
+ }
+ return {};
}
-static Maybe<ResourceName> getResourceName(const android::ResTable& table, ResourceId id) {
- android::ResTable::resource_name resName = {};
- if (!table.getResourceName(id.id, true, &resName)) {
- return {};
- }
- return ResourceUtils::toResourceName(resName);
+static Maybe<ResourceName> getResourceName(const android::ResTable& table,
+ ResourceId id) {
+ android::ResTable::resource_name resName = {};
+ if (!table.getResourceName(id.id, true, &resName)) {
+ return {};
+ }
+ return ResourceUtils::toResourceName(resName);
}
-std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findById(ResourceId id) {
- const android::ResTable& table = mAssets.getResources(false);
- Maybe<ResourceName> maybeName = getResourceName(table, id);
- if (!maybeName) {
- return {};
- }
-
- uint32_t typeSpecFlags = 0;
- table.getResourceFlags(id.id, &typeSpecFlags);
-
- std::unique_ptr<SymbolTable::Symbol> s;
- if (maybeName.value().type == ResourceType::kAttr) {
- s = lookupAttributeInTable(table, id);
- } else {
- s = util::make_unique<SymbolTable::Symbol>();
- s->id = id;
- }
-
- if (s) {
- s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
- return s;
- }
+std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findById(
+ ResourceId id) {
+ const android::ResTable& table = mAssets.getResources(false);
+ Maybe<ResourceName> maybeName = getResourceName(table, id);
+ if (!maybeName) {
return {};
+ }
+
+ uint32_t typeSpecFlags = 0;
+ table.getResourceFlags(id.id, &typeSpecFlags);
+
+ std::unique_ptr<SymbolTable::Symbol> s;
+ if (maybeName.value().type == ResourceType::kAttr) {
+ s = lookupAttributeInTable(table, id);
+ } else {
+ s = util::make_unique<SymbolTable::Symbol>();
+ s->id = id;
+ }
+
+ if (s) {
+ s->isPublic =
+ (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
+ return s;
+ }
+ return {};
}
std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findByReference(
- const Reference& ref) {
- // AssetManager always prefers IDs.
- if (ref.id) {
- return findById(ref.id.value());
- } else if (ref.name) {
- return findByName(ref.name.value());
- }
- return {};
+ const Reference& ref) {
+ // AssetManager always prefers IDs.
+ if (ref.id) {
+ return findById(ref.id.value());
+ } else if (ref.name) {
+ return findByName(ref.name.value());
+ }
+ return {};
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index bd31416..89d87de 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -34,99 +34,100 @@
namespace aapt {
inline android::hash_t hash_type(const ResourceName& name) {
- 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);
- hash = android::JenkinsHashMix(hash, (uint32_t) strHash(name.entry));
- return hash;
+ 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);
+ hash = android::JenkinsHashMix(hash, (uint32_t)strHash(name.entry));
+ return hash;
}
inline android::hash_t hash_type(const ResourceId& id) {
- return android::hash_type(id.id);
+ return android::hash_type(id.id);
}
class ISymbolSource;
class SymbolTable {
-public:
- struct Symbol {
- Symbol() : Symbol(Maybe<ResourceId>{}) {
- }
+ public:
+ struct Symbol {
+ Symbol() : Symbol(Maybe<ResourceId>{}) {}
- explicit Symbol(const Maybe<ResourceId>& i) : Symbol(i, nullptr) {
- }
+ explicit Symbol(const Maybe<ResourceId>& i) : Symbol(i, nullptr) {}
- Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr) :
- Symbol(i, attr, false) {
- }
+ Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr)
+ : Symbol(i, attr, false) {}
- Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr, bool pub) :
- id(i), attribute(attr), isPublic(pub) {
- }
+ Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr,
+ bool pub)
+ : id(i), attribute(attr), isPublic(pub) {}
- Symbol(const Symbol&) = default;
- Symbol(Symbol&&) = default;
- Symbol& operator=(const Symbol&) = default;
- Symbol& operator=(Symbol&&) = default;
+ Symbol(const Symbol&) = default;
+ Symbol(Symbol&&) = default;
+ Symbol& operator=(const Symbol&) = default;
+ Symbol& operator=(Symbol&&) = default;
- Maybe<ResourceId> id;
- std::shared_ptr<Attribute> attribute;
- bool isPublic = false;
- };
+ Maybe<ResourceId> id;
+ std::shared_ptr<Attribute> attribute;
+ bool isPublic = false;
+ };
- SymbolTable() : mCache(200), mIdCache(200) {
- }
+ SymbolTable() : mCache(200), mIdCache(200) {}
- void appendSource(std::unique_ptr<ISymbolSource> source);
- void prependSource(std::unique_ptr<ISymbolSource> source);
+ void appendSource(std::unique_ptr<ISymbolSource> source);
+ void prependSource(std::unique_ptr<ISymbolSource> source);
- /**
- * Never hold on to the result between calls to findByName or findById. The results
- * are typically stored in a cache which may evict entries.
- */
- const Symbol* findByName(const ResourceName& name);
- const Symbol* findById(const ResourceId& id);
+ /**
+ * Never hold on to the result between calls to findByName or findById. The
+ * results
+ * are typically stored in a cache which may evict entries.
+ */
+ const Symbol* findByName(const ResourceName& name);
+ const Symbol* findById(const ResourceId& id);
- /**
- * Let's the ISymbolSource decide whether looking up by name or ID is faster, if both
- * are available.
- */
- const Symbol* findByReference(const Reference& ref);
+ /**
+ * Let's the ISymbolSource decide whether looking up by name or ID is faster,
+ * if both
+ * are available.
+ */
+ const Symbol* findByReference(const Reference& ref);
-private:
- std::vector<std::unique_ptr<ISymbolSource>> mSources;
+ private:
+ std::vector<std::unique_ptr<ISymbolSource>> mSources;
- // We use shared_ptr because unique_ptr is not supported and
- // we need automatic deletion.
- android::LruCache<ResourceName, std::shared_ptr<Symbol>> mCache;
- android::LruCache<ResourceId, std::shared_ptr<Symbol>> mIdCache;
+ // We use shared_ptr because unique_ptr is not supported and
+ // we need automatic deletion.
+ android::LruCache<ResourceName, std::shared_ptr<Symbol>> mCache;
+ android::LruCache<ResourceId, std::shared_ptr<Symbol>> mIdCache;
- DISALLOW_COPY_AND_ASSIGN(SymbolTable);
+ DISALLOW_COPY_AND_ASSIGN(SymbolTable);
};
/**
- * An interface that a symbol source implements in order to surface symbol information
+ * An interface that a symbol source implements in order to surface symbol
+ * information
* to the symbol table.
*/
class ISymbolSource {
-public:
- virtual ~ISymbolSource() = default;
+ public:
+ virtual ~ISymbolSource() = default;
- virtual std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) = 0;
- virtual std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) = 0;
+ virtual std::unique_ptr<SymbolTable::Symbol> findByName(
+ const ResourceName& name) = 0;
+ virtual std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) = 0;
- /**
- * Default implementation tries the name if it exists, else the ID.
- */
- virtual std::unique_ptr<SymbolTable::Symbol> findByReference(const Reference& ref) {
- if (ref.name) {
- return findByName(ref.name.value());
- } else if (ref.id) {
- return findById(ref.id.value());
- }
- return {};
+ /**
+ * Default implementation tries the name if it exists, else the ID.
+ */
+ virtual std::unique_ptr<SymbolTable::Symbol> findByReference(
+ const Reference& ref) {
+ if (ref.name) {
+ return findByName(ref.name.value());
+ } else if (ref.id) {
+ return findById(ref.id.value());
}
+ return {};
+ }
};
/**
@@ -135,38 +136,40 @@
* Lookups by ID are ignored.
*/
class ResourceTableSymbolSource : public ISymbolSource {
-public:
- explicit ResourceTableSymbolSource(ResourceTable* table) : mTable(table) {
- }
+ public:
+ explicit ResourceTableSymbolSource(ResourceTable* table) : mTable(table) {}
- std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override;
+ std::unique_ptr<SymbolTable::Symbol> findByName(
+ const ResourceName& name) override;
- std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override {
- return {};
- }
+ std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override {
+ return {};
+ }
-private:
- ResourceTable* mTable;
+ private:
+ ResourceTable* mTable;
- DISALLOW_COPY_AND_ASSIGN(ResourceTableSymbolSource);
+ DISALLOW_COPY_AND_ASSIGN(ResourceTableSymbolSource);
};
class AssetManagerSymbolSource : public ISymbolSource {
-public:
- AssetManagerSymbolSource() = default;
+ public:
+ AssetManagerSymbolSource() = default;
- bool addAssetPath(const StringPiece& path);
+ bool addAssetPath(const StringPiece& path);
- std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override;
- std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override;
- std::unique_ptr<SymbolTable::Symbol> findByReference(const Reference& ref) override;
+ std::unique_ptr<SymbolTable::Symbol> findByName(
+ const ResourceName& name) override;
+ std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override;
+ std::unique_ptr<SymbolTable::Symbol> findByReference(
+ const Reference& ref) override;
-private:
- android::AssetManager mAssets;
+ private:
+ android::AssetManager mAssets;
- DISALLOW_COPY_AND_ASSIGN(AssetManagerSymbolSource);
+ DISALLOW_COPY_AND_ASSIGN(AssetManagerSymbolSource);
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_PROCESS_SYMBOLTABLE_H */
diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp
index 1626352..00474f7 100644
--- a/tools/aapt2/process/SymbolTable_test.cpp
+++ b/tools/aapt2/process/SymbolTable_test.cpp
@@ -20,34 +20,38 @@
namespace aapt {
TEST(ResourceTableSymbolSourceTest, FindSymbols) {
- std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
- .addSimple("android:id/foo", ResourceId(0x01020000))
- .addSimple("android:id/bar")
- .addValue("android:attr/foo", ResourceId(0x01010000),
- test::AttributeBuilder().build())
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .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("android:id/foo")));
- EXPECT_NE(nullptr, symbolSource.findByName(test::parseNameOrDie("android:id/bar")));
+ ResourceTableSymbolSource symbolSource(table.get());
+ 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("android:attr/foo"));
- ASSERT_NE(nullptr, s);
- EXPECT_NE(nullptr, s->attribute);
+ std::unique_ptr<SymbolTable::Symbol> s =
+ symbolSource.findByName(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("android:^attr-private/foo", ResourceId(0x01010000),
- test::AttributeBuilder().build())
- .build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .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("android:attr/foo"));
- ASSERT_NE(nullptr, s);
- EXPECT_NE(nullptr, s->attribute);
+ ResourceTableSymbolSource symbolSource(table.get());
+ std::unique_ptr<SymbolTable::Symbol> s =
+ symbolSource.findByName(test::parseNameOrDie("android:attr/foo"));
+ ASSERT_NE(nullptr, s);
+ EXPECT_NE(nullptr, s->attribute);
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/aapt2/proto/ProtoHelpers.h b/tools/aapt2/proto/ProtoHelpers.h
index 02e67f1..7271e8b 100644
--- a/tools/aapt2/proto/ProtoHelpers.h
+++ b/tools/aapt2/proto/ProtoHelpers.h
@@ -30,14 +30,18 @@
void serializeStringPoolToPb(const StringPool& pool, pb::StringPool* outPbPool);
-void serializeSourceToPb(const Source& source, StringPool* srcPool, pb::Source* outPbSource);
-void deserializeSourceFromPb(const pb::Source& pbSource, const android::ResStringPool& srcPool,
+void serializeSourceToPb(const Source& source, StringPool* srcPool,
+ pb::Source* outPbSource);
+void deserializeSourceFromPb(const pb::Source& pbSource,
+ const android::ResStringPool& srcPool,
Source* outSource);
pb::SymbolStatus_Visibility serializeVisibilityToPb(SymbolState state);
-SymbolState deserializeVisibilityFromPb(pb::SymbolStatus_Visibility pbVisibility);
+SymbolState deserializeVisibilityFromPb(
+ pb::SymbolStatus_Visibility pbVisibility);
-void serializeConfig(const ConfigDescription& config, pb::ConfigDescription* outPbConfig);
+void serializeConfig(const ConfigDescription& config,
+ pb::ConfigDescription* outPbConfig);
bool deserializeConfigDescriptionFromPb(const pb::ConfigDescription& pbConfig,
ConfigDescription* outConfig);
@@ -47,6 +51,6 @@
pb::Plural_Arity serializePluralEnumToPb(size_t pluralIdx);
size_t deserializePluralEnumFromPb(pb::Plural_Arity arity);
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_PROTO_PROTOHELPERS_H */
diff --git a/tools/aapt2/proto/ProtoSerialize.h b/tools/aapt2/proto/ProtoSerialize.h
index cc7c251..378dafd 100644
--- a/tools/aapt2/proto/ProtoSerialize.h
+++ b/tools/aapt2/proto/ProtoSerialize.h
@@ -29,49 +29,49 @@
namespace aapt {
class CompiledFileOutputStream {
-public:
- explicit CompiledFileOutputStream(google::protobuf::io::ZeroCopyOutputStream* out);
+ public:
+ explicit CompiledFileOutputStream(
+ google::protobuf::io::ZeroCopyOutputStream* out);
- void WriteLittleEndian32(uint32_t value);
- void WriteCompiledFile(const pb::CompiledFile* compiledFile);
- void WriteData(const BigBuffer* buffer);
- void WriteData(const void* data, size_t len);
- bool HadError();
+ void WriteLittleEndian32(uint32_t value);
+ void WriteCompiledFile(const pb::CompiledFile* compiledFile);
+ void WriteData(const BigBuffer* buffer);
+ void WriteData(const void* data, size_t len);
+ bool HadError();
-private:
- DISALLOW_COPY_AND_ASSIGN(CompiledFileOutputStream);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CompiledFileOutputStream);
- void ensureAlignedWrite();
+ void ensureAlignedWrite();
- google::protobuf::io::CodedOutputStream mOut;
+ google::protobuf::io::CodedOutputStream mOut;
};
class CompiledFileInputStream {
-public:
- explicit CompiledFileInputStream(const void* data, size_t size);
+ public:
+ explicit CompiledFileInputStream(const void* data, size_t size);
- bool ReadLittleEndian32(uint32_t* outVal);
- bool ReadCompiledFile(pb::CompiledFile* outVal);
- bool ReadDataMetaData(uint64_t* outOffset, uint64_t* outLen);
+ bool ReadLittleEndian32(uint32_t* outVal);
+ bool ReadCompiledFile(pb::CompiledFile* outVal);
+ bool ReadDataMetaData(uint64_t* outOffset, uint64_t* outLen);
-private:
- DISALLOW_COPY_AND_ASSIGN(CompiledFileInputStream);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CompiledFileInputStream);
- void ensureAlignedRead();
+ void ensureAlignedRead();
- google::protobuf::io::CodedInputStream mIn;
+ google::protobuf::io::CodedInputStream mIn;
};
std::unique_ptr<pb::ResourceTable> serializeTableToPb(ResourceTable* table);
-std::unique_ptr<ResourceTable> deserializeTableFromPb(const pb::ResourceTable& pbTable,
- const Source& source,
- IDiagnostics* diag);
+std::unique_ptr<ResourceTable> deserializeTableFromPb(
+ const pb::ResourceTable& pbTable, const Source& source, IDiagnostics* diag);
-std::unique_ptr<pb::CompiledFile> serializeCompiledFileToPb(const ResourceFile& file);
-std::unique_ptr<ResourceFile> deserializeCompiledFileFromPb(const pb::CompiledFile& pbFile,
- const Source& source,
- IDiagnostics* diag);
+std::unique_ptr<pb::CompiledFile> serializeCompiledFileToPb(
+ const ResourceFile& file);
+std::unique_ptr<ResourceFile> deserializeCompiledFileFromPb(
+ const pb::CompiledFile& pbFile, const Source& source, IDiagnostics* diag);
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_FLATTEN_TABLEPROTOSERIALIZER_H */
diff --git a/tools/aapt2/split/TableSplitter.h b/tools/aapt2/split/TableSplitter.h
index 2fa5c47..d7ecc82 100644
--- a/tools/aapt2/split/TableSplitter.h
+++ b/tools/aapt2/split/TableSplitter.h
@@ -29,50 +29,49 @@
namespace aapt {
struct SplitConstraints {
- std::set<ConfigDescription> configs;
+ std::set<ConfigDescription> configs;
};
struct TableSplitterOptions {
- /**
- * The preferred density to keep in the table, stripping out all others.
- */
- Maybe<uint16_t> preferredDensity;
+ /**
+ * The preferred density to keep in the table, stripping out all others.
+ */
+ Maybe<uint16_t> preferredDensity;
- /**
- * Configuration filter that determines which resource configuration values end up in
- * the final table.
- */
- IConfigFilter* configFilter = nullptr;
+ /**
+ * Configuration filter that determines which resource configuration values
+ * end up in
+ * the final table.
+ */
+ IConfigFilter* configFilter = nullptr;
};
class TableSplitter {
-public:
- TableSplitter(const std::vector<SplitConstraints>& splits,
- const TableSplitterOptions& options) :
- mSplitConstraints(splits), mPreferredDensity(options.preferredDensity),
- mConfigFilter(options.configFilter) {
- for (size_t i = 0; i < mSplitConstraints.size(); i++) {
- mSplits.push_back(util::make_unique<ResourceTable>());
- }
+ public:
+ TableSplitter(const std::vector<SplitConstraints>& splits,
+ const TableSplitterOptions& options)
+ : mSplitConstraints(splits),
+ mPreferredDensity(options.preferredDensity),
+ mConfigFilter(options.configFilter) {
+ for (size_t i = 0; i < mSplitConstraints.size(); i++) {
+ mSplits.push_back(util::make_unique<ResourceTable>());
}
+ }
- bool verifySplitConstraints(IAaptContext* context);
+ bool verifySplitConstraints(IAaptContext* context);
- void splitTable(ResourceTable* originalTable);
+ void splitTable(ResourceTable* originalTable);
- std::vector<std::unique_ptr<ResourceTable>>& getSplits() {
- return mSplits;
- }
+ std::vector<std::unique_ptr<ResourceTable>>& getSplits() { return mSplits; }
-private:
- std::vector<SplitConstraints> mSplitConstraints;
- std::vector<std::unique_ptr<ResourceTable>> mSplits;
- Maybe<uint16_t> mPreferredDensity;
- IConfigFilter* mConfigFilter;
+ private:
+ std::vector<SplitConstraints> mSplitConstraints;
+ std::vector<std::unique_ptr<ResourceTable>> mSplits;
+ Maybe<uint16_t> mPreferredDensity;
+ IConfigFilter* mConfigFilter;
- DISALLOW_COPY_AND_ASSIGN(TableSplitter);
+ DISALLOW_COPY_AND_ASSIGN(TableSplitter);
};
-
}
#endif /* AAPT_SPLIT_TABLESPLITTER_H */
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 637e991..c647159 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -29,232 +29,242 @@
namespace test {
class ResourceTableBuilder {
-private:
- DummyDiagnosticsImpl mDiagnostics;
- std::unique_ptr<ResourceTable> mTable = util::make_unique<ResourceTable>();
+ private:
+ DummyDiagnosticsImpl mDiagnostics;
+ std::unique_ptr<ResourceTable> mTable = util::make_unique<ResourceTable>();
-public:
- ResourceTableBuilder() = default;
+ public:
+ ResourceTableBuilder() = default;
- StringPool* getStringPool() {
- return &mTable->stringPool;
- }
+ StringPool* getStringPool() { return &mTable->stringPool; }
- ResourceTableBuilder& setPackageId(const StringPiece& packageName, uint8_t id) {
- ResourceTablePackage* package = mTable->createPackage(packageName, id);
- assert(package);
- return *this;
- }
+ ResourceTableBuilder& setPackageId(const StringPiece& packageName,
+ uint8_t id) {
+ ResourceTablePackage* package = mTable->createPackage(packageName, id);
+ assert(package);
+ return *this;
+ }
- ResourceTableBuilder& addSimple(const StringPiece& name, const ResourceId& id = {}) {
- return addValue(name, id, util::make_unique<Id>());
- }
+ ResourceTableBuilder& addSimple(const StringPiece& name,
+ const ResourceId& id = {}) {
+ return addValue(name, id, util::make_unique<Id>());
+ }
- ResourceTableBuilder& addSimple(const StringPiece& name, const ConfigDescription& config,
- const ResourceId& id = {}) {
- return addValue(name, config, id, util::make_unique<Id>());
- }
+ 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 StringPiece& name,
+ const StringPiece& ref) {
+ return addReference(name, {}, ref);
+ }
- ResourceTableBuilder& addReference(const StringPiece& name, const ResourceId& id,
- const StringPiece& ref) {
- return addValue(name, id, util::make_unique<Reference>(parseNameOrDie(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 StringPiece& name, const StringPiece& str) {
- return addString(name, {}, str);
- }
+ ResourceTableBuilder& addString(const StringPiece& name,
+ const StringPiece& str) {
+ return addString(name, {}, 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 StringPiece& name, const ResourceId& id,
+ const StringPiece& str) {
+ return addValue(name, id,
+ util::make_unique<String>(mTable->stringPool.makeRef(str)));
+ }
- 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& 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 StringPiece& name, const StringPiece& path) {
- return addFileReference(name, {}, path);
- }
+ ResourceTableBuilder& addFileReference(const StringPiece& name,
+ const StringPiece& path) {
+ return addFileReference(name, {}, 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 StringPiece& name,
+ const ResourceId& id,
+ const StringPiece& path) {
+ return addValue(name, id, util::make_unique<FileReference>(
+ mTable->stringPool.makeRef(path)));
+ }
- ResourceTableBuilder& addFileReference(const StringPiece& name, const StringPiece& path,
- const ConfigDescription& config) {
- return addValue(name, config, {},
- util::make_unique<FileReference>(mTable->stringPool.makeRef(path)));
- }
+ ResourceTableBuilder& addFileReference(const StringPiece& name,
+ const StringPiece& path,
+ const ConfigDescription& config) {
+ return addValue(name, config, {}, util::make_unique<FileReference>(
+ mTable->stringPool.makeRef(path)));
+ }
- ResourceTableBuilder& addValue(const StringPiece& name,
- std::unique_ptr<Value> value) {
- return addValue(name, {}, std::move(value));
- }
+ ResourceTableBuilder& addValue(const StringPiece& name,
+ std::unique_ptr<Value> value) {
+ return addValue(name, {}, 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 StringPiece& name, const ResourceId& id,
+ std::unique_ptr<Value> value) {
+ return addValue(name, {}, id, std::move(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::move(value), &mDiagnostics);
- assert(result);
- return *this;
- }
+ 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::move(value), &mDiagnostics);
+ assert(result);
+ return *this;
+ }
- ResourceTableBuilder& setSymbolState(const StringPiece& name, const ResourceId& id,
- SymbolState state) {
- ResourceName resName = parseNameOrDie(name);
- Symbol symbol;
- symbol.state = state;
- bool result = mTable->setSymbolStateAllowMangled(resName, id, symbol, &mDiagnostics);
- assert(result);
- return *this;
- }
+ ResourceTableBuilder& setSymbolState(const StringPiece& name,
+ const ResourceId& id,
+ SymbolState state) {
+ ResourceName resName = parseNameOrDie(name);
+ Symbol symbol;
+ symbol.state = state;
+ bool result =
+ mTable->setSymbolStateAllowMangled(resName, id, symbol, &mDiagnostics);
+ assert(result);
+ return *this;
+ }
- std::unique_ptr<ResourceTable> build() {
- return std::move(mTable);
- }
+ std::unique_ptr<ResourceTable> build() { return std::move(mTable); }
};
-inline std::unique_ptr<Reference> buildReference(const StringPiece& ref,
- const Maybe<ResourceId>& id = {}) {
- std::unique_ptr<Reference> reference = util::make_unique<Reference>(parseNameOrDie(ref));
- reference->id = id;
- return reference;
+inline std::unique_ptr<Reference> buildReference(
+ const StringPiece& ref, const Maybe<ResourceId>& id = {}) {
+ std::unique_ptr<Reference> reference =
+ util::make_unique<Reference>(parseNameOrDie(ref));
+ reference->id = id;
+ return reference;
}
-inline std::unique_ptr<BinaryPrimitive> buildPrimitive(uint8_t type, uint32_t data) {
- android::Res_value value = {};
- value.size = sizeof(value);
- value.dataType = type;
- value.data = data;
- return util::make_unique<BinaryPrimitive>(value);
+inline std::unique_ptr<BinaryPrimitive> buildPrimitive(uint8_t type,
+ uint32_t data) {
+ android::Res_value value = {};
+ value.size = sizeof(value);
+ value.dataType = type;
+ value.data = data;
+ return util::make_unique<BinaryPrimitive>(value);
}
template <typename T>
class ValueBuilder {
-private:
- std::unique_ptr<Value> mValue;
+ private:
+ std::unique_ptr<Value> mValue;
-public:
- template <typename... Args>
- explicit ValueBuilder(Args&&... args) : mValue(new T{ std::forward<Args>(args)... }) {
- }
+ public:
+ template <typename... Args>
+ explicit ValueBuilder(Args&&... args)
+ : mValue(new T{std::forward<Args>(args)...}) {}
- template <typename... Args>
- ValueBuilder& setSource(Args&&... args) {
- mValue->setSource(Source{ std::forward<Args>(args)... });
- return *this;
- }
+ template <typename... Args>
+ ValueBuilder& setSource(Args&&... args) {
+ mValue->setSource(Source{std::forward<Args>(args)...});
+ return *this;
+ }
- ValueBuilder& setComment(const StringPiece& str) {
- mValue->setComment(str);
- return *this;
- }
+ ValueBuilder& setComment(const StringPiece& str) {
+ mValue->setComment(str);
+ return *this;
+ }
- std::unique_ptr<Value> build() {
- return std::move(mValue);
- }
+ std::unique_ptr<Value> build() { return std::move(mValue); }
};
class AttributeBuilder {
-private:
- std::unique_ptr<Attribute> mAttr;
+ private:
+ std::unique_ptr<Attribute> mAttr;
-public:
- explicit AttributeBuilder(bool weak = false) : mAttr(util::make_unique<Attribute>(weak)) {
- mAttr->typeMask = android::ResTable_map::TYPE_ANY;
- }
+ public:
+ explicit AttributeBuilder(bool weak = false)
+ : mAttr(util::make_unique<Attribute>(weak)) {
+ mAttr->typeMask = android::ResTable_map::TYPE_ANY;
+ }
- AttributeBuilder& setTypeMask(uint32_t typeMask) {
- mAttr->typeMask = typeMask;
- return *this;
- }
+ AttributeBuilder& setTypeMask(uint32_t typeMask) {
+ mAttr->typeMask = typeMask;
+ return *this;
+ }
- AttributeBuilder& addItem(const StringPiece& name, uint32_t value) {
- mAttr->symbols.push_back(Attribute::Symbol{
- Reference(ResourceName({}, ResourceType::kId, name)),
- value});
- return *this;
- }
+ AttributeBuilder& addItem(const StringPiece& name, uint32_t value) {
+ mAttr->symbols.push_back(Attribute::Symbol{
+ Reference(ResourceName({}, ResourceType::kId, name)), value});
+ return *this;
+ }
- std::unique_ptr<Attribute> build() {
- return std::move(mAttr);
- }
+ std::unique_ptr<Attribute> build() { return std::move(mAttr); }
};
class StyleBuilder {
-private:
- std::unique_ptr<Style> mStyle = util::make_unique<Style>();
+ private:
+ std::unique_ptr<Style> mStyle = util::make_unique<Style>();
-public:
- StyleBuilder& setParent(const StringPiece& str) {
- mStyle->parent = Reference(parseNameOrDie(str));
- return *this;
- }
+ public:
+ StyleBuilder& setParent(const StringPiece& str) {
+ mStyle->parent = Reference(parseNameOrDie(str));
+ return *this;
+ }
- 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 StringPiece& str, std::unique_ptr<Item> value) {
+ mStyle->entries.push_back(
+ Style::Entry{Reference(parseNameOrDie(str)), std::move(value)});
+ return *this;
+ }
- StyleBuilder& addItem(const StringPiece& str, const ResourceId& id, std::unique_ptr<Item> value) {
- addItem(str, std::move(value));
- mStyle->entries.back().key.id = id;
- return *this;
- }
+ StyleBuilder& addItem(const StringPiece& str, const ResourceId& id,
+ std::unique_ptr<Item> value) {
+ addItem(str, std::move(value));
+ mStyle->entries.back().key.id = id;
+ return *this;
+ }
- std::unique_ptr<Style> build() {
- return std::move(mStyle);
- }
+ std::unique_ptr<Style> build() { return std::move(mStyle); }
};
class StyleableBuilder {
-private:
- std::unique_ptr<Styleable> mStyleable = util::make_unique<Styleable>();
+ private:
+ std::unique_ptr<Styleable> mStyleable = util::make_unique<Styleable>();
-public:
- StyleableBuilder& addItem(const StringPiece& str, const Maybe<ResourceId>& id = {}) {
- mStyleable->entries.push_back(Reference(parseNameOrDie(str)));
- mStyleable->entries.back().id = id;
- return *this;
- }
+ public:
+ StyleableBuilder& addItem(const StringPiece& str,
+ const Maybe<ResourceId>& id = {}) {
+ mStyleable->entries.push_back(Reference(parseNameOrDie(str)));
+ mStyleable->entries.back().id = id;
+ return *this;
+ }
- std::unique_ptr<Styleable> build() {
- return std::move(mStyleable);
- }
+ std::unique_ptr<Styleable> build() { return std::move(mStyleable); }
};
inline std::unique_ptr<xml::XmlResource> buildXmlDom(const StringPiece& str) {
- std::stringstream in;
- in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str;
- StdErrDiagnostics diag;
- std::unique_ptr<xml::XmlResource> doc = xml::inflate(&in, &diag, Source("test.xml"));
- assert(doc);
- return doc;
+ std::stringstream in;
+ in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str;
+ StdErrDiagnostics diag;
+ std::unique_ptr<xml::XmlResource> doc =
+ xml::inflate(&in, &diag, Source("test.xml"));
+ assert(doc);
+ return doc;
}
-inline std::unique_ptr<xml::XmlResource> buildXmlDomForPackageName(IAaptContext* context,
- const StringPiece& str) {
- std::unique_ptr<xml::XmlResource> doc = buildXmlDom(str);
- doc->file.name.package = context->getCompilationPackage();
- return doc;
+inline std::unique_ptr<xml::XmlResource> buildXmlDomForPackageName(
+ IAaptContext* context, const StringPiece& str) {
+ std::unique_ptr<xml::XmlResource> doc = buildXmlDom(str);
+ doc->file.name.package = context->getCompilationPackage();
+ return doc;
}
-} // namespace test
-} // namespace aapt
+} // namespace test
+} // namespace aapt
#endif /* AAPT_TEST_BUILDERS_H */
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index 7fafcbe..2d571e7 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -30,7 +30,8 @@
#include <iostream>
//
-// GTEST 1.7 doesn't explicitly cast to bool, which causes explicit operators to fail to compile.
+// GTEST 1.7 doesn't explicitly cast to bool, which causes explicit operators to
+// fail to compile.
//
#define AAPT_ASSERT_TRUE(v) ASSERT_TRUE(bool(v))
#define AAPT_ASSERT_FALSE(v) ASSERT_FALSE(bool(v))
@@ -41,81 +42,83 @@
namespace test {
struct DummyDiagnosticsImpl : public IDiagnostics {
- void log(Level level, DiagMessageActual& actualMsg) override {
- switch (level) {
- case Level::Note:
- return;
+ void log(Level level, DiagMessageActual& actualMsg) override {
+ switch (level) {
+ case Level::Note:
+ return;
- case Level::Warn:
- std::cerr << actualMsg.source << ": warn: " << actualMsg.message << "." << std::endl;
- break;
+ case Level::Warn:
+ std::cerr << actualMsg.source << ": warn: " << actualMsg.message << "."
+ << std::endl;
+ break;
- case Level::Error:
- std::cerr << actualMsg.source << ": error: " << actualMsg.message << "." << std::endl;
- break;
- }
+ case Level::Error:
+ std::cerr << actualMsg.source << ": error: " << actualMsg.message << "."
+ << std::endl;
+ break;
}
+ }
};
inline IDiagnostics* getDiagnostics() {
- static DummyDiagnosticsImpl diag;
- return &diag;
+ static DummyDiagnosticsImpl diag;
+ return &diag;
}
inline ResourceName parseNameOrDie(const StringPiece& str) {
- ResourceNameRef ref;
- bool result = ResourceUtils::parseResourceName(str, &ref);
- assert(result && "invalid resource name");
- return ref.toResourceName();
+ ResourceNameRef ref;
+ bool result = ResourceUtils::parseResourceName(str, &ref);
+ assert(result && "invalid resource name");
+ return ref.toResourceName();
}
inline ConfigDescription parseConfigOrDie(const StringPiece& str) {
- ConfigDescription config;
- bool result = ConfigDescription::parse(str, &config);
- assert(result && "invalid configuration");
- return config;
+ ConfigDescription config;
+ bool result = ConfigDescription::parse(str, &config);
+ assert(result && "invalid configuration");
+ return config;
}
-template <typename T> T* getValueForConfigAndProduct(ResourceTable* table,
- const StringPiece& resName,
- const ConfigDescription& config,
- const StringPiece& product) {
- Maybe<ResourceTable::SearchResult> result = table->findResource(parseNameOrDie(resName));
- if (result) {
- ResourceConfigValue* configValue = result.value().entry->findValue(config, product);
- if (configValue) {
- return valueCast<T>(configValue->value.get());
- }
+template <typename T>
+T* getValueForConfigAndProduct(ResourceTable* table, const StringPiece& resName,
+ const ConfigDescription& config,
+ const StringPiece& product) {
+ Maybe<ResourceTable::SearchResult> result =
+ table->findResource(parseNameOrDie(resName));
+ if (result) {
+ ResourceConfigValue* configValue =
+ result.value().entry->findValue(config, product);
+ if (configValue) {
+ return valueCast<T>(configValue->value.get());
}
- return nullptr;
+ }
+ return nullptr;
}
-template <typename T> T* getValueForConfig(ResourceTable* table, const StringPiece& resName,
- const ConfigDescription& config) {
- return getValueForConfigAndProduct<T>(table, resName, config, {});
+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 StringPiece& resName) {
- return getValueForConfig<T>(table, resName, {});
+template <typename T>
+T* getValue(ResourceTable* table, const StringPiece& resName) {
+ return getValueForConfig<T>(table, resName, {});
}
class TestFile : public io::IFile {
-private:
- Source mSource;
+ private:
+ Source mSource;
-public:
- explicit TestFile(const StringPiece& path) : mSource(path) {}
+ public:
+ explicit TestFile(const StringPiece& path) : mSource(path) {}
- std::unique_ptr<io::IData> openAsData() override {
- return {};
- }
+ std::unique_ptr<io::IData> openAsData() override { return {}; }
- const Source& getSource() const override {
- return mSource;
- }
+ const Source& getSource() const override { return mSource; }
};
-} // namespace test
-} // namespace aapt
+} // namespace test
+} // namespace aapt
#endif /* AAPT_TEST_COMMON_H */
diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h
index 54f16db..6c7f6f7 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -18,10 +18,10 @@
#define AAPT_TEST_CONTEXT_H
#include "NameMangler.h"
-#include "util/Util.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
#include "test/Common.h"
+#include "util/Util.h"
#include <cassert>
#include <list>
@@ -30,152 +30,143 @@
namespace test {
class Context : public IAaptContext {
-public:
- SymbolTable* getExternalSymbols() override {
- return &mSymbols;
- }
+ public:
+ SymbolTable* getExternalSymbols() override { return &mSymbols; }
- IDiagnostics* getDiagnostics() override {
- return &mDiagnostics;
- }
+ IDiagnostics* getDiagnostics() override { return &mDiagnostics; }
- const std::string& getCompilationPackage() override {
- assert(mCompilationPackage && "package name not set");
- return mCompilationPackage.value();
- }
+ const std::string& getCompilationPackage() override {
+ assert(mCompilationPackage && "package name not set");
+ return mCompilationPackage.value();
+ }
- uint8_t getPackageId() override {
- assert(mPackageId && "package ID not set");
- return mPackageId.value();
- }
+ uint8_t getPackageId() override {
+ assert(mPackageId && "package ID not set");
+ return mPackageId.value();
+ }
- NameMangler* getNameMangler() override {
- return &mNameMangler;
- }
+ NameMangler* getNameMangler() override { return &mNameMangler; }
- bool verbose() override {
- return false;
- }
+ bool verbose() override { return false; }
- int getMinSdkVersion() override {
- return mMinSdkVersion;
- }
+ int getMinSdkVersion() override { return mMinSdkVersion; }
-private:
- friend class ContextBuilder;
+ private:
+ friend class ContextBuilder;
- Maybe<std::string> mCompilationPackage;
- Maybe<uint8_t> mPackageId;
- StdErrDiagnostics mDiagnostics;
- SymbolTable mSymbols;
- NameMangler mNameMangler = NameMangler({});
- int mMinSdkVersion = 0;
+ Maybe<std::string> mCompilationPackage;
+ Maybe<uint8_t> mPackageId;
+ StdErrDiagnostics mDiagnostics;
+ SymbolTable mSymbols;
+ NameMangler mNameMangler = NameMangler({});
+ int mMinSdkVersion = 0;
};
class ContextBuilder {
-private:
- std::unique_ptr<Context> mContext = std::unique_ptr<Context>(new Context());
+ private:
+ std::unique_ptr<Context> mContext = std::unique_ptr<Context>(new Context());
-public:
- ContextBuilder& setCompilationPackage(const StringPiece& package) {
- mContext->mCompilationPackage = package.toString();
- return *this;
- }
+ public:
+ ContextBuilder& setCompilationPackage(const StringPiece& package) {
+ mContext->mCompilationPackage = package.toString();
+ return *this;
+ }
- ContextBuilder& setPackageId(uint8_t id) {
- mContext->mPackageId = id;
- return *this;
- }
+ ContextBuilder& setPackageId(uint8_t id) {
+ mContext->mPackageId = id;
+ return *this;
+ }
- ContextBuilder& setNameManglerPolicy(const NameManglerPolicy& policy) {
- mContext->mNameMangler = NameMangler(policy);
- return *this;
- }
+ ContextBuilder& setNameManglerPolicy(const NameManglerPolicy& policy) {
+ mContext->mNameMangler = NameMangler(policy);
+ return *this;
+ }
- ContextBuilder& addSymbolSource(std::unique_ptr<ISymbolSource> src) {
- mContext->getExternalSymbols()->appendSource(std::move(src));
- return *this;
- }
+ ContextBuilder& addSymbolSource(std::unique_ptr<ISymbolSource> src) {
+ mContext->getExternalSymbols()->appendSource(std::move(src));
+ return *this;
+ }
- ContextBuilder& setMinSdkVersion(int minSdk) {
- mContext->mMinSdkVersion = minSdk;
- return *this;
- }
+ ContextBuilder& setMinSdkVersion(int minSdk) {
+ mContext->mMinSdkVersion = minSdk;
+ return *this;
+ }
- std::unique_ptr<Context> build() {
- return std::move(mContext);
- }
+ std::unique_ptr<Context> build() { return std::move(mContext); }
};
class StaticSymbolSourceBuilder {
-public:
- 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);
- mSymbolSource->mNameMap[parseNameOrDie(name)] = symbol.get();
- mSymbolSource->mIdMap[id] = symbol.get();
- mSymbolSource->mSymbols.push_back(std::move(symbol));
- return *this;
+ public:
+ 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);
+ mSymbolSource->mNameMap[parseNameOrDie(name)] = symbol.get();
+ mSymbolSource->mIdMap[id] = symbol.get();
+ mSymbolSource->mSymbols.push_back(std::move(symbol));
+ return *this;
+ }
+
+ 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);
+ mSymbolSource->mNameMap[parseNameOrDie(name)] = symbol.get();
+ mSymbolSource->mIdMap[id] = symbol.get();
+ mSymbolSource->mSymbols.push_back(std::move(symbol));
+ return *this;
+ }
+
+ std::unique_ptr<ISymbolSource> build() { return std::move(mSymbolSource); }
+
+ private:
+ class StaticSymbolSource : public ISymbolSource {
+ public:
+ StaticSymbolSource() = default;
+
+ std::unique_ptr<SymbolTable::Symbol> findByName(
+ const ResourceName& name) override {
+ auto iter = mNameMap.find(name);
+ if (iter != mNameMap.end()) {
+ return cloneSymbol(iter->second);
+ }
+ return nullptr;
}
- 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);
- mSymbolSource->mNameMap[parseNameOrDie(name)] = symbol.get();
- mSymbolSource->mIdMap[id] = symbol.get();
- mSymbolSource->mSymbols.push_back(std::move(symbol));
- return *this;
+ std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override {
+ auto iter = mIdMap.find(id);
+ if (iter != mIdMap.end()) {
+ return cloneSymbol(iter->second);
+ }
+ return nullptr;
}
- std::unique_ptr<ISymbolSource> build() {
- return std::move(mSymbolSource);
+ std::list<std::unique_ptr<SymbolTable::Symbol>> mSymbols;
+ std::map<ResourceName, SymbolTable::Symbol*> mNameMap;
+ std::map<ResourceId, SymbolTable::Symbol*> mIdMap;
+
+ private:
+ std::unique_ptr<SymbolTable::Symbol> cloneSymbol(SymbolTable::Symbol* sym) {
+ std::unique_ptr<SymbolTable::Symbol> clone =
+ util::make_unique<SymbolTable::Symbol>();
+ clone->id = sym->id;
+ if (sym->attribute) {
+ clone->attribute =
+ std::unique_ptr<Attribute>(sym->attribute->clone(nullptr));
+ }
+ clone->isPublic = sym->isPublic;
+ return clone;
}
-private:
- class StaticSymbolSource : public ISymbolSource {
- public:
- StaticSymbolSource() = default;
+ DISALLOW_COPY_AND_ASSIGN(StaticSymbolSource);
+ };
- std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override {
- auto iter = mNameMap.find(name);
- if (iter != mNameMap.end()) {
- return cloneSymbol(iter->second);
- }
- return nullptr;
- }
-
- std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override {
- auto iter = mIdMap.find(id);
- if (iter != mIdMap.end()) {
- return cloneSymbol(iter->second);
- }
- return nullptr;
- }
-
- std::list<std::unique_ptr<SymbolTable::Symbol>> mSymbols;
- std::map<ResourceName, SymbolTable::Symbol*> mNameMap;
- std::map<ResourceId, SymbolTable::Symbol*> mIdMap;
-
- private:
- std::unique_ptr<SymbolTable::Symbol> cloneSymbol(SymbolTable::Symbol* sym) {
- std::unique_ptr<SymbolTable::Symbol> clone = util::make_unique<SymbolTable::Symbol>();
- clone->id = sym->id;
- if (sym->attribute) {
- clone->attribute = std::unique_ptr<Attribute>(sym->attribute->clone(nullptr));
- }
- clone->isPublic = sym->isPublic;
- return clone;
- }
-
- DISALLOW_COPY_AND_ASSIGN(StaticSymbolSource);
- };
-
- std::unique_ptr<StaticSymbolSource> mSymbolSource = util::make_unique<StaticSymbolSource>();
+ std::unique_ptr<StaticSymbolSource> mSymbolSource =
+ util::make_unique<StaticSymbolSource>();
};
-} // namespace test
-} // namespace aapt
+} // namespace test
+} // namespace aapt
#endif /* AAPT_TEST_CONTEXT_H */
diff --git a/tools/aapt2/test/Test.h b/tools/aapt2/test/Test.h
index d4845cf..c9188bf 100644
--- a/tools/aapt2/test/Test.h
+++ b/tools/aapt2/test/Test.h
@@ -24,9 +24,7 @@
#include <gtest/gtest.h>
namespace aapt {
-namespace test {
+namespace test {} // namespace test
+} // namespace aapt
-} // namespace test
-} // namespace aapt
-
-#endif // AAPT_TEST_TEST_H
+#endif // AAPT_TEST_TEST_H
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.h b/tools/aapt2/unflatten/BinaryResourceParser.h
index 12bc13d..99f2bd4 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.h
+++ b/tools/aapt2/unflatten/BinaryResourceParser.h
@@ -39,80 +39,86 @@
* chunks and types.
*/
class BinaryResourceParser {
-public:
- /*
- * Creates a parser, which will read `len` bytes from `data`, and
- * add any resources parsed to `table`. `source` is for logging purposes.
- */
- BinaryResourceParser(IAaptContext* context, ResourceTable* table, const Source& source,
- const void* data, size_t dataLen);
+ public:
+ /*
+ * Creates a parser, which will read `len` bytes from `data`, and
+ * add any resources parsed to `table`. `source` is for logging purposes.
+ */
+ BinaryResourceParser(IAaptContext* context, ResourceTable* table,
+ const Source& source, const void* data, size_t dataLen);
- BinaryResourceParser(const BinaryResourceParser&) = delete; // No copy.
+ BinaryResourceParser(const BinaryResourceParser&) = delete; // No copy.
- /*
- * Parses the binary resource table and returns true if successful.
- */
- bool parse();
+ /*
+ * Parses the binary resource table and returns true if successful.
+ */
+ bool parse();
-private:
- bool parseTable(const android::ResChunk_header* chunk);
- bool parsePackage(const android::ResChunk_header* chunk);
- bool parseTypeSpec(const android::ResChunk_header* chunk);
- bool parseType(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
+ private:
+ bool parseTable(const android::ResChunk_header* chunk);
+ bool parsePackage(const android::ResChunk_header* chunk);
+ bool parseTypeSpec(const android::ResChunk_header* chunk);
+ bool parseType(const ResourceTablePackage* package,
+ const android::ResChunk_header* chunk);
- std::unique_ptr<Item> parseValue(const ResourceNameRef& name, const ConfigDescription& config,
- const android::Res_value* value, uint16_t flags);
+ std::unique_ptr<Item> parseValue(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const android::Res_value* value,
+ uint16_t flags);
- std::unique_ptr<Value> parseMapEntry(const ResourceNameRef& name,
- const ConfigDescription& config,
- const android::ResTable_map_entry* map);
+ std::unique_ptr<Value> parseMapEntry(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const android::ResTable_map_entry* map);
- std::unique_ptr<Style> parseStyle(const ResourceNameRef& name, const ConfigDescription& config,
+ std::unique_ptr<Style> parseStyle(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const android::ResTable_map_entry* map);
+
+ std::unique_ptr<Attribute> parseAttr(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const android::ResTable_map_entry* map);
+
+ std::unique_ptr<Array> parseArray(const ResourceNameRef& name,
+ const ConfigDescription& config,
+ const android::ResTable_map_entry* map);
+
+ std::unique_ptr<Plural> parsePlural(const ResourceNameRef& name,
+ const ConfigDescription& config,
const android::ResTable_map_entry* map);
- std::unique_ptr<Attribute> parseAttr(const ResourceNameRef& name,
- const ConfigDescription& config,
- const android::ResTable_map_entry* map);
+ /**
+ * If the mapEntry is a special type that denotes meta data (source, comment),
+ * then it is
+ * read and added to the Value.
+ * Returns true if the mapEntry was meta data.
+ */
+ bool collectMetaData(const android::ResTable_map& mapEntry, Value* value);
- std::unique_ptr<Array> parseArray(const ResourceNameRef& name, const ConfigDescription& config,
- const android::ResTable_map_entry* map);
+ IAaptContext* mContext;
+ ResourceTable* mTable;
- std::unique_ptr<Plural> parsePlural(const ResourceNameRef& name,
- const ConfigDescription& config,
- const android::ResTable_map_entry* map);
+ const Source mSource;
- /**
- * If the mapEntry is a special type that denotes meta data (source, comment), then it is
- * read and added to the Value.
- * Returns true if the mapEntry was meta data.
- */
- bool collectMetaData(const android::ResTable_map& mapEntry, Value* value);
+ const void* mData;
+ const size_t mDataLen;
- IAaptContext* mContext;
- ResourceTable* mTable;
+ // The standard value string pool for resource values.
+ android::ResStringPool mValuePool;
- const Source mSource;
+ // The string pool that holds the names of the types defined
+ // in this table.
+ android::ResStringPool mTypePool;
- const void* mData;
- const size_t mDataLen;
+ // The string pool that holds the names of the entries defined
+ // in this table.
+ android::ResStringPool mKeyPool;
- // The standard value string pool for resource values.
- android::ResStringPool mValuePool;
-
- // The string pool that holds the names of the types defined
- // in this table.
- android::ResStringPool mTypePool;
-
- // The string pool that holds the names of the entries defined
- // in this table.
- android::ResStringPool mKeyPool;
-
- // A mapping of resource ID to resource name. When we finish parsing
- // we use this to convert all resource IDs to symbolic references.
- std::map<ResourceId, ResourceName> mIdIndex;
+ // A mapping of resource ID to resource name. When we finish parsing
+ // we use this to convert all resource IDs to symbolic references.
+ std::map<ResourceId, ResourceName> mIdIndex;
};
-} // namespace aapt
+} // namespace aapt
namespace android {
@@ -121,13 +127,14 @@
*/
inline const ResTable_map* begin(const ResTable_map_entry* map) {
- return (const ResTable_map*)((const uint8_t*) map + aapt::util::deviceToHost32(map->size));
+ return (const ResTable_map*)((const uint8_t*)map +
+ aapt::util::deviceToHost32(map->size));
}
inline const ResTable_map* end(const ResTable_map_entry* map) {
- return begin(map) + aapt::util::deviceToHost32(map->count);
+ return begin(map) + aapt::util::deviceToHost32(map->count);
}
-} // namespace android
+} // namespace android
-#endif // AAPT_BINARY_RESOURCE_PARSER_H
+#endif // AAPT_BINARY_RESOURCE_PARSER_H
diff --git a/tools/aapt2/unflatten/ResChunkPullParser.h b/tools/aapt2/unflatten/ResChunkPullParser.h
index a51d5bf..24fa63d 100644
--- a/tools/aapt2/unflatten/ResChunkPullParser.h
+++ b/tools/aapt2/unflatten/ResChunkPullParser.h
@@ -37,59 +37,62 @@
* pointing to the data portion of a chunk.
*/
class ResChunkPullParser {
-public:
- enum class Event {
- StartDocument,
- EndDocument,
- BadDocument,
+ public:
+ enum class Event {
+ StartDocument,
+ EndDocument,
+ BadDocument,
- Chunk,
- };
+ Chunk,
+ };
- /**
- * Returns false if the event is EndDocument or BadDocument.
- */
- static bool isGoodEvent(Event event);
+ /**
+ * Returns false if the event is EndDocument or BadDocument.
+ */
+ static bool isGoodEvent(Event event);
- /**
- * Create a ResChunkPullParser to read android::ResChunk_headers
- * from the memory pointed to by data, of len bytes.
- */
- ResChunkPullParser(const void* data, size_t len);
+ /**
+ * Create a ResChunkPullParser to read android::ResChunk_headers
+ * from the memory pointed to by data, of len bytes.
+ */
+ ResChunkPullParser(const void* data, size_t len);
- ResChunkPullParser(const ResChunkPullParser&) = delete;
+ ResChunkPullParser(const ResChunkPullParser&) = delete;
- Event getEvent() const;
- const std::string& getLastError() const;
- const android::ResChunk_header* getChunk() const;
+ Event getEvent() const;
+ const std::string& getLastError() const;
+ const android::ResChunk_header* getChunk() const;
- /**
- * Move to the next android::ResChunk_header.
- */
- Event next();
+ /**
+ * Move to the next android::ResChunk_header.
+ */
+ Event next();
-private:
- Event mEvent;
- const android::ResChunk_header* mData;
- size_t mLen;
- const android::ResChunk_header* mCurrentChunk;
- std::string mLastError;
+ private:
+ Event mEvent;
+ const android::ResChunk_header* mData;
+ size_t mLen;
+ const android::ResChunk_header* mCurrentChunk;
+ std::string mLastError;
};
template <typename T>
inline static const T* convertTo(const android::ResChunk_header* chunk) {
- if (util::deviceToHost16(chunk->headerSize) < sizeof(T)) {
- return nullptr;
- }
- return reinterpret_cast<const T*>(chunk);
+ if (util::deviceToHost16(chunk->headerSize) < sizeof(T)) {
+ return nullptr;
+ }
+ return reinterpret_cast<const T*>(chunk);
}
-inline static const uint8_t* getChunkData(const android::ResChunk_header* chunk) {
- return reinterpret_cast<const uint8_t*>(chunk) + util::deviceToHost16(chunk->headerSize);
+inline static const uint8_t* getChunkData(
+ const android::ResChunk_header* chunk) {
+ return reinterpret_cast<const uint8_t*>(chunk) +
+ util::deviceToHost16(chunk->headerSize);
}
inline static uint32_t getChunkDataLen(const android::ResChunk_header* chunk) {
- return util::deviceToHost32(chunk->size) - util::deviceToHost16(chunk->headerSize);
+ return util::deviceToHost32(chunk->size) -
+ util::deviceToHost16(chunk->headerSize);
}
//
@@ -97,28 +100,27 @@
//
inline bool ResChunkPullParser::isGoodEvent(ResChunkPullParser::Event event) {
- return event != Event::EndDocument && event != Event::BadDocument;
+ return event != Event::EndDocument && event != Event::BadDocument;
}
-inline ResChunkPullParser::ResChunkPullParser(const void* data, size_t len) :
- mEvent(Event::StartDocument),
- mData(reinterpret_cast<const android::ResChunk_header*>(data)),
- mLen(len),
- mCurrentChunk(nullptr) {
-}
+inline ResChunkPullParser::ResChunkPullParser(const void* data, size_t len)
+ : mEvent(Event::StartDocument),
+ mData(reinterpret_cast<const android::ResChunk_header*>(data)),
+ mLen(len),
+ mCurrentChunk(nullptr) {}
inline ResChunkPullParser::Event ResChunkPullParser::getEvent() const {
- return mEvent;
+ return mEvent;
}
inline const std::string& ResChunkPullParser::getLastError() const {
- return mLastError;
+ return mLastError;
}
inline const android::ResChunk_header* ResChunkPullParser::getChunk() const {
- return mCurrentChunk;
+ return mCurrentChunk;
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_RES_CHUNK_PULL_PARSER_H
+#endif // AAPT_RES_CHUNK_PULL_PARSER_H
diff --git a/tools/aapt2/util/BigBuffer.h b/tools/aapt2/util/BigBuffer.h
index 685614f..b273733 100644
--- a/tools/aapt2/util/BigBuffer.h
+++ b/tools/aapt2/util/BigBuffer.h
@@ -32,156 +32,153 @@
* block is allocated and appended to the end of the list.
*/
class BigBuffer {
-public:
+ public:
+ /**
+ * A contiguous block of allocated memory.
+ */
+ struct Block {
/**
- * A contiguous block of allocated memory.
+ * Pointer to the memory.
*/
- struct Block {
- /**
- * Pointer to the memory.
- */
- std::unique_ptr<uint8_t[]> buffer;
-
- /**
- * Size of memory that is currently occupied. The actual
- * allocation may be larger.
- */
- size_t size;
-
- private:
- friend class BigBuffer;
-
- /**
- * The size of the memory block allocation.
- */
- size_t mBlockSize;
- };
-
- typedef std::vector<Block>::const_iterator const_iterator;
+ std::unique_ptr<uint8_t[]> buffer;
/**
- * Create a BigBuffer with block allocation sizes
- * of blockSize.
+ * Size of memory that is currently occupied. The actual
+ * allocation may be larger.
*/
- explicit BigBuffer(size_t blockSize);
+ size_t size;
- BigBuffer(const BigBuffer&) = delete; // No copying.
-
- BigBuffer(BigBuffer&& rhs);
+ private:
+ friend class BigBuffer;
/**
- * Number of occupied bytes in all the allocated blocks.
+ * The size of the memory block allocation.
*/
- size_t size() const;
-
- /**
- * Returns a pointer to an array of T, where T is
- * a POD type. The elements are zero-initialized.
- */
- template <typename T>
- T* nextBlock(size_t count = 1);
-
- /**
- * Returns the next block available and puts the size in outCount.
- * This is useful for grabbing blocks where the size doesn't matter.
- * Use backUp() to give back any bytes that were not used.
- */
- void* nextBlock(size_t* outCount);
-
- /**
- * Backs up count bytes. This must only be called after nextBlock()
- * and can not be larger than sizeof(T) * count of the last nextBlock()
- * call.
- */
- void backUp(size_t count);
-
- /**
- * Moves the specified BigBuffer into this one. When this method
- * returns, buffer is empty.
- */
- void appendBuffer(BigBuffer&& buffer);
-
- /**
- * Pads the block with 'bytes' bytes of zero values.
- */
- void pad(size_t bytes);
-
- /**
- * Pads the block so that it aligns on a 4 byte boundary.
- */
- void align4();
-
- size_t getBlockSize() const;
-
- const_iterator begin() const;
- const_iterator end() const;
-
-private:
- /**
- * Returns a pointer to a buffer of the requested size.
- * The buffer is zero-initialized.
- */
- void* nextBlockImpl(size_t size);
-
size_t mBlockSize;
- size_t mSize;
- std::vector<Block> mBlocks;
+ };
+
+ typedef std::vector<Block>::const_iterator const_iterator;
+
+ /**
+ * Create a BigBuffer with block allocation sizes
+ * of blockSize.
+ */
+ explicit BigBuffer(size_t blockSize);
+
+ BigBuffer(const BigBuffer&) = delete; // No copying.
+
+ BigBuffer(BigBuffer&& rhs);
+
+ /**
+ * Number of occupied bytes in all the allocated blocks.
+ */
+ size_t size() const;
+
+ /**
+ * Returns a pointer to an array of T, where T is
+ * a POD type. The elements are zero-initialized.
+ */
+ template <typename T>
+ T* nextBlock(size_t count = 1);
+
+ /**
+ * Returns the next block available and puts the size in outCount.
+ * This is useful for grabbing blocks where the size doesn't matter.
+ * Use backUp() to give back any bytes that were not used.
+ */
+ void* nextBlock(size_t* outCount);
+
+ /**
+ * Backs up count bytes. This must only be called after nextBlock()
+ * and can not be larger than sizeof(T) * count of the last nextBlock()
+ * call.
+ */
+ void backUp(size_t count);
+
+ /**
+ * Moves the specified BigBuffer into this one. When this method
+ * returns, buffer is empty.
+ */
+ void appendBuffer(BigBuffer&& buffer);
+
+ /**
+ * Pads the block with 'bytes' bytes of zero values.
+ */
+ void pad(size_t bytes);
+
+ /**
+ * Pads the block so that it aligns on a 4 byte boundary.
+ */
+ void align4();
+
+ size_t getBlockSize() const;
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ private:
+ /**
+ * Returns a pointer to a buffer of the requested size.
+ * The buffer is zero-initialized.
+ */
+ void* nextBlockImpl(size_t size);
+
+ size_t mBlockSize;
+ size_t mSize;
+ std::vector<Block> mBlocks;
};
-inline BigBuffer::BigBuffer(size_t blockSize) : mBlockSize(blockSize), mSize(0) {
-}
+inline BigBuffer::BigBuffer(size_t blockSize)
+ : mBlockSize(blockSize), mSize(0) {}
-inline BigBuffer::BigBuffer(BigBuffer&& rhs) :
- mBlockSize(rhs.mBlockSize), mSize(rhs.mSize), mBlocks(std::move(rhs.mBlocks)) {
-}
+inline BigBuffer::BigBuffer(BigBuffer&& rhs)
+ : mBlockSize(rhs.mBlockSize),
+ mSize(rhs.mSize),
+ mBlocks(std::move(rhs.mBlocks)) {}
-inline size_t BigBuffer::size() const {
- return mSize;
-}
+inline size_t BigBuffer::size() const { return mSize; }
-inline size_t BigBuffer::getBlockSize() const {
- return mBlockSize;
-}
+inline size_t BigBuffer::getBlockSize() const { return mBlockSize; }
template <typename T>
inline T* BigBuffer::nextBlock(size_t count) {
- static_assert(std::is_standard_layout<T>::value, "T must be standard_layout type");
- assert(count != 0);
- return reinterpret_cast<T*>(nextBlockImpl(sizeof(T) * count));
+ static_assert(std::is_standard_layout<T>::value,
+ "T must be standard_layout type");
+ assert(count != 0);
+ return reinterpret_cast<T*>(nextBlockImpl(sizeof(T) * count));
}
inline void BigBuffer::backUp(size_t count) {
- Block& block = mBlocks.back();
- block.size -= count;
- mSize -= count;
+ Block& block = mBlocks.back();
+ block.size -= count;
+ mSize -= count;
}
inline void BigBuffer::appendBuffer(BigBuffer&& buffer) {
- std::move(buffer.mBlocks.begin(), buffer.mBlocks.end(), std::back_inserter(mBlocks));
- mSize += buffer.mSize;
- buffer.mBlocks.clear();
- buffer.mSize = 0;
+ std::move(buffer.mBlocks.begin(), buffer.mBlocks.end(),
+ std::back_inserter(mBlocks));
+ mSize += buffer.mSize;
+ buffer.mBlocks.clear();
+ buffer.mSize = 0;
}
-inline void BigBuffer::pad(size_t bytes) {
- nextBlock<char>(bytes);
-}
+inline void BigBuffer::pad(size_t bytes) { nextBlock<char>(bytes); }
inline void BigBuffer::align4() {
- const size_t unaligned = mSize % 4;
- if (unaligned != 0) {
- pad(4 - unaligned);
- }
+ const size_t unaligned = mSize % 4;
+ if (unaligned != 0) {
+ pad(4 - unaligned);
+ }
}
inline BigBuffer::const_iterator BigBuffer::begin() const {
- return mBlocks.begin();
+ return mBlocks.begin();
}
inline BigBuffer::const_iterator BigBuffer::end() const {
- return mBlocks.end();
+ return mBlocks.end();
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_BIG_BUFFER_H
+#endif // AAPT_BIG_BUFFER_H
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index 52c2052..d90c6b6 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -39,15 +39,15 @@
#endif
enum class FileType {
- kUnknown = 0,
- kNonexistant,
- kRegular,
- kDirectory,
- kCharDev,
- kBlockDev,
- kFifo,
- kSymlink,
- kSocket,
+ kUnknown = 0,
+ kNonexistant,
+ kRegular,
+ kDirectory,
+ kCharDev,
+ kBlockDev,
+ kFifo,
+ kSymlink,
+ kSocket,
};
FileType getFileType(const StringPiece& path);
@@ -93,12 +93,14 @@
/**
* Creates a FileMap for the file at path.
*/
-Maybe<android::FileMap> mmapPath(const StringPiece& path, std::string* outError);
+Maybe<android::FileMap> mmapPath(const StringPiece& path,
+ std::string* outError);
/**
* Reads the file at path and appends each line to the outArgList vector.
*/
-bool appendArgsFromFile(const StringPiece& path, std::vector<std::string>* outArgList,
+bool appendArgsFromFile(const StringPiece& path,
+ std::vector<std::string>* outArgList,
std::string* outError);
/*
@@ -108,37 +110,36 @@
* FileFilter::setPattern(const std::string&) method.
*/
class FileFilter {
-public:
- explicit FileFilter(IDiagnostics* diag) : mDiag(diag) {
- }
+ public:
+ explicit FileFilter(IDiagnostics* diag) : mDiag(diag) {}
- /*
- * Patterns syntax:
- * - Delimiter is :
- * - Entry can start with the flag ! to avoid printing a warning
- * about the file being ignored.
- * - Entry can have the flag "<dir>" to match only directories
- * or <file> to match only files. Default is to match both.
- * - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
- * where prefix/suffix must have at least 1 character (so that
- * we don't match a '*' catch-all pattern.)
- * - The special filenames "." and ".." are always ignored.
- * - Otherwise the full string is matched.
- * - match is not case-sensitive.
- */
- bool setPattern(const StringPiece& pattern);
+ /*
+ * Patterns syntax:
+ * - Delimiter is :
+ * - Entry can start with the flag ! to avoid printing a warning
+ * about the file being ignored.
+ * - Entry can have the flag "<dir>" to match only directories
+ * or <file> to match only files. Default is to match both.
+ * - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
+ * where prefix/suffix must have at least 1 character (so that
+ * we don't match a '*' catch-all pattern.)
+ * - The special filenames "." and ".." are always ignored.
+ * - Otherwise the full string is matched.
+ * - match is not case-sensitive.
+ */
+ bool setPattern(const StringPiece& pattern);
- /**
- * Applies the filter, returning true for pass, false for fail.
- */
- bool operator()(const std::string& filename, FileType type) const;
+ /**
+ * Applies the filter, returning true for pass, false for fail.
+ */
+ bool operator()(const std::string& filename, FileType type) const;
-private:
- IDiagnostics* mDiag;
- std::vector<std::string> mPatternTokens;
+ private:
+ IDiagnostics* mDiag;
+ std::vector<std::string> mPatternTokens;
};
-} // namespace file
-} // namespace aapt
+} // namespace file
+} // namespace aapt
-#endif // AAPT_FILES_H
+#endif // AAPT_FILES_H
diff --git a/tools/aapt2/util/ImmutableMap.h b/tools/aapt2/util/ImmutableMap.h
index b1f9e9d..6f48764 100644
--- a/tools/aapt2/util/ImmutableMap.h
+++ b/tools/aapt2/util/ImmutableMap.h
@@ -26,59 +26,57 @@
template <typename TKey, typename TValue>
class ImmutableMap {
- static_assert(is_comparable<TKey, TKey>::value, "key is not comparable");
+ static_assert(is_comparable<TKey, TKey>::value, "key is not comparable");
-private:
- std::vector<std::pair<TKey, TValue>> mData;
+ private:
+ std::vector<std::pair<TKey, TValue>> mData;
- explicit ImmutableMap(std::vector<std::pair<TKey, TValue>> data) : mData(std::move(data)) {
+ explicit ImmutableMap(std::vector<std::pair<TKey, TValue>> data)
+ : mData(std::move(data)) {}
+
+ public:
+ using const_iterator = typename decltype(mData)::const_iterator;
+
+ ImmutableMap(ImmutableMap&&) = default;
+ ImmutableMap& operator=(ImmutableMap&&) = default;
+
+ ImmutableMap(const ImmutableMap&) = delete;
+ ImmutableMap& operator=(const ImmutableMap&) = delete;
+
+ static ImmutableMap<TKey, TValue> createPreSorted(
+ std::initializer_list<std::pair<TKey, TValue>> list) {
+ return ImmutableMap(
+ std::vector<std::pair<TKey, TValue>>(list.begin(), list.end()));
+ }
+
+ static ImmutableMap<TKey, TValue> createAndSort(
+ std::initializer_list<std::pair<TKey, TValue>> list) {
+ std::vector<std::pair<TKey, TValue>> data(list.begin(), list.end());
+ std::sort(data.begin(), data.end());
+ return ImmutableMap(std::move(data));
+ }
+
+ template <typename TKey2, typename = typename std::enable_if<
+ is_comparable<TKey, TKey2>::value>::type>
+ const_iterator find(const TKey2& key) const {
+ auto cmp = [](const std::pair<TKey, TValue>& candidate,
+ const TKey2& target) -> bool {
+ return candidate.first < target;
+ };
+
+ const_iterator endIter = end();
+ auto iter = std::lower_bound(mData.begin(), endIter, key, cmp);
+ if (iter == endIter || iter->first == key) {
+ return iter;
}
+ return endIter;
+ }
-public:
- using const_iterator = typename decltype(mData)::const_iterator;
+ const_iterator begin() const { return mData.begin(); }
- ImmutableMap(ImmutableMap&&) = default;
- ImmutableMap& operator=(ImmutableMap&&) = default;
-
- ImmutableMap(const ImmutableMap&) = delete;
- ImmutableMap& operator=(const ImmutableMap&) = delete;
-
- static ImmutableMap<TKey, TValue> createPreSorted(
- std::initializer_list<std::pair<TKey, TValue>> list) {
- return ImmutableMap(std::vector<std::pair<TKey, TValue>>(list.begin(), list.end()));
- }
-
- static ImmutableMap<TKey, TValue> createAndSort(
- std::initializer_list<std::pair<TKey, TValue>> list) {
- std::vector<std::pair<TKey, TValue>> data(list.begin(), list.end());
- std::sort(data.begin(), data.end());
- return ImmutableMap(std::move(data));
- }
-
- template <typename TKey2,
- typename = typename std::enable_if<is_comparable<TKey, TKey2>::value>::type>
- const_iterator find(const TKey2& key) const {
- auto cmp = [](const std::pair<TKey, TValue>& candidate, const TKey2& target) -> bool {
- return candidate.first < target;
- };
-
- const_iterator endIter = end();
- auto iter = std::lower_bound(mData.begin(), endIter, key, cmp);
- if (iter == endIter || iter->first == key) {
- return iter;
- }
- return endIter;
- }
-
- const_iterator begin() const {
- return mData.begin();
- }
-
- const_iterator end() const {
- return mData.end();
- }
+ const_iterator end() const { return mData.end(); }
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_UTIL_IMMUTABLEMAP_H */
diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h
index 129f6d9..90a0198 100644
--- a/tools/aapt2/util/Maybe.h
+++ b/tools/aapt2/util/Maybe.h
@@ -32,303 +32,292 @@
*/
template <typename T>
class Maybe {
-public:
- /**
- * Construct Nothing.
- */
- Maybe();
+ public:
+ /**
+ * Construct Nothing.
+ */
+ Maybe();
- ~Maybe();
+ ~Maybe();
- Maybe(const Maybe& rhs);
+ Maybe(const Maybe& rhs);
- template <typename U>
- Maybe(const Maybe<U>& rhs); // NOLINT(implicit)
+ template <typename U>
+ Maybe(const Maybe<U>& rhs); // NOLINT(implicit)
- Maybe(Maybe&& rhs);
+ Maybe(Maybe&& rhs);
- template <typename U>
- Maybe(Maybe<U>&& rhs); // NOLINT(implicit)
+ template <typename U>
+ Maybe(Maybe<U>&& rhs); // NOLINT(implicit)
- Maybe& operator=(const Maybe& rhs);
+ Maybe& operator=(const Maybe& rhs);
- template <typename U>
- Maybe& operator=(const Maybe<U>& rhs);
+ template <typename U>
+ Maybe& operator=(const Maybe<U>& rhs);
- Maybe& operator=(Maybe&& rhs);
+ Maybe& operator=(Maybe&& rhs);
- template <typename U>
- Maybe& operator=(Maybe<U>&& rhs);
+ template <typename U>
+ Maybe& operator=(Maybe<U>&& rhs);
- /**
- * Construct a Maybe holding a value.
- */
- Maybe(const T& value); // NOLINT(implicit)
+ /**
+ * Construct a Maybe holding a value.
+ */
+ Maybe(const T& value); // NOLINT(implicit)
- /**
- * Construct a Maybe holding a value.
- */
- Maybe(T&& value); // NOLINT(implicit)
+ /**
+ * Construct a Maybe holding a value.
+ */
+ Maybe(T&& value); // NOLINT(implicit)
- /**
- * True if this holds a value, false if
- * it holds Nothing.
- */
- explicit operator bool() const;
+ /**
+ * True if this holds a value, false if
+ * it holds Nothing.
+ */
+ explicit operator bool() const;
- /**
- * Gets the value if one exists, or else
- * panics.
- */
- T& value();
+ /**
+ * Gets the value if one exists, or else
+ * panics.
+ */
+ T& value();
- /**
- * Gets the value if one exists, or else
- * panics.
- */
- const T& value() const;
+ /**
+ * Gets the value if one exists, or else
+ * panics.
+ */
+ const T& value() const;
- T valueOrDefault(const T& def) const;
+ T valueOrDefault(const T& def) const;
-private:
- template <typename U>
- friend class Maybe;
+ private:
+ template <typename U>
+ friend class Maybe;
- template <typename U>
- Maybe& copy(const Maybe<U>& rhs);
+ template <typename U>
+ Maybe& copy(const Maybe<U>& rhs);
- template <typename U>
- Maybe& move(Maybe<U>&& rhs);
+ template <typename U>
+ Maybe& move(Maybe<U>&& rhs);
- void destroy();
+ void destroy();
- bool mNothing;
+ bool mNothing;
- typename std::aligned_storage<sizeof(T), alignof(T)>::type mStorage;
+ typename std::aligned_storage<sizeof(T), alignof(T)>::type mStorage;
};
template <typename T>
-Maybe<T>::Maybe()
-: mNothing(true) {
-}
+Maybe<T>::Maybe() : mNothing(true) {}
template <typename T>
Maybe<T>::~Maybe() {
- if (!mNothing) {
- destroy();
- }
+ if (!mNothing) {
+ destroy();
+ }
}
template <typename T>
-Maybe<T>::Maybe(const Maybe& rhs)
-: mNothing(rhs.mNothing) {
- if (!rhs.mNothing) {
- new (&mStorage) T(reinterpret_cast<const T&>(rhs.mStorage));
- }
+Maybe<T>::Maybe(const Maybe& rhs) : mNothing(rhs.mNothing) {
+ if (!rhs.mNothing) {
+ new (&mStorage) T(reinterpret_cast<const T&>(rhs.mStorage));
+ }
}
template <typename T>
template <typename U>
-Maybe<T>::Maybe(const Maybe<U>& rhs)
-: mNothing(rhs.mNothing) {
- if (!rhs.mNothing) {
- new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
- }
+Maybe<T>::Maybe(const Maybe<U>& rhs) : mNothing(rhs.mNothing) {
+ if (!rhs.mNothing) {
+ new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
+ }
}
template <typename T>
-Maybe<T>::Maybe(Maybe&& rhs)
-: mNothing(rhs.mNothing) {
- if (!rhs.mNothing) {
- rhs.mNothing = true;
+Maybe<T>::Maybe(Maybe&& rhs) : mNothing(rhs.mNothing) {
+ if (!rhs.mNothing) {
+ rhs.mNothing = true;
- // Move the value from rhs.
- new (&mStorage) T(std::move(reinterpret_cast<T&>(rhs.mStorage)));
- rhs.destroy();
- }
+ // Move the value from rhs.
+ new (&mStorage) T(std::move(reinterpret_cast<T&>(rhs.mStorage)));
+ rhs.destroy();
+ }
}
template <typename T>
template <typename U>
-Maybe<T>::Maybe(Maybe<U>&& rhs)
-: mNothing(rhs.mNothing) {
- if (!rhs.mNothing) {
- rhs.mNothing = true;
+Maybe<T>::Maybe(Maybe<U>&& rhs) : mNothing(rhs.mNothing) {
+ if (!rhs.mNothing) {
+ rhs.mNothing = true;
- // Move the value from rhs.
- new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
- rhs.destroy();
- }
+ // Move the value from rhs.
+ new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
+ rhs.destroy();
+ }
}
template <typename T>
inline Maybe<T>& Maybe<T>::operator=(const Maybe& rhs) {
- // Delegate to the actual assignment.
- return copy(rhs);
+ // Delegate to the actual assignment.
+ return copy(rhs);
}
template <typename T>
template <typename U>
inline Maybe<T>& Maybe<T>::operator=(const Maybe<U>& rhs) {
- return copy(rhs);
+ return copy(rhs);
}
template <typename T>
template <typename U>
Maybe<T>& Maybe<T>::copy(const Maybe<U>& rhs) {
- if (mNothing && rhs.mNothing) {
- // Both are nothing, nothing to do.
- return *this;
- } else if (!mNothing && !rhs.mNothing) {
- // We both are something, so assign rhs to us.
- reinterpret_cast<T&>(mStorage) = reinterpret_cast<const U&>(rhs.mStorage);
- } else if (mNothing) {
- // We are nothing but rhs is something.
- mNothing = rhs.mNothing;
-
- // Copy the value from rhs.
- new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
- } else {
- // We are something but rhs is nothing, so destroy our value.
- mNothing = rhs.mNothing;
- destroy();
- }
+ if (mNothing && rhs.mNothing) {
+ // Both are nothing, nothing to do.
return *this;
+ } else if (!mNothing && !rhs.mNothing) {
+ // We both are something, so assign rhs to us.
+ reinterpret_cast<T&>(mStorage) = reinterpret_cast<const U&>(rhs.mStorage);
+ } else if (mNothing) {
+ // We are nothing but rhs is something.
+ mNothing = rhs.mNothing;
+
+ // Copy the value from rhs.
+ new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
+ } else {
+ // We are something but rhs is nothing, so destroy our value.
+ mNothing = rhs.mNothing;
+ destroy();
+ }
+ return *this;
}
template <typename T>
inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) {
- // Delegate to the actual assignment.
- return move(std::forward<Maybe<T>>(rhs));
+ // Delegate to the actual assignment.
+ return move(std::forward<Maybe<T>>(rhs));
}
template <typename T>
template <typename U>
inline Maybe<T>& Maybe<T>::operator=(Maybe<U>&& rhs) {
- return move(std::forward<Maybe<U>>(rhs));
+ return move(std::forward<Maybe<U>>(rhs));
}
template <typename T>
template <typename U>
Maybe<T>& Maybe<T>::move(Maybe<U>&& rhs) {
- if (mNothing && rhs.mNothing) {
- // Both are nothing, nothing to do.
- return *this;
- } else if (!mNothing && !rhs.mNothing) {
- // We both are something, so move assign rhs to us.
- rhs.mNothing = true;
- reinterpret_cast<T&>(mStorage) = std::move(reinterpret_cast<U&>(rhs.mStorage));
- rhs.destroy();
- } else if (mNothing) {
- // We are nothing but rhs is something.
- mNothing = false;
- rhs.mNothing = true;
-
- // Move the value from rhs.
- new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
- rhs.destroy();
- } else {
- // We are something but rhs is nothing, so destroy our value.
- mNothing = true;
- destroy();
- }
+ if (mNothing && rhs.mNothing) {
+ // Both are nothing, nothing to do.
return *this;
+ } else if (!mNothing && !rhs.mNothing) {
+ // We both are something, so move assign rhs to us.
+ rhs.mNothing = true;
+ reinterpret_cast<T&>(mStorage) =
+ std::move(reinterpret_cast<U&>(rhs.mStorage));
+ rhs.destroy();
+ } else if (mNothing) {
+ // We are nothing but rhs is something.
+ mNothing = false;
+ rhs.mNothing = true;
+
+ // Move the value from rhs.
+ new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
+ rhs.destroy();
+ } else {
+ // We are something but rhs is nothing, so destroy our value.
+ mNothing = true;
+ destroy();
+ }
+ return *this;
}
template <typename T>
-Maybe<T>::Maybe(const T& value)
-: mNothing(false) {
- new (&mStorage) T(value);
+Maybe<T>::Maybe(const T& value) : mNothing(false) {
+ new (&mStorage) T(value);
}
template <typename T>
-Maybe<T>::Maybe(T&& value)
-: mNothing(false) {
- new (&mStorage) T(std::forward<T>(value));
+Maybe<T>::Maybe(T&& value) : mNothing(false) {
+ new (&mStorage) T(std::forward<T>(value));
}
template <typename T>
Maybe<T>::operator bool() const {
- return !mNothing;
+ return !mNothing;
}
template <typename T>
T& Maybe<T>::value() {
- assert(!mNothing && "Maybe<T>::value() called on Nothing");
- return reinterpret_cast<T&>(mStorage);
+ assert(!mNothing && "Maybe<T>::value() called on Nothing");
+ return reinterpret_cast<T&>(mStorage);
}
template <typename T>
const T& Maybe<T>::value() const {
- assert(!mNothing && "Maybe<T>::value() called on Nothing");
- return reinterpret_cast<const T&>(mStorage);
+ assert(!mNothing && "Maybe<T>::value() called on Nothing");
+ return reinterpret_cast<const T&>(mStorage);
}
template <typename T>
T Maybe<T>::valueOrDefault(const T& def) const {
- if (mNothing) {
- return def;
- }
- return reinterpret_cast<const T&>(mStorage);
+ if (mNothing) {
+ return def;
+ }
+ return reinterpret_cast<const T&>(mStorage);
}
template <typename T>
void Maybe<T>::destroy() {
- reinterpret_cast<T&>(mStorage).~T();
+ reinterpret_cast<T&>(mStorage).~T();
}
template <typename T>
inline Maybe<typename std::remove_reference<T>::type> make_value(T&& value) {
- return Maybe<typename std::remove_reference<T>::type>(std::forward<T>(value));
+ return Maybe<typename std::remove_reference<T>::type>(std::forward<T>(value));
}
template <typename T>
inline Maybe<T> make_nothing() {
- return Maybe<T>();
+ return Maybe<T>();
}
/**
- * Define the == operator between Maybe<T> and Maybe<U> only if the operator T == U is defined.
- * That way the compiler will show an error at the callsite when comparing two Maybe<> objects
+ * Define the == operator between Maybe<T> and Maybe<U> only if the operator T
+ * == U is defined.
+ * That way the compiler will show an error at the callsite when comparing two
+ * Maybe<> objects
* whose inner types can't be compared.
*/
template <typename T, typename U>
-typename std::enable_if<
- has_eq_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 true;
- }
- return false;
+typename std::enable_if<has_eq_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 true;
+ }
+ return false;
}
/**
* Same as operator== but negated.
*/
template <typename T, typename U>
-typename std::enable_if<
- has_eq_op<T, U>::value,
- bool
->::type operator!=(const Maybe<T>& a, const Maybe<U>& b) {
- return !(a == b);
+typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator!=(
+ const Maybe<T>& a, const Maybe<U>& b) {
+ 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;
+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
+} // namespace aapt
-#endif // AAPT_MAYBE_H
+#endif // AAPT_MAYBE_H
diff --git a/tools/aapt2/util/StringPiece.h b/tools/aapt2/util/StringPiece.h
index 266c003..de93822 100644
--- a/tools/aapt2/util/StringPiece.h
+++ b/tools/aapt2/util/StringPiece.h
@@ -17,11 +17,11 @@
#ifndef AAPT_STRING_PIECE_H
#define AAPT_STRING_PIECE_H
-#include <ostream>
-#include <string>
#include <utils/JenkinsHash.h>
#include <utils/String8.h>
#include <utils/Unicode.h>
+#include <ostream>
+#include <string>
namespace aapt {
@@ -35,45 +35,46 @@
*/
template <typename TChar>
class BasicStringPiece {
-public:
- using const_iterator = const TChar*;
- using difference_type = size_t;
+ public:
+ using const_iterator = const TChar*;
+ using difference_type = size_t;
- // End of string marker.
- constexpr static const size_t npos = static_cast<size_t>(-1);
+ // End of string marker.
+ constexpr static const size_t npos = static_cast<size_t>(-1);
- BasicStringPiece();
- BasicStringPiece(const BasicStringPiece<TChar>& str);
- BasicStringPiece(const std::basic_string<TChar>& str); // NOLINT(implicit)
- BasicStringPiece(const TChar* str); // NOLINT(implicit)
- BasicStringPiece(const TChar* str, size_t len);
+ BasicStringPiece();
+ BasicStringPiece(const BasicStringPiece<TChar>& str);
+ BasicStringPiece(const std::basic_string<TChar>& str); // NOLINT(implicit)
+ BasicStringPiece(const TChar* str); // NOLINT(implicit)
+ BasicStringPiece(const TChar* str, size_t len);
- BasicStringPiece<TChar>& operator=(const BasicStringPiece<TChar>& rhs);
- BasicStringPiece<TChar>& assign(const TChar* str, size_t len);
+ BasicStringPiece<TChar>& operator=(const BasicStringPiece<TChar>& rhs);
+ BasicStringPiece<TChar>& assign(const TChar* str, size_t len);
- BasicStringPiece<TChar> substr(size_t start, size_t len = npos) const;
- BasicStringPiece<TChar> substr(BasicStringPiece<TChar>::const_iterator begin,
- BasicStringPiece<TChar>::const_iterator end) const;
+ BasicStringPiece<TChar> substr(size_t start, size_t len = npos) const;
+ BasicStringPiece<TChar> substr(
+ BasicStringPiece<TChar>::const_iterator begin,
+ BasicStringPiece<TChar>::const_iterator end) const;
- const TChar* data() const;
- size_t length() const;
- size_t size() const;
- bool empty() const;
- std::basic_string<TChar> toString() const;
+ const TChar* data() const;
+ size_t length() const;
+ size_t size() const;
+ bool empty() const;
+ std::basic_string<TChar> toString() const;
- bool contains(const BasicStringPiece<TChar>& rhs) const;
- int compare(const BasicStringPiece<TChar>& rhs) const;
- bool operator<(const BasicStringPiece<TChar>& rhs) const;
- bool operator>(const BasicStringPiece<TChar>& rhs) const;
- bool operator==(const BasicStringPiece<TChar>& rhs) const;
- bool operator!=(const BasicStringPiece<TChar>& rhs) const;
+ bool contains(const BasicStringPiece<TChar>& rhs) const;
+ int compare(const BasicStringPiece<TChar>& rhs) const;
+ bool operator<(const BasicStringPiece<TChar>& rhs) const;
+ bool operator>(const BasicStringPiece<TChar>& rhs) const;
+ bool operator==(const BasicStringPiece<TChar>& rhs) const;
+ bool operator!=(const BasicStringPiece<TChar>& rhs) const;
- const_iterator begin() const;
- const_iterator end() const;
+ const_iterator begin() const;
+ const_iterator end() const;
-private:
- const TChar* mData;
- size_t mLength;
+ private:
+ const TChar* mData;
+ size_t mLength;
};
using StringPiece = BasicStringPiece<char>;
@@ -87,198 +88,210 @@
constexpr const size_t BasicStringPiece<TChar>::npos;
template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece() : mData(nullptr) , mLength(0) {
-}
+inline BasicStringPiece<TChar>::BasicStringPiece()
+ : mData(nullptr), mLength(0) {}
template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece(const BasicStringPiece<TChar>& str) :
- mData(str.mData), mLength(str.mLength) {
-}
+inline BasicStringPiece<TChar>::BasicStringPiece(
+ const BasicStringPiece<TChar>& str)
+ : mData(str.mData), mLength(str.mLength) {}
template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece(const std::basic_string<TChar>& str) :
- mData(str.data()), mLength(str.length()) {
-}
+inline BasicStringPiece<TChar>::BasicStringPiece(
+ const std::basic_string<TChar>& str)
+ : mData(str.data()), mLength(str.length()) {}
template <>
-inline BasicStringPiece<char>::BasicStringPiece(const char* str) :
- mData(str), mLength(str != nullptr ? strlen(str) : 0) {
-}
+inline BasicStringPiece<char>::BasicStringPiece(const char* str)
+ : mData(str), mLength(str != nullptr ? strlen(str) : 0) {}
template <>
-inline BasicStringPiece<char16_t>::BasicStringPiece(const char16_t* str) :
- mData(str), mLength(str != nullptr ? strlen16(str) : 0) {
-}
+inline BasicStringPiece<char16_t>::BasicStringPiece(const char16_t* str)
+ : mData(str), mLength(str != nullptr ? strlen16(str) : 0) {}
template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece(const TChar* str, size_t len) :
- mData(str), mLength(len) {
-}
+inline BasicStringPiece<TChar>::BasicStringPiece(const TChar* str, size_t len)
+ : mData(str), mLength(len) {}
template <typename TChar>
inline BasicStringPiece<TChar>& BasicStringPiece<TChar>::operator=(
- const BasicStringPiece<TChar>& rhs) {
- mData = rhs.mData;
- mLength = rhs.mLength;
- return *this;
+ const BasicStringPiece<TChar>& rhs) {
+ mData = rhs.mData;
+ mLength = rhs.mLength;
+ return *this;
}
template <typename TChar>
-inline BasicStringPiece<TChar>& BasicStringPiece<TChar>::assign(const TChar* str, size_t len) {
- mData = str;
- mLength = len;
- return *this;
-}
-
-
-template <typename TChar>
-inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(size_t start, size_t len) const {
- if (len == npos) {
- len = mLength - start;
- }
-
- if (start > mLength || start + len > mLength) {
- return BasicStringPiece<TChar>();
- }
- return BasicStringPiece<TChar>(mData + start, len);
+inline BasicStringPiece<TChar>& BasicStringPiece<TChar>::assign(
+ const TChar* str, size_t len) {
+ mData = str;
+ mLength = len;
+ return *this;
}
template <typename TChar>
inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(
- BasicStringPiece<TChar>::const_iterator begin,
- BasicStringPiece<TChar>::const_iterator end) const {
- return BasicStringPiece<TChar>(begin, end - begin);
+ size_t start, size_t len) const {
+ if (len == npos) {
+ len = mLength - start;
+ }
+
+ if (start > mLength || start + len > mLength) {
+ return BasicStringPiece<TChar>();
+ }
+ return BasicStringPiece<TChar>(mData + start, len);
+}
+
+template <typename TChar>
+inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(
+ BasicStringPiece<TChar>::const_iterator begin,
+ BasicStringPiece<TChar>::const_iterator end) const {
+ return BasicStringPiece<TChar>(begin, end - begin);
}
template <typename TChar>
inline const TChar* BasicStringPiece<TChar>::data() const {
- return mData;
+ return mData;
}
template <typename TChar>
inline size_t BasicStringPiece<TChar>::length() const {
- return mLength;
+ return mLength;
}
template <typename TChar>
inline size_t BasicStringPiece<TChar>::size() const {
- return mLength;
+ return mLength;
}
template <typename TChar>
inline bool BasicStringPiece<TChar>::empty() const {
- return mLength == 0;
+ return mLength == 0;
}
template <typename TChar>
inline std::basic_string<TChar> BasicStringPiece<TChar>::toString() const {
- return std::basic_string<TChar>(mData, mLength);
+ return std::basic_string<TChar>(mData, mLength);
}
template <>
-inline bool BasicStringPiece<char>::contains(const BasicStringPiece<char>& rhs) const {
- if (!mData || !rhs.mData) {
- return false;
- }
- if (rhs.mLength > mLength) {
- return false;
- }
- return strstr(mData, rhs.mData) != nullptr;
+inline bool BasicStringPiece<char>::contains(
+ const BasicStringPiece<char>& rhs) const {
+ if (!mData || !rhs.mData) {
+ return false;
+ }
+ if (rhs.mLength > mLength) {
+ return false;
+ }
+ return strstr(mData, rhs.mData) != nullptr;
}
template <>
-inline int BasicStringPiece<char>::compare(const BasicStringPiece<char>& rhs) const {
- const char nullStr = '\0';
- const char* b1 = mData != nullptr ? mData : &nullStr;
- const char* e1 = b1 + mLength;
- const char* b2 = rhs.mData != nullptr ? rhs.mData : &nullStr;
- const char* e2 = b2 + rhs.mLength;
+inline int BasicStringPiece<char>::compare(
+ const BasicStringPiece<char>& rhs) const {
+ const char nullStr = '\0';
+ const char* b1 = mData != nullptr ? mData : &nullStr;
+ const char* e1 = b1 + mLength;
+ const char* b2 = rhs.mData != nullptr ? rhs.mData : &nullStr;
+ const char* e2 = b2 + rhs.mLength;
- while (b1 < e1 && b2 < e2) {
- const int d = static_cast<int>(*b1++) - static_cast<int>(*b2++);
- if (d) {
- return d;
- }
+ while (b1 < e1 && b2 < e2) {
+ const int d = static_cast<int>(*b1++) - static_cast<int>(*b2++);
+ if (d) {
+ return d;
}
- return static_cast<int>(mLength - rhs.mLength);
+ }
+ return static_cast<int>(mLength - rhs.mLength);
}
-inline ::std::ostream& operator<<(::std::ostream& out, const BasicStringPiece<char16_t>& str) {
- android::String8 utf8(str.data(), str.size());
- return out.write(utf8.string(), utf8.size());
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const BasicStringPiece<char16_t>& str) {
+ android::String8 utf8(str.data(), str.size());
+ return out.write(utf8.string(), utf8.size());
}
template <>
-inline bool BasicStringPiece<char16_t>::contains(const BasicStringPiece<char16_t>& rhs) const {
- if (!mData || !rhs.mData) {
- return false;
- }
- if (rhs.mLength > mLength) {
- return false;
- }
- return strstr16(mData, rhs.mData) != nullptr;
+inline bool BasicStringPiece<char16_t>::contains(
+ const BasicStringPiece<char16_t>& rhs) const {
+ if (!mData || !rhs.mData) {
+ return false;
+ }
+ if (rhs.mLength > mLength) {
+ return false;
+ }
+ return strstr16(mData, rhs.mData) != nullptr;
}
template <>
-inline int BasicStringPiece<char16_t>::compare(const BasicStringPiece<char16_t>& rhs) const {
- const char16_t nullStr = u'\0';
- const char16_t* b1 = mData != nullptr ? mData : &nullStr;
- const char16_t* b2 = rhs.mData != nullptr ? rhs.mData : &nullStr;
- return strzcmp16(b1, mLength, b2, rhs.mLength);
+inline int BasicStringPiece<char16_t>::compare(
+ const BasicStringPiece<char16_t>& rhs) const {
+ const char16_t nullStr = u'\0';
+ const char16_t* b1 = mData != nullptr ? mData : &nullStr;
+ const char16_t* b2 = rhs.mData != nullptr ? rhs.mData : &nullStr;
+ return strzcmp16(b1, mLength, b2, rhs.mLength);
}
template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator<(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) < 0;
+inline bool BasicStringPiece<TChar>::operator<(
+ const BasicStringPiece<TChar>& rhs) const {
+ return compare(rhs) < 0;
}
template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator>(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) > 0;
+inline bool BasicStringPiece<TChar>::operator>(
+ const BasicStringPiece<TChar>& rhs) const {
+ return compare(rhs) > 0;
}
template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator==(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) == 0;
+inline bool BasicStringPiece<TChar>::operator==(
+ const BasicStringPiece<TChar>& rhs) const {
+ return compare(rhs) == 0;
}
template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator!=(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) != 0;
+inline bool BasicStringPiece<TChar>::operator!=(
+ const BasicStringPiece<TChar>& rhs) const {
+ return compare(rhs) != 0;
}
template <typename TChar>
-inline typename BasicStringPiece<TChar>::const_iterator BasicStringPiece<TChar>::begin() const {
- return mData;
+inline typename BasicStringPiece<TChar>::const_iterator
+BasicStringPiece<TChar>::begin() const {
+ return mData;
}
template <typename TChar>
-inline typename BasicStringPiece<TChar>::const_iterator BasicStringPiece<TChar>::end() const {
- return mData + mLength;
+inline typename BasicStringPiece<TChar>::const_iterator
+BasicStringPiece<TChar>::end() const {
+ return mData + mLength;
}
-inline ::std::ostream& operator<<(::std::ostream& out, const BasicStringPiece<char>& str) {
- return out.write(str.data(), str.size());
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const BasicStringPiece<char>& str) {
+ return out.write(str.data(), str.size());
}
-} // namespace aapt
+} // namespace aapt
-inline ::std::ostream& operator<<(::std::ostream& out, const std::u16string& str) {
- android::String8 utf8(str.data(), str.size());
- return out.write(utf8.string(), utf8.size());
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const std::u16string& str) {
+ android::String8 utf8(str.data(), str.size());
+ 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);
- }
+ 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
+} // namespace std
-#endif // AAPT_STRING_PIECE_H
+#endif // AAPT_STRING_PIECE_H
diff --git a/tools/aapt2/util/TypeTraits.h b/tools/aapt2/util/TypeTraits.h
index 76c13d6..b6539ed 100644
--- a/tools/aapt2/util/TypeTraits.h
+++ b/tools/aapt2/util/TypeTraits.h
@@ -21,19 +21,20 @@
namespace aapt {
-#define DEFINE_HAS_BINARY_OP_TRAIT(name, op) \
- template <typename T, typename U> \
- struct name { \
- template <typename V, typename W> \
- static constexpr decltype(std::declval<V>() op std::declval<W>(), bool()) test(int) { \
- return true; \
- } \
- template <typename V, typename W> \
- static constexpr bool test(...) { \
- return false; \
- } \
- static constexpr bool value = test<T, U>(int()); \
-}
+#define DEFINE_HAS_BINARY_OP_TRAIT(name, op) \
+ template <typename T, typename U> \
+ struct name { \
+ template <typename V, typename W> \
+ static constexpr decltype(std::declval<V>() op std::declval<W>(), bool()) \
+ test(int) { \
+ return true; \
+ } \
+ template <typename V, typename W> \
+ static constexpr bool test(...) { \
+ return false; \
+ } \
+ static constexpr bool value = test<T, U>(int()); \
+ }
DEFINE_HAS_BINARY_OP_TRAIT(has_eq_op, ==);
DEFINE_HAS_BINARY_OP_TRAIT(has_lt_op, <);
@@ -43,9 +44,10 @@
*/
template <typename T, typename U>
struct is_comparable {
- static constexpr bool value = has_eq_op<T, U>::value && has_lt_op<T, U>::value;
+ static constexpr bool value =
+ has_eq_op<T, U>::value && has_lt_op<T, U>::value;
};
-} // namespace aapt
+} // namespace aapt
#endif /* AAPT_UTIL_TYPETRAITS_H */
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index 9c88354..077e193 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -56,16 +56,14 @@
* UTF-16 isspace(). It basically checks for lower range characters that are
* whitespace.
*/
-inline bool isspace16(char16_t c) {
- return c < 0x0080 && isspace(c);
-}
+inline bool isspace16(char16_t c) { return c < 0x0080 && isspace(c); }
/**
* Returns an iterator to the first character that is not alpha-numeric and that
* is not in the allowedChars set.
*/
-StringPiece::const_iterator findNonAlphaNumericAndNotInSet(const StringPiece& str,
- const StringPiece& allowedChars);
+StringPiece::const_iterator findNonAlphaNumericAndNotInSet(
+ const StringPiece& str, const StringPiece& allowedChars);
/**
* Tests that the string is a valid Java class name.
@@ -78,7 +76,8 @@
bool isJavaPackageName(const StringPiece& str);
/**
- * Converts the class name to a fully qualified class name from the given `package`. Ex:
+ * Converts the class name to a fully qualified class name from the given
+ * `package`. Ex:
*
* asdf --> package.asdf
* .asdf --> package.asdf
@@ -89,111 +88,114 @@
const StringPiece& className);
/**
- * Makes a std::unique_ptr<> with the template parameter inferred by the compiler.
+ * Makes a std::unique_ptr<> with the template parameter inferred by the
+ * compiler.
* This will be present in C++14 and can be removed then.
*/
template <typename T, class... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
- return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
+ return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}
/**
- * Writes a set of items to the std::ostream, joining the times with the provided
+ * Writes a set of items to the std::ostream, joining the times with the
+ * provided
* separator.
*/
template <typename Container>
-::std::function<::std::ostream&(::std::ostream&)> joiner(const Container& container,
- const char* sep) {
- using std::begin;
- using std::end;
- const auto beginIter = begin(container);
- const auto endIter = end(container);
- return [beginIter, endIter, sep](::std::ostream& out) -> ::std::ostream& {
- for (auto iter = beginIter; iter != endIter; ++iter) {
- if (iter != beginIter) {
- out << sep;
- }
- out << *iter;
- }
- return out;
- };
+::std::function<::std::ostream&(::std::ostream&)> joiner(
+ const Container& container, const char* sep) {
+ using std::begin;
+ using std::end;
+ const auto beginIter = begin(container);
+ const auto endIter = end(container);
+ return [beginIter, endIter, sep](::std::ostream& out) -> ::std::ostream& {
+ for (auto iter = beginIter; iter != endIter; ++iter) {
+ if (iter != beginIter) {
+ out << sep;
+ }
+ out << *iter;
+ }
+ return out;
+ };
}
-inline ::std::function<::std::ostream&(::std::ostream&)> formatSize(size_t size) {
- return [size](::std::ostream& out) -> ::std::ostream& {
- constexpr size_t K = 1024u;
- constexpr size_t M = K * K;
- constexpr size_t G = M * K;
- if (size < K) {
- out << size << "B";
- } else if (size < M) {
- out << (double(size) / K) << " KiB";
- } else if (size < G) {
- out << (double(size) / M) << " MiB";
- } else {
- out << (double(size) / G) << " GiB";
- }
- return out;
- };
+inline ::std::function<::std::ostream&(::std::ostream&)> formatSize(
+ size_t size) {
+ return [size](::std::ostream& out) -> ::std::ostream& {
+ constexpr size_t K = 1024u;
+ constexpr size_t M = K * K;
+ constexpr size_t G = M * K;
+ if (size < K) {
+ out << size << "B";
+ } else if (size < M) {
+ out << (double(size) / K) << " KiB";
+ } else if (size < G) {
+ out << (double(size) / M) << " MiB";
+ } else {
+ out << (double(size) / G) << " GiB";
+ }
+ return out;
+ };
}
/**
- * Helper method to extract a UTF-16 string from a StringPool. If the string is stored as UTF-8,
+ * 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.
*/
StringPiece16 getString16(const android::ResStringPool& pool, size_t idx);
/**
- * 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.
+ * 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
- * explicitly specifying an index) when there are more than one argument. This is an error
- * because translations may rearrange the order of the arguments in the string, which will
+ * Checks that the Java string format contains no non-positional arguments
+ * (arguments without
+ * explicitly specifying an index) when there are more than one argument. This
+ * is an error
+ * because translations may rearrange the order of the arguments in the string,
+ * which will
* break the string interpolation.
*/
bool verifyJavaStringFormat(const StringPiece& str);
class StringBuilder {
-public:
- StringBuilder& append(const StringPiece& str);
- const std::string& str() const;
- const std::string& error() const;
+ public:
+ StringBuilder& append(const StringPiece& str);
+ const std::string& str() const;
+ const std::string& error() const;
- // When building StyledStrings, we need UTF-16 indices into the string,
- // which is what the Java layer expects when dealing with java String.charAt().
- size_t utf16Len() const;
+ // When building StyledStrings, we need UTF-16 indices into the string,
+ // which is what the Java layer expects when dealing with java
+ // String.charAt().
+ size_t utf16Len() const;
- operator bool() const;
+ operator bool() const;
-private:
- std::string mStr;
- size_t mUtf16Len = 0;
- bool mQuote = false;
- bool mTrailingSpace = false;
- bool mLastCharWasEscape = false;
- std::string mError;
+ private:
+ std::string mStr;
+ size_t mUtf16Len = 0;
+ bool mQuote = false;
+ bool mTrailingSpace = false;
+ bool mLastCharWasEscape = false;
+ std::string mError;
};
-inline const std::string& StringBuilder::str() const {
- return mStr;
-}
+inline const std::string& StringBuilder::str() const { return mStr; }
-inline const std::string& StringBuilder::error() const {
- return mError;
-}
+inline const std::string& StringBuilder::error() const { return mError; }
-inline size_t StringBuilder::utf16Len() const {
- return mUtf16Len;
-}
+inline size_t StringBuilder::utf16Len() const { return mUtf16Len; }
-inline StringBuilder::operator bool() const {
- return mError.empty();
-}
+inline StringBuilder::operator bool() const { return mError.empty(); }
/**
* Converts a UTF8 string to a UTF16 string.
@@ -216,65 +218,51 @@
* any memory on the heap nor use standard containers.
*/
class Tokenizer {
-public:
- class iterator {
- public:
- iterator(const iterator&) = default;
- iterator& operator=(const iterator&) = default;
+ public:
+ class iterator {
+ public:
+ iterator(const iterator&) = default;
+ iterator& operator=(const iterator&) = default;
- iterator& operator++();
+ iterator& operator++();
- StringPiece operator*() {
- return mToken;
- }
- bool operator==(const iterator& rhs) const;
- bool operator!=(const iterator& rhs) const;
+ StringPiece operator*() { return mToken; }
+ bool operator==(const iterator& rhs) const;
+ bool operator!=(const iterator& rhs) const;
- private:
- friend class Tokenizer;
+ private:
+ friend class Tokenizer;
- iterator(StringPiece s, char sep, StringPiece tok, bool end);
+ iterator(StringPiece s, char sep, StringPiece tok, bool end);
- StringPiece mStr;
- char mSeparator;
- StringPiece mToken;
- bool mEnd;
- };
+ StringPiece mStr;
+ char mSeparator;
+ StringPiece mToken;
+ bool mEnd;
+ };
- Tokenizer(StringPiece str, char sep);
+ Tokenizer(StringPiece str, char sep);
- iterator begin() {
- return mBegin;
- }
+ iterator begin() { return mBegin; }
- iterator end() {
- return mEnd;
- }
+ iterator end() { return mEnd; }
-private:
- const iterator mBegin;
- const iterator mEnd;
+ private:
+ const iterator mBegin;
+ const iterator mEnd;
};
inline Tokenizer tokenize(const StringPiece& str, char sep) {
- return Tokenizer(str, sep);
+ return Tokenizer(str, sep);
}
-inline uint16_t hostToDevice16(uint16_t value) {
- return htods(value);
-}
+inline uint16_t hostToDevice16(uint16_t value) { return htods(value); }
-inline uint32_t hostToDevice32(uint32_t value) {
- return htodl(value);
-}
+inline uint32_t hostToDevice32(uint32_t value) { return htodl(value); }
-inline uint16_t deviceToHost16(uint16_t value) {
- return dtohs(value);
-}
+inline uint16_t deviceToHost16(uint16_t value) { return dtohs(value); }
-inline uint32_t deviceToHost32(uint32_t value) {
- return dtohl(value);
-}
+inline uint32_t deviceToHost32(uint32_t value) { return dtohl(value); }
/**
* Given a path like: res/xml-sw600dp/foo.xml
@@ -288,17 +276,19 @@
bool extractResFilePathParts(const StringPiece& path, StringPiece* outPrefix,
StringPiece* outEntry, StringPiece* outSuffix);
-} // namespace util
+} // namespace util
/**
- * Stream operator for functions. Calls the function with the stream as an argument.
+ * Stream operator for functions. Calls the function with the stream as an
+ * argument.
* In the aapt namespace for lookup.
*/
-inline ::std::ostream& operator<<(::std::ostream& out,
- const ::std::function<::std::ostream&(::std::ostream&)>& f) {
- return f(out);
+inline ::std::ostream& operator<<(
+ ::std::ostream& out,
+ const ::std::function<::std::ostream&(::std::ostream&)>& f) {
+ return f(out);
}
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_UTIL_H
+#endif // AAPT_UTIL_H
diff --git a/tools/aapt2/xml/XmlActionExecutor.h b/tools/aapt2/xml/XmlActionExecutor.h
index cad508c..ca21b08 100644
--- a/tools/aapt2/xml/XmlActionExecutor.h
+++ b/tools/aapt2/xml/XmlActionExecutor.h
@@ -30,79 +30,84 @@
namespace xml {
enum class XmlActionExecutorPolicy {
- /**
- * Actions on run if elements are matched, errors occur only when actions return false.
- */
- None,
+ /**
+ * Actions on run if elements are matched, errors occur only when actions
+ * return false.
+ */
+ None,
- /**
- * The actions defined must match and run. If an element is found that does not match
- * an action, an error occurs.
- */
- Whitelist,
+ /**
+ * The actions defined must match and run. If an element is found that does
+ * not match
+ * an action, an error occurs.
+ */
+ Whitelist,
};
/**
- * Contains the actions to perform at this XML node. This is a recursive data structure that
+ * Contains the actions to perform at this XML node. This is a recursive data
+ * structure that
* holds XmlNodeActions for child XML nodes.
*/
class XmlNodeAction {
-public:
- using ActionFuncWithDiag = std::function<bool(Element*, SourcePathDiagnostics*)>;
- using ActionFunc = std::function<bool(Element*)>;
+ public:
+ using ActionFuncWithDiag =
+ std::function<bool(Element*, SourcePathDiagnostics*)>;
+ using ActionFunc = std::function<bool(Element*)>;
- /**
- * Find or create a child XmlNodeAction that will be performed for the child element
- * with the name `name`.
- */
- XmlNodeAction& operator[](const std::string& name) {
- return mMap[name];
- }
+ /**
+ * Find or create a child XmlNodeAction that will be performed for the child
+ * element
+ * with the name `name`.
+ */
+ XmlNodeAction& operator[](const std::string& name) { return mMap[name]; }
- /**
- * Add an action to be performed at this XmlNodeAction.
- */
- void action(ActionFunc f);
- void action(ActionFuncWithDiag);
+ /**
+ * Add an action to be performed at this XmlNodeAction.
+ */
+ void action(ActionFunc f);
+ void action(ActionFuncWithDiag);
-private:
- friend class XmlActionExecutor;
+ private:
+ friend class XmlActionExecutor;
- bool execute(XmlActionExecutorPolicy policy, SourcePathDiagnostics* diag, Element* el) const;
+ bool execute(XmlActionExecutorPolicy policy, SourcePathDiagnostics* diag,
+ Element* el) const;
- std::map<std::string, XmlNodeAction> mMap;
- std::vector<ActionFuncWithDiag> mActions;
+ std::map<std::string, XmlNodeAction> mMap;
+ std::vector<ActionFuncWithDiag> mActions;
};
/**
- * Allows the definition of actions to execute at specific XML elements defined by their
+ * Allows the definition of actions to execute at specific XML elements defined
+ * by their
* hierarchy.
*/
class XmlActionExecutor {
-public:
- XmlActionExecutor() = default;
+ public:
+ XmlActionExecutor() = default;
- /**
- * Find or create a root XmlNodeAction that will be performed for the root XML element
- * with the name `name`.
- */
- XmlNodeAction& operator[](const std::string& name) {
- return mMap[name];
- }
+ /**
+ * Find or create a root XmlNodeAction that will be performed for the root XML
+ * element
+ * with the name `name`.
+ */
+ XmlNodeAction& operator[](const std::string& name) { return mMap[name]; }
- /**
- * Execute the defined actions for this XmlResource.
- * Returns true if all actions return true, otherwise returns false.
- */
- bool execute(XmlActionExecutorPolicy policy, IDiagnostics* diag, XmlResource* doc) const;
+ /**
+ * Execute the defined actions for this XmlResource.
+ * Returns true if all actions return true, otherwise returns false.
+ */
+ bool execute(XmlActionExecutorPolicy policy, IDiagnostics* diag,
+ XmlResource* doc) const;
-private:
- std::map<std::string, XmlNodeAction> mMap;
+ private:
+ std::map<std::string, XmlNodeAction> mMap;
- DISALLOW_COPY_AND_ASSIGN(XmlActionExecutor);
+ DISALLOW_COPY_AND_ASSIGN(XmlActionExecutor);
};
-} // namespace xml
-} // namespace aapt
+} // namespace xml
+} // namespace aapt
#endif /* AAPT_XML_XMLPATTERN_H */
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index e4f41b0..932303e 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -38,18 +38,18 @@
* Base class for all XML nodes.
*/
class Node {
-public:
- Node* parent = nullptr;
- size_t lineNumber = 0;
- size_t columnNumber = 0;
- std::string comment;
- std::vector<std::unique_ptr<Node>> children;
+ public:
+ Node* parent = nullptr;
+ size_t lineNumber = 0;
+ size_t columnNumber = 0;
+ std::string comment;
+ std::vector<std::unique_ptr<Node>> children;
- virtual ~Node() = default;
+ virtual ~Node() = default;
- void addChild(std::unique_ptr<Node> child);
- virtual void accept(RawVisitor* visitor) = 0;
- virtual std::unique_ptr<Node> clone() = 0;
+ void addChild(std::unique_ptr<Node> child);
+ virtual void accept(RawVisitor* visitor) = 0;
+ virtual std::unique_ptr<Node> clone() = 0;
};
/**
@@ -58,178 +58,173 @@
*/
template <typename Derived>
class BaseNode : public Node {
-public:
- virtual void accept(RawVisitor* visitor) override;
+ public:
+ virtual void accept(RawVisitor* visitor) override;
};
/**
* A Namespace XML node. Can only have one child.
*/
class Namespace : public BaseNode<Namespace> {
-public:
- std::string namespacePrefix;
- std::string namespaceUri;
+ public:
+ std::string namespacePrefix;
+ std::string namespaceUri;
- std::unique_ptr<Node> clone() override;
+ std::unique_ptr<Node> clone() override;
};
struct AaptAttribute {
- Maybe<ResourceId> id;
- aapt::Attribute attribute;
+ Maybe<ResourceId> id;
+ aapt::Attribute attribute;
};
/**
* An XML attribute.
*/
struct Attribute {
- std::string namespaceUri;
- std::string name;
- std::string value;
+ std::string namespaceUri;
+ std::string name;
+ std::string value;
- Maybe<AaptAttribute> compiledAttribute;
- std::unique_ptr<Item> compiledValue;
+ Maybe<AaptAttribute> compiledAttribute;
+ std::unique_ptr<Item> compiledValue;
};
/**
* An Element XML node.
*/
class Element : public BaseNode<Element> {
-public:
- std::string namespaceUri;
- std::string name;
- std::vector<Attribute> attributes;
+ public:
+ std::string namespaceUri;
+ std::string name;
+ std::vector<Attribute> attributes;
- 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();
- std::unique_ptr<Node> clone() override;
+ 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();
+ std::unique_ptr<Node> clone() override;
};
/**
* A Text (CDATA) XML node. Can not have any children.
*/
class Text : public BaseNode<Text> {
-public:
- std::string text;
+ public:
+ std::string text;
- std::unique_ptr<Node> clone() override;
+ std::unique_ptr<Node> clone() override;
};
/**
* An XML resource with a source, name, and XML tree.
*/
class XmlResource {
-public:
- ResourceFile file;
- std::unique_ptr<xml::Node> root;
+ public:
+ ResourceFile file;
+ std::unique_ptr<xml::Node> root;
};
/**
* Inflates an XML DOM from a text stream, logging errors to the logger.
* Returns the root node on success, or nullptr on failure.
*/
-std::unique_ptr<XmlResource> inflate(std::istream* in, IDiagnostics* diag, const Source& source);
+std::unique_ptr<XmlResource> inflate(std::istream* in, IDiagnostics* diag,
+ const Source& source);
/**
* Inflates an XML DOM from a binary ResXMLTree, logging errors to the logger.
* Returns the root node on success, or nullptr on failure.
*/
-std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen, IDiagnostics* diag,
- const Source& source);
+std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen,
+ IDiagnostics* diag, const Source& source);
Element* findRootElement(XmlResource* doc);
Element* findRootElement(Node* node);
/**
- * A visitor interface for the different XML Node subtypes. This will not traverse into
+ * A visitor interface for the different XML Node subtypes. This will not
+ * traverse into
* children. Use Visitor for that.
*/
class RawVisitor {
-public:
- virtual ~RawVisitor() = default;
+ public:
+ virtual ~RawVisitor() = default;
- virtual void visit(Namespace* node) {}
- virtual void visit(Element* node) {}
- virtual void visit(Text* text) {}
+ virtual void visit(Namespace* node) {}
+ virtual void visit(Element* node) {}
+ virtual void visit(Text* text) {}
};
/**
* Visitor whose default implementation visits the children nodes of any node.
*/
class Visitor : public RawVisitor {
-public:
- using RawVisitor::visit;
+ public:
+ using RawVisitor::visit;
- void visit(Namespace* node) override {
- visitChildren(node);
- }
+ void visit(Namespace* node) override { visitChildren(node); }
- void visit(Element* node) override {
- visitChildren(node);
- }
+ void visit(Element* node) override { visitChildren(node); }
- void visit(Text* text) override {
- visitChildren(text);
- }
+ void visit(Text* text) override { visitChildren(text); }
- void visitChildren(Node* node) {
- for (auto& child : node->children) {
- child->accept(this);
- }
+ void visitChildren(Node* node) {
+ for (auto& child : node->children) {
+ child->accept(this);
}
+ }
};
/**
* An XML DOM visitor that will record the package name for a namespace prefix.
*/
class PackageAwareVisitor : public Visitor, public IPackageDeclStack {
-public:
- using Visitor::visit;
+ public:
+ using Visitor::visit;
- void visit(Namespace* ns) override;
- Maybe<ExtractedPackage> transformPackageAlias(
- const StringPiece& alias, const StringPiece& localPackage) const override;
+ void visit(Namespace* ns) override;
+ Maybe<ExtractedPackage> transformPackageAlias(
+ const StringPiece& alias, const StringPiece& localPackage) const override;
-private:
- struct PackageDecl {
- std::string prefix;
- ExtractedPackage package;
- };
+ private:
+ struct PackageDecl {
+ std::string prefix;
+ ExtractedPackage package;
+ };
- std::vector<PackageDecl> mPackageDecls;
+ std::vector<PackageDecl> mPackageDecls;
};
// Implementations
template <typename Derived>
void BaseNode<Derived>::accept(RawVisitor* visitor) {
- visitor->visit(static_cast<Derived*>(this));
+ visitor->visit(static_cast<Derived*>(this));
}
template <typename T>
class NodeCastImpl : public RawVisitor {
-public:
- using RawVisitor::visit;
+ public:
+ using RawVisitor::visit;
- T* value = nullptr;
+ T* value = nullptr;
- void visit(T* v) override {
- value = v;
- }
+ void visit(T* v) override { value = v; }
};
template <typename T>
T* nodeCast(Node* node) {
- NodeCastImpl<T> visitor;
- node->accept(&visitor);
- return visitor.value;
+ NodeCastImpl<T> visitor;
+ node->accept(&visitor);
+ return visitor.value;
}
-} // namespace xml
-} // namespace aapt
+} // namespace xml
+} // namespace aapt
-#endif // AAPT_XML_DOM_H
+#endif // AAPT_XML_DOM_H
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index a24d109..ce69df6 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -23,8 +23,8 @@
#include "util/StringPiece.h"
#include "xml/XmlUtil.h"
-#include <algorithm>
#include <expat.h>
+#include <algorithm>
#include <istream>
#include <ostream>
#include <queue>
@@ -36,263 +36,288 @@
namespace xml {
class XmlPullParser : public IPackageDeclStack {
-public:
- enum class Event {
- kBadDocument,
- kStartDocument,
- kEndDocument,
+ public:
+ enum class Event {
+ kBadDocument,
+ kStartDocument,
+ kEndDocument,
- kStartNamespace,
- kEndNamespace,
- kStartElement,
- kEndElement,
- kText,
- kComment,
- };
+ kStartNamespace,
+ kEndNamespace,
+ kStartElement,
+ kEndElement,
+ kText,
+ kComment,
+ };
- /**
- * Skips to the next direct descendant node of the given startDepth,
- * skipping namespace nodes.
- *
- * When nextChildNode returns true, you can expect Comments, Text, and StartElement events.
- */
- static bool nextChildNode(XmlPullParser* parser, size_t startDepth);
- static bool skipCurrentElement(XmlPullParser* parser);
- static bool isGoodEvent(Event event);
+ /**
+ * Skips to the next direct descendant node of the given startDepth,
+ * skipping namespace nodes.
+ *
+ * When nextChildNode returns true, you can expect Comments, Text, and
+ * StartElement events.
+ */
+ static bool nextChildNode(XmlPullParser* parser, size_t startDepth);
+ static bool skipCurrentElement(XmlPullParser* parser);
+ static bool isGoodEvent(Event event);
- explicit XmlPullParser(std::istream& in);
- ~XmlPullParser();
+ explicit XmlPullParser(std::istream& in);
+ ~XmlPullParser();
- /**
- * Returns the current event that is being processed.
- */
- Event getEvent() const;
+ /**
+ * Returns the current event that is being processed.
+ */
+ Event getEvent() const;
- const std::string& getLastError() const;
+ const std::string& getLastError() const;
- /**
- * Note, unlike XmlPullParser, the first call to next() will return
- * StartElement of the first element.
- */
- Event next();
+ /**
+ * Note, unlike XmlPullParser, the first call to next() will return
+ * StartElement of the first element.
+ */
+ Event next();
- //
- // These are available for all nodes.
- //
+ //
+ // These are available for all nodes.
+ //
- const std::string& getComment() const;
- size_t getLineNumber() const;
- size_t getDepth() const;
+ const std::string& getComment() const;
+ size_t getLineNumber() const;
+ size_t getDepth() const;
- /**
- * Returns the character data for a Text event.
- */
- const std::string& getText() const;
+ /**
+ * Returns the character data for a Text event.
+ */
+ const std::string& getText() const;
- //
- // Namespace prefix and URI are available for StartNamespace and EndNamespace.
- //
+ //
+ // Namespace prefix and URI are available for StartNamespace and EndNamespace.
+ //
- const std::string& getNamespacePrefix() const;
- const std::string& getNamespaceUri() const;
+ const std::string& getNamespacePrefix() const;
+ const std::string& getNamespaceUri() const;
- //
- // These are available for StartElement and EndElement.
- //
+ //
+ // These are available for StartElement and EndElement.
+ //
- const std::string& getElementNamespace() const;
- const std::string& getElementName() const;
+ const std::string& getElementNamespace() const;
+ const std::string& getElementName() const;
- /*
- * Uses the current stack of namespaces to resolve the package. Eg:
- * xmlns:app = "http://schemas.android.com/apk/res/com.android.app"
- * ...
- * android:text="@app:string/message"
- *
- * In this case, 'app' will be converted to 'com.android.app'.
- *
- * If xmlns:app="http://schemas.android.com/apk/res-auto", then
- * 'package' will be set to 'defaultPackage'.
- */
- Maybe<ExtractedPackage> transformPackageAlias(
- const StringPiece& alias, const StringPiece& localPackage) const override;
+ /*
+ * Uses the current stack of namespaces to resolve the package. Eg:
+ * xmlns:app = "http://schemas.android.com/apk/res/com.android.app"
+ * ...
+ * android:text="@app:string/message"
+ *
+ * In this case, 'app' will be converted to 'com.android.app'.
+ *
+ * If xmlns:app="http://schemas.android.com/apk/res-auto", then
+ * 'package' will be set to 'defaultPackage'.
+ */
+ Maybe<ExtractedPackage> transformPackageAlias(
+ const StringPiece& alias, const StringPiece& localPackage) const override;
- //
- // Remaining methods are for retrieving information about attributes
- // associated with a StartElement.
- //
- // Attributes must be in sorted order (according to the less than operator
- // of struct Attribute).
- //
+ //
+ // Remaining methods are for retrieving information about attributes
+ // associated with a StartElement.
+ //
+ // Attributes must be in sorted order (according to the less than operator
+ // of struct Attribute).
+ //
- struct Attribute {
- std::string namespaceUri;
- std::string name;
- std::string value;
+ struct Attribute {
+ std::string namespaceUri;
+ std::string name;
+ std::string value;
- int compare(const Attribute& rhs) const;
- bool operator<(const Attribute& rhs) const;
- bool operator==(const Attribute& rhs) const;
- bool operator!=(const Attribute& rhs) const;
- };
+ int compare(const Attribute& rhs) const;
+ bool operator<(const Attribute& rhs) const;
+ bool operator==(const Attribute& rhs) const;
+ bool operator!=(const Attribute& rhs) const;
+ };
- using const_iterator = std::vector<Attribute>::const_iterator;
+ using const_iterator = std::vector<Attribute>::const_iterator;
- const_iterator beginAttributes() const;
- const_iterator endAttributes() const;
- size_t getAttributeCount() const;
- const_iterator findAttribute(StringPiece namespaceUri, StringPiece name) const;
+ const_iterator beginAttributes() const;
+ const_iterator endAttributes() const;
+ size_t getAttributeCount() const;
+ const_iterator findAttribute(StringPiece namespaceUri,
+ StringPiece name) const;
-private:
- static void XMLCALL startNamespaceHandler(void* userData, const char* prefix, const char* uri);
- static void XMLCALL startElementHandler(void* userData, const char* name, const char** attrs);
- static void XMLCALL characterDataHandler(void* userData, const char* s, int len);
- static void XMLCALL endElementHandler(void* userData, const char* name);
- static void XMLCALL endNamespaceHandler(void* userData, const char* prefix);
- static void XMLCALL commentDataHandler(void* userData, const char* comment);
+ private:
+ static void XMLCALL startNamespaceHandler(void* userData, const char* prefix,
+ const char* uri);
+ static void XMLCALL startElementHandler(void* userData, const char* name,
+ const char** attrs);
+ static void XMLCALL characterDataHandler(void* userData, const char* s,
+ int len);
+ static void XMLCALL endElementHandler(void* userData, const char* name);
+ static void XMLCALL endNamespaceHandler(void* userData, const char* prefix);
+ static void XMLCALL commentDataHandler(void* userData, const char* comment);
- struct EventData {
- Event event;
- size_t lineNumber;
- size_t depth;
- std::string data1;
- std::string data2;
- std::vector<Attribute> attributes;
- };
+ struct EventData {
+ Event event;
+ size_t lineNumber;
+ size_t depth;
+ std::string data1;
+ std::string data2;
+ std::vector<Attribute> attributes;
+ };
- std::istream& mIn;
- XML_Parser mParser;
- char mBuffer[16384];
- std::queue<EventData> mEventQueue;
- std::string mLastError;
- const std::string mEmpty;
- size_t mDepth;
- std::stack<std::string> mNamespaceUris;
+ std::istream& mIn;
+ XML_Parser mParser;
+ char mBuffer[16384];
+ std::queue<EventData> mEventQueue;
+ std::string mLastError;
+ const std::string mEmpty;
+ size_t mDepth;
+ std::stack<std::string> mNamespaceUris;
- struct PackageDecl {
- std::string prefix;
- ExtractedPackage package;
- };
- std::vector<PackageDecl> mPackageAliases;
+ struct PackageDecl {
+ std::string prefix;
+ ExtractedPackage package;
+ };
+ std::vector<PackageDecl> mPackageAliases;
};
/**
* Finds the attribute in the current element within the global namespace.
*/
-Maybe<StringPiece> findAttribute(const XmlPullParser* parser, const StringPiece& 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
+ * Finds the attribute in the current element within the global namespace. The
+ * attribute's value
* must not be the empty string.
*/
-Maybe<StringPiece> findNonEmptyAttribute(const XmlPullParser* parser, const StringPiece& name);
+Maybe<StringPiece> findNonEmptyAttribute(const XmlPullParser* parser,
+ const StringPiece& name);
//
// Implementation
//
-inline ::std::ostream& operator<<(::std::ostream& out, XmlPullParser::Event event) {
- switch (event) {
- case XmlPullParser::Event::kBadDocument: return out << "BadDocument";
- case XmlPullParser::Event::kStartDocument: return out << "StartDocument";
- case XmlPullParser::Event::kEndDocument: return out << "EndDocument";
- case XmlPullParser::Event::kStartNamespace: return out << "StartNamespace";
- case XmlPullParser::Event::kEndNamespace: return out << "EndNamespace";
- case XmlPullParser::Event::kStartElement: return out << "StartElement";
- case XmlPullParser::Event::kEndElement: return out << "EndElement";
- case XmlPullParser::Event::kText: return out << "Text";
- case XmlPullParser::Event::kComment: return out << "Comment";
- }
- return out;
+inline ::std::ostream& operator<<(::std::ostream& out,
+ XmlPullParser::Event event) {
+ switch (event) {
+ case XmlPullParser::Event::kBadDocument:
+ return out << "BadDocument";
+ case XmlPullParser::Event::kStartDocument:
+ return out << "StartDocument";
+ case XmlPullParser::Event::kEndDocument:
+ return out << "EndDocument";
+ case XmlPullParser::Event::kStartNamespace:
+ return out << "StartNamespace";
+ case XmlPullParser::Event::kEndNamespace:
+ return out << "EndNamespace";
+ case XmlPullParser::Event::kStartElement:
+ return out << "StartElement";
+ case XmlPullParser::Event::kEndElement:
+ return out << "EndElement";
+ case XmlPullParser::Event::kText:
+ return out << "Text";
+ case XmlPullParser::Event::kComment:
+ return out << "Comment";
+ }
+ return out;
}
-inline bool XmlPullParser::nextChildNode(XmlPullParser* parser, size_t startDepth) {
- Event event;
+inline bool XmlPullParser::nextChildNode(XmlPullParser* parser,
+ size_t startDepth) {
+ Event event;
- // First get back to the start depth.
- while (isGoodEvent(event = parser->next()) && parser->getDepth() > startDepth + 1) {}
+ // First get back to the start depth.
+ while (isGoodEvent(event = parser->next()) &&
+ parser->getDepth() > startDepth + 1) {
+ }
- // Now look for the first good node.
- while ((event != Event::kEndElement || parser->getDepth() > startDepth) && isGoodEvent(event)) {
- switch (event) {
- case Event::kText:
- case Event::kComment:
- case Event::kStartElement:
- return true;
- default:
- break;
- }
- event = parser->next();
+ // Now look for the first good node.
+ while ((event != Event::kEndElement || parser->getDepth() > startDepth) &&
+ isGoodEvent(event)) {
+ switch (event) {
+ case Event::kText:
+ case Event::kComment:
+ case Event::kStartElement:
+ return true;
+ default:
+ break;
}
- return false;
+ event = parser->next();
+ }
+ return false;
}
inline bool XmlPullParser::skipCurrentElement(XmlPullParser* parser) {
- int depth = 1;
- while (depth > 0) {
- switch (parser->next()) {
- case Event::kEndDocument:
- return true;
- case Event::kBadDocument:
- return false;
- case Event::kStartElement:
- depth++;
- break;
- case Event::kEndElement:
- depth--;
- break;
- default:
- break;
- }
+ int depth = 1;
+ while (depth > 0) {
+ switch (parser->next()) {
+ case Event::kEndDocument:
+ return true;
+ case Event::kBadDocument:
+ return false;
+ case Event::kStartElement:
+ depth++;
+ break;
+ case Event::kEndElement:
+ depth--;
+ break;
+ default:
+ break;
}
- return true;
+ }
+ return true;
}
inline bool XmlPullParser::isGoodEvent(XmlPullParser::Event event) {
- return event != Event::kBadDocument && event != Event::kEndDocument;
+ return event != Event::kBadDocument && event != Event::kEndDocument;
}
inline int XmlPullParser::Attribute::compare(const Attribute& rhs) const {
- int cmp = namespaceUri.compare(rhs.namespaceUri);
- if (cmp != 0) return cmp;
- return name.compare(rhs.name);
+ int cmp = namespaceUri.compare(rhs.namespaceUri);
+ if (cmp != 0) return cmp;
+ return name.compare(rhs.name);
}
inline bool XmlPullParser::Attribute::operator<(const Attribute& rhs) const {
- return compare(rhs) < 0;
+ return compare(rhs) < 0;
}
inline bool XmlPullParser::Attribute::operator==(const Attribute& rhs) const {
- return compare(rhs) == 0;
+ return compare(rhs) == 0;
}
inline bool XmlPullParser::Attribute::operator!=(const Attribute& rhs) const {
- return compare(rhs) != 0;
+ return compare(rhs) != 0;
}
-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<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;
- if (cmp > 0) return false;
- cmp = attr.name.compare(0, attr.name.size(), rhs.second.data(), rhs.second.size());
- if (cmp < 0) return true;
- return false;
- }
- );
+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<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;
+ if (cmp > 0) return false;
+ cmp = attr.name.compare(0, attr.name.size(), rhs.second.data(),
+ rhs.second.size());
+ if (cmp < 0) return true;
+ return false;
+ });
- if (iter != endIter && namespaceUri == iter->namespaceUri && name == iter->name) {
- return iter;
- }
- return endIter;
+ if (iter != endIter && namespaceUri == iter->namespaceUri &&
+ name == iter->name) {
+ return iter;
+ }
+ return endIter;
}
-} // namespace xml
-} // namespace aapt
+} // namespace xml
+} // namespace aapt
-#endif // AAPT_XML_PULL_PARSER_H
+#endif // AAPT_XML_PULL_PARSER_H
diff --git a/tools/aapt2/xml/XmlUtil.h b/tools/aapt2/xml/XmlUtil.h
index a6ad79d..96de654 100644
--- a/tools/aapt2/xml/XmlUtil.h
+++ b/tools/aapt2/xml/XmlUtil.h
@@ -26,9 +26,12 @@
namespace xml {
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";
+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";
constexpr const char* kSchemaTools = "http://schemas.android.com/tools";
constexpr const char* kSchemaAapt = "http://schemas.android.com/aapt";
@@ -36,17 +39,19 @@
* Result of extracting a package name from a namespace URI declaration.
*/
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::string package;
+ /**
+ * 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::string package;
- /**
- * True if the package's private namespace was declared. This means that private resources
- * are made visible.
- */
- bool privateNamespace;
+ /**
+ * True if the package's private namespace was declared. This means that
+ * private resources
+ * are made visible.
+ */
+ bool privateNamespace;
};
/**
@@ -57,7 +62,8 @@
* Special case: if namespaceUri is http://schemas.android.com/apk/res-auto,
* returns an empty package name.
*/
-Maybe<ExtractedPackage> extractPackageFromNamespace(const std::string& namespaceUri);
+Maybe<ExtractedPackage> extractPackageFromNamespace(
+ const std::string& namespaceUri);
/**
* Returns an XML Android namespace for the given package of the form:
@@ -68,31 +74,37 @@
*
* http://schemas.android.com/apk/prv/res/<package>
*/
-std::string buildPackageNamespace(const StringPiece& package, bool privateReference=false);
+std::string buildPackageNamespace(const StringPiece& package,
+ bool privateReference = false);
/**
- * Interface representing a stack of XML namespace declarations. When looking up the package
+ * Interface representing a stack of XML namespace declarations. When looking up
+ * the package
* for a namespace prefix, the stack is checked from top to bottom.
*/
struct IPackageDeclStack {
- virtual ~IPackageDeclStack() = default;
+ virtual ~IPackageDeclStack() = default;
- /**
- * Returns an ExtractedPackage struct if the alias given corresponds with a package declaration.
- */
- virtual Maybe<ExtractedPackage> transformPackageAlias(
- const StringPiece& alias, const StringPiece& localPackage) const = 0;
+ /**
+ * Returns an ExtractedPackage struct if the alias given corresponds with a
+ * package declaration.
+ */
+ virtual Maybe<ExtractedPackage> transformPackageAlias(
+ const StringPiece& alias, const StringPiece& localPackage) const = 0;
};
/**
- * Helper function for transforming the original Reference inRef to a fully qualified reference
- * via the IPackageDeclStack. This will also mark the Reference as private if the namespace of
+ * Helper function for transforming the original Reference inRef to a fully
+ * qualified reference
+ * via the IPackageDeclStack. This will also mark the Reference as private if
+ * the namespace of
* the package declaration was private.
*/
void transformReferenceFromNamespace(IPackageDeclStack* declStack,
- const StringPiece& localPackage, Reference* inRef);
+ const StringPiece& localPackage,
+ Reference* inRef);
-} // namespace xml
-} // namespace aapt
+} // namespace xml
+} // namespace aapt
#endif /* AAPT_XML_XMLUTIL_H */