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