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