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