2 // Copyright 2006 The Android Open Source Project
4 // Build resource files from raw assets.
8 #include "ResourceTable.h"
10 #include <host/pseudolocalize.h>
11 #include <utils/ByteOrder.h>
15 #ifndef HAVE_MS_C_RUNTIME
20 #define NOISY_PARSE(x) //x
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";
25 const char* const XLIFF_XMLNS
= "urn:oasis:names:tc:xliff:document:1.2";
26 const char* const ALLOWED_XLIFF_ELEMENTS
[] = {
37 bool isWhitespace(const char16_t* str
)
39 while (*str
!= 0 && *str
< 128 && isspace(*str
)) {
45 static const String16
RESOURCES_PREFIX(RESOURCES_ROOT_NAMESPACE
);
47 String16
getNamespaceResourcePackage(String16 namespaceUri
)
49 //printf("%s starts with %s?\n", String8(namespaceUri).string(),
50 // String8(RESOURCES_PREFIX).string());
51 if (!namespaceUri
.startsWith(RESOURCES_PREFIX
)) return String16();
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
);
58 status_t
parseStyledString(Bundle
* bundle
,
61 const String16
& endTag
,
63 Vector
<StringPool::entry_style_span
>* outSpans
,
66 Vector
<StringPool::entry_style_span
> spanStack
;
71 bool firstTime
= true;
74 ResXMLTree::event_code_t code
;
75 while ((code
=inXml
->next()) != ResXMLTree::END_DOCUMENT
&& code
!= ResXMLTree::BAD_DOCUMENT
) {
77 if (code
== ResXMLTree::TEXT
) {
78 String16
text(inXml
->getText(&len
));
79 if (firstTime
&& text
.size() > 0) {
81 if (text
.string()[0] == '@') {
82 // If this is a resource reference, don't do the pseudoloc.
83 pseudolocalize
= false;
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())));
91 curString
.append(text
);
93 } else if (code
== ResXMLTree::START_TAG
) {
94 const String16
element16(inXml
->getElementName(&len
));
95 const String8
element8(element16
);
98 const uint16_t* ns
= inXml
->getElementNamespace(&nslen
);
100 ns
= (const uint16_t*)"\0\0";
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
]) {
109 // in this case, treat it like it was just text, in other words, do nothing
110 // here and silently drop this element
115 SourcePos(String8(fileName
), inXml
->getLineNumber()).error(
116 "Found unsupported XLIFF tag <%s>\n",
118 return UNKNOWN_ERROR
;
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
;
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
;
136 rawString
.append(curString
);
137 curString
= String16();
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
);
149 //printf("Span: %s\n", String8(span.name).string());
150 span
.span
.firstChar
= span
.span
.lastChar
= outString
->size();
151 spanStack
.push(span
);
153 } else if (code
== ResXMLTree::END_TAG
) {
155 const uint16_t* ns
= inXml
->getElementNamespace(&nslen
);
157 ns
= (const uint16_t*)"\0\0";
160 const String8
nspace(String16(ns
, nslen
));
161 if (nspace
== XLIFF_XMLNS
) {
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
;
171 rawString
.append(curString
);
172 curString
= String16();
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
;
184 StringPool::entry_style_span span
= spanStack
.top();
186 ssize_t semi
= span
.name
.findFirst(';');
188 spanTag
.setTo(span
.name
.string(), semi
);
190 spanTag
.setTo(span
.name
);
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
;
200 if (outString
->size() > 0) {
201 span
.span
.lastChar
= outString
->size()-1;
202 if (span
.span
.lastChar
>= span
.span
.firstChar
) {
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());
215 } else if (code
== ResXMLTree::START_NAMESPACE
) {
220 if (code
== ResXMLTree::BAD_DOCUMENT
) {
221 SourcePos(String8(fileName
), inXml
->getLineNumber()).error(
222 "Error parsing XML\n");
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(
231 errorMsg
, String8(curString
).string());
232 return UNKNOWN_ERROR
;
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
);
246 struct namespace_entry
{
251 static String8
make_prefix(int depth
)
255 for (i
=0; i
<depth
; i
++) {
261 static String8
build_namespace(const Vector
<namespace_entry
>& namespaces
,
267 const size_t N
= namespaces
.size();
268 for (size_t i
=0; i
<N
; i
++) {
269 const namespace_entry
& ne
= namespaces
.itemAt(i
);
280 void printXMLBlock(ResXMLTree
* block
)
284 Vector
<namespace_entry
> namespaces
;
286 ResXMLTree::event_code_t code
;
288 while ((code
=block
->next()) != ResXMLTree::END_DOCUMENT
&& code
!= ResXMLTree::BAD_DOCUMENT
) {
289 String8 prefix
= make_prefix(depth
);
291 if (code
== ResXMLTree::START_TAG
) {
293 const uint16_t* ns16
= block
->getElementNamespace(&len
);
294 String8 elemNs
= build_namespace(namespaces
, ns16
);
295 const uint16_t* com16
= block
->getComment(&len
);
297 printf("%s <!-- %s -->\n", prefix
.string(), String8(com16
).string());
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();
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());
312 printf("%s%s(0x%08x)", ns
.string(), name
.string(), res
);
314 printf("%s%s", ns
.string(), name
.string());
317 block
->getAttributeValue(i
, &value
);
318 if (value
.dataType
== Res_value::TYPE_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
) {
326 String8(block
->getAttributeStringValue(i
, &len
)).string());
328 printf("=(type 0x%x)0x%x", (int)value
.dataType
, (int)value
.data
);
330 const char16_t* val
= block
->getAttributeStringValue(i
, &len
);
332 printf(" (Raw: \"%s\")", String8(val
).string());
336 } else if (code
== ResXMLTree::END_TAG
) {
338 } else if (code
== ResXMLTree::START_NAMESPACE
) {
341 const uint16_t* prefix16
= block
->getNamespacePrefix(&len
);
343 ns
.prefix
= String8(prefix16
);
347 ns
.uri
= String8(block
->getNamespaceUri(&len
));
349 printf("%sN: %s=%s\n", prefix
.string(), ns
.prefix
.string(),
352 } else if (code
== ResXMLTree::END_NAMESPACE
) {
354 const namespace_entry
& ns
= namespaces
.top();
356 const uint16_t* prefix16
= block
->getNamespacePrefix(&len
);
359 pr
= String8(prefix16
);
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());
368 String8 uri
= String8(block
->getNamespaceUri(&len
));
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());
375 } else if (code
== ResXMLTree::TEXT
) {
377 printf("%sC: \"%s\"\n", prefix
.string(), String8(block
->getText(&len
)).string());
384 status_t
parseXMLResource(const sp
<AaptFile
>& file
, ResXMLTree
* outTree
,
385 bool stripAll
, bool keepComments
,
386 const char** cDataTags
)
388 sp
<XMLNode
> root
= XMLNode::parse(file
);
390 return UNKNOWN_ERROR
;
392 root
->removeWhitespace(stripAll
, cDataTags
);
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
) {
401 err
= outTree
->setTo(rsc
->getData(), rsc
->getSize(), true);
402 if (err
!= NO_ERROR
) {
406 NOISY(printf("Output XML:\n"));
407 NOISY(printXMLBlock(outTree
));
412 sp
<XMLNode
> XMLNode::parse(const sp
<AaptFile
>& file
)
415 int fd
= open(file
->getSourceFile().string(), O_RDONLY
| O_BINARY
);
417 SourcePos(file
->getSourceFile(), -1).error("Unable to open file for read: %s",
422 XML_Parser parser
= XML_ParserCreateNS(NULL
, 1);
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
);
435 len
= read(fd
, buf
, sizeof(buf
));
436 done
= len
< (ssize_t
)sizeof(buf
);
438 SourcePos(file
->getSourceFile(), -1).error("Error reading file: %s\n", strerror(errno
));
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
)));
450 XML_ParserFree(parser
);
451 if (state
.root
== NULL
) {
452 SourcePos(file
->getSourceFile(), -1).error("No XML data generated when parsing");
458 XMLNode::XMLNode(const String8
& filename
, const String16
& s1
, const String16
& s2
, bool isNamespace
)
459 : mNextAttributeIndex(0x80000000)
460 , mFilename(filename
)
461 , mStartLineNumber(0)
465 mNamespacePrefix
= s1
;
473 XMLNode::XMLNode(const String8
& filename
)
474 : mFilename(filename
)
478 XMLNode::type
XMLNode::getType() const
480 if (mElementName
.size() != 0) {
483 if (mNamespaceUri
.size() != 0) {
484 return TYPE_NAMESPACE
;
489 const String16
& XMLNode::getNamespacePrefix() const
491 return mNamespacePrefix
;
494 const String16
& XMLNode::getNamespaceUri() const
496 return mNamespaceUri
;
499 const String16
& XMLNode::getElementNamespace() const
501 return mNamespaceUri
;
504 const String16
& XMLNode::getElementName() const
509 const Vector
<sp
<XMLNode
> >& XMLNode::getChildren() const
514 const Vector
<XMLNode::attribute_entry
>&
515 XMLNode::getAttributes() const
520 const String16
& XMLNode::getCData() const
525 const String16
& XMLNode::getComment() const
530 int32_t XMLNode::getStartLineNumber() const
532 return mStartLineNumber
;
535 int32_t XMLNode::getEndLineNumber() const
537 return mEndLineNumber
;
540 status_t
XMLNode::addChild(const sp
<XMLNode
>& child
)
542 if (getType() == TYPE_CDATA
) {
543 SourcePos(mFilename
, child
->getStartLineNumber()).error("Child to CDATA node.");
544 return UNKNOWN_ERROR
;
546 //printf("Adding child %p to parent %p\n", child.get(), this);
547 mChildren
.add(child
);
551 status_t
XMLNode::addAttribute(const String16
& ns
, const String16
& name
,
552 const String16
& value
)
554 if (getType() == TYPE_CDATA
) {
555 SourcePos(mFilename
, getStartLineNumber()).error("Child to CDATA node.");
556 return UNKNOWN_ERROR
;
559 e
.index
= mNextAttributeIndex
++;
564 mAttributeOrder
.add(e
.index
, mAttributes
.size()-1);
568 void XMLNode::setAttributeResID(size_t attrIdx
, uint32_t resId
)
570 attribute_entry
& e
= mAttributes
.editItemAt(attrIdx
);
572 mAttributeOrder
.removeItem(e
.nameResId
);
574 mAttributeOrder
.removeItem(e
.index
);
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(),
581 mAttributes
.editItemAt(attrIdx
).nameResId
= resId
;
582 mAttributeOrder
.add(resId
, attrIdx
);
585 status_t
XMLNode::appendChars(const String16
& chars
)
587 if (getType() != TYPE_CDATA
) {
588 SourcePos(mFilename
, getStartLineNumber()).error("Adding characters to element node.");
589 return UNKNOWN_ERROR
;
591 mChars
.append(chars
);
595 status_t
XMLNode::appendComment(const String16
& comment
)
597 if (mComment
.size() > 0) {
598 mComment
.append(String16("\n"));
600 mComment
.append(comment
);
604 void XMLNode::setStartLineNumber(int32_t line
)
606 mStartLineNumber
= line
;
609 void XMLNode::setEndLineNumber(int32_t line
)
611 mEndLineNumber
= line
;
614 void XMLNode::removeWhitespace(bool stripAll
, const char** cDataTags
)
616 //printf("Removing whitespace in %s\n", String8(mElementName).string());
617 size_t N
= mChildren
.size();
619 String8
tag(mElementName
);
620 const char** p
= cDataTags
;
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
)) {
636 //printf("Space ends at %d in \"%s\"\n",
637 // (int)(p-node->mChars.string()),
638 // String8(node->mChars).string());
642 mChildren
.removeAt(i
);
646 node
->mChars
= String16(" ");
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
)) {
654 if (p
> node
->mChars
.string()) {
657 if (e
< (node
->mChars
.string()+node
->mChars
.size()-1)) {
660 if (p
> node
->mChars
.string() ||
661 e
< (node
->mChars
.string()+node
->mChars
.size()-1)) {
662 String16
tmp(p
, e
-p
+1);
667 node
->removeWhitespace(stripAll
, cDataTags
);
672 status_t
XMLNode::parseValues(const sp
<AaptAssets
>& assets
,
673 ResourceTable
* table
)
675 bool hasErrors
= false;
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
),
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
)) {
691 NOISY(printf("Attr %s: type=0x%x, str=%s\n",
692 String8(e
.name
).string(), e
.value
.dataType
,
693 String8(e
.string
).string()));
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
) {
703 return hasErrors
? UNKNOWN_ERROR
: NO_ERROR
;
706 status_t
XMLNode::assignResourceIds(const sp
<AaptAssets
>& assets
,
707 const ResourceTable
* table
)
709 bool hasErrors
= false;
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());
732 NOISY(printf("XML attribute name %s: resid=0x%08x\n",
733 String8(e
.name
).string(), res
));
734 setAttributeResID(i
, res
);
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());
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
) {
751 return hasErrors
? UNKNOWN_ERROR
: NO_ERROR
;
754 status_t
XMLNode::flatten(const sp
<AaptFile
>& dest
,
755 bool stripComments
, bool stripRawValues
) const
758 Vector
<uint32_t> resids
;
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
);
766 // Next collect all remainibng strings.
767 collect_strings(&strings
, &resids
, stripComments
, stripRawValues
);
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());
778 sp
<AaptFile
> stringPool
= strings
.createStringBlock();
779 NOISY(aout
<< "String pool:"
780 << HexDump(stringPool
->getData(), stringPool
->getSize()) << endl
);
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
));
787 const size_t basePos
= dest
->getSize();
788 dest
->writeData(&header
, sizeof(header
));
789 dest
->writeData(stringPool
->getData(), stringPool
->getSize());
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
]);
807 flatten_node(strings
, dest
, stripComments
, stripRawValues
);
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
);
814 NOISY(aout
<< "XML resource:"
815 << HexDump(dest
->getData(), dest
->getSize()) << endl
);
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());
826 void XMLNode::print(int indent
)
830 for (i
=0; i
<indent
; i
++) {
833 if (getType() == TYPE_ELEMENT
) {
834 String8
elemNs(getNamespaceUri());
835 if (elemNs
.size() > 0) {
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
);
848 const attribute_entry
& attr
= mAttributes
.itemAt(idx
);
849 String8
attrNs(attr
.ns
);
850 if (attrNs
.size() > 0) {
853 if (attr
.nameResId
) {
854 printf("%s%s(0x%08x)", attrNs
.string(),
855 String8(attr
.name
).string(), attr
.nameResId
);
857 printf("%s%s", attrNs
.string(), String8(attr
.name
).string());
859 printf("=%s", String8(attr
.string
).string());
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());
868 printf("%s C: \"%s\"\n", prefix
.string(), String8(getCData()).string());
870 int N
= mChildren
.size();
871 for (i
=0; i
<N
; i
++) {
872 mChildren
.itemAt(i
)->print(indent
+1);
876 static void splitName(const char* name
, String16
* outNs
, String16
* outName
)
878 const char* p
= name
;
879 while (*p
!= 0 && *p
!= 1) {
884 *outName
= String16(name
);
886 *outNs
= String16(name
, (p
-name
));
887 *outName
= String16(p
+1);
892 XMLNode::startNamespace(void *userData
, const char *prefix
, const char *uri
)
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
);
904 st
->stack
.push(node
);
908 XMLNode::startElement(void *userData
, const char *name
, const char **atts
)
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();
920 if (st
->stack
.size() > 0) {
921 st
->stack
.itemAt(st
->stack
.size()-1)->addChild(node
);
925 st
->stack
.push(node
);
927 for (int i
= 0; atts
[i
]; i
+= 2) {
928 splitName(atts
[i
], &ns16
, &name16
);
929 node
->addAttribute(ns16
, name16
, String16(atts
[i
+1]));
934 XMLNode::characterData(void *userData
, const XML_Char
*s
, int len
)
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) {
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.
952 node
= XMLNode::newCData(st
->filename
);
953 node
->setStartLineNumber(XML_GetCurrentLineNumber(st
->parser
));
954 parent
->addChild(node
);
957 node
->appendChars(String16(s
, len
));
961 XMLNode::endElement(void *userData
, const char *name
)
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();
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
);
980 XMLNode::endNamespace(void *userData
, const char *prefix
)
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
);
993 XMLNode::commentData(void *userData
, const char *comment
)
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"));
1000 st
->pendingComment
.append(String16(comment
));
1003 status_t
XMLNode::collect_strings(StringPool
* dest
, Vector
<uint32_t>* outResIds
,
1004 bool stripComments
, bool stripRawValues
) const
1006 collect_attr_strings(dest
, outResIds
, true);
1009 if (mNamespacePrefix
.size() > 0) {
1010 dest
->add(mNamespacePrefix
, true);
1012 if (mNamespaceUri
.size() > 0) {
1013 dest
->add(mNamespaceUri
, true);
1015 if (mElementName
.size() > 0) {
1016 dest
->add(mElementName
, true);
1019 if (!stripComments
&& mComment
.size() > 0) {
1020 dest
->add(mComment
, true);
1023 const int NA
= mAttributes
.size();
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);
1030 if (!stripRawValues
|| ae
.needStringValue()) {
1031 dest
->add(ae
.string
, true);
1034 if (ae.value.dataType == Res_value::TYPE_NULL
1035 || ae.value.dataType == Res_value::TYPE_STRING) {
1036 dest->add(ae.string, true);
1041 if (mElementName
.size() == 0) {
1042 // If not an element, include the CDATA, even if it is empty.
1043 dest
->add(mChars
, true);
1046 const int NC
= mChildren
.size();
1048 for (i
=0; i
<NC
; i
++) {
1049 mChildren
.itemAt(i
)->collect_strings(dest
, outResIds
,
1050 stripComments
, stripRawValues
);
1056 status_t
XMLNode::collect_attr_strings(StringPool
* outPool
,
1057 Vector
<uint32_t>* outResIds
, bool allAttrs
) const {
1058 const int NA
= mAttributes
.size();
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
1066 const Vector
<size_t>* indices
= outPool
->offsetsForString(attr
.name
);
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
);
1075 // We don't need to assign a resource ID for this one.
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
) {
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
));
1093 while ((ssize_t
)outResIds
->size() <= idx
) {
1096 outResIds
->replaceAt(id
, idx
);
1099 attr
.namePoolIdx
= idx
;
1100 NOISY(printf("String %s offset=0x%08x\n",
1101 String8(attr
.name
).string(), idx
));
1108 status_t
XMLNode::collect_resid_strings(StringPool
* outPool
,
1109 Vector
<uint32_t>* outResIds
) const
1111 collect_attr_strings(outPool
, outResIds
, false);
1113 const int NC
= mChildren
.size();
1115 for (int i
=0; i
<NC
; i
++) {
1116 mChildren
.itemAt(i
)->collect_resid_strings(outPool
, outResIds
);
1122 status_t
XMLNode::flatten_node(const StringPool
& strings
, const sp
<AaptFile
>& dest
,
1123 bool stripComments
, bool stripRawValues
) const
1125 ResXMLTree_node node
;
1126 ResXMLTree_cdataExt cdataExt
;
1127 ResXMLTree_namespaceExt namespaceExt
;
1128 ResXMLTree_attrExt attrExt
;
1129 const void* extData
= NULL
;
1131 ResXMLTree_attribute attr
;
1133 const size_t NA
= mAttributes
.size();
1134 const size_t NC
= mChildren
.size();
1137 LOG_ALWAYS_FATAL_IF(NA
!= mAttributeOrder
.size(), "Attributes messed up!");
1139 const String16
id16("id");
1140 const String16
class16("class");
1141 const String16
style16("style");
1143 const type type
= getType();
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());
1156 node
.comment
.index
= htodl((uint32_t)-1);
1158 if (type
== TYPE_ELEMENT
) {
1159 node
.header
.type
= htods(RES_XML_START_ELEMENT_TYPE
);
1161 extSize
= sizeof(attrExt
);
1162 memset(&attrExt
, 0, sizeof(attrExt
));
1163 if (mNamespaceUri
.size() > 0) {
1164 attrExt
.ns
.index
= htodl(strings
.offsetForString(mNamespaceUri
));
1166 attrExt
.ns
.index
= htodl((uint32_t)-1);
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);
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
));
1196 namespaceExt
.prefix
.index
= htodl((uint32_t)-1);
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!");
1214 node
.header
.size
= htodl(sizeof(node
) + extSize
+ (sizeof(attr
)*NA
));
1216 dest
->writeData(&node
, sizeof(node
));
1218 dest
->writeData(extData
, extSize
);
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
));
1227 attr
.ns
.index
= htodl((uint32_t)-1);
1229 attr
.name
.index
= htodl(ae
.namePoolIdx
);
1231 if (!stripRawValues
|| ae
.needStringValue()) {
1232 attr
.rawValue
.index
= htodl(strings
.offsetForString(ae
.string
));
1234 attr
.rawValue
.index
= htodl((uint32_t)-1);
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
));
1243 attr
.typedValue
.res0
= 0;
1244 attr
.typedValue
.dataType
= ae
.value
.dataType
;
1245 attr
.typedValue
.data
= htodl(ae
.value
.data
);
1247 dest
->writeData(&attr
, sizeof(attr
));
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
) {
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
);