11ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski/*
21ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * Copyright (C) 2015 The Android Open Source Project
31ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski *
41ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
51ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * you may not use this file except in compliance with the License.
61ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * You may obtain a copy of the License at
71ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski *
81ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
91ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski *
101ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * Unless required by applicable law or agreed to in writing, software
111ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
121ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * See the License for the specific language governing permissions and
141ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * limitations under the License.
151ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski */
161ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
171ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "ResourceUtils.h"
18ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
19ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include <sstream>
20ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
212eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski#include "android-base/stringprintf.h"
22ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include "androidfw/ResourceTypes.h"
23929d6517dfd338f0d481dbe6587643d5aef27ec6Adam Lesinski#include "androidfw/ResourceUtils.h"
24ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
25cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski#include "NameMangler.h"
26fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski#include "SdkConstants.h"
274670805ea441edb8b280f9312571e7799f1284cfAdam Lesinski#include "format/binary/ResourceTypeExtensions.h"
282eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski#include "text/Unicode.h"
292eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski#include "text/Utf8Iterator.h"
30a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski#include "util/Files.h"
311ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski#include "util/Util.h"
321ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
332eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinskiusing ::aapt::text::Utf8Iterator;
344670805ea441edb8b280f9312571e7799f1284cfAdam Lesinskiusing ::android::StringPiece;
354670805ea441edb8b280f9312571e7799f1284cfAdam Lesinskiusing ::android::StringPiece16;
362eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinskiusing ::android::base::StringPrintf;
37d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski
381ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskinamespace aapt {
391ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinskinamespace ResourceUtils {
401ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
41ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam LesinskiMaybe<ResourceName> ToResourceName(
42ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const android::ResTable::resource_name& name_in) {
43ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  ResourceName name_out;
44ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!name_in.package) {
45cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
46cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
47cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
48ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  name_out.package =
49ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      util::Utf16ToUtf8(StringPiece16(name_in.package, name_in.packageLen));
50cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
51cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const ResourceType* type;
52ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (name_in.type) {
53ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    type = ParseResourceType(
54ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        util::Utf16ToUtf8(StringPiece16(name_in.type, name_in.typeLen)));
55ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  } else if (name_in.type8) {
56ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    type = ParseResourceType(StringPiece(name_in.type8, name_in.typeLen));
57cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  } else {
58cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
59cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
60d0f116b619feede0cfdb647157ce5ab4d50a1c46Adam Lesinski
61cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (!type) {
62cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
63cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
64d0f116b619feede0cfdb647157ce5ab4d50a1c46Adam Lesinski
65ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  name_out.type = *type;
66d0f116b619feede0cfdb647157ce5ab4d50a1c46Adam Lesinski
67ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (name_in.name) {
68ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    name_out.entry =
69ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        util::Utf16ToUtf8(StringPiece16(name_in.name, name_in.nameLen));
70ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  } else if (name_in.name8) {
71d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski    name_out.entry.assign(name_in.name8, name_in.nameLen);
72cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  } else {
73cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
74cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
75ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  return name_out;
76d0f116b619feede0cfdb647157ce5ab4d50a1c46Adam Lesinski}
77d0f116b619feede0cfdb647157ce5ab4d50a1c46Adam Lesinski
78ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool ParseResourceName(const StringPiece& str, ResourceNameRef* out_ref,
79ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                       bool* out_private) {
80cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (str.empty()) {
81cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
82cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
83cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
84cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  size_t offset = 0;
85cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool priv = false;
86cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (str.data()[0] == '*') {
87cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    priv = true;
88cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    offset = 1;
89cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
90cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
91cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  StringPiece package;
92cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  StringPiece type;
93cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  StringPiece entry;
94929d6517dfd338f0d481dbe6587643d5aef27ec6Adam Lesinski  if (!android::ExtractResourceName(str.substr(offset, str.size() - offset), &package, &type,
95929d6517dfd338f0d481dbe6587643d5aef27ec6Adam Lesinski                                    &entry)) {
96cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
97cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
98cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
99ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const ResourceType* parsed_type = ParseResourceType(type);
100ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!parsed_type) {
101cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
102cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
1037298bc9c857541b444b2f1639dbed17599cbe5e9Adam Lesinski
104cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (entry.empty()) {
105cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
106cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
107cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
108ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (out_ref) {
109ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    out_ref->package = package;
110ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    out_ref->type = *parsed_type;
111ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    out_ref->entry = entry;
112cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
113cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
114ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (out_private) {
115ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    *out_private = priv;
116cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
117cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
1181ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
1191ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
120ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool ParseReference(const StringPiece& str, ResourceNameRef* out_ref,
121ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                    bool* out_create, bool* out_private) {
122ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  StringPiece trimmed_str(util::TrimWhitespace(str));
123ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (trimmed_str.empty()) {
124cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
125cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
12659e04c6f92da584b322c87072f18e6cab4de4c60Adam Lesinski
127cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool create = false;
128cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool priv = false;
129ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (trimmed_str.data()[0] == '@') {
130cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    size_t offset = 1;
131ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (trimmed_str.data()[1] == '+') {
132cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      create = true;
133cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      offset += 1;
134467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    }
135467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski
136cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    ResourceNameRef name;
137ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!ParseResourceName(
138ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski            trimmed_str.substr(offset, trimmed_str.size() - offset), &name,
139cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            &priv)) {
140cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
141467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    }
142467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski
143cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (create && priv) {
144cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
145467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    }
146467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski
147cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (create && name.type != ResourceType::kId) {
148cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
149467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    }
150467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski
151ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (out_ref) {
152ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      *out_ref = name;
153467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    }
154467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski
155ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (out_create) {
156ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      *out_create = create;
157467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski    }
158467f171315f9c2037fcd3eb5edcfabc40671bf7bAdam Lesinski
159ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (out_private) {
160ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      *out_private = priv;
1611ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
162cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return true;
163cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
164cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return false;
1651ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
1661ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
167ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool IsReference(const StringPiece& str) {
168ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  return ParseReference(str, nullptr, nullptr, nullptr);
1692ae4a877d1623f851040ce69239552c873f1abf0Adam Lesinski}
1702ae4a877d1623f851040ce69239552c873f1abf0Adam Lesinski
171ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool ParseAttributeReference(const StringPiece& str, ResourceNameRef* out_ref) {
172ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  StringPiece trimmed_str(util::TrimWhitespace(str));
173ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (trimmed_str.empty()) {
174cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
175cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
176cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
177ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (*trimmed_str.data() == '?') {
178cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    StringPiece package;
179cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    StringPiece type;
180cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    StringPiece entry;
181929d6517dfd338f0d481dbe6587643d5aef27ec6Adam Lesinski    if (!android::ExtractResourceName(trimmed_str.substr(1, trimmed_str.size() - 1), &package,
182929d6517dfd338f0d481dbe6587643d5aef27ec6Adam Lesinski                                      &type, &entry)) {
183cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
1841ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
1851ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
186cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (!type.empty() && type != "attr") {
187cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
1881ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
189cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
190cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (entry.empty()) {
191cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return false;
192cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
193cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
194ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (out_ref) {
195ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      out_ref->package = package;
196ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      out_ref->type = ResourceType::kAttr;
197ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      out_ref->entry = entry;
198cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
199cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return true;
200cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
201cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return false;
2021ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
2031ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
204ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool IsAttributeReference(const StringPiece& str) {
205ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  return ParseAttributeReference(str, nullptr);
2067298bc9c857541b444b2f1639dbed17599cbe5e9Adam Lesinski}
2077298bc9c857541b444b2f1639dbed17599cbe5e9Adam Lesinski
2081ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski/*
2091ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * Style parent's are a bit different. We accept the following formats:
2101ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski *
21152364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski * @[[*]package:][style/]<entry>
21224b8ff0faf7c59323d0171cdd825ca09e712aa1eAdam Lesinski * ?[[*]package:]style/<entry>
21324b8ff0faf7c59323d0171cdd825ca09e712aa1eAdam Lesinski * <[*]package>:[style/]<entry>
21424b8ff0faf7c59323d0171cdd825ca09e712aa1eAdam Lesinski * [[*]package:style/]<entry>
2151ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski */
216ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam LesinskiMaybe<Reference> ParseStyleParentReference(const StringPiece& str,
217ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                           std::string* out_error) {
218cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (str.empty()) {
219cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
220cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
221cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
222cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  StringPiece name = str;
223cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
224ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  bool has_leading_identifiers = false;
225ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  bool private_ref = false;
226cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
227cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Skip over these identifiers. A style's parent is a normal reference.
228cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (name.data()[0] == '@' || name.data()[0] == '?') {
229ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    has_leading_identifiers = true;
230cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    name = name.substr(1, name.size() - 1);
231cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
232cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
233cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (name.data()[0] == '*') {
234ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    private_ref = true;
235cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    name = name.substr(1, name.size() - 1);
236cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
237cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
238cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ResourceNameRef ref;
239cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ref.type = ResourceType::kStyle;
240cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
241ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  StringPiece type_str;
242929d6517dfd338f0d481dbe6587643d5aef27ec6Adam Lesinski  android::ExtractResourceName(name, &ref.package, &type_str, &ref.entry);
243ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!type_str.empty()) {
244cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // If we have a type, make sure it is a Style.
245ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const ResourceType* parsed_type = ParseResourceType(type_str);
246ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!parsed_type || *parsed_type != ResourceType::kStyle) {
247cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      std::stringstream err;
248ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      err << "invalid resource type '" << type_str << "' for parent of style";
249ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      *out_error = err.str();
250cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return {};
251cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
252cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
253cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
254ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!has_leading_identifiers && ref.package.empty() && !type_str.empty()) {
255cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    std::stringstream err;
256cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    err << "invalid parent reference '" << str << "'";
257ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    *out_error = err.str();
258cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
259cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
2601ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
261cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  Reference result(ref);
262ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  result.private_reference = private_ref;
263cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return result;
2641ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
2651ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
266ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam LesinskiMaybe<Reference> ParseXmlAttributeName(const StringPiece& str) {
267ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  StringPiece trimmed_str = util::TrimWhitespace(str);
268ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const char* start = trimmed_str.data();
269ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const char* const end = start + trimmed_str.size();
270cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const char* p = start;
271cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
272cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  Reference ref;
273cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (p != end && *p == '*') {
274ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    ref.private_reference = true;
275cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    start++;
276cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    p++;
277cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
278cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
279cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  StringPiece package;
280cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  StringPiece name;
281cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  while (p != end) {
282cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (*p == ':') {
283cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      package = StringPiece(start, p - start);
284cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      name = StringPiece(p + 1, end - (p + 1));
285cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      break;
286cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
287cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    p++;
288cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
289cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
290d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski  ref.name = ResourceName(package, ResourceType::kAttr, name.empty() ? trimmed_str : name);
291cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return Maybe<Reference>(std::move(ref));
2925eeaaddffd23d8d85aeb321e3ceea626e42cf9deAdam Lesinski}
2935eeaaddffd23d8d85aeb321e3ceea626e42cf9deAdam Lesinski
294ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistd::unique_ptr<Reference> TryParseReference(const StringPiece& str,
295ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                             bool* out_create) {
296cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ResourceNameRef ref;
297ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  bool private_ref = false;
298ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (ParseReference(str, &ref, out_create, &private_ref)) {
299cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
300ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value->private_reference = private_ref;
301cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return value;
302cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
303cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
304ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (ParseAttributeReference(str, &ref)) {
305ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (out_create) {
306ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      *out_create = false;
307cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
308cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
309cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
310cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return {};
3111ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
3121ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
313bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinskistd::unique_ptr<Item> TryParseNullOrEmpty(const StringPiece& str) {
314bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski  const StringPiece trimmed_str(util::TrimWhitespace(str));
315ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (trimmed_str == "@null") {
316bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski    return MakeNull();
317ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  } else if (trimmed_str == "@empty") {
318bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski    return MakeEmpty();
319cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
320bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski  return {};
321bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski}
322bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski
323bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinskistd::unique_ptr<Reference> MakeNull() {
324bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski  // TYPE_NULL with data set to 0 is interpreted by the runtime as an error.
325bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski  // Instead we set the data type to TYPE_REFERENCE with a value of 0.
326bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski  return util::make_unique<Reference>();
327bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski}
328bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski
329bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinskistd::unique_ptr<BinaryPrimitive> MakeEmpty() {
330bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski  return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_NULL,
331bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski                                            android::Res_value::DATA_NULL_EMPTY);
3321ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
3331ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
334ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistd::unique_ptr<BinaryPrimitive> TryParseEnumSymbol(const Attribute* enum_attr,
335d0f116b619feede0cfdb647157ce5ab4d50a1c46Adam Lesinski                                                    const StringPiece& str) {
336ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  StringPiece trimmed_str(util::TrimWhitespace(str));
337ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (const Attribute::Symbol& symbol : enum_attr->symbols) {
338cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Enum symbols are stored as @package:id/symbol resources,
339cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // so we need to match against the 'entry' part of the identifier.
340ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const ResourceName& enum_symbol_resource_name = symbol.symbol.name.value();
341ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (trimmed_str == enum_symbol_resource_name.entry) {
342cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      android::Res_value value = {};
343cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      value.dataType = android::Res_value::TYPE_INT_DEC;
344cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      value.data = symbol.value;
345cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return util::make_unique<BinaryPrimitive>(value);
346cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
347cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
348cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return {};
3491ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
3501ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
351ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistd::unique_ptr<BinaryPrimitive> TryParseFlagSymbol(const Attribute* flag_attr,
352d0f116b619feede0cfdb647157ce5ab4d50a1c46Adam Lesinski                                                    const StringPiece& str) {
353cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  android::Res_value flags = {};
354cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  flags.dataType = android::Res_value::TYPE_INT_HEX;
355cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  flags.data = 0u;
356cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
357ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (util::TrimWhitespace(str).empty()) {
358cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Empty string is a valid flag (0).
359cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return util::make_unique<BinaryPrimitive>(flags);
360cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
361cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
362ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (StringPiece part : util::Tokenize(str, '|')) {
363ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    StringPiece trimmed_part = util::TrimWhitespace(part);
36452364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski
365ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    bool flag_set = false;
366ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    for (const Attribute::Symbol& symbol : flag_attr->symbols) {
367cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Flag symbols are stored as @package:id/symbol resources,
368cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // so we need to match against the 'entry' part of the identifier.
369ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      const ResourceName& flag_symbol_resource_name =
370ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          symbol.symbol.name.value();
371ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (trimmed_part == flag_symbol_resource_name.entry) {
372cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        flags.data |= symbol.value;
373ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        flag_set = true;
374cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        break;
375cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
37652364f7ae31716d7827ea8f8566f4a28bd30a921Adam Lesinski    }
3771ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
378ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (!flag_set) {
379cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return {};
3801ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    }
381cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
382cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return util::make_unique<BinaryPrimitive>(flags);
3831ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
3841ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
385ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic uint32_t ParseHex(char c, bool* out_error) {
386cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (c >= '0' && c <= '9') {
387cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return c - '0';
388cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  } else if (c >= 'a' && c <= 'f') {
389cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return c - 'a' + 0xa;
390cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  } else if (c >= 'A' && c <= 'F') {
391cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return c - 'A' + 0xa;
392cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  } else {
393ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    *out_error = true;
394cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return 0xffffffffu;
395cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
3961ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
3971ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
398ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistd::unique_ptr<BinaryPrimitive> TryParseColor(const StringPiece& str) {
399ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  StringPiece color_str(util::TrimWhitespace(str));
400ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const char* start = color_str.data();
401ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const size_t len = color_str.size();
402cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (len == 0 || start[0] != '#') {
403cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
404cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
405cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
406cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  android::Res_value value = {};
407cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool error = false;
408cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (len == 4) {
409cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
410cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    value.data = 0xff000000u;
411ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[1], &error) << 20;
412ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[1], &error) << 16;
413ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[2], &error) << 12;
414ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[2], &error) << 8;
415ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[3], &error) << 4;
416ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[3], &error);
417cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  } else if (len == 5) {
418cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4;
419ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[1], &error) << 28;
420ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[1], &error) << 24;
421ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[2], &error) << 20;
422ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[2], &error) << 16;
423ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[3], &error) << 12;
424ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[3], &error) << 8;
425ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[4], &error) << 4;
426ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[4], &error);
427cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  } else if (len == 7) {
428cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8;
429cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    value.data = 0xff000000u;
430ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[1], &error) << 20;
431ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[2], &error) << 16;
432ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[3], &error) << 12;
433ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[4], &error) << 8;
434ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[5], &error) << 4;
435ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[6], &error);
436cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  } else if (len == 9) {
437cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8;
438ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[1], &error) << 28;
439ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[2], &error) << 24;
440ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[3], &error) << 20;
441ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[4], &error) << 16;
442ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[5], &error) << 12;
443ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[6], &error) << 8;
444ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[7], &error) << 4;
445ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    value.data |= ParseHex(start[8], &error);
446cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  } else {
447cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
448cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
449cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return error ? std::unique_ptr<BinaryPrimitive>()
450cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski               : util::make_unique<BinaryPrimitive>(value);
4511ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
4521ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
453ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam LesinskiMaybe<bool> ParseBool(const StringPiece& str) {
454ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  StringPiece trimmed_str(util::TrimWhitespace(str));
455ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (trimmed_str == "true" || trimmed_str == "TRUE" || trimmed_str == "True") {
456cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return Maybe<bool>(true);
457ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  } else if (trimmed_str == "false" || trimmed_str == "FALSE" ||
458ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski             trimmed_str == "False") {
459cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return Maybe<bool>(false);
460cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
461cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return {};
46236c73a595910e96f3552f938eeb81d46356067a1Adam Lesinski}
46336c73a595910e96f3552f938eeb81d46356067a1Adam Lesinski
464ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam LesinskiMaybe<uint32_t> ParseInt(const StringPiece& str) {
465ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::u16string str16 = util::Utf8ToUtf16(str);
466cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  android::Res_value value;
467cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
468cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return value.data;
469cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
470cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return {};
471b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski}
472b23f1e077b02a1d62bcf5e34655e8dc979e124faAdam Lesinski
473ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam LesinskiMaybe<ResourceId> ParseResourceId(const StringPiece& str) {
474ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  StringPiece trimmed_str(util::TrimWhitespace(str));
475cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
476ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
477cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  android::Res_value value;
478cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
479cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (value.dataType == android::Res_value::TYPE_INT_HEX) {
480cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      ResourceId id(value.data);
481ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski      if (id.is_valid_dynamic()) {
482cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        return id;
483cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
484cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
485cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
486cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return {};
487bf0bd0f9ac1ffa0231cff0f6591dede48b3c6d52Adam Lesinski}
488bf0bd0f9ac1ffa0231cff0f6591dede48b3c6d52Adam Lesinski
489ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam LesinskiMaybe<int> ParseSdkVersion(const StringPiece& str) {
490ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  StringPiece trimmed_str(util::TrimWhitespace(str));
491cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
492ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
493cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  android::Res_value value;
494cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
495cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return static_cast<int>(value.data);
496cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
497cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
498cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Try parsing the code name.
499ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::pair<StringPiece, int> entry = GetDevelopmentSdkCodeNameAndVersion();
500ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (entry.first == trimmed_str) {
501cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return entry.second;
502cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
503cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return {};
504fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski}
505fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski
506ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistd::unique_ptr<BinaryPrimitive> TryParseBool(const StringPiece& str) {
507ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (Maybe<bool> maybe_result = ParseBool(str)) {
5085924d8c9ab7bd8614e8bd99864903ce9d50f3bf7Adam Lesinski    const uint32_t data = maybe_result.value() ? 0xffffffffu : 0u;
5095924d8c9ab7bd8614e8bd99864903ce9d50f3bf7Adam Lesinski    return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_BOOLEAN, data);
510cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
511cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return {};
5121ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
5131ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
5145924d8c9ab7bd8614e8bd99864903ce9d50f3bf7Adam Lesinskistd::unique_ptr<BinaryPrimitive> MakeBool(bool val) {
5155924d8c9ab7bd8614e8bd99864903ce9d50f3bf7Adam Lesinski  return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_BOOLEAN,
5165924d8c9ab7bd8614e8bd99864903ce9d50f3bf7Adam Lesinski                                            val ? 0xffffffffu : 0u);
5175924d8c9ab7bd8614e8bd99864903ce9d50f3bf7Adam Lesinski}
5185924d8c9ab7bd8614e8bd99864903ce9d50f3bf7Adam Lesinski
519ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistd::unique_ptr<BinaryPrimitive> TryParseInt(const StringPiece& str) {
5208a3bffea4926aa691ea1fbc0e7b67ed954e83d2cAdam Lesinski  std::u16string str16 = util::Utf8ToUtf16(util::TrimWhitespace(str));
521cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  android::Res_value value;
522cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (!android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
523cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
524cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
525cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return util::make_unique<BinaryPrimitive>(value);
5261ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
5271ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
528d05b9130fdbff8062084b566a380c5b058273d75Shane Farmerstd::unique_ptr<BinaryPrimitive> MakeInt(uint32_t val) {
529d05b9130fdbff8062084b566a380c5b058273d75Shane Farmer  return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_DEC, val);
530d05b9130fdbff8062084b566a380c5b058273d75Shane Farmer}
531d05b9130fdbff8062084b566a380c5b058273d75Shane Farmer
532ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistd::unique_ptr<BinaryPrimitive> TryParseFloat(const StringPiece& str) {
5338a3bffea4926aa691ea1fbc0e7b67ed954e83d2cAdam Lesinski  std::u16string str16 = util::Utf8ToUtf16(util::TrimWhitespace(str));
534cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  android::Res_value value;
535cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (!android::ResTable::stringToFloat(str16.data(), str16.size(), &value)) {
536cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
537cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
538cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return util::make_unique<BinaryPrimitive>(value);
5391ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
5401ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
541ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskiuint32_t AndroidTypeToAttributeTypeMask(uint16_t type) {
542cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  switch (type) {
5431ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    case android::Res_value::TYPE_NULL:
5441ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    case android::Res_value::TYPE_REFERENCE:
5451ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    case android::Res_value::TYPE_ATTRIBUTE:
5461ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    case android::Res_value::TYPE_DYNAMIC_REFERENCE:
547b5dc4bd49a036e3403ca17e961d2c8e13e038295Adam Lesinski    case android::Res_value::TYPE_DYNAMIC_ATTRIBUTE:
548cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return android::ResTable_map::TYPE_REFERENCE;
5491ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
5501ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    case android::Res_value::TYPE_STRING:
551cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return android::ResTable_map::TYPE_STRING;
5521ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
5531ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    case android::Res_value::TYPE_FLOAT:
554cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return android::ResTable_map::TYPE_FLOAT;
5551ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
5561ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    case android::Res_value::TYPE_DIMENSION:
557cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return android::ResTable_map::TYPE_DIMENSION;
5581ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
5591ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    case android::Res_value::TYPE_FRACTION:
560cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return android::ResTable_map::TYPE_FRACTION;
5611ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
5621ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    case android::Res_value::TYPE_INT_DEC:
5631ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    case android::Res_value::TYPE_INT_HEX:
564cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return android::ResTable_map::TYPE_INTEGER |
565cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski             android::ResTable_map::TYPE_ENUM |
566cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski             android::ResTable_map::TYPE_FLAGS;
5671ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
5681ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    case android::Res_value::TYPE_INT_BOOLEAN:
569cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return android::ResTable_map::TYPE_BOOLEAN;
5701ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
5711ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    case android::Res_value::TYPE_INT_COLOR_ARGB8:
5721ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    case android::Res_value::TYPE_INT_COLOR_RGB8:
5731ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    case android::Res_value::TYPE_INT_COLOR_ARGB4:
5741ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    case android::Res_value::TYPE_INT_COLOR_RGB4:
575cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return android::ResTable_map::TYPE_COLOR;
5761ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
5771ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski    default:
578cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return 0;
579cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  };
5801ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
5811ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
582ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistd::unique_ptr<Item> TryParseItemForAttribute(
583ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const StringPiece& value, uint32_t type_mask,
584ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const std::function<void(const ResourceName&)>& on_create_reference) {
585bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski  using android::ResTable_map;
586bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski
587bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski  auto null_or_empty = TryParseNullOrEmpty(value);
588ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (null_or_empty) {
589bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski    return null_or_empty;
590cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
591cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
592cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool create = false;
593bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski  auto reference = TryParseReference(value, &create);
594cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (reference) {
595ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (create && on_create_reference) {
596ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      on_create_reference(reference->name.value());
597cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
598cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return std::move(reference);
599cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
600cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
601bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski  if (type_mask & ResTable_map::TYPE_COLOR) {
602cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Try parsing this as a color.
603bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski    auto color = TryParseColor(value);
604cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (color) {
605cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return std::move(color);
606cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
607cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
608cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
609bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski  if (type_mask & ResTable_map::TYPE_BOOLEAN) {
610cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Try parsing this as a boolean.
611bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski    auto boolean = TryParseBool(value);
612cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (boolean) {
613cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return std::move(boolean);
614cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
615cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
616cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
617bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski  if (type_mask & ResTable_map::TYPE_INTEGER) {
618cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Try parsing this as an integer.
619bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski    auto integer = TryParseInt(value);
620cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (integer) {
621cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return std::move(integer);
622cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
623cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
624cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
625bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski  const uint32_t float_mask =
626bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski      ResTable_map::TYPE_FLOAT | ResTable_map::TYPE_DIMENSION | ResTable_map::TYPE_FRACTION;
627ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (type_mask & float_mask) {
628cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Try parsing this as a float.
629bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski    auto floating_point = TryParseFloat(value);
630ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (floating_point) {
631bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski      if (type_mask & AndroidTypeToAttributeTypeMask(floating_point->value.dataType)) {
632ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        return std::move(floating_point);
633cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
634cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
635cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
636cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return {};
6371ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
6381ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
6391ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski/**
6401ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * We successively try to parse the string as a resource type that the Attribute
6411ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski * allows.
6421ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski */
643ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistd::unique_ptr<Item> TryParseItemForAttribute(
644cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    const StringPiece& str, const Attribute* attr,
645ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const std::function<void(const ResourceName&)>& on_create_reference) {
646bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski  using android::ResTable_map;
647bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski
648ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const uint32_t type_mask = attr->type_mask;
649bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski  auto value = TryParseItemForAttribute(str, type_mask, on_create_reference);
650cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (value) {
651cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return value;
652cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
653cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
654bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski  if (type_mask & ResTable_map::TYPE_ENUM) {
655cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Try parsing this as an enum.
656bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski    auto enum_value = TryParseEnumSymbol(attr, str);
657ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (enum_value) {
658ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      return std::move(enum_value);
659cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
660cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
661cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
662bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski  if (type_mask & ResTable_map::TYPE_FLAGS) {
663cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Try parsing this as a flag.
664bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski    auto flag_value = TryParseFlagSymbol(attr, str);
665ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (flag_value) {
666ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      return std::move(flag_value);
667cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
668cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
669cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return {};
6701ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski}
6711ab598f46c3ff520a67f9d80194847741f3467abAdam Lesinski
672bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinskistd::string BuildResourceFileName(const ResourceFile& res_file, const NameMangler* mangler) {
673cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::stringstream out;
674ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out << "res/" << res_file.name.type;
675ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (res_file.config != ConfigDescription{}) {
676ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    out << "-" << res_file.config;
677cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
678cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  out << "/";
679cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
680ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (mangler && mangler->ShouldMangle(res_file.name.package)) {
681ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    out << NameMangler::MangleEntry(res_file.name.package, res_file.name.entry);
682cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  } else {
683ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    out << res_file.name.entry;
684cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
685ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  out << file::GetExtension(res_file.source.path);
686cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return out.str();
687a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski}
688a6fe345be955368a13aea76aefb4db821aad11dfAdam Lesinski
689d0f492db038c6210c1138865d816bfb134376538Adam Lesinskistd::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const ConfigDescription& config,
690d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                          const android::ResStringPool& src_pool,
691d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                          const android::Res_value& res_value,
692d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                          StringPool* dst_pool) {
693d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  if (type == ResourceType::kId) {
694d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    return util::make_unique<Id>();
695d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
696d0f492db038c6210c1138865d816bfb134376538Adam Lesinski
697d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  const uint32_t data = util::DeviceToHost32(res_value.data);
698d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  switch (res_value.dataType) {
699d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    case android::Res_value::TYPE_STRING: {
700d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      const std::string str = util::GetString(src_pool, data);
701d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      const android::ResStringPool_span* spans = src_pool.styleAt(data);
702d0f492db038c6210c1138865d816bfb134376538Adam Lesinski
703d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      // Check if the string has a valid style associated with it.
704d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      if (spans != nullptr && spans->name.index != android::ResStringPool_span::END) {
705d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        StyleString style_str = {str};
706d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        while (spans->name.index != android::ResStringPool_span::END) {
707d0f492db038c6210c1138865d816bfb134376538Adam Lesinski          style_str.spans.push_back(Span{util::GetString(src_pool, spans->name.index),
708d0f492db038c6210c1138865d816bfb134376538Adam Lesinski                                         spans->firstChar, spans->lastChar});
709d0f492db038c6210c1138865d816bfb134376538Adam Lesinski          spans++;
710d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        }
711d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        return util::make_unique<StyledString>(dst_pool->MakeRef(
712060b53d0287f9e685fb5b49b52a864ef85315a22Adam Lesinski            style_str, StringPool::Context(StringPool::Context::kNormalPriority, config)));
713d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      } else {
714d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        if (type != ResourceType::kString && util::StartsWith(str, "res/")) {
715d0f492db038c6210c1138865d816bfb134376538Adam Lesinski          // This must be a FileReference.
716e59f0d80ec19249f72c07ae191ad673d040443e3Adam Lesinski          std::unique_ptr<FileReference> file_ref =
717e59f0d80ec19249f72c07ae191ad673d040443e3Adam Lesinski              util::make_unique<FileReference>(dst_pool->MakeRef(
718e59f0d80ec19249f72c07ae191ad673d040443e3Adam Lesinski                  str, StringPool::Context(StringPool::Context::kHighPriority, config)));
71970fdf76c52224217179529be868c47b3194f1551Pierre Lecesne          if (type == ResourceType::kRaw) {
72070fdf76c52224217179529be868c47b3194f1551Pierre Lecesne            file_ref->type = ResourceFile::Type::kUnknown;
72170fdf76c52224217179529be868c47b3194f1551Pierre Lecesne          } else if (util::EndsWith(*file_ref->path, ".xml")) {
722e59f0d80ec19249f72c07ae191ad673d040443e3Adam Lesinski            file_ref->type = ResourceFile::Type::kBinaryXml;
723e59f0d80ec19249f72c07ae191ad673d040443e3Adam Lesinski          } else if (util::EndsWith(*file_ref->path, ".png")) {
724e59f0d80ec19249f72c07ae191ad673d040443e3Adam Lesinski            file_ref->type = ResourceFile::Type::kPng;
725e59f0d80ec19249f72c07ae191ad673d040443e3Adam Lesinski          }
726e59f0d80ec19249f72c07ae191ad673d040443e3Adam Lesinski          return std::move(file_ref);
727d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        }
728d0f492db038c6210c1138865d816bfb134376538Adam Lesinski
729d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        // There are no styles associated with this string, so treat it as a simple string.
730d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        return util::make_unique<String>(dst_pool->MakeRef(str, StringPool::Context(config)));
731d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      }
732d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    } break;
733d0f492db038c6210c1138865d816bfb134376538Adam Lesinski
734d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    case android::Res_value::TYPE_REFERENCE:
735d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    case android::Res_value::TYPE_ATTRIBUTE:
736d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    case android::Res_value::TYPE_DYNAMIC_REFERENCE:
737d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    case android::Res_value::TYPE_DYNAMIC_ATTRIBUTE: {
738d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      Reference::Type ref_type = Reference::Type::kResource;
739d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      if (res_value.dataType == android::Res_value::TYPE_ATTRIBUTE ||
740d0f492db038c6210c1138865d816bfb134376538Adam Lesinski          res_value.dataType == android::Res_value::TYPE_DYNAMIC_ATTRIBUTE) {
741d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        ref_type = Reference::Type::kAttribute;
742d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      }
743d0f492db038c6210c1138865d816bfb134376538Adam Lesinski
744bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski      if (data == 0u) {
745d0f492db038c6210c1138865d816bfb134376538Adam Lesinski        // A reference of 0, must be the magic @null reference.
746bab4ef56d7803f3a50ccfaca2729509338fcbb23Adam Lesinski        return util::make_unique<Reference>();
747d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      }
748d0f492db038c6210c1138865d816bfb134376538Adam Lesinski
749d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      // This is a normal reference.
750d0f492db038c6210c1138865d816bfb134376538Adam Lesinski      return util::make_unique<Reference>(data, ref_type);
751d0f492db038c6210c1138865d816bfb134376538Adam Lesinski    } break;
752d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  }
753d0f492db038c6210c1138865d816bfb134376538Adam Lesinski
754d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  // Treat this as a raw binary primitive.
755d0f492db038c6210c1138865d816bfb134376538Adam Lesinski  return util::make_unique<BinaryPrimitive>(res_value);
756d0f492db038c6210c1138865d816bfb134376538Adam Lesinski}
757d0f492db038c6210c1138865d816bfb134376538Adam Lesinski
7582eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski// Converts the codepoint to UTF-8 and appends it to the string.
7592eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinskistatic bool AppendCodepointToUtf8String(char32_t codepoint, std::string* output) {
7602eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  ssize_t len = utf32_to_utf8_length(&codepoint, 1);
7612eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  if (len < 0) {
7622eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    return false;
7632eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  }
7642eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
7652eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  const size_t start_append_pos = output->size();
7662eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
7672eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  // Make room for the next character.
7682eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  output->resize(output->size() + len);
7692eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
7702eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  char* dst = &*(output->begin() + start_append_pos);
7712eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  utf32_to_utf8(&codepoint, 1, dst, len + 1);
7722eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  return true;
7732eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski}
7742eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
7752eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski// Reads up to 4 UTF-8 characters that represent a Unicode escape sequence, and appends the
7762eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski// Unicode codepoint represented by the escape sequence to the string.
7772eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinskistatic bool AppendUnicodeEscapeSequence(Utf8Iterator* iter, std::string* output) {
7782eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  char32_t code = 0;
7792eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  for (size_t i = 0; i < 4 && iter->HasNext(); i++) {
7802eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    char32_t codepoint = iter->Next();
7812eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    char32_t a;
7822eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    if (codepoint >= U'0' && codepoint <= U'9') {
7832eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski      a = codepoint - U'0';
7842eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    } else if (codepoint >= U'a' && codepoint <= U'f') {
7852eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski      a = codepoint - U'a' + 10;
7862eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    } else if (codepoint >= U'A' && codepoint <= U'F') {
7872eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski      a = codepoint - U'A' + 10;
7882eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    } else {
7892eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski      return {};
7902eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    }
7912eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    code = (code << 4) | a;
7922eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  }
7932eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  return AppendCodepointToUtf8String(code, output);
7942eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski}
7952eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
7962eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam LesinskiStringBuilder::StringBuilder(bool preserve_spaces)
7972eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    : preserve_spaces_(preserve_spaces), quote_(preserve_spaces) {
7982eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski}
7992eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
8002eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam LesinskiStringBuilder& StringBuilder::AppendText(const std::string& text) {
8012eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  if (!error_.empty()) {
8022eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    return *this;
8032eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  }
8042eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
8052eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  const size_t previous_len = xml_string_.text.size();
8062eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  Utf8Iterator iter(text);
8072eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  while (iter.HasNext()) {
8082eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    char32_t codepoint = iter.Next();
8099beaa9cfe3bd8c7c59eed053ff6ca18951c34a86Ryan Mitchell    if (!quote_ && iswspace(codepoint)) {
8102eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski      if (!last_codepoint_was_space_) {
8112eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski        // Emit a space if it's the first.
8122eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski        xml_string_.text += ' ';
8132eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski        last_codepoint_was_space_ = true;
8142eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski      }
8152eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
8162eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski      // Keep eating spaces.
8172eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski      continue;
8182eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    }
8192eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
8202eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    // This is not a space.
8212eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    last_codepoint_was_space_ = false;
8222eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
8232eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    if (codepoint == U'\\') {
8242eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski      if (iter.HasNext()) {
8252eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski        codepoint = iter.Next();
8262eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski        switch (codepoint) {
8272eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski          case U't':
8282eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski            xml_string_.text += '\t';
8292eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski            break;
8302eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
8312eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski          case U'n':
8322eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski            xml_string_.text += '\n';
8332eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski            break;
8342eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
8352eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski          case U'#':
8362eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski          case U'@':
8372eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski          case U'?':
8382eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski          case U'"':
8392eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski          case U'\'':
8402eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski          case U'\\':
8412eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski            xml_string_.text += static_cast<char>(codepoint);
8422eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski            break;
8432eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
8442eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski          case U'u':
8452eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski            if (!AppendUnicodeEscapeSequence(&iter, &xml_string_.text)) {
8462eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski              error_ =
8472eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski                  StringPrintf("invalid unicode escape sequence in string\n\"%s\"", text.c_str());
8482eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski              return *this;
8492eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski            }
8502eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski            break;
8512eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
8522eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski          default:
8532eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski            // Ignore the escape character and just include the codepoint.
8542eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski            AppendCodepointToUtf8String(codepoint, &xml_string_.text);
8552eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski            break;
8562eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski        }
8572eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski      }
8582eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    } else if (!preserve_spaces_ && codepoint == U'"') {
8592eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski      // Only toggle the quote state when we are not preserving spaces.
8602eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski      quote_ = !quote_;
8612eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
8622eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    } else if (!quote_ && codepoint == U'\'') {
8632eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski      // This should be escaped.
8642eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski      error_ = StringPrintf("unescaped apostrophe in string\n\"%s\"", text.c_str());
8652eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski      return *this;
8662eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
8672eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    } else {
8682eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski      AppendCodepointToUtf8String(codepoint, &xml_string_.text);
8692eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    }
8702eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  }
8712eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
8722eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  // Accumulate the added string's UTF-16 length.
8732eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  const uint8_t* utf8_data = reinterpret_cast<const uint8_t*>(xml_string_.text.c_str());
8742eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  const size_t utf8_length = xml_string_.text.size();
8752eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  ssize_t len = utf8_to_utf16_length(utf8_data + previous_len, utf8_length - previous_len);
8762eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  if (len < 0) {
8772eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    error_ = StringPrintf("invalid unicode code point in string\n\"%s\"", utf8_data + previous_len);
8782eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    return *this;
8792eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  }
8802eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
8812eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  utf16_len_ += static_cast<uint32_t>(len);
8822eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  return *this;
8832eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski}
8842eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
8852eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam LesinskiStringBuilder::SpanHandle StringBuilder::StartSpan(const std::string& name) {
8862eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  if (!error_.empty()) {
8872eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    return 0u;
8882eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  }
8892eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
8902eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  // When we start a span, all state associated with whitespace truncation and quotation is ended.
8912eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  ResetTextState();
8922eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  Span span;
8932eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  span.name = name;
8942eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  span.first_char = span.last_char = utf16_len_;
8952eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  xml_string_.spans.push_back(std::move(span));
8962eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  return xml_string_.spans.size() - 1;
8972eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski}
8982eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
8992eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinskivoid StringBuilder::EndSpan(SpanHandle handle) {
9002eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  if (!error_.empty()) {
9012eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    return;
9022eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  }
9032eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
9042eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  // When we end a span, all state associated with whitespace truncation and quotation is ended.
9052eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  ResetTextState();
9062eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  xml_string_.spans[handle].last_char = utf16_len_ - 1u;
9072eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski}
9082eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
9092eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam LesinskiStringBuilder::UntranslatableHandle StringBuilder::StartUntranslatable() {
9102eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  if (!error_.empty()) {
9112eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    return 0u;
9122eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  }
9132eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
9142eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  UntranslatableSection section;
9152eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  section.start = section.end = xml_string_.text.size();
9162eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  xml_string_.untranslatable_sections.push_back(section);
9172eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  return xml_string_.untranslatable_sections.size() - 1;
9182eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski}
9192eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
9202eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinskivoid StringBuilder::EndUntranslatable(UntranslatableHandle handle) {
9212eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  if (!error_.empty()) {
9222eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski    return;
9232eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  }
9242eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  xml_string_.untranslatable_sections[handle].end = xml_string_.text.size();
9252eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski}
9262eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
9272eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam LesinskiFlattenedXmlString StringBuilder::GetFlattenedString() const {
9282eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  return xml_string_;
9292eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski}
9302eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
9312eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinskistd::string StringBuilder::to_string() const {
9322eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  return xml_string_.text;
9332eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski}
9342eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
9352eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam LesinskiStringBuilder::operator bool() const {
9362eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  return error_.empty();
9372eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski}
9382eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
9392eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinskistd::string StringBuilder::GetError() const {
9402eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  return error_;
9412eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski}
9422eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
9432eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinskivoid StringBuilder::ResetTextState() {
9442eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  quote_ = preserve_spaces_;
9452eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski  last_codepoint_was_space_ = false;
9462eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski}
9472eed52ecc0c2fa3e96530e4b5556eaa82f7c2dfcAdam Lesinski
948cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}  // namespace ResourceUtils
949cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}  // namespace aapt
950