Home | History | Annotate | Download | only in aapt
      1 //
      2 // Copyright 2006 The Android Open Source Project
      3 //
      4 // Build resource files from raw assets.
      5 //
      6 
      7 #include "XMLNode.h"
      8 #include "ResourceTable.h"
      9 
     10 #include <host/pseudolocalize.h>
     11 #include <utils/ByteOrder.h>
     12 #include <errno.h>
     13 #include <string.h>
     14 
     15 #ifndef HAVE_MS_C_RUNTIME
     16 #define O_BINARY 0
     17 #endif
     18 
     19 #define NOISY(x) //x
     20 #define NOISY_PARSE(x) //x
     21 
     22 const char* const RESOURCES_ROOT_NAMESPACE = "http://schemas.android.com/apk/res/";
     23 const char* const RESOURCES_ANDROID_NAMESPACE = "http://schemas.android.com/apk/res/android";
     24 const char* const RESOURCES_ROOT_PRV_NAMESPACE = "http://schemas.android.com/apk/prv/res/";
     25 
     26 const char* const XLIFF_XMLNS = "urn:oasis:names:tc:xliff:document:1.2";
     27 const char* const ALLOWED_XLIFF_ELEMENTS[] = {
     28         "bpt",
     29         "ept",
     30         "it",
     31         "ph",
     32         "g",
     33         "bx",
     34         "ex",
     35         "x"
     36     };
     37 
     38 bool isWhitespace(const char16_t* str)
     39 {
     40     while (*str != 0 && *str < 128 && isspace(*str)) {
     41         str++;
     42     }
     43     return *str == 0;
     44 }
     45 
     46 static const String16 RESOURCES_PREFIX(RESOURCES_ROOT_NAMESPACE);
     47 static const String16 RESOURCES_PRV_PREFIX(RESOURCES_ROOT_PRV_NAMESPACE);
     48 
     49 String16 getNamespaceResourcePackage(String16 namespaceUri, bool* outIsPublic)
     50 {
     51     //printf("%s starts with %s?\n", String8(namespaceUri).string(),
     52     //       String8(RESOURCES_PREFIX).string());
     53     size_t prefixSize;
     54     bool isPublic = true;
     55     if (namespaceUri.startsWith(RESOURCES_PREFIX)) {
     56         prefixSize = RESOURCES_PREFIX.size();
     57     } else if (namespaceUri.startsWith(RESOURCES_PRV_PREFIX)) {
     58         isPublic = false;
     59         prefixSize = RESOURCES_PRV_PREFIX.size();
     60     } else {
     61         if (outIsPublic) *outIsPublic = isPublic; // = true
     62         return String16();
     63     }
     64 
     65     //printf("YES!\n");
     66     //printf("namespace: %s\n", String8(String16(namespaceUri, namespaceUri.size()-prefixSize, prefixSize)).string());
     67     if (outIsPublic) *outIsPublic = isPublic;
     68     return String16(namespaceUri, namespaceUri.size()-prefixSize, prefixSize);
     69 }
     70 
     71 status_t parseStyledString(Bundle* bundle,
     72                            const char* fileName,
     73                            ResXMLTree* inXml,
     74                            const String16& endTag,
     75                            String16* outString,
     76                            Vector<StringPool::entry_style_span>* outSpans,
     77                            bool pseudolocalize)
     78 {
     79     Vector<StringPool::entry_style_span> spanStack;
     80     String16 curString;
     81     String16 rawString;
     82     const char* errorMsg;
     83     int xliffDepth = 0;
     84     bool firstTime = true;
     85 
     86     size_t len;
     87     ResXMLTree::event_code_t code;
     88     while ((code=inXml->next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
     89 
     90         if (code == ResXMLTree::TEXT) {
     91             String16 text(inXml->getText(&len));
     92             if (firstTime && text.size() > 0) {
     93                 firstTime = false;
     94                 if (text.string()[0] == '@') {
     95                     // If this is a resource reference, don't do the pseudoloc.
     96                     pseudolocalize = false;
     97                 }
     98             }
     99             if (xliffDepth == 0 && pseudolocalize) {
    100                 std::string orig(String8(text).string());
    101                 std::string pseudo = pseudolocalize_string(orig);
    102                 curString.append(String16(String8(pseudo.c_str())));
    103             } else {
    104                 curString.append(text);
    105             }
    106         } else if (code == ResXMLTree::START_TAG) {
    107             const String16 element16(inXml->getElementName(&len));
    108             const String8 element8(element16);
    109 
    110             size_t nslen;
    111             const uint16_t* ns = inXml->getElementNamespace(&nslen);
    112             if (ns == NULL) {
    113                 ns = (const uint16_t*)"\0\0";
    114                 nslen = 0;
    115             }
    116             const String8 nspace(String16(ns, nslen));
    117             if (nspace == XLIFF_XMLNS) {
    118                 const int N = sizeof(ALLOWED_XLIFF_ELEMENTS)/sizeof(ALLOWED_XLIFF_ELEMENTS[0]);
    119                 for (int i=0; i<N; i++) {
    120                     if (element8 == ALLOWED_XLIFF_ELEMENTS[i]) {
    121                         xliffDepth++;
    122                         // in this case, treat it like it was just text, in other words, do nothing
    123                         // here and silently drop this element
    124                         goto moveon;
    125                     }
    126                 }
    127                 {
    128                     SourcePos(String8(fileName), inXml->getLineNumber()).error(
    129                             "Found unsupported XLIFF tag <%s>\n",
    130                             element8.string());
    131                     return UNKNOWN_ERROR;
    132                 }
    133 moveon:
    134                 continue;
    135             }
    136 
    137             if (outSpans == NULL) {
    138                 SourcePos(String8(fileName), inXml->getLineNumber()).error(
    139                         "Found style tag <%s> where styles are not allowed\n", element8.string());
    140                 return UNKNOWN_ERROR;
    141             }
    142 
    143             if (!ResTable::collectString(outString, curString.string(),
    144                                          curString.size(), false, &errorMsg, true)) {
    145                 SourcePos(String8(fileName), inXml->getLineNumber()).error("%s (in %s)\n",
    146                         errorMsg, String8(curString).string());
    147                 return UNKNOWN_ERROR;
    148             }
    149             rawString.append(curString);
    150             curString = String16();
    151 
    152             StringPool::entry_style_span span;
    153             span.name = element16;
    154             for (size_t ai=0; ai<inXml->getAttributeCount(); ai++) {
    155                 span.name.append(String16(";"));
    156                 const char16_t* str = inXml->getAttributeName(ai, &len);
    157                 span.name.append(str, len);
    158                 span.name.append(String16("="));
    159                 str = inXml->getAttributeStringValue(ai, &len);
    160                 span.name.append(str, len);
    161             }
    162             //printf("Span: %s\n", String8(span.name).string());
    163             span.span.firstChar = span.span.lastChar = outString->size();
    164             spanStack.push(span);
    165 
    166         } else if (code == ResXMLTree::END_TAG) {
    167             size_t nslen;
    168             const uint16_t* ns = inXml->getElementNamespace(&nslen);
    169             if (ns == NULL) {
    170                 ns = (const uint16_t*)"\0\0";
    171                 nslen = 0;
    172             }
    173             const String8 nspace(String16(ns, nslen));
    174             if (nspace == XLIFF_XMLNS) {
    175                 xliffDepth--;
    176                 continue;
    177             }
    178             if (!ResTable::collectString(outString, curString.string(),
    179                                          curString.size(), false, &errorMsg, true)) {
    180                 SourcePos(String8(fileName), inXml->getLineNumber()).error("%s (in %s)\n",
    181                         errorMsg, String8(curString).string());
    182                 return UNKNOWN_ERROR;
    183             }
    184             rawString.append(curString);
    185             curString = String16();
    186 
    187             if (spanStack.size() == 0) {
    188                 if (strcmp16(inXml->getElementName(&len), endTag.string()) != 0) {
    189                     SourcePos(String8(fileName), inXml->getLineNumber()).error(
    190                             "Found tag %s where <%s> close is expected\n",
    191                             String8(inXml->getElementName(&len)).string(),
    192                             String8(endTag).string());
    193                     return UNKNOWN_ERROR;
    194                 }
    195                 break;
    196             }
    197             StringPool::entry_style_span span = spanStack.top();
    198             String16 spanTag;
    199             ssize_t semi = span.name.findFirst(';');
    200             if (semi >= 0) {
    201                 spanTag.setTo(span.name.string(), semi);
    202             } else {
    203                 spanTag.setTo(span.name);
    204             }
    205             if (strcmp16(inXml->getElementName(&len), spanTag.string()) != 0) {
    206                 SourcePos(String8(fileName), inXml->getLineNumber()).error(
    207                         "Found close tag %s where close tag %s is expected\n",
    208                         String8(inXml->getElementName(&len)).string(),
    209                         String8(spanTag).string());
    210                 return UNKNOWN_ERROR;
    211             }
    212             bool empty = true;
    213             if (outString->size() > 0) {
    214                 span.span.lastChar = outString->size()-1;
    215                 if (span.span.lastChar >= span.span.firstChar) {
    216                     empty = false;
    217                     outSpans->add(span);
    218                 }
    219             }
    220             spanStack.pop();
    221 
    222             /*
    223              * This warning seems to be just an irritation to most people,
    224              * since it is typically introduced by translators who then never
    225              * see the warning.
    226              */
    227             if (0 && empty) {
    228                 fprintf(stderr, "%s:%d: warning: empty '%s' span found in text '%s'\n",
    229                         fileName, inXml->getLineNumber(),
    230                         String8(spanTag).string(), String8(*outString).string());
    231 
    232             }
    233         } else if (code == ResXMLTree::START_NAMESPACE) {
    234             // nothing
    235         }
    236     }
    237 
    238     if (code == ResXMLTree::BAD_DOCUMENT) {
    239             SourcePos(String8(fileName), inXml->getLineNumber()).error(
    240                     "Error parsing XML\n");
    241     }
    242 
    243     if (outSpans != NULL && outSpans->size() > 0) {
    244         if (curString.size() > 0) {
    245             if (!ResTable::collectString(outString, curString.string(),
    246                                          curString.size(), false, &errorMsg, true)) {
    247                 SourcePos(String8(fileName), inXml->getLineNumber()).error(
    248                         "%s (in %s)\n",
    249                         errorMsg, String8(curString).string());
    250                 return UNKNOWN_ERROR;
    251             }
    252         }
    253     } else {
    254         // There is no style information, so string processing will happen
    255         // later as part of the overall type conversion.  Return to the
    256         // client the raw unprocessed text.
    257         rawString.append(curString);
    258         outString->setTo(rawString);
    259     }
    260 
    261     return NO_ERROR;
    262 }
    263 
    264 struct namespace_entry {
    265     String8 prefix;
    266     String8 uri;
    267 };
    268 
    269 static String8 make_prefix(int depth)
    270 {
    271     String8 prefix;
    272     int i;
    273     for (i=0; i<depth; i++) {
    274         prefix.append("  ");
    275     }
    276     return prefix;
    277 }
    278 
    279 static String8 build_namespace(const Vector<namespace_entry>& namespaces,
    280         const uint16_t* ns)
    281 {
    282     String8 str;
    283     if (ns != NULL) {
    284         str = String8(ns);
    285         const size_t N = namespaces.size();
    286         for (size_t i=0; i<N; i++) {
    287             const namespace_entry& ne = namespaces.itemAt(i);
    288             if (ne.uri == str) {
    289                 str = ne.prefix;
    290                 break;
    291             }
    292         }
    293         str.append(":");
    294     }
    295     return str;
    296 }
    297 
    298 void printXMLBlock(ResXMLTree* block)
    299 {
    300     block->restart();
    301 
    302     Vector<namespace_entry> namespaces;
    303 
    304     ResXMLTree::event_code_t code;
    305     int depth = 0;
    306     while ((code=block->next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
    307         String8 prefix = make_prefix(depth);
    308         int i;
    309         if (code == ResXMLTree::START_TAG) {
    310             size_t len;
    311             const uint16_t* ns16 = block->getElementNamespace(&len);
    312             String8 elemNs = build_namespace(namespaces, ns16);
    313             const uint16_t* com16 = block->getComment(&len);
    314             if (com16) {
    315                 printf("%s <!-- %s -->\n", prefix.string(), String8(com16).string());
    316             }
    317             printf("%sE: %s%s (line=%d)\n", prefix.string(), elemNs.string(),
    318                    String8(block->getElementName(&len)).string(),
    319                    block->getLineNumber());
    320             int N = block->getAttributeCount();
    321             depth++;
    322             prefix = make_prefix(depth);
    323             for (i=0; i<N; i++) {
    324                 uint32_t res = block->getAttributeNameResID(i);
    325                 ns16 = block->getAttributeNamespace(i, &len);
    326                 String8 ns = build_namespace(namespaces, ns16);
    327                 String8 name(block->getAttributeName(i, &len));
    328                 printf("%sA: ", prefix.string());
    329                 if (res) {
    330                     printf("%s%s(0x%08x)", ns.string(), name.string(), res);
    331                 } else {
    332                     printf("%s%s", ns.string(), name.string());
    333                 }
    334                 Res_value value;
    335                 block->getAttributeValue(i, &value);
    336                 if (value.dataType == Res_value::TYPE_NULL) {
    337                     printf("=(null)");
    338                 } else if (value.dataType == Res_value::TYPE_REFERENCE) {
    339                     printf("=@0x%x", (int)value.data);
    340                 } else if (value.dataType == Res_value::TYPE_ATTRIBUTE) {
    341                     printf("=?0x%x", (int)value.data);
    342                 } else if (value.dataType == Res_value::TYPE_STRING) {
    343                     printf("=\"%s\"",
    344                            String8(block->getAttributeStringValue(i, &len)).string());
    345                 } else {
    346                     printf("=(type 0x%x)0x%x", (int)value.dataType, (int)value.data);
    347                 }
    348                 const char16_t* val = block->getAttributeStringValue(i, &len);
    349                 if (val != NULL) {
    350                     printf(" (Raw: \"%s\")", String8(val).string());
    351                 }
    352                 printf("\n");
    353             }
    354         } else if (code == ResXMLTree::END_TAG) {
    355             depth--;
    356         } else if (code == ResXMLTree::START_NAMESPACE) {
    357             namespace_entry ns;
    358             size_t len;
    359             const uint16_t* prefix16 = block->getNamespacePrefix(&len);
    360             if (prefix16) {
    361                 ns.prefix = String8(prefix16);
    362             } else {
    363                 ns.prefix = "<DEF>";
    364             }
    365             ns.uri = String8(block->getNamespaceUri(&len));
    366             namespaces.push(ns);
    367             printf("%sN: %s=%s\n", prefix.string(), ns.prefix.string(),
    368                     ns.uri.string());
    369             depth++;
    370         } else if (code == ResXMLTree::END_NAMESPACE) {
    371             depth--;
    372             const namespace_entry& ns = namespaces.top();
    373             size_t len;
    374             const uint16_t* prefix16 = block->getNamespacePrefix(&len);
    375             String8 pr;
    376             if (prefix16) {
    377                 pr = String8(prefix16);
    378             } else {
    379                 pr = "<DEF>";
    380             }
    381             if (ns.prefix != pr) {
    382                 prefix = make_prefix(depth);
    383                 printf("%s*** BAD END NS PREFIX: found=%s, expected=%s\n",
    384                         prefix.string(), pr.string(), ns.prefix.string());
    385             }
    386             String8 uri = String8(block->getNamespaceUri(&len));
    387             if (ns.uri != uri) {
    388                 prefix = make_prefix(depth);
    389                 printf("%s *** BAD END NS URI: found=%s, expected=%s\n",
    390                         prefix.string(), uri.string(), ns.uri.string());
    391             }
    392             namespaces.pop();
    393         } else if (code == ResXMLTree::TEXT) {
    394             size_t len;
    395             printf("%sC: \"%s\"\n", prefix.string(), String8(block->getText(&len)).string());
    396         }
    397     }
    398 
    399     block->restart();
    400 }
    401 
    402 status_t parseXMLResource(const sp<AaptFile>& file, ResXMLTree* outTree,
    403                           bool stripAll, bool keepComments,
    404                           const char** cDataTags)
    405 {
    406     sp<XMLNode> root = XMLNode::parse(file);
    407     if (root == NULL) {
    408         return UNKNOWN_ERROR;
    409     }
    410     root->removeWhitespace(stripAll, cDataTags);
    411 
    412     NOISY(printf("Input XML from %s:\n", (const char*)file->getPrintableSource()));
    413     NOISY(root->print());
    414     sp<AaptFile> rsc = new AaptFile(String8(), AaptGroupEntry(), String8());
    415     status_t err = root->flatten(rsc, !keepComments, false);
    416     if (err != NO_ERROR) {
    417         return err;
    418     }
    419     err = outTree->setTo(rsc->getData(), rsc->getSize(), true);
    420     if (err != NO_ERROR) {
    421         return err;
    422     }
    423 
    424     NOISY(printf("Output XML:\n"));
    425     NOISY(printXMLBlock(outTree));
    426 
    427     return NO_ERROR;
    428 }
    429 
    430 sp<XMLNode> XMLNode::parse(const sp<AaptFile>& file)
    431 {
    432     char buf[16384];
    433     int fd = open(file->getSourceFile().string(), O_RDONLY | O_BINARY);
    434     if (fd < 0) {
    435         SourcePos(file->getSourceFile(), -1).error("Unable to open file for read: %s",
    436                 strerror(errno));
    437         return NULL;
    438     }
    439 
    440     XML_Parser parser = XML_ParserCreateNS(NULL, 1);
    441     ParseState state;
    442     state.filename = file->getPrintableSource();
    443     state.parser = parser;
    444     XML_SetUserData(parser, &state);
    445     XML_SetElementHandler(parser, startElement, endElement);
    446     XML_SetNamespaceDeclHandler(parser, startNamespace, endNamespace);
    447     XML_SetCharacterDataHandler(parser, characterData);
    448     XML_SetCommentHandler(parser, commentData);
    449 
    450     ssize_t len;
    451     bool done;
    452     do {
    453         len = read(fd, buf, sizeof(buf));
    454         done = len < (ssize_t)sizeof(buf);
    455         if (len < 0) {
    456             SourcePos(file->getSourceFile(), -1).error("Error reading file: %s\n", strerror(errno));
    457             close(fd);
    458             return NULL;
    459         }
    460         if (XML_Parse(parser, buf, len, done) == XML_STATUS_ERROR) {
    461             SourcePos(file->getSourceFile(), (int)XML_GetCurrentLineNumber(parser)).error(
    462                     "Error parsing XML: %s\n", XML_ErrorString(XML_GetErrorCode(parser)));
    463             close(fd);
    464             return NULL;
    465         }
    466     } while (!done);
    467 
    468     XML_ParserFree(parser);
    469     if (state.root == NULL) {
    470         SourcePos(file->getSourceFile(), -1).error("No XML data generated when parsing");
    471     }
    472     close(fd);
    473     return state.root;
    474 }
    475 
    476 XMLNode::XMLNode(const String8& filename, const String16& s1, const String16& s2, bool isNamespace)
    477     : mNextAttributeIndex(0x80000000)
    478     , mFilename(filename)
    479     , mStartLineNumber(0)
    480     , mEndLineNumber(0)
    481     , mUTF8(false)
    482 {
    483     if (isNamespace) {
    484         mNamespacePrefix = s1;
    485         mNamespaceUri = s2;
    486     } else {
    487         mNamespaceUri = s1;
    488         mElementName = s2;
    489     }
    490 }
    491 
    492 XMLNode::XMLNode(const String8& filename)
    493     : mFilename(filename)
    494 {
    495     memset(&mCharsValue, 0, sizeof(mCharsValue));
    496 }
    497 
    498 XMLNode::type XMLNode::getType() const
    499 {
    500     if (mElementName.size() != 0) {
    501         return TYPE_ELEMENT;
    502     }
    503     if (mNamespaceUri.size() != 0) {
    504         return TYPE_NAMESPACE;
    505     }
    506     return TYPE_CDATA;
    507 }
    508 
    509 const String16& XMLNode::getNamespacePrefix() const
    510 {
    511     return mNamespacePrefix;
    512 }
    513 
    514 const String16& XMLNode::getNamespaceUri() const
    515 {
    516     return mNamespaceUri;
    517 }
    518 
    519 const String16& XMLNode::getElementNamespace() const
    520 {
    521     return mNamespaceUri;
    522 }
    523 
    524 const String16& XMLNode::getElementName() const
    525 {
    526     return mElementName;
    527 }
    528 
    529 const Vector<sp<XMLNode> >& XMLNode::getChildren() const
    530 {
    531     return mChildren;
    532 }
    533 
    534 const String8& XMLNode::getFilename() const
    535 {
    536     return mFilename;
    537 }
    538 
    539 const Vector<XMLNode::attribute_entry>&
    540     XMLNode::getAttributes() const
    541 {
    542     return mAttributes;
    543 }
    544 
    545 const XMLNode::attribute_entry* XMLNode::getAttribute(const String16& ns,
    546         const String16& name) const
    547 {
    548     for (size_t i=0; i<mAttributes.size(); i++) {
    549         const attribute_entry& ae(mAttributes.itemAt(i));
    550         if (ae.ns == ns && ae.name == name) {
    551             return &ae;
    552         }
    553     }
    554 
    555     return NULL;
    556 }
    557 
    558 XMLNode::attribute_entry* XMLNode::editAttribute(const String16& ns,
    559         const String16& name)
    560 {
    561     for (size_t i=0; i<mAttributes.size(); i++) {
    562         attribute_entry * ae = &mAttributes.editItemAt(i);
    563         if (ae->ns == ns && ae->name == name) {
    564             return ae;
    565         }
    566     }
    567 
    568     return NULL;
    569 }
    570 
    571 const String16& XMLNode::getCData() const
    572 {
    573     return mChars;
    574 }
    575 
    576 const String16& XMLNode::getComment() const
    577 {
    578     return mComment;
    579 }
    580 
    581 int32_t XMLNode::getStartLineNumber() const
    582 {
    583     return mStartLineNumber;
    584 }
    585 
    586 int32_t XMLNode::getEndLineNumber() const
    587 {
    588     return mEndLineNumber;
    589 }
    590 
    591 sp<XMLNode> XMLNode::searchElement(const String16& tagNamespace, const String16& tagName)
    592 {
    593     if (getType() == XMLNode::TYPE_ELEMENT
    594             && mNamespaceUri == tagNamespace
    595             && mElementName == tagName) {
    596         return this;
    597     }
    598 
    599     for (size_t i=0; i<mChildren.size(); i++) {
    600         sp<XMLNode> found = mChildren.itemAt(i)->searchElement(tagNamespace, tagName);
    601         if (found != NULL) {
    602             return found;
    603         }
    604     }
    605 
    606     return NULL;
    607 }
    608 
    609 sp<XMLNode> XMLNode::getChildElement(const String16& tagNamespace, const String16& tagName)
    610 {
    611     for (size_t i=0; i<mChildren.size(); i++) {
    612         sp<XMLNode> child = mChildren.itemAt(i);
    613         if (child->getType() == XMLNode::TYPE_ELEMENT
    614                 && child->mNamespaceUri == tagNamespace
    615                 && child->mElementName == tagName) {
    616             return child;
    617         }
    618     }
    619 
    620     return NULL;
    621 }
    622 
    623 status_t XMLNode::addChild(const sp<XMLNode>& child)
    624 {
    625     if (getType() == TYPE_CDATA) {
    626         SourcePos(mFilename, child->getStartLineNumber()).error("Child to CDATA node.");
    627         return UNKNOWN_ERROR;
    628     }
    629     //printf("Adding child %p to parent %p\n", child.get(), this);
    630     mChildren.add(child);
    631     return NO_ERROR;
    632 }
    633 
    634 status_t XMLNode::insertChildAt(const sp<XMLNode>& child, size_t index)
    635 {
    636     if (getType() == TYPE_CDATA) {
    637         SourcePos(mFilename, child->getStartLineNumber()).error("Child to CDATA node.");
    638         return UNKNOWN_ERROR;
    639     }
    640     //printf("Adding child %p to parent %p\n", child.get(), this);
    641     mChildren.insertAt(child, index);
    642     return NO_ERROR;
    643 }
    644 
    645 status_t XMLNode::addAttribute(const String16& ns, const String16& name,
    646                                const String16& value)
    647 {
    648     if (getType() == TYPE_CDATA) {
    649         SourcePos(mFilename, getStartLineNumber()).error("Child to CDATA node.");
    650         return UNKNOWN_ERROR;
    651     }
    652     attribute_entry e;
    653     e.index = mNextAttributeIndex++;
    654     e.ns = ns;
    655     e.name = name;
    656     e.string = value;
    657     mAttributes.add(e);
    658     mAttributeOrder.add(e.index, mAttributes.size()-1);
    659     return NO_ERROR;
    660 }
    661 
    662 void XMLNode::setAttributeResID(size_t attrIdx, uint32_t resId)
    663 {
    664     attribute_entry& e = mAttributes.editItemAt(attrIdx);
    665     if (e.nameResId) {
    666         mAttributeOrder.removeItem(e.nameResId);
    667     } else {
    668         mAttributeOrder.removeItem(e.index);
    669     }
    670     NOISY(printf("Elem %s %s=\"%s\": set res id = 0x%08x\n",
    671             String8(getElementName()).string(),
    672             String8(mAttributes.itemAt(attrIdx).name).string(),
    673             String8(mAttributes.itemAt(attrIdx).string).string(),
    674             resId));
    675     mAttributes.editItemAt(attrIdx).nameResId = resId;
    676     mAttributeOrder.add(resId, attrIdx);
    677 }
    678 
    679 status_t XMLNode::appendChars(const String16& chars)
    680 {
    681     if (getType() != TYPE_CDATA) {
    682         SourcePos(mFilename, getStartLineNumber()).error("Adding characters to element node.");
    683         return UNKNOWN_ERROR;
    684     }
    685     mChars.append(chars);
    686     return NO_ERROR;
    687 }
    688 
    689 status_t XMLNode::appendComment(const String16& comment)
    690 {
    691     if (mComment.size() > 0) {
    692         mComment.append(String16("\n"));
    693     }
    694     mComment.append(comment);
    695     return NO_ERROR;
    696 }
    697 
    698 void XMLNode::setStartLineNumber(int32_t line)
    699 {
    700     mStartLineNumber = line;
    701 }
    702 
    703 void XMLNode::setEndLineNumber(int32_t line)
    704 {
    705     mEndLineNumber = line;
    706 }
    707 
    708 void XMLNode::removeWhitespace(bool stripAll, const char** cDataTags)
    709 {
    710     //printf("Removing whitespace in %s\n", String8(mElementName).string());
    711     size_t N = mChildren.size();
    712     if (cDataTags) {
    713         String8 tag(mElementName);
    714         const char** p = cDataTags;
    715         while (*p) {
    716             if (tag == *p) {
    717                 stripAll = false;
    718                 break;
    719             }
    720         }
    721     }
    722     for (size_t i=0; i<N; i++) {
    723         sp<XMLNode> node = mChildren.itemAt(i);
    724         if (node->getType() == TYPE_CDATA) {
    725             // This is a CDATA node...
    726             const char16_t* p = node->mChars.string();
    727             while (*p != 0 && *p < 128 && isspace(*p)) {
    728                 p++;
    729             }
    730             //printf("Space ends at %d in \"%s\"\n",
    731             //       (int)(p-node->mChars.string()),
    732             //       String8(node->mChars).string());
    733             if (*p == 0) {
    734                 if (stripAll) {
    735                     // Remove this node!
    736                     mChildren.removeAt(i);
    737                     N--;
    738                     i--;
    739                 } else {
    740                     node->mChars = String16(" ");
    741                 }
    742             } else {
    743                 // Compact leading/trailing whitespace.
    744                 const char16_t* e = node->mChars.string()+node->mChars.size()-1;
    745                 while (e > p && *e < 128 && isspace(*e)) {
    746                     e--;
    747                 }
    748                 if (p > node->mChars.string()) {
    749                     p--;
    750                 }
    751                 if (e < (node->mChars.string()+node->mChars.size()-1)) {
    752                     e++;
    753                 }
    754                 if (p > node->mChars.string() ||
    755                     e < (node->mChars.string()+node->mChars.size()-1)) {
    756                     String16 tmp(p, e-p+1);
    757                     node->mChars = tmp;
    758                 }
    759             }
    760         } else {
    761             node->removeWhitespace(stripAll, cDataTags);
    762         }
    763     }
    764 }
    765 
    766 status_t XMLNode::parseValues(const sp<AaptAssets>& assets,
    767                               ResourceTable* table)
    768 {
    769     bool hasErrors = false;
    770 
    771     if (getType() == TYPE_ELEMENT) {
    772         const size_t N = mAttributes.size();
    773         String16 defPackage(assets->getPackage());
    774         for (size_t i=0; i<N; i++) {
    775             attribute_entry& e = mAttributes.editItemAt(i);
    776             AccessorCookie ac(SourcePos(mFilename, getStartLineNumber()), String8(e.name),
    777                     String8(e.string));
    778             table->setCurrentXmlPos(SourcePos(mFilename, getStartLineNumber()));
    779             if (!assets->getIncludedResources()
    780                     .stringToValue(&e.value, &e.string,
    781                                   e.string.string(), e.string.size(), true, true,
    782                                   e.nameResId, NULL, &defPackage, table, &ac)) {
    783                 hasErrors = true;
    784             }
    785             NOISY(printf("Attr %s: type=0x%x, str=%s\n",
    786                    String8(e.name).string(), e.value.dataType,
    787                    String8(e.string).string()));
    788         }
    789     }
    790     const size_t N = mChildren.size();
    791     for (size_t i=0; i<N; i++) {
    792         status_t err = mChildren.itemAt(i)->parseValues(assets, table);
    793         if (err != NO_ERROR) {
    794             hasErrors = true;
    795         }
    796     }
    797     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
    798 }
    799 
    800 status_t XMLNode::assignResourceIds(const sp<AaptAssets>& assets,
    801                                     const ResourceTable* table)
    802 {
    803     bool hasErrors = false;
    804 
    805     if (getType() == TYPE_ELEMENT) {
    806         String16 attr("attr");
    807         const char* errorMsg;
    808         const size_t N = mAttributes.size();
    809         for (size_t i=0; i<N; i++) {
    810             const attribute_entry& e = mAttributes.itemAt(i);
    811             if (e.ns.size() <= 0) continue;
    812             bool nsIsPublic;
    813             String16 pkg(getNamespaceResourcePackage(e.ns, &nsIsPublic));
    814             NOISY(printf("Elem %s %s=\"%s\": namespace(%s) %s ===> %s\n",
    815                     String8(getElementName()).string(),
    816                     String8(e.name).string(),
    817                     String8(e.string).string(),
    818                     String8(e.ns).string(),
    819                     (nsIsPublic) ? "public" : "private",
    820                     String8(pkg).string()));
    821             if (pkg.size() <= 0) continue;
    822             uint32_t res = table != NULL
    823                 ? table->getResId(e.name, &attr, &pkg, &errorMsg, nsIsPublic)
    824                 : assets->getIncludedResources().
    825                     identifierForName(e.name.string(), e.name.size(),
    826                                       attr.string(), attr.size(),
    827                                       pkg.string(), pkg.size());
    828             if (res != 0) {
    829                 NOISY(printf("XML attribute name %s: resid=0x%08x\n",
    830                              String8(e.name).string(), res));
    831                 setAttributeResID(i, res);
    832             } else {
    833                 SourcePos(mFilename, getStartLineNumber()).error(
    834                         "No resource identifier found for attribute '%s' in package '%s'\n",
    835                         String8(e.name).string(), String8(pkg).string());
    836                 hasErrors = true;
    837             }
    838         }
    839     }
    840     const size_t N = mChildren.size();
    841     for (size_t i=0; i<N; i++) {
    842         status_t err = mChildren.itemAt(i)->assignResourceIds(assets, table);
    843         if (err < NO_ERROR) {
    844             hasErrors = true;
    845         }
    846     }
    847 
    848     return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
    849 }
    850 
    851 status_t XMLNode::flatten(const sp<AaptFile>& dest,
    852         bool stripComments, bool stripRawValues) const
    853 {
    854     StringPool strings = StringPool(false, mUTF8);
    855     Vector<uint32_t> resids;
    856 
    857     // First collect just the strings for attribute names that have a
    858     // resource ID assigned to them.  This ensures that the resource ID
    859     // array is compact, and makes it easier to deal with attribute names
    860     // in different namespaces (and thus with different resource IDs).
    861     collect_resid_strings(&strings, &resids);
    862 
    863     // Next collect all remainibng strings.
    864     collect_strings(&strings, &resids, stripComments, stripRawValues);
    865 
    866 #if 0  // No longer compiles
    867     NOISY(printf("Found strings:\n");
    868         const size_t N = strings.size();
    869         for (size_t i=0; i<N; i++) {
    870             printf("%s\n", String8(strings.entryAt(i).string).string());
    871         }
    872     );
    873 #endif
    874 
    875     sp<AaptFile> stringPool = strings.createStringBlock();
    876     NOISY(aout << "String pool:"
    877           << HexDump(stringPool->getData(), stringPool->getSize()) << endl);
    878 
    879     ResXMLTree_header header;
    880     memset(&header, 0, sizeof(header));
    881     header.header.type = htods(RES_XML_TYPE);
    882     header.header.headerSize = htods(sizeof(header));
    883 
    884     const size_t basePos = dest->getSize();
    885     dest->writeData(&header, sizeof(header));
    886     dest->writeData(stringPool->getData(), stringPool->getSize());
    887 
    888     // If we have resource IDs, write them.
    889     if (resids.size() > 0) {
    890         const size_t resIdsPos = dest->getSize();
    891         const size_t resIdsSize =
    892             sizeof(ResChunk_header)+(sizeof(uint32_t)*resids.size());
    893         ResChunk_header* idsHeader = (ResChunk_header*)
    894             (((const uint8_t*)dest->editData(resIdsPos+resIdsSize))+resIdsPos);
    895         idsHeader->type = htods(RES_XML_RESOURCE_MAP_TYPE);
    896         idsHeader->headerSize = htods(sizeof(*idsHeader));
    897         idsHeader->size = htodl(resIdsSize);
    898         uint32_t* ids = (uint32_t*)(idsHeader+1);
    899         for (size_t i=0; i<resids.size(); i++) {
    900             *ids++ = htodl(resids[i]);
    901         }
    902     }
    903 
    904     flatten_node(strings, dest, stripComments, stripRawValues);
    905 
    906     void* data = dest->editData();
    907     ResXMLTree_header* hd = (ResXMLTree_header*)(((uint8_t*)data)+basePos);
    908     size_t size = dest->getSize()-basePos;
    909     hd->header.size = htodl(dest->getSize()-basePos);
    910 
    911     NOISY(aout << "XML resource:"
    912           << HexDump(dest->getData(), dest->getSize()) << endl);
    913 
    914     #if PRINT_STRING_METRICS
    915     fprintf(stderr, "**** total xml size: %d / %d%% strings (in %s)\n",
    916         dest->getSize(), (stringPool->getSize()*100)/dest->getSize(),
    917         dest->getPath().string());
    918     #endif
    919 
    920     return NO_ERROR;
    921 }
    922 
    923 void XMLNode::print(int indent)
    924 {
    925     String8 prefix;
    926     int i;
    927     for (i=0; i<indent; i++) {
    928         prefix.append("  ");
    929     }
    930     if (getType() == TYPE_ELEMENT) {
    931         String8 elemNs(getNamespaceUri());
    932         if (elemNs.size() > 0) {
    933             elemNs.append(":");
    934         }
    935         printf("%s E: %s%s", prefix.string(),
    936                elemNs.string(), String8(getElementName()).string());
    937         int N = mAttributes.size();
    938         for (i=0; i<N; i++) {
    939             ssize_t idx = mAttributeOrder.valueAt(i);
    940             if (i == 0) {
    941                 printf(" / ");
    942             } else {
    943                 printf(", ");
    944             }
    945             const attribute_entry& attr = mAttributes.itemAt(idx);
    946             String8 attrNs(attr.ns);
    947             if (attrNs.size() > 0) {
    948                 attrNs.append(":");
    949             }
    950             if (attr.nameResId) {
    951                 printf("%s%s(0x%08x)", attrNs.string(),
    952                        String8(attr.name).string(), attr.nameResId);
    953             } else {
    954                 printf("%s%s", attrNs.string(), String8(attr.name).string());
    955             }
    956             printf("=%s", String8(attr.string).string());
    957         }
    958         printf("\n");
    959     } else if (getType() == TYPE_NAMESPACE) {
    960         printf("%s N: %s=%s\n", prefix.string(),
    961                getNamespacePrefix().size() > 0
    962                     ? String8(getNamespacePrefix()).string() : "<DEF>",
    963                String8(getNamespaceUri()).string());
    964     } else {
    965         printf("%s C: \"%s\"\n", prefix.string(), String8(getCData()).string());
    966     }
    967     int N = mChildren.size();
    968     for (i=0; i<N; i++) {
    969         mChildren.itemAt(i)->print(indent+1);
    970     }
    971 }
    972 
    973 static void splitName(const char* name, String16* outNs, String16* outName)
    974 {
    975     const char* p = name;
    976     while (*p != 0 && *p != 1) {
    977         p++;
    978     }
    979     if (*p == 0) {
    980         *outNs = String16();
    981         *outName = String16(name);
    982     } else {
    983         *outNs = String16(name, (p-name));
    984         *outName = String16(p+1);
    985     }
    986 }
    987 
    988 void XMLCALL
    989 XMLNode::startNamespace(void *userData, const char *prefix, const char *uri)
    990 {
    991     NOISY_PARSE(printf("Start Namespace: %s %s\n", prefix, uri));
    992     ParseState* st = (ParseState*)userData;
    993     sp<XMLNode> node = XMLNode::newNamespace(st->filename,
    994             String16(prefix != NULL ? prefix : ""), String16(uri));
    995     node->setStartLineNumber(XML_GetCurrentLineNumber(st->parser));
    996     if (st->stack.size() > 0) {
    997         st->stack.itemAt(st->stack.size()-1)->addChild(node);
    998     } else {
    999         st->root = node;
   1000     }
   1001     st->stack.push(node);
   1002 }
   1003 
   1004 void XMLCALL
   1005 XMLNode::startElement(void *userData, const char *name, const char **atts)
   1006 {
   1007     NOISY_PARSE(printf("Start Element: %s\n", name));
   1008     ParseState* st = (ParseState*)userData;
   1009     String16 ns16, name16;
   1010     splitName(name, &ns16, &name16);
   1011     sp<XMLNode> node = XMLNode::newElement(st->filename, ns16, name16);
   1012     node->setStartLineNumber(XML_GetCurrentLineNumber(st->parser));
   1013     if (st->pendingComment.size() > 0) {
   1014         node->appendComment(st->pendingComment);
   1015         st->pendingComment = String16();
   1016     }
   1017     if (st->stack.size() > 0) {
   1018         st->stack.itemAt(st->stack.size()-1)->addChild(node);
   1019     } else {
   1020         st->root = node;
   1021     }
   1022     st->stack.push(node);
   1023 
   1024     for (int i = 0; atts[i]; i += 2) {
   1025         splitName(atts[i], &ns16, &name16);
   1026         node->addAttribute(ns16, name16, String16(atts[i+1]));
   1027     }
   1028 }
   1029 
   1030 void XMLCALL
   1031 XMLNode::characterData(void *userData, const XML_Char *s, int len)
   1032 {
   1033     NOISY_PARSE(printf("CDATA: \"%s\"\n", String8(s, len).string()));
   1034     ParseState* st = (ParseState*)userData;
   1035     sp<XMLNode> node = NULL;
   1036     if (st->stack.size() == 0) {
   1037         return;
   1038     }
   1039     sp<XMLNode> parent = st->stack.itemAt(st->stack.size()-1);
   1040     if (parent != NULL && parent->getChildren().size() > 0) {
   1041         node = parent->getChildren()[parent->getChildren().size()-1];
   1042         if (node->getType() != TYPE_CDATA) {
   1043             // Last node is not CDATA, need to make a new node.
   1044             node = NULL;
   1045         }
   1046     }
   1047 
   1048     if (node == NULL) {
   1049         node = XMLNode::newCData(st->filename);
   1050         node->setStartLineNumber(XML_GetCurrentLineNumber(st->parser));
   1051         parent->addChild(node);
   1052     }
   1053 
   1054     node->appendChars(String16(s, len));
   1055 }
   1056 
   1057 void XMLCALL
   1058 XMLNode::endElement(void *userData, const char *name)
   1059 {
   1060     NOISY_PARSE(printf("End Element: %s\n", name));
   1061     ParseState* st = (ParseState*)userData;
   1062     sp<XMLNode> node = st->stack.itemAt(st->stack.size()-1);
   1063     node->setEndLineNumber(XML_GetCurrentLineNumber(st->parser));
   1064     if (st->pendingComment.size() > 0) {
   1065         node->appendComment(st->pendingComment);
   1066         st->pendingComment = String16();
   1067     }
   1068     String16 ns16, name16;
   1069     splitName(name, &ns16, &name16);
   1070     LOG_ALWAYS_FATAL_IF(node->getElementNamespace() != ns16
   1071                         || node->getElementName() != name16,
   1072                         "Bad end element %s", name);
   1073     st->stack.pop();
   1074 }
   1075 
   1076 void XMLCALL
   1077 XMLNode::endNamespace(void *userData, const char *prefix)
   1078 {
   1079     const char* nonNullPrefix = prefix != NULL ? prefix : "";
   1080     NOISY_PARSE(printf("End Namespace: %s\n", prefix));
   1081     ParseState* st = (ParseState*)userData;
   1082     sp<XMLNode> node = st->stack.itemAt(st->stack.size()-1);
   1083     node->setEndLineNumber(XML_GetCurrentLineNumber(st->parser));
   1084     LOG_ALWAYS_FATAL_IF(node->getNamespacePrefix() != String16(nonNullPrefix),
   1085                         "Bad end namespace %s", prefix);
   1086     st->stack.pop();
   1087 }
   1088 
   1089 void XMLCALL
   1090 XMLNode::commentData(void *userData, const char *comment)
   1091 {
   1092     NOISY_PARSE(printf("Comment: %s\n", comment));
   1093     ParseState* st = (ParseState*)userData;
   1094     if (st->pendingComment.size() > 0) {
   1095         st->pendingComment.append(String16("\n"));
   1096     }
   1097     st->pendingComment.append(String16(comment));
   1098 }
   1099 
   1100 status_t XMLNode::collect_strings(StringPool* dest, Vector<uint32_t>* outResIds,
   1101         bool stripComments, bool stripRawValues) const
   1102 {
   1103     collect_attr_strings(dest, outResIds, true);
   1104 
   1105     int i;
   1106     if (mNamespacePrefix.size() > 0) {
   1107         dest->add(mNamespacePrefix, true);
   1108     }
   1109     if (mNamespaceUri.size() > 0) {
   1110         dest->add(mNamespaceUri, true);
   1111     }
   1112     if (mElementName.size() > 0) {
   1113         dest->add(mElementName, true);
   1114     }
   1115 
   1116     if (!stripComments && mComment.size() > 0) {
   1117         dest->add(mComment, true);
   1118     }
   1119 
   1120     const int NA = mAttributes.size();
   1121 
   1122     for (i=0; i<NA; i++) {
   1123         const attribute_entry& ae = mAttributes.itemAt(i);
   1124         if (ae.ns.size() > 0) {
   1125             dest->add(ae.ns, true);
   1126         }
   1127         if (!stripRawValues || ae.needStringValue()) {
   1128             dest->add(ae.string, true);
   1129         }
   1130         /*
   1131         if (ae.value.dataType == Res_value::TYPE_NULL
   1132                 || ae.value.dataType == Res_value::TYPE_STRING) {
   1133             dest->add(ae.string, true);
   1134         }
   1135         */
   1136     }
   1137 
   1138     if (mElementName.size() == 0) {
   1139         // If not an element, include the CDATA, even if it is empty.
   1140         dest->add(mChars, true);
   1141     }
   1142 
   1143     const int NC = mChildren.size();
   1144 
   1145     for (i=0; i<NC; i++) {
   1146         mChildren.itemAt(i)->collect_strings(dest, outResIds,
   1147                 stripComments, stripRawValues);
   1148     }
   1149 
   1150     return NO_ERROR;
   1151 }
   1152 
   1153 status_t XMLNode::collect_attr_strings(StringPool* outPool,
   1154         Vector<uint32_t>* outResIds, bool allAttrs) const {
   1155     const int NA = mAttributes.size();
   1156 
   1157     for (int i=0; i<NA; i++) {
   1158         const attribute_entry& attr = mAttributes.itemAt(i);
   1159         uint32_t id = attr.nameResId;
   1160         if (id || allAttrs) {
   1161             // See if we have already assigned this resource ID to a pooled
   1162             // string...
   1163             const Vector<size_t>* indices = outPool->offsetsForString(attr.name);
   1164             ssize_t idx = -1;
   1165             if (indices != NULL) {
   1166                 const int NJ = indices->size();
   1167                 const size_t NR = outResIds->size();
   1168                 for (int j=0; j<NJ; j++) {
   1169                     size_t strIdx = indices->itemAt(j);
   1170                     if (strIdx >= NR) {
   1171                         if (id == 0) {
   1172                             // We don't need to assign a resource ID for this one.
   1173                             idx = strIdx;
   1174                             break;
   1175                         }
   1176                         // Just ignore strings that are out of range of
   1177                         // the currently assigned resource IDs...  we add
   1178                         // strings as we assign the first ID.
   1179                     } else if (outResIds->itemAt(strIdx) == id) {
   1180                         idx = strIdx;
   1181                         break;
   1182                     }
   1183                 }
   1184             }
   1185             if (idx < 0) {
   1186                 idx = outPool->add(attr.name);
   1187                 NOISY(printf("Adding attr %s (resid 0x%08x) to pool: idx=%d\n",
   1188                         String8(attr.name).string(), id, idx));
   1189                 if (id != 0) {
   1190                     while ((ssize_t)outResIds->size() <= idx) {
   1191                         outResIds->add(0);
   1192                     }
   1193                     outResIds->replaceAt(id, idx);
   1194                 }
   1195             }
   1196             attr.namePoolIdx = idx;
   1197             NOISY(printf("String %s offset=0x%08x\n",
   1198                          String8(attr.name).string(), idx));
   1199         }
   1200     }
   1201 
   1202     return NO_ERROR;
   1203 }
   1204 
   1205 status_t XMLNode::collect_resid_strings(StringPool* outPool,
   1206         Vector<uint32_t>* outResIds) const
   1207 {
   1208     collect_attr_strings(outPool, outResIds, false);
   1209 
   1210     const int NC = mChildren.size();
   1211 
   1212     for (int i=0; i<NC; i++) {
   1213         mChildren.itemAt(i)->collect_resid_strings(outPool, outResIds);
   1214     }
   1215 
   1216     return NO_ERROR;
   1217 }
   1218 
   1219 status_t XMLNode::flatten_node(const StringPool& strings, const sp<AaptFile>& dest,
   1220         bool stripComments, bool stripRawValues) const
   1221 {
   1222     ResXMLTree_node node;
   1223     ResXMLTree_cdataExt cdataExt;
   1224     ResXMLTree_namespaceExt namespaceExt;
   1225     ResXMLTree_attrExt attrExt;
   1226     const void* extData = NULL;
   1227     size_t extSize = 0;
   1228     ResXMLTree_attribute attr;
   1229 
   1230     const size_t NA = mAttributes.size();
   1231     const size_t NC = mChildren.size();
   1232     size_t i;
   1233 
   1234     LOG_ALWAYS_FATAL_IF(NA != mAttributeOrder.size(), "Attributes messed up!");
   1235 
   1236     const String16 id16("id");
   1237     const String16 class16("class");
   1238     const String16 style16("style");
   1239 
   1240     const type type = getType();
   1241 
   1242     memset(&node, 0, sizeof(node));
   1243     memset(&attr, 0, sizeof(attr));
   1244     node.header.headerSize = htods(sizeof(node));
   1245     node.lineNumber = htodl(getStartLineNumber());
   1246     if (!stripComments) {
   1247         node.comment.index = htodl(
   1248             mComment.size() > 0 ? strings.offsetForString(mComment) : -1);
   1249         //if (mComment.size() > 0) {
   1250         //  printf("Flattening comment: %s\n", String8(mComment).string());
   1251         //}
   1252     } else {
   1253         node.comment.index = htodl((uint32_t)-1);
   1254     }
   1255     if (type == TYPE_ELEMENT) {
   1256         node.header.type = htods(RES_XML_START_ELEMENT_TYPE);
   1257         extData = &attrExt;
   1258         extSize = sizeof(attrExt);
   1259         memset(&attrExt, 0, sizeof(attrExt));
   1260         if (mNamespaceUri.size() > 0) {
   1261             attrExt.ns.index = htodl(strings.offsetForString(mNamespaceUri));
   1262         } else {
   1263             attrExt.ns.index = htodl((uint32_t)-1);
   1264         }
   1265         attrExt.name.index = htodl(strings.offsetForString(mElementName));
   1266         attrExt.attributeStart = htods(sizeof(attrExt));
   1267         attrExt.attributeSize = htods(sizeof(attr));
   1268         attrExt.attributeCount = htods(NA);
   1269         attrExt.idIndex = htods(0);
   1270         attrExt.classIndex = htods(0);
   1271         attrExt.styleIndex = htods(0);
   1272         for (i=0; i<NA; i++) {
   1273             ssize_t idx = mAttributeOrder.valueAt(i);
   1274             const attribute_entry& ae = mAttributes.itemAt(idx);
   1275             if (ae.ns.size() == 0) {
   1276                 if (ae.name == id16) {
   1277                     attrExt.idIndex = htods(i+1);
   1278                 } else if (ae.name == class16) {
   1279                     attrExt.classIndex = htods(i+1);
   1280                 } else if (ae.name == style16) {
   1281                     attrExt.styleIndex = htods(i+1);
   1282                 }
   1283             }
   1284         }
   1285     } else if (type == TYPE_NAMESPACE) {
   1286         node.header.type = htods(RES_XML_START_NAMESPACE_TYPE);
   1287         extData = &namespaceExt;
   1288         extSize = sizeof(namespaceExt);
   1289         memset(&namespaceExt, 0, sizeof(namespaceExt));
   1290         if (mNamespacePrefix.size() > 0) {
   1291             namespaceExt.prefix.index = htodl(strings.offsetForString(mNamespacePrefix));
   1292         } else {
   1293             namespaceExt.prefix.index = htodl((uint32_t)-1);
   1294         }
   1295         namespaceExt.prefix.index = htodl(strings.offsetForString(mNamespacePrefix));
   1296         namespaceExt.uri.index = htodl(strings.offsetForString(mNamespaceUri));
   1297         LOG_ALWAYS_FATAL_IF(NA != 0, "Namespace nodes can't have attributes!");
   1298     } else if (type == TYPE_CDATA) {
   1299         node.header.type = htods(RES_XML_CDATA_TYPE);
   1300         extData = &cdataExt;
   1301         extSize = sizeof(cdataExt);
   1302         memset(&cdataExt, 0, sizeof(cdataExt));
   1303         cdataExt.data.index = htodl(strings.offsetForString(mChars));
   1304         cdataExt.typedData.size = htods(sizeof(cdataExt.typedData));
   1305         cdataExt.typedData.res0 = 0;
   1306         cdataExt.typedData.dataType = mCharsValue.dataType;
   1307         cdataExt.typedData.data = htodl(mCharsValue.data);
   1308         LOG_ALWAYS_FATAL_IF(NA != 0, "CDATA nodes can't have attributes!");
   1309     }
   1310 
   1311     node.header.size = htodl(sizeof(node) + extSize + (sizeof(attr)*NA));
   1312 
   1313     dest->writeData(&node, sizeof(node));
   1314     if (extSize > 0) {
   1315         dest->writeData(extData, extSize);
   1316     }
   1317 
   1318     for (i=0; i<NA; i++) {
   1319         ssize_t idx = mAttributeOrder.valueAt(i);
   1320         const attribute_entry& ae = mAttributes.itemAt(idx);
   1321         if (ae.ns.size() > 0) {
   1322             attr.ns.index = htodl(strings.offsetForString(ae.ns));
   1323         } else {
   1324             attr.ns.index = htodl((uint32_t)-1);
   1325         }
   1326         attr.name.index = htodl(ae.namePoolIdx);
   1327 
   1328         if (!stripRawValues || ae.needStringValue()) {
   1329             attr.rawValue.index = htodl(strings.offsetForString(ae.string));
   1330         } else {
   1331             attr.rawValue.index = htodl((uint32_t)-1);
   1332         }
   1333         attr.typedValue.size = htods(sizeof(attr.typedValue));
   1334         if (ae.value.dataType == Res_value::TYPE_NULL
   1335                 || ae.value.dataType == Res_value::TYPE_STRING) {
   1336             attr.typedValue.res0 = 0;
   1337             attr.typedValue.dataType = Res_value::TYPE_STRING;
   1338             attr.typedValue.data = htodl(strings.offsetForString(ae.string));
   1339         } else {
   1340             attr.typedValue.res0 = 0;
   1341             attr.typedValue.dataType = ae.value.dataType;
   1342             attr.typedValue.data = htodl(ae.value.data);
   1343         }
   1344         dest->writeData(&attr, sizeof(attr));
   1345     }
   1346 
   1347     for (i=0; i<NC; i++) {
   1348         status_t err = mChildren.itemAt(i)->flatten_node(strings, dest,
   1349                 stripComments, stripRawValues);
   1350         if (err != NO_ERROR) {
   1351             return err;
   1352         }
   1353     }
   1354 
   1355     if (type == TYPE_ELEMENT) {
   1356         ResXMLTree_endElementExt endElementExt;
   1357         memset(&endElementExt, 0, sizeof(endElementExt));
   1358         node.header.type = htods(RES_XML_END_ELEMENT_TYPE);
   1359         node.header.size = htodl(sizeof(node)+sizeof(endElementExt));
   1360         node.lineNumber = htodl(getEndLineNumber());
   1361         node.comment.index = htodl((uint32_t)-1);
   1362         endElementExt.ns.index = attrExt.ns.index;
   1363         endElementExt.name.index = attrExt.name.index;
   1364         dest->writeData(&node, sizeof(node));
   1365         dest->writeData(&endElementExt, sizeof(endElementExt));
   1366     } else if (type == TYPE_NAMESPACE) {
   1367         node.header.type = htods(RES_XML_END_NAMESPACE_TYPE);
   1368         node.lineNumber = htodl(getEndLineNumber());
   1369         node.comment.index = htodl((uint32_t)-1);
   1370         node.header.size = htodl(sizeof(node)+extSize);
   1371         dest->writeData(&node, sizeof(node));
   1372         dest->writeData(extData, extSize);
   1373     }
   1374 
   1375     return NO_ERROR;
   1376 }
   1377