24aad163bc
Previously, you could only reference namespace prefixes in attribute names: <View xmlns:appcompat="http://schemas.android.com/apk/res/android.support.v7.appcompat" appcompat:name="hey" ... Now you can also reference them in resource names within an attribute value: ... android:text="@appcompat:string/confirm" ... Which will be treated as "@android.support.v7.appcompat:string/confirm". Change-Id: Ib076e867a990c80cf877a704eb77cd1ef0b23b52
260 lines
7.6 KiB
C++
260 lines
7.6 KiB
C++
/*
|
|
* Copyright (C) 2015 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "BinaryXmlPullParser.h"
|
|
#include "Maybe.h"
|
|
#include "Util.h"
|
|
|
|
#include <androidfw/ResourceTypes.h>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace aapt {
|
|
|
|
static XmlPullParser::Event codeToEvent(android::ResXMLParser::event_code_t code) {
|
|
switch (code) {
|
|
case android::ResXMLParser::START_DOCUMENT:
|
|
return XmlPullParser::Event::kStartDocument;
|
|
case android::ResXMLParser::END_DOCUMENT:
|
|
return XmlPullParser::Event::kEndDocument;
|
|
case android::ResXMLParser::START_NAMESPACE:
|
|
return XmlPullParser::Event::kStartNamespace;
|
|
case android::ResXMLParser::END_NAMESPACE:
|
|
return XmlPullParser::Event::kEndNamespace;
|
|
case android::ResXMLParser::START_TAG:
|
|
return XmlPullParser::Event::kStartElement;
|
|
case android::ResXMLParser::END_TAG:
|
|
return XmlPullParser::Event::kEndElement;
|
|
case android::ResXMLParser::TEXT:
|
|
return XmlPullParser::Event::kText;
|
|
default:
|
|
break;
|
|
}
|
|
return XmlPullParser::Event::kBadDocument;
|
|
}
|
|
|
|
BinaryXmlPullParser::BinaryXmlPullParser(const std::shared_ptr<android::ResXMLTree>& parser)
|
|
: mParser(parser), mEvent(Event::kStartDocument), mHasComment(false), sEmpty(), sEmpty8(),
|
|
mDepth(0) {
|
|
}
|
|
|
|
XmlPullParser::Event BinaryXmlPullParser::next() {
|
|
mStr1.clear();
|
|
mStr2.clear();
|
|
mAttributes.clear();
|
|
|
|
android::ResXMLParser::event_code_t code;
|
|
if (mHasComment) {
|
|
mHasComment = false;
|
|
code = mParser->getEventType();
|
|
} else {
|
|
code = mParser->next();
|
|
if (code != android::ResXMLParser::BAD_DOCUMENT) {
|
|
size_t len;
|
|
const char16_t* comment = mParser->getComment(&len);
|
|
if (comment) {
|
|
mHasComment = true;
|
|
mStr1.assign(comment, len);
|
|
return XmlPullParser::Event::kComment;
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t len;
|
|
const char16_t* data;
|
|
mEvent = codeToEvent(code);
|
|
switch (mEvent) {
|
|
case Event::kStartNamespace:
|
|
case Event::kEndNamespace: {
|
|
data = mParser->getNamespacePrefix(&len);
|
|
if (data) {
|
|
mStr1.assign(data, len);
|
|
} else {
|
|
mStr1.clear();
|
|
}
|
|
data = mParser->getNamespaceUri(&len);
|
|
if (data) {
|
|
mStr2.assign(data, len);
|
|
} else {
|
|
mStr2.clear();
|
|
}
|
|
|
|
Maybe<std::u16string> result = util::extractPackageFromNamespace(mStr2);
|
|
if (result) {
|
|
if (mEvent == Event::kStartNamespace) {
|
|
mPackageAliases.emplace_back(mStr1, result.value());
|
|
} else {
|
|
assert(mPackageAliases.back().second == result.value());
|
|
mPackageAliases.pop_back();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Event::kStartElement:
|
|
copyAttributes();
|
|
// fallthrough
|
|
|
|
case Event::kEndElement:
|
|
data = mParser->getElementNamespace(&len);
|
|
if (data) {
|
|
mStr1.assign(data, len);
|
|
} else {
|
|
mStr1.clear();
|
|
}
|
|
data = mParser->getElementName(&len);
|
|
if (data) {
|
|
mStr2.assign(data, len);
|
|
} else {
|
|
mStr2.clear();
|
|
}
|
|
break;
|
|
|
|
case Event::kText:
|
|
data = mParser->getText(&len);
|
|
if (data) {
|
|
mStr1.assign(data, len);
|
|
} else {
|
|
mStr1.clear();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return mEvent;
|
|
}
|
|
|
|
XmlPullParser::Event BinaryXmlPullParser::getEvent() const {
|
|
if (mHasComment) {
|
|
return XmlPullParser::Event::kComment;
|
|
}
|
|
return mEvent;
|
|
}
|
|
|
|
const std::string& BinaryXmlPullParser::getLastError() const {
|
|
return sEmpty8;
|
|
}
|
|
|
|
const std::u16string& BinaryXmlPullParser::getComment() const {
|
|
if (mHasComment) {
|
|
return mStr1;
|
|
}
|
|
return sEmpty;
|
|
}
|
|
|
|
size_t BinaryXmlPullParser::getLineNumber() const {
|
|
return mParser->getLineNumber();
|
|
}
|
|
|
|
size_t BinaryXmlPullParser::getDepth() const {
|
|
return mDepth;
|
|
}
|
|
|
|
const std::u16string& BinaryXmlPullParser::getText() const {
|
|
if (!mHasComment && mEvent == XmlPullParser::Event::kText) {
|
|
return mStr1;
|
|
}
|
|
return sEmpty;
|
|
}
|
|
|
|
const std::u16string& BinaryXmlPullParser::getNamespacePrefix() const {
|
|
if (!mHasComment && (mEvent == XmlPullParser::Event::kStartNamespace ||
|
|
mEvent == XmlPullParser::Event::kEndNamespace)) {
|
|
return mStr1;
|
|
}
|
|
return sEmpty;
|
|
}
|
|
|
|
const std::u16string& BinaryXmlPullParser::getNamespaceUri() const {
|
|
if (!mHasComment && (mEvent == XmlPullParser::Event::kStartNamespace ||
|
|
mEvent == XmlPullParser::Event::kEndNamespace)) {
|
|
return mStr2;
|
|
}
|
|
return sEmpty;
|
|
}
|
|
|
|
bool BinaryXmlPullParser::applyPackageAlias(std::u16string* package,
|
|
const std::u16string& defaultPackage) const {
|
|
const auto endIter = mPackageAliases.rend();
|
|
for (auto iter = mPackageAliases.rbegin(); iter != endIter; ++iter) {
|
|
if (iter->first == *package) {
|
|
if (iter->second.empty()) {
|
|
*package = defaultPackage;
|
|
} else {
|
|
*package = iter->second;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const std::u16string& BinaryXmlPullParser::getElementNamespace() const {
|
|
if (!mHasComment && (mEvent == XmlPullParser::Event::kStartElement ||
|
|
mEvent == XmlPullParser::Event::kEndElement)) {
|
|
return mStr1;
|
|
}
|
|
return sEmpty;
|
|
}
|
|
|
|
const std::u16string& BinaryXmlPullParser::getElementName() const {
|
|
if (!mHasComment && (mEvent == XmlPullParser::Event::kStartElement ||
|
|
mEvent == XmlPullParser::Event::kEndElement)) {
|
|
return mStr2;
|
|
}
|
|
return sEmpty;
|
|
}
|
|
|
|
size_t BinaryXmlPullParser::getAttributeCount() const {
|
|
return mAttributes.size();
|
|
}
|
|
|
|
XmlPullParser::const_iterator BinaryXmlPullParser::beginAttributes() const {
|
|
return mAttributes.begin();
|
|
}
|
|
|
|
XmlPullParser::const_iterator BinaryXmlPullParser::endAttributes() const {
|
|
return mAttributes.end();
|
|
}
|
|
|
|
void BinaryXmlPullParser::copyAttributes() {
|
|
const size_t attrCount = mParser->getAttributeCount();
|
|
if (attrCount > 0) {
|
|
mAttributes.reserve(attrCount);
|
|
for (size_t i = 0; i < attrCount; i++) {
|
|
XmlPullParser::Attribute attr;
|
|
size_t len;
|
|
const char16_t* str = mParser->getAttributeNamespace(i, &len);
|
|
if (str) {
|
|
attr.namespaceUri.assign(str, len);
|
|
}
|
|
str = mParser->getAttributeName(i, &len);
|
|
if (str) {
|
|
attr.name.assign(str, len);
|
|
}
|
|
str = mParser->getAttributeStringValue(i, &len);
|
|
if (str) {
|
|
attr.value.assign(str, len);
|
|
}
|
|
mAttributes.push_back(std::move(attr));
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace aapt
|