1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "BindingXmlPullParser.h" 18#include "Util.h" 19 20#include <iostream> 21#include <sstream> 22#include <string> 23#include <vector> 24 25namespace aapt { 26 27constexpr const char16_t* kBindingNamespaceUri = u"http://schemas.android.com/apk/binding"; 28constexpr const char16_t* kAndroidNamespaceUri = u"http://schemas.android.com/apk/res/android"; 29constexpr const char16_t* kVariableTagName = u"variable"; 30constexpr const char* kBindingTagPrefix = "android:binding_"; 31 32BindingXmlPullParser::BindingXmlPullParser(const std::shared_ptr<XmlPullParser>& parser) : 33 mParser(parser), mOverride(false), mNextTagId(0) { 34} 35 36bool BindingXmlPullParser::readVariableDeclaration() { 37 VarDecl var; 38 39 const auto endAttrIter = mParser->endAttributes(); 40 for (auto attrIter = mParser->beginAttributes(); attrIter != endAttrIter; ++attrIter) { 41 if (!attrIter->namespaceUri.empty()) { 42 continue; 43 } 44 45 if (attrIter->name == u"name") { 46 var.name = util::utf16ToUtf8(attrIter->value); 47 } else if (attrIter->name == u"type") { 48 var.type = util::utf16ToUtf8(attrIter->value); 49 } 50 } 51 52 XmlPullParser::skipCurrentElement(mParser.get()); 53 54 if (var.name.empty()) { 55 mLastError = "variable declaration missing name"; 56 return false; 57 } 58 59 if (var.type.empty()) { 60 mLastError = "variable declaration missing type"; 61 return false; 62 } 63 64 mVarDecls.push_back(std::move(var)); 65 return true; 66} 67 68bool BindingXmlPullParser::readExpressions() { 69 mOverride = true; 70 std::vector<XmlPullParser::Attribute> expressions; 71 std::string idValue; 72 73 const auto endAttrIter = mParser->endAttributes(); 74 for (auto attr = mParser->beginAttributes(); attr != endAttrIter; ++attr) { 75 if (attr->namespaceUri == kAndroidNamespaceUri && attr->name == u"id") { 76 idValue = util::utf16ToUtf8(attr->value); 77 } else { 78 StringPiece16 value = util::trimWhitespace(attr->value); 79 if (util::stringStartsWith<char16_t>(value, u"@{") && 80 util::stringEndsWith<char16_t>(value, u"}")) { 81 // This is attribute's value is an expression of the form 82 // @{expression}. We need to capture the expression inside. 83 expressions.push_back(XmlPullParser::Attribute{ 84 attr->namespaceUri, 85 attr->name, 86 value.substr(2, value.size() - 3).toString() 87 }); 88 } else { 89 // This is a normal attribute, use as is. 90 mAttributes.emplace_back(*attr); 91 } 92 } 93 } 94 95 // Check if we have any expressions. 96 if (!expressions.empty()) { 97 // We have expressions, so let's assign the target a tag number 98 // and add it to our targets list. 99 int32_t targetId = mNextTagId++; 100 mTargets.push_back(Target{ 101 util::utf16ToUtf8(mParser->getElementName()), 102 idValue, 103 targetId, 104 std::move(expressions) 105 }); 106 107 std::stringstream numGen; 108 numGen << kBindingTagPrefix << targetId; 109 mAttributes.push_back(XmlPullParser::Attribute{ 110 std::u16string(kAndroidNamespaceUri), 111 std::u16string(u"tag"), 112 util::utf8ToUtf16(numGen.str()) 113 }); 114 } 115 return true; 116} 117 118XmlPullParser::Event BindingXmlPullParser::next() { 119 // Clear old state in preparation for the next event. 120 mOverride = false; 121 mAttributes.clear(); 122 123 while (true) { 124 Event event = mParser->next(); 125 if (event == Event::kStartElement) { 126 if (mParser->getElementNamespace().empty() && 127 mParser->getElementName() == kVariableTagName) { 128 // This is a variable tag. Record data from it, and 129 // then discard the entire element. 130 if (!readVariableDeclaration()) { 131 // mLastError is set, so getEvent will return kBadDocument. 132 return getEvent(); 133 } 134 continue; 135 } else { 136 // Check for expressions of the form @{} in attribute text. 137 const auto endAttrIter = mParser->endAttributes(); 138 for (auto attr = mParser->beginAttributes(); attr != endAttrIter; ++attr) { 139 StringPiece16 value = util::trimWhitespace(attr->value); 140 if (util::stringStartsWith<char16_t>(value, u"@{") && 141 util::stringEndsWith<char16_t>(value, u"}")) { 142 if (!readExpressions()) { 143 return getEvent(); 144 } 145 break; 146 } 147 } 148 } 149 } else if (event == Event::kStartNamespace || event == Event::kEndNamespace) { 150 if (mParser->getNamespaceUri() == kBindingNamespaceUri) { 151 // Skip binding namespace tags. 152 continue; 153 } 154 } 155 return event; 156 } 157 return Event::kBadDocument; 158} 159 160bool BindingXmlPullParser::writeToFile(std::ostream& out) const { 161 out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; 162 out << "<Layout directory=\"\" layout=\"\" layoutId=\"\">\n"; 163 164 // Write the variables. 165 out << " <Variables>\n"; 166 for (const VarDecl& v : mVarDecls) { 167 out << " <entries name=\"" << v.name << "\" type=\"" << v.type << "\"/>\n"; 168 } 169 out << " </Variables>\n"; 170 171 // Write the imports. 172 173 std::stringstream tagGen; 174 175 // Write the targets. 176 out << " <Targets>\n"; 177 for (const Target& t : mTargets) { 178 tagGen.str({}); 179 tagGen << kBindingTagPrefix << t.tagId; 180 out << " <Target boundClass=\"" << t.className << "\" id=\"" << t.id 181 << "\" tag=\"" << tagGen.str() << "\">\n"; 182 out << " <Expressions>\n"; 183 for (const XmlPullParser::Attribute& a : t.expressions) { 184 out << " <Expression attribute=\"" << a.namespaceUri << ":" << a.name 185 << "\" text=\"" << a.value << "\"/>\n"; 186 } 187 out << " </Expressions>\n"; 188 out << " </Target>\n"; 189 } 190 out << " </Targets>\n"; 191 192 out << "</Layout>\n"; 193 return bool(out); 194} 195 196XmlPullParser::const_iterator BindingXmlPullParser::beginAttributes() const { 197 if (mOverride) { 198 return mAttributes.begin(); 199 } 200 return mParser->beginAttributes(); 201} 202 203XmlPullParser::const_iterator BindingXmlPullParser::endAttributes() const { 204 if (mOverride) { 205 return mAttributes.end(); 206 } 207 return mParser->endAttributes(); 208} 209 210size_t BindingXmlPullParser::getAttributeCount() const { 211 if (mOverride) { 212 return mAttributes.size(); 213 } 214 return mParser->getAttributeCount(); 215} 216 217XmlPullParser::Event BindingXmlPullParser::getEvent() const { 218 if (!mLastError.empty()) { 219 return Event::kBadDocument; 220 } 221 return mParser->getEvent(); 222} 223 224const std::string& BindingXmlPullParser::getLastError() const { 225 if (!mLastError.empty()) { 226 return mLastError; 227 } 228 return mParser->getLastError(); 229} 230 231const std::u16string& BindingXmlPullParser::getComment() const { 232 return mParser->getComment(); 233} 234 235size_t BindingXmlPullParser::getLineNumber() const { 236 return mParser->getLineNumber(); 237} 238 239size_t BindingXmlPullParser::getDepth() const { 240 return mParser->getDepth(); 241} 242 243const std::u16string& BindingXmlPullParser::getText() const { 244 return mParser->getText(); 245} 246 247const std::u16string& BindingXmlPullParser::getNamespacePrefix() const { 248 return mParser->getNamespacePrefix(); 249} 250 251const std::u16string& BindingXmlPullParser::getNamespaceUri() const { 252 return mParser->getNamespaceUri(); 253} 254 255bool BindingXmlPullParser::applyPackageAlias(std::u16string* package, 256 const std::u16string& defaultPackage) const { 257 return mParser->applyPackageAlias(package, defaultPackage); 258} 259 260const std::u16string& BindingXmlPullParser::getElementNamespace() const { 261 return mParser->getElementNamespace(); 262} 263 264const std::u16string& BindingXmlPullParser::getElementName() const { 265 return mParser->getElementName(); 266} 267 268} // namespace aapt 269