]> git.saurik.com Git - android/aapt.git/blobdiff - ResourceTable.cpp
Fix aapt to be able to add configs from overlays.
[android/aapt.git] / ResourceTable.cpp
index 33f8f721bd6227f5e2cd1a3a8894384b2e76988f..95a238401832e78941f045652788cc9950378c80 100644 (file)
@@ -23,6 +23,16 @@ status_t compileXmlFile(const sp<AaptAssets>& assets,
     if (root == NULL) {
         return UNKNOWN_ERROR;
     }
     if (root == NULL) {
         return UNKNOWN_ERROR;
     }
+    
+    return compileXmlFile(assets, root, target, table, options);
+}
+
+status_t compileXmlFile(const sp<AaptAssets>& assets,
+                        const sp<XMLNode>& root,
+                        const sp<AaptFile>& target,
+                        ResourceTable* table,
+                        int options)
+{
     if ((options&XML_COMPILE_STRIP_WHITESPACE) != 0) {
         root->removeWhitespace(true, NULL);
     } else  if ((options&XML_COMPILE_COMPACT_WHITESPACE) != 0) {
     if ((options&XML_COMPILE_STRIP_WHITESPACE) != 0) {
         root->removeWhitespace(true, NULL);
     } else  if ((options&XML_COMPILE_COMPACT_WHITESPACE) != 0) {
@@ -546,6 +556,7 @@ status_t parseAndAddBag(Bundle* bundle,
                         const String16& itemIdent,
                         int32_t curFormat,
                         bool pseudolocalize,
                         const String16& itemIdent,
                         int32_t curFormat,
                         bool pseudolocalize,
+                        const bool overwrite,
                         ResourceTable* outTable)
 {
     status_t err;
                         ResourceTable* outTable)
 {
     status_t err;
@@ -572,7 +583,7 @@ status_t parseAndAddBag(Bundle* bundle,
 
     err = outTable->addBag(SourcePos(in->getPrintableSource(), block->getLineNumber()),
                            myPackage, curType, ident, parentIdent, itemIdent, str,
 
     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;
 }
 
     return err;
 }
 
@@ -588,6 +599,7 @@ status_t parseAndAddEntry(Bundle* bundle,
                         bool curIsStyled,
                         int32_t curFormat,
                         bool pseudolocalize,
                         bool curIsStyled,
                         int32_t curFormat,
                         bool pseudolocalize,
+                        const bool overwrite,
                         ResourceTable* outTable)
 {
     status_t err;
                         ResourceTable* outTable)
 {
     status_t err;
@@ -610,7 +622,7 @@ status_t parseAndAddEntry(Bundle* bundle,
 
     err = outTable->addEntry(SourcePos(in->getPrintableSource(), block->getLineNumber()),
                              myPackage, curType, ident, str, &spans, &config,
 
     err = outTable->addEntry(SourcePos(in->getPrintableSource(), block->getLineNumber()),
                              myPackage, curType, ident, str, &spans, &config,
-                             false, curFormat);
+                             false, curFormat, overwrite);
 
     return err;
 }
 
     return err;
 }
@@ -619,6 +631,7 @@ status_t compileResourceFile(Bundle* bundle,
                              const sp<AaptAssets>& assets,
                              const sp<AaptFile>& in,
                              const ResTable_config& defParams,
                              const sp<AaptAssets>& assets,
                              const sp<AaptFile>& in,
                              const ResTable_config& defParams,
+                             const bool overwrite,
                              ResourceTable* outTable)
 {
     ResXMLTree block;
                              ResourceTable* outTable)
 {
     ResXMLTree block;
@@ -641,13 +654,16 @@ status_t compileResourceFile(Bundle* bundle,
     const String16 bool16("bool");
     const String16 integer16("integer");
     const String16 dimen16("dimen");
     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");
     const String16 string_array16("string-array");
     const String16 integer_array16("integer-array");
     const String16 public16("public");
     const String16 style16("style");
     const String16 plurals16("plurals");
     const String16 array16("array");
     const String16 string_array16("string-array");
     const String16 integer_array16("integer-array");
     const String16 public16("public");
+    const String16 public_padding16("public-padding");
     const String16 private_symbols16("private-symbols");
     const String16 private_symbols16("private-symbols");
+    const String16 add_resource16("add-resource");
     const String16 skip16("skip");
     const String16 eat_comment16("eat-comment");
 
     const String16 skip16("skip");
     const String16 eat_comment16("eat-comment");
 
@@ -681,7 +697,7 @@ status_t compileResourceFile(Bundle* bundle,
 
     bool hasErrors = false;
     
 
     bool hasErrors = false;
     
-    uint32_t nextPublicId = 0;
+    DefaultKeyedVector<String16, uint32_t> nextPublicId(0);
 
     ResXMLTree::event_code_t code;
     do {
 
     ResXMLTree::event_code_t code;
     do {
@@ -714,6 +730,7 @@ status_t compileResourceFile(Bundle* bundle,
             String16 curType;
             int32_t curFormat = ResTable_map::TYPE_ANY;
             bool curIsBag = false;
             String16 curType;
             int32_t curFormat = ResTable_map::TYPE_ANY;
             bool curIsBag = false;
+            bool curIsBagReplaceOnOverwrite = false;
             bool curIsStyled = false;
             bool curIsPseudolocalizable = false;
             bool localHasErrors = false;
             bool curIsStyled = false;
             bool curIsPseudolocalizable = false;
             bool localHasErrors = false;
@@ -770,15 +787,15 @@ status_t compileResourceFile(Bundle* bundle,
                         hasErrors = localHasErrors = true;
                     } else {
                         ident = identValue.data;
                         hasErrors = localHasErrors = true;
                     } else {
                         ident = identValue.data;
-                        nextPublicId = ident+1;
+                        nextPublicId.replaceValueFor(type, ident+1);
                     }
                     }
-                } else if (nextPublicId == 0) {
+                } else if (nextPublicId.indexOfKey(type) < 0) {
                     srcPos.error("No 'id' attribute supplied <public>,"
                             " and no previous id defined in this file.\n");
                     hasErrors = localHasErrors = true;
                 } else if (!localHasErrors) {
                     srcPos.error("No 'id' attribute supplied <public>,"
                             " and no previous id defined in this file.\n");
                     hasErrors = localHasErrors = true;
                 } else if (!localHasErrors) {
-                    ident = nextPublicId;
-                    nextPublicId++;
+                    ident = nextPublicId.valueFor(type);
+                    nextPublicId.replaceValueFor(type, ident+1);
                 }
 
                 if (!localHasErrors) {
                 }
 
                 if (!localHasErrors) {
@@ -812,6 +829,116 @@ status_t compileResourceFile(Bundle* bundle,
                 }
                 continue;
 
                 }
                 continue;
 
+            } else if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) {
+                SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
+            
+                String16 type;
+                ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
+                if (typeIdx < 0) {
+                    srcPos.error("A 'type' attribute is required for <public-padding>\n");
+                    hasErrors = localHasErrors = true;
+                }
+                type = String16(block.getAttributeStringValue(typeIdx, &len));
+
+                String16 name;
+                ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
+                if (nameIdx < 0) {
+                    srcPos.error("A 'name' attribute is required for <public-padding>\n");
+                    hasErrors = localHasErrors = true;
+                }
+                name = String16(block.getAttributeStringValue(nameIdx, &len));
+
+                uint32_t start = 0;
+                ssize_t startIdx = block.indexOfAttribute(NULL, "start");
+                if (startIdx >= 0) {
+                    const char16_t* startStr = block.getAttributeStringValue(startIdx, &len);
+                    Res_value startValue;
+                    if (!ResTable::stringToInt(startStr, len, &startValue)) {
+                        srcPos.error("Given 'start' attribute is not an integer: %s\n",
+                                String8(block.getAttributeStringValue(startIdx, &len)).string());
+                        hasErrors = localHasErrors = true;
+                    } else {
+                        start = startValue.data;
+                    }
+                } else if (nextPublicId.indexOfKey(type) < 0) {
+                    srcPos.error("No 'start' attribute supplied <public-padding>,"
+                            " and no previous id defined in this file.\n");
+                    hasErrors = localHasErrors = true;
+                } else if (!localHasErrors) {
+                    start = nextPublicId.valueFor(type);
+                }
+
+                uint32_t end = 0;
+                ssize_t endIdx = block.indexOfAttribute(NULL, "end");
+                if (endIdx >= 0) {
+                    const char16_t* endStr = block.getAttributeStringValue(endIdx, &len);
+                    Res_value endValue;
+                    if (!ResTable::stringToInt(endStr, len, &endValue)) {
+                        srcPos.error("Given 'end' attribute is not an integer: %s\n",
+                                String8(block.getAttributeStringValue(endIdx, &len)).string());
+                        hasErrors = localHasErrors = true;
+                    } else {
+                        end = endValue.data;
+                    }
+                } else {
+                    srcPos.error("No 'end' attribute supplied <public-padding>\n");
+                    hasErrors = localHasErrors = true;
+                }
+
+                if (end >= start) {
+                    nextPublicId.replaceValueFor(type, end+1);
+                } else {
+                    srcPos.error("Padding start '%ul' is after end '%ul'\n",
+                            start, end);
+                    hasErrors = localHasErrors = true;
+                }
+                
+                String16 comment(
+                    block.getComment(&len) ? block.getComment(&len) : nulStr);
+                for (uint32_t curIdent=start; curIdent<=end; curIdent++) {
+                    if (localHasErrors) {
+                        break;
+                    }
+                    String16 curName(name);
+                    char buf[64];
+                    sprintf(buf, "%d", (int)(end-curIdent+1));
+                    curName.append(String16(buf));
+                    
+                    err = outTable->addEntry(srcPos, myPackage, type, curName,
+                                             String16("padding"), NULL, &curParams, false,
+                                             ResTable_map::TYPE_STRING, overwrite);
+                    if (err < NO_ERROR) {
+                        hasErrors = localHasErrors = true;
+                        break;
+                    }
+                    err = outTable->addPublic(srcPos, myPackage, type,
+                            curName, curIdent);
+                    if (err < NO_ERROR) {
+                        hasErrors = localHasErrors = true;
+                        break;
+                    }
+                    sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
+                    if (symbols != NULL) {
+                        symbols = symbols->addNestedSymbol(String8(type), srcPos);
+                    }
+                    if (symbols != NULL) {
+                        symbols->makeSymbolPublic(String8(curName), srcPos);
+                        symbols->appendComment(String8(curName), comment, srcPos);
+                    } else {
+                        srcPos.error("Unable to create symbols!\n");
+                        hasErrors = localHasErrors = true;
+                    }
+                }
+
+                while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+                    if (code == ResXMLTree::END_TAG) {
+                        if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) {
+                            break;
+                        }
+                    }
+                }
+                continue;
+
             } else if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
                 String16 pkg;
                 ssize_t pkgIdx = block.indexOfAttribute(NULL, "package");
             } else if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
                 String16 pkg;
                 ssize_t pkgIdx = block.indexOfAttribute(NULL, "package");
@@ -834,6 +961,36 @@ status_t compileResourceFile(Bundle* bundle,
                 }
                 continue;
 
                 }
                 continue;
 
+            } else if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) {
+                SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
+            
+                String16 typeName;
+                ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
+                if (typeIdx < 0) {
+                    srcPos.error("A 'type' attribute is required for <add-resource>\n");
+                    hasErrors = localHasErrors = true;
+                }
+                typeName = String16(block.getAttributeStringValue(typeIdx, &len));
+
+                String16 name;
+                ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
+                if (nameIdx < 0) {
+                    srcPos.error("A 'name' attribute is required for <add-resource>\n");
+                    hasErrors = localHasErrors = true;
+                }
+                name = String16(block.getAttributeStringValue(nameIdx, &len));
+
+                outTable->canAddEntry(srcPos, myPackage, typeName, name);
+
+                while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+                    if (code == ResXMLTree::END_TAG) {
+                        if (strcmp16(block.getElementName(&len), private_symbols16.string()) == 0) {
+                            break;
+                        }
+                    }
+                }
+                continue;
+                
             } else if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
                 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
                                 
             } else if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
                 SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
                                 
@@ -979,7 +1136,7 @@ status_t compileResourceFile(Bundle* bundle,
                         if (locale.size() > 0) {
                             fprintf(stderr, "aapt: warning: string '%s' in %s marked untranslatable but exists"
                                     " in locale '%s'\n", String8(name).string(),
                         if (locale.size() > 0) {
                             fprintf(stderr, "aapt: warning: string '%s' in %s marked untranslatable but exists"
                                     " in locale '%s'\n", String8(name).string(),
-                                    bundle->getResourceSourceDir(),
+                                    bundle->getResourceSourceDirs()[0],
                                     locale.string());
                             // hasErrors = localHasErrors = true;
                         } else {
                                     locale.string());
                             // hasErrors = localHasErrors = true;
                         } else {
@@ -1019,6 +1176,10 @@ status_t compileResourceFile(Bundle* bundle,
                 curTag = &dimen16;
                 curType = dimen16;
                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_DIMENSION;
                 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;
             } else if (strcmp16(block.getElementName(&len), bag16.string()) == 0) {
                 curTag = &bag16;
                 curIsBag = true;
@@ -1042,6 +1203,7 @@ status_t compileResourceFile(Bundle* bundle,
                 curTag = &array16;
                 curType = array16;
                 curIsBag = true;
                 curTag = &array16;
                 curType = array16;
                 curIsBag = true;
+                curIsBagReplaceOnOverwrite = true;
                 ssize_t formatIdx = block.indexOfAttribute(NULL, "format");
                 if (formatIdx >= 0) {
                     String16 formatStr = String16(block.getAttributeStringValue(
                 ssize_t formatIdx = block.indexOfAttribute(NULL, "format");
                 if (formatIdx >= 0) {
                     String16 formatStr = String16(block.getAttributeStringValue(
@@ -1060,12 +1222,14 @@ status_t compileResourceFile(Bundle* bundle,
                 curType = array16;
                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
                 curIsBag = true;
                 curType = array16;
                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
                 curIsBag = true;
+                curIsBagReplaceOnOverwrite = true;
                 curIsPseudolocalizable = true;
             } else if (strcmp16(block.getElementName(&len), integer_array16.string()) == 0) {
                 curTag = &integer_array16;
                 curType = array16;
                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER;
                 curIsBag = true;
                 curIsPseudolocalizable = true;
             } else if (strcmp16(block.getElementName(&len), integer_array16.string()) == 0) {
                 curTag = &integer_array16;
                 curType = array16;
                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_INTEGER;
                 curIsBag = true;
+                curIsBagReplaceOnOverwrite = true;
             } else {
                 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
                         "Found tag %s where item is expected\n",
             } else {
                 SourcePos(in->getPrintableSource(), block.getLineNumber()).error(
                         "Found tag %s where item is expected\n",
@@ -1100,8 +1264,10 @@ status_t compileResourceFile(Bundle* bundle,
                 }
 
                 if (!localHasErrors) {
                 }
 
                 if (!localHasErrors) {
-                    err = outTable->startBag(SourcePos(in->getPrintableSource(), block.getLineNumber()),
-                                             myPackage, curType, ident, parentIdent, &curParams);
+                    err = outTable->startBag(SourcePos(in->getPrintableSource(),
+                            block.getLineNumber()), myPackage, curType, ident,
+                            parentIdent, &curParams,
+                            overwrite, curIsBagReplaceOnOverwrite);
                     if (err != NO_ERROR) {
                         hasErrors = localHasErrors = true;
                     }
                     if (err != NO_ERROR) {
                         hasErrors = localHasErrors = true;
                     }
@@ -1172,7 +1338,8 @@ status_t compileResourceFile(Bundle* bundle,
                         block.getPosition(&parserPosition);
 
                         err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType,
                         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()) {
                         if (err == NO_ERROR) {
                             if (curIsPseudolocalizable && localeIsDefined(curParams)
                                     && bundle->getPseudolocalize()) {
@@ -1181,7 +1348,7 @@ status_t compileResourceFile(Bundle* bundle,
                                 block.setPosition(parserPosition);
                                 err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage,
                                         curType, ident, parentIdent, itemIdent, curFormat, true,
                                 block.setPosition(parserPosition);
                                 err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage,
                                         curType, ident, parentIdent, itemIdent, curFormat, true,
-                                        outTable);
+                                        overwrite, outTable);
 #endif
                             }
                         } 
 #endif
                             }
                         } 
@@ -1204,7 +1371,7 @@ status_t compileResourceFile(Bundle* bundle,
                 block.getPosition(&parserPosition);
 
                 err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident,
                 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;
 
                 if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR?
                     hasErrors = localHasErrors = true;
@@ -1215,7 +1382,7 @@ status_t compileResourceFile(Bundle* bundle,
                         // pseudolocalize here
                         block.setPosition(parserPosition);
                         err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType,
                         // pseudolocalize here
                         block.setPosition(parserPosition);
                         err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType,
-                                ident, *curTag, curIsStyled, curFormat, true, outTable);
+                                ident, *curTag, curIsStyled, curFormat, true, overwrite, outTable);
                         if (err != NO_ERROR) {
                             hasErrors = localHasErrors = true;
                         }
                         if (err != NO_ERROR) {
                             hasErrors = localHasErrors = true;
                         }
@@ -1297,7 +1464,7 @@ status_t ResourceTable::addIncludedResources(Bundle* bundle, const sp<AaptAssets
             } else if (id != 0) {
                 if (id == 127) {
                     if (mHaveAppPackage) {
             } else if (id != 0) {
                 if (id == 127) {
                     if (mHaveAppPackage) {
-                        fprintf(stderr, "Included resource have two application packages!\n");
+                        fprintf(stderr, "Included resources have two application packages!\n");
                         return UNKNOWN_ERROR;
                     }
                     mHaveAppPackage = true;
                         return UNKNOWN_ERROR;
                     }
                     mHaveAppPackage = true;
@@ -1360,7 +1527,8 @@ status_t ResourceTable::addEntry(const SourcePos& sourcePos,
                                  const Vector<StringPool::entry_style_span>* style,
                                  const ResTable_config* params,
                                  const bool doSetIndex,
                                  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.
 {
     // Check for adding entries in other packages...  for now we do
     // nothing.  We need to do the right thing here to support skinning.
@@ -1379,12 +1547,13 @@ status_t ResourceTable::addEntry(const SourcePos& sourcePos,
                String8(value).string());
     }
 #endif
                String8(value).string());
     }
 #endif
-    
-    sp<Entry> e = getEntry(package, type, name, sourcePos, params, doSetIndex);
+
+    sp<Entry> e = getEntry(package, type, name, sourcePos, overwrite,
+                           params, doSetIndex);
     if (e == NULL) {
         return UNKNOWN_ERROR;
     }
     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++;
     }
     if (err == NO_ERROR) {
         mNumLocal++;
     }
@@ -1397,8 +1566,11 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos,
                                  const String16& name,
                                  const String16& bagParent,
                                  const ResTable_config* params,
                                  const String16& name,
                                  const String16& bagParent,
                                  const ResTable_config* params,
+                                 bool overlay,
                                  bool replace, bool isId)
 {
                                  bool replace, bool isId)
 {
+    status_t result = NO_ERROR;
+
     // Check for adding entries in other packages...  for now we do
     // nothing.  We need to do the right thing here to support skinning.
     uint32_t rid = mAssets->getIncludedResources()
     // Check for adding entries in other packages...  for now we do
     // nothing.  We need to do the right thing here to support skinning.
     uint32_t rid = mAssets->getIncludedResources()
@@ -1415,8 +1587,24 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos,
                sourcePos.file.striing(), sourcePos.line, String8(type).string());
     }
 #endif
                sourcePos.file.striing(), sourcePos.line, String8(type).string());
     }
 #endif
-    
-    sp<Entry> e = getEntry(package, type, name, sourcePos, params);
+    if (overlay && !hasBagOrEntry(package, type, name)) {
+        bool canAdd = false;
+        sp<Package> p = mPackages.valueFor(package);
+        if (p != NULL) {
+            sp<Type> t = p->getTypes().valueFor(type);
+            if (t != NULL) {
+                if (t->getCanAddEntries().indexOf(name) >= 0) {
+                    canAdd = true;
+                }
+            }
+        }
+        if (!canAdd) {
+            sourcePos.error("Resource does not already exist in overlay at '%s'; use <add-resource> to add.\n",
+                            String8(name).string());
+            return UNKNOWN_ERROR;
+        }
+    }
+    sp<Entry> e = getEntry(package, type, name, sourcePos, overlay, params);
     if (e == NULL) {
         return UNKNOWN_ERROR;
     }
     if (e == NULL) {
         return UNKNOWN_ERROR;
     }
@@ -1432,8 +1620,15 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos,
         }
         e->setParent(bagParent);
     }
         }
         e->setParent(bagParent);
     }
-    
-    return e->makeItABag(sourcePos);
+
+    if ((result = e->makeItABag(sourcePos)) != NO_ERROR) {
+        return result;
+    }
+
+    if (overlay && replace) { 
+        return e->emptyBag(sourcePos);
+    }
+    return result;
 }
 
 status_t ResourceTable::addBag(const SourcePos& sourcePos,
 }
 
 status_t ResourceTable::addBag(const SourcePos& sourcePos,
@@ -1463,8 +1658,7 @@ status_t ResourceTable::addBag(const SourcePos& sourcePos,
                sourcePos.file.striing(), sourcePos.line, String8(type).string());
     }
 #endif
                sourcePos.file.striing(), sourcePos.line, String8(type).string());
     }
 #endif
-    
-    sp<Entry> e = getEntry(package, type, name, sourcePos, params);
+    sp<Entry> e = getEntry(package, type, name, sourcePos, replace, params);
     if (e == NULL) {
         return UNKNOWN_ERROR;
     }
     if (e == NULL) {
         return UNKNOWN_ERROR;
     }
@@ -1573,6 +1767,15 @@ bool ResourceTable::appendTypeComment(const String16& package,
     return false;
 }
 
     return false;
 }
 
+void ResourceTable::canAddEntry(const SourcePos& pos,
+        const String16& package, const String16& type, const String16& name)
+{
+    sp<Type> t = getType(package, type, pos);
+    if (t != NULL) {
+        t->canAddEntry(name);
+    }
+}
+
 size_t ResourceTable::size() const {
     return mPackages.size();
 }
 size_t ResourceTable::size() const {
     return mPackages.size();
 }
@@ -2125,7 +2328,7 @@ ResourceTable::validateLocalizations(void)
         // 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:",
         // 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->getResourceSourceDir());
+                    String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]);
             for (set<String8>::iterator locales = configSet.begin();
                  locales != configSet.end();
                  locales++) {
             for (set<String8>::iterator locales = configSet.begin();
                  locales != configSet.end();
                  locales++) {
@@ -2160,15 +2363,14 @@ ResourceTable::validateLocalizations(void)
                         // consider that string to have fulfilled the localization requirement.
                         String8 region(config.string(), 2);
                         if (configSet.find(region) == configSet.end()) {
                         // consider that string to have fulfilled the localization requirement.
                         String8 region(config.string(), 2);
                         if (configSet.find(region) == configSet.end()) {
-                            // TODO: force an error if there is no default to fall back to
                             if (configSet.count(defaultLocale) == 0) {
                             if (configSet.count(defaultLocale) == 0) {
-                                fprintf(stdout, "aapt: warning: "
+                                fprintf(stdout, "aapt: error: "
                                         "*** string '%s' has no default or required localization "
                                         "for '%s' in %s\n",
                                         String8(nameIter->first).string(),
                                         config.string(),
                                         "*** string '%s' has no default or required localization "
                                         "for '%s' in %s\n",
                                         String8(nameIter->first).string(),
                                         config.string(),
-                                        mBundle->getResourceSourceDir());
-                                //err = UNKNOWN_ERROR;
+                                        mBundle->getResourceSourceDirs()[0]);
+                                err = UNKNOWN_ERROR;
                             }
                         }
                     }
                             }
                         }
                     }
@@ -2728,7 +2930,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,
 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);
 
 {
     Item item(sourcePos, false, value, style);
 
@@ -2740,14 +2943,14 @@ status_t ResourceTable::Entry::setItem(const SourcePos& sourcePos,
                         item.sourcePos.file.string(), item.sourcePos.line);
         return UNKNOWN_ERROR;
     }
                         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(),
                         mItem.sourcePos.file.string(), mItem.sourcePos.line);
         return UNKNOWN_ERROR;
     }
         sourcePos.error("Resource entry %s is already defined.\n"
                         "%s:%d: Originally defined here.\n",
                         String8(mName).string(),
                         mItem.sourcePos.file.string(), mItem.sourcePos.line);
         return UNKNOWN_ERROR;
     }
-    
+
     mType = TYPE_ITEM;
     mItem = item;
     mItemFormat = format;
     mType = TYPE_ITEM;
     mItem = item;
     mItemFormat = format;
@@ -2788,6 +2991,17 @@ status_t ResourceTable::Entry::addToBag(const SourcePos& sourcePos,
     return NO_ERROR;
 }
 
     return NO_ERROR;
 }
 
+status_t ResourceTable::Entry::emptyBag(const SourcePos& sourcePos)
+{
+    status_t err = makeItABag(sourcePos);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    mBag.clear();
+    return NO_ERROR;
+}
+
 status_t ResourceTable::Entry::generateAttributes(ResourceTable* table,
                                                   const String16& package)
 {
 status_t ResourceTable::Entry::generateAttributes(ResourceTable* table,
                                                   const String16& package)
 {
@@ -3053,14 +3267,26 @@ status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos,
     return NO_ERROR;
 }
 
     return NO_ERROR;
 }
 
+void ResourceTable::Type::canAddEntry(const String16& name)
+{
+    mCanAddEntries.add(name);
+}
+
 sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
                                                        const SourcePos& sourcePos,
                                                        const ResTable_config* config,
 sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
                                                        const SourcePos& sourcePos,
                                                        const ResTable_config* config,
-                                                       bool doSetIndex)
+                                                       bool doSetIndex,
+                                                       bool overlay)
 {
     int pos = -1;
     sp<ConfigList> c = mConfigs.valueFor(entry);
     if (c == NULL) {
 {
     int pos = -1;
     sp<ConfigList> c = mConfigs.valueFor(entry);
     if (c == NULL) {
+        if (overlay == true && mCanAddEntries.indexOf(entry) < 0) {
+            sourcePos.error("Resource at %s appears in overlay but not"
+                            " in the base package; use <add-resource> to add.\n",
+                            String8(entry).string());
+            return NULL;
+        }
         c = new ConfigList(entry, sourcePos);
         mConfigs.add(entry, c);
         pos = (int)mOrderedConfigs.size();
         c = new ConfigList(entry, sourcePos);
         mConfigs.add(entry, c);
         pos = (int)mOrderedConfigs.size();
@@ -3359,6 +3585,7 @@ sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package,
                                                  const String16& type,
                                                  const String16& name,
                                                  const SourcePos& sourcePos,
                                                  const String16& type,
                                                  const String16& name,
                                                  const SourcePos& sourcePos,
+                                                 bool overlay,
                                                  const ResTable_config* config,
                                                  bool doSetIndex)
 {
                                                  const ResTable_config* config,
                                                  bool doSetIndex)
 {
@@ -3366,7 +3593,7 @@ sp<ResourceTable::Entry> ResourceTable::getEntry(const String16& package,
     if (t == NULL) {
         return NULL;
     }
     if (t == NULL) {
         return NULL;
     }
-    return t->getEntry(name, sourcePos, config, doSetIndex);
+    return t->getEntry(name, sourcePos, config, doSetIndex, overlay);
 }
 
 sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
 }
 
 sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
@@ -3385,26 +3612,26 @@ sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
 
     }
     if (p == NULL) {
 
     }
     if (p == NULL) {
-        fprintf(stderr, "WARNING: Package not found for resource #%08x\n", resID);
+        fprintf(stderr, "warning: Package not found for resource #%08x\n", resID);
         return NULL;
     }
 
     int tid = Res_GETTYPE(resID);
     if (tid < 0 || tid >= (int)p->getOrderedTypes().size()) {
         return NULL;
     }
 
     int tid = Res_GETTYPE(resID);
     if (tid < 0 || tid >= (int)p->getOrderedTypes().size()) {
-        fprintf(stderr, "WARNING: Type not found for resource #%08x\n", resID);
+        fprintf(stderr, "warning: Type not found for resource #%08x\n", resID);
         return NULL;
     }
     sp<Type> t = p->getOrderedTypes()[tid];
 
     int eid = Res_GETENTRY(resID);
     if (eid < 0 || eid >= (int)t->getOrderedConfigs().size()) {
         return NULL;
     }
     sp<Type> t = p->getOrderedTypes()[tid];
 
     int eid = Res_GETENTRY(resID);
     if (eid < 0 || eid >= (int)t->getOrderedConfigs().size()) {
-        fprintf(stderr, "WARNING: Entry not found for resource #%08x\n", resID);
+        fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
         return NULL;
     }
 
     sp<ConfigList> c = t->getOrderedConfigs()[eid];
     if (c == NULL) {
         return NULL;
     }
 
     sp<ConfigList> c = t->getOrderedConfigs()[eid];
     if (c == NULL) {
-        fprintf(stderr, "WARNING: Entry not found for resource #%08x\n", resID);
+        fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
         return NULL;
     }
     
         return NULL;
     }
     
@@ -3412,7 +3639,7 @@ sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
     if (config) cdesc = *config;
     sp<Entry> e = c->getEntries().valueFor(cdesc);
     if (c == NULL) {
     if (config) cdesc = *config;
     sp<Entry> e = c->getEntries().valueFor(cdesc);
     if (c == NULL) {
-        fprintf(stderr, "WARNING: Entry configuration not found for resource #%08x\n", resID);
+        fprintf(stderr, "warning: Entry configuration not found for resource #%08x\n", resID);
         return NULL;
     }
     
         return NULL;
     }
     
@@ -3430,7 +3657,7 @@ const ResourceTable::Item* ResourceTable::getItem(uint32_t resID, uint32_t attrI
     for (size_t i=0; i<N; i++) {
         const Item& it = e->getBag().valueAt(i);
         if (it.bagKeyId == 0) {
     for (size_t i=0; i<N; i++) {
         const Item& it = e->getBag().valueAt(i);
         if (it.bagKeyId == 0) {
-            fprintf(stderr, "WARNING: ID not yet assigned to '%s' in bag '%s'\n",
+            fprintf(stderr, "warning: ID not yet assigned to '%s' in bag '%s'\n",
                     String8(e->getName()).string(),
                     String8(e->getBag().keyAt(i)).string());
         }
                     String8(e->getName()).string(),
                     String8(e->getBag().keyAt(i)).string());
         }
@@ -3458,7 +3685,7 @@ bool ResourceTable::getItemValue(
                     break;
                 }
             }
                     break;
                 }
             }
-            fprintf(stderr, "WARNING: Circular reference detected in key '%s' of bag '%s'\n",
+            fprintf(stderr, "warning: Circular reference detected in key '%s' of bag '%s'\n",
                     String8(e->getName()).string(),
                     String8(e->getBag().keyAt(i)).string());
             return false;
                     String8(e->getName()).string(),
                     String8(e->getBag().keyAt(i)).string());
             return false;