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