]> git.saurik.com Git - android/aapt.git/blobdiff - ResourceTable.cpp
auto import from //depot/cupcake/@136594
[android/aapt.git] / ResourceTable.cpp
index 6fe196acb515c831098daa395fc556e4f91f55f4..6f71a1ed0d46947cdaf21cf81da1fe85b7e2c531 100644 (file)
@@ -115,7 +115,7 @@ static const flag_entry gFormatFlags[] = {
       "a floating point value, such as \"<code>1.2</code>\"."},
     { dimensionArray, sizeof(dimensionArray)/2, ResTable_map::TYPE_DIMENSION,
       "a dimension value, which is a floating point number appended with a unit such as \"<code>14.5sp</code>\".\n"
-      "Available units are: px (pixels), db (density-independent pixels), sp (scaled pixels based on preferred font size),\n"
+      "Available units are: px (pixels), dp (density-independent pixels), sp (scaled pixels based on preferred font size),\n"
       "in (inches), mm (millimeters)." },
     { fractionArray, sizeof(fractionArray)/2, ResTable_map::TYPE_FRACTION,
       "a fractional value, which is a floating point number appended with either % or %p, such as \"<code>14.5%</code>\".\n"
@@ -546,6 +546,7 @@ status_t parseAndAddBag(Bundle* bundle,
                         const String16& itemIdent,
                         int32_t curFormat,
                         bool pseudolocalize,
+                        const bool overwrite,
                         ResourceTable* outTable)
 {
     status_t err;
@@ -572,7 +573,7 @@ status_t parseAndAddBag(Bundle* bundle,
 
     err = outTable->addBag(SourcePos(in->getPrintableSource(), block->getLineNumber()),
                            myPackage, curType, ident, parentIdent, itemIdent, str,
-                           &spans, &config, false, false, curFormat);
+                           &spans, &config, overwrite, false, curFormat);
     return err;
 }
 
@@ -588,6 +589,7 @@ status_t parseAndAddEntry(Bundle* bundle,
                         bool curIsStyled,
                         int32_t curFormat,
                         bool pseudolocalize,
+                        const bool overwrite,
                         ResourceTable* outTable)
 {
     status_t err;
@@ -610,7 +612,7 @@ status_t parseAndAddEntry(Bundle* bundle,
 
     err = outTable->addEntry(SourcePos(in->getPrintableSource(), block->getLineNumber()),
                              myPackage, curType, ident, str, &spans, &config,
-                             false, curFormat);
+                             false, curFormat, overwrite);
 
     return err;
 }
@@ -619,6 +621,7 @@ status_t compileResourceFile(Bundle* bundle,
                              const sp<AaptAssets>& assets,
                              const sp<AaptFile>& in,
                              const ResTable_config& defParams,
+                             const bool overwrite,
                              ResourceTable* outTable)
 {
     ResXMLTree block;
@@ -638,8 +641,10 @@ status_t compileResourceFile(Bundle* bundle,
     const String16 string16("string");
     const String16 drawable16("drawable");
     const String16 color16("color");
+    const String16 bool16("bool");
     const String16 integer16("integer");
     const String16 dimen16("dimen");
+    const String16 fraction16("fraction");
     const String16 style16("style");
     const String16 plurals16("plurals");
     const String16 array16("array");
@@ -671,6 +676,10 @@ status_t compileResourceFile(Bundle* bundle,
     const String16 many16("many");
     const String16 quantityMany16("^many");
 
+    // useful attribute names and special values
+    const String16 name16("name");
+    const String16 translatable16("translatable");
+    const String16 false16("false");
 
     const String16 myPackage(assets->getPackage());
 
@@ -950,6 +959,45 @@ status_t compileResourceFile(Bundle* bundle,
                 }
                 curIsStyled = true;
             } else if (strcmp16(block.getElementName(&len), string16.string()) == 0) {
+                // Note the existence and locale of every string we process
+                char rawLocale[16];
+                curParams.getLocale(rawLocale);
+                String8 locale(rawLocale);
+                String16 name;
+                String16 translatable;
+
+                size_t n = block.getAttributeCount();
+                for (size_t i = 0; i < n; i++) {
+                    size_t length;
+                    const uint16_t* attr = block.getAttributeName(i, &length);
+                    if (strcmp16(attr, name16.string()) == 0) {
+                        name.setTo(block.getAttributeStringValue(i, &length));
+                    } else if (strcmp16(attr, translatable16.string()) == 0) {
+                        translatable.setTo(block.getAttributeStringValue(i, &length));
+                    }
+                }
+                
+                if (name.size() > 0) {
+                    if (translatable == false16) {
+                        // Untranslatable strings must only exist in the default [empty] locale
+                        if (locale.size() > 0) {
+                            fprintf(stderr, "aapt: warning: string '%s' in %s marked untranslatable but exists"
+                                    " in locale '%s'\n", String8(name).string(),
+                                    bundle->getResourceSourceDirs()[0],
+                                    locale.string());
+                            // hasErrors = localHasErrors = true;
+                        } else {
+                            // Intentionally empty block:
+                            //
+                            // Don't add untranslatable strings to the localization table; that
+                            // way if we later see localizations of them, they'll be flagged as
+                            // having no default translation.
+                        }
+                    } else {
+                        outTable->addLocalization(name, locale);
+                    }
+                }
+
                 curTag = &string16;
                 curType = string16;
                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
@@ -963,6 +1011,10 @@ status_t compileResourceFile(Bundle* bundle,
                 curTag = &color16;
                 curType = color16;
                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR;
+            } else if (strcmp16(block.getElementName(&len), bool16.string()) == 0) {
+                curTag = &bool16;
+                curType = bool16;
+                curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_BOOLEAN;
             } else if (strcmp16(block.getElementName(&len), integer16.string()) == 0) {
                 curTag = &integer16;
                 curType = integer16;
@@ -971,6 +1023,10 @@ status_t compileResourceFile(Bundle* bundle,
                 curTag = &dimen16;
                 curType = dimen16;
                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_DIMENSION;
+            } else if (strcmp16(block.getElementName(&len), fraction16.string()) == 0) {
+                curTag = &fraction16;
+                curType = fraction16;
+                curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_FRACTION;
             } else if (strcmp16(block.getElementName(&len), bag16.string()) == 0) {
                 curTag = &bag16;
                 curIsBag = true;
@@ -1124,7 +1180,8 @@ status_t compileResourceFile(Bundle* bundle,
                         block.getPosition(&parserPosition);
 
                         err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType,
-                                ident, parentIdent, itemIdent, curFormat, false, outTable);
+                                ident, parentIdent, itemIdent, curFormat, 
+                                false, overwrite, outTable);
                         if (err == NO_ERROR) {
                             if (curIsPseudolocalizable && localeIsDefined(curParams)
                                     && bundle->getPseudolocalize()) {
@@ -1133,7 +1190,7 @@ status_t compileResourceFile(Bundle* bundle,
                                 block.setPosition(parserPosition);
                                 err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage,
                                         curType, ident, parentIdent, itemIdent, curFormat, true,
-                                        outTable);
+                                        overwrite, outTable);
 #endif
                             }
                         } 
@@ -1156,7 +1213,7 @@ status_t compileResourceFile(Bundle* bundle,
                 block.getPosition(&parserPosition);
 
                 err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident,
-                        *curTag, curIsStyled, curFormat, false, outTable);
+                        *curTag, curIsStyled, curFormat, false, overwrite, outTable);
 
                 if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR?
                     hasErrors = localHasErrors = true;
@@ -1167,7 +1224,7 @@ status_t compileResourceFile(Bundle* bundle,
                         // pseudolocalize here
                         block.setPosition(parserPosition);
                         err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType,
-                                ident, *curTag, curIsStyled, curFormat, true, outTable);
+                                ident, *curTag, curIsStyled, curFormat, true, false, outTable);
                         if (err != NO_ERROR) {
                             hasErrors = localHasErrors = true;
                         }
@@ -1312,7 +1369,8 @@ status_t ResourceTable::addEntry(const SourcePos& sourcePos,
                                  const Vector<StringPool::entry_style_span>* style,
                                  const ResTable_config* params,
                                  const bool doSetIndex,
-                                 const int32_t format)
+                                 const int32_t format,
+                                 const bool overwrite)
 {
     // Check for adding entries in other packages...  for now we do
     // nothing.  We need to do the right thing here to support skinning.
@@ -1336,7 +1394,7 @@ status_t ResourceTable::addEntry(const SourcePos& sourcePos,
     if (e == NULL) {
         return UNKNOWN_ERROR;
     }
-    status_t err = e->setItem(sourcePos, value, style, format);
+    status_t err = e->setItem(sourcePos, value, style, format, overwrite);
     if (err == NO_ERROR) {
         mNumLocal++;
     }
@@ -1553,17 +1611,26 @@ inline uint32_t ResourceTable::getResId(const sp<Package>& p,
 
 uint32_t ResourceTable::getResId(const String16& package,
                                  const String16& type,
-                                 const String16& name) const
+                                 const String16& name,
+                                 bool onlyPublic) const
 {
     sp<Package> p = mPackages.valueFor(package);
     if (p == NULL) return 0;
 
     // First look for this in the included resources...
+    uint32_t specFlags = 0;
     uint32_t rid = mAssets->getIncludedResources()
         .identifierForName(name.string(), name.size(),
                            type.string(), type.size(),
-                           package.string(), package.size());
+                           package.string(), package.size(),
+                           &specFlags);
     if (rid != 0) {
+        if (onlyPublic) {
+            if ((specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) {
+                return 0;
+            }
+        }
+        
         if (Res_INTERNALID(rid)) {
             return rid;
         }
@@ -1584,7 +1651,8 @@ uint32_t ResourceTable::getResId(const String16& package,
 uint32_t ResourceTable::getResId(const String16& ref,
                                  const String16* defType,
                                  const String16* defPackage,
-                                 const char** outErrorMsg) const
+                                 const char** outErrorMsg,
+                                 bool onlyPublic) const
 {
     String16 package, type, name;
     if (!ResTable::expandResourceRef(
@@ -1603,7 +1671,7 @@ uint32_t ResourceTable::getResId(const String16& ref,
                      String8(name).string()));
         return 0;
     }
-    uint32_t res = getResId(package, type, name);
+    uint32_t res = getResId(package, type, name, onlyPublic);
     NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n",
                  String8(package).string(), String8(type).string(),
                  String8(name).string(), res));
@@ -2036,6 +2104,92 @@ status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols) {
 }
 
 
+void
+ResourceTable::addLocalization(const String16& name, const String8& locale)
+{
+    mLocalizations[name].insert(locale);
+}
+
+
+/*!
+ * Flag various sorts of localization problems.  '+' indicates checks already implemented;
+ * '-' indicates checks that will be implemented in the future.
+ *
+ * + A localized string for which no default-locale version exists => warning
+ * + A string for which no version in an explicitly-requested locale exists => warning
+ * + A localized translation of an translateable="false" string => warning
+ * - A localized string not provided in every locale used by the table
+ */
+status_t
+ResourceTable::validateLocalizations(void)
+{
+    status_t err = NO_ERROR;
+    const String8 defaultLocale;
+
+    // For all strings...
+    for (map<String16, set<String8> >::iterator nameIter = mLocalizations.begin();
+         nameIter != mLocalizations.end();
+         nameIter++) {
+        const set<String8>& configSet = nameIter->second;   // naming convenience
+
+        // Look for strings with no default localization
+        if (configSet.count(defaultLocale) == 0) {
+            fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:",
+                    String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]);
+            for (set<String8>::iterator locales = configSet.begin();
+                 locales != configSet.end();
+                 locales++) {
+                fprintf(stdout, " %s", (*locales).string());
+            }
+            fprintf(stdout, "\n");
+            // !!! TODO: throw an error here in some circumstances
+        }
+
+        // Check that all requested localizations are present for this string
+        if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) {
+            const char* allConfigs = mBundle->getConfigurations();
+            const char* start = allConfigs;
+            const char* comma;
+            
+            do {
+                String8 config;
+                comma = strchr(start, ',');
+                if (comma != NULL) {
+                    config.setTo(start, comma - start);
+                    start = comma + 1;
+                } else {
+                    config.setTo(start);
+                }
+
+                // don't bother with the pseudolocale "zz_ZZ"
+                if (config != "zz_ZZ") {
+                    if (configSet.find(config) == configSet.end()) {
+                        // okay, no specific localization found.  it's possible that we are
+                        // requiring a specific regional localization [e.g. de_DE] but there is an
+                        // available string in the generic language localization [e.g. de];
+                        // consider that string to have fulfilled the localization requirement.
+                        String8 region(config.string(), 2);
+                        if (configSet.find(region) == configSet.end()) {
+                            if (configSet.count(defaultLocale) == 0) {
+                                fprintf(stdout, "aapt: error: "
+                                        "*** string '%s' has no default or required localization "
+                                        "for '%s' in %s\n",
+                                        String8(nameIter->first).string(),
+                                        config.string(),
+                                        mBundle->getResourceSourceDirs()[0]);
+                                err = UNKNOWN_ERROR;
+                            }
+                        }
+                    }
+                }
+           } while (comma != NULL);
+        }
+    }
+
+    return err;
+}
+
+
 status_t
 ResourceFilter::parse(const char* arg)
 {
@@ -2187,6 +2341,10 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                 }
                 const size_t N = c->getEntries().size();
                 for (size_t ei=0; ei<N; ei++) {
+                    ConfigDescription config = c->getEntries().keyAt(ei);
+                    if (!filter.match(config)) {
+                        continue;
+                    }
                     sp<Entry> e = c->getEntries().valueAt(ei);
                     if (e == NULL) {
                         continue;
@@ -2579,7 +2737,8 @@ status_t ResourceTable::Entry::makeItABag(const SourcePos& sourcePos)
 status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos,
                                        const String16& value,
                                        const Vector<StringPool::entry_style_span>* style,
-                                       int32_t format)
+                                       int32_t format,
+                                       const bool overwrite)
 {
     Item item(sourcePos, false, value, style);
 
@@ -2591,7 +2750,7 @@ status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos,
                         item.sourcePos.file.string(), item.sourcePos.line);
         return UNKNOWN_ERROR;
     }
-    if (mType != TYPE_UNKNOWN) {
+    if ( (mType != TYPE_UNKNOWN) && (overwrite == false) ) {
         sourcePos.error("Resource entry %s is already defined.\n"
                         "%s:%d: Originally defined here.\n",
                         String8(mName).string(),
@@ -3330,4 +3489,3 @@ bool ResourceTable::getItemValue(
     }
     return res;
 }
-