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