aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party/java
diff options
context:
space:
mode:
authorGravatar Andrew Pellegrini <apell@google.com>2017-01-12 20:11:35 +0000
committerGravatar Marcel Hlopko <hlopko@google.com>2017-01-13 10:57:30 +0000
commitb7a731189eee8a57c6aee289f7b1bdae91b32d99 (patch)
tree83aaca4fbb1e00d705a9bee83f2e2014d9a9e5bd /third_party/java
parent748bbef68037ae57f973bdfe4405b3ccc3791574 (diff)
Fix ResourceUsageModel analysis of <style> resource bugs by overriding this behavior in the extended class owned by ResourceUsageAnalyzer. This makes the fix available to both Blaze and Bazel, since Bazel uses the AOSP released version of the com.tools.android.lint library, which contains ResourceUsageModel, instead of the source version available in [].
This change is based on the upstream patch [] and will be removed when the patch is included in the packaged version released with Bazel. -- PiperOrigin-RevId: 144355049 MOS_MIGRATED_REVID=144355049
Diffstat (limited to 'third_party/java')
-rw-r--r--third_party/java/aosp_gradle_core/java/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java243
1 files changed, 228 insertions, 15 deletions
diff --git a/third_party/java/aosp_gradle_core/java/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java b/third_party/java/aosp_gradle_core/java/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java
index 3b730456fb..325e47d38f 100644
--- a/third_party/java/aosp_gradle_core/java/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java
+++ b/third_party/java/aosp_gradle_core/java/com/android/build/gradle/tasks/ResourceUsageAnalyzer.java
@@ -15,14 +15,26 @@
*/
package com.android.build.gradle.tasks;
+import static com.android.SdkConstants.ANDROID_STYLE_RESOURCE_PREFIX;
+import static com.android.SdkConstants.ANDROID_URI;
+import static com.android.SdkConstants.ATTR_DISCARD;
+import static com.android.SdkConstants.ATTR_KEEP;
import static com.android.SdkConstants.ATTR_NAME;
+import static com.android.SdkConstants.ATTR_PARENT;
+import static com.android.SdkConstants.ATTR_SHRINK_MODE;
import static com.android.SdkConstants.ATTR_TYPE;
import static com.android.SdkConstants.DOT_CLASS;
import static com.android.SdkConstants.DOT_JAR;
import static com.android.SdkConstants.DOT_XML;
import static com.android.SdkConstants.FD_RES_VALUES;
+import static com.android.SdkConstants.PREFIX_ANDROID;
+import static com.android.SdkConstants.PREFIX_RESOURCE_REF;
+import static com.android.SdkConstants.PREFIX_THEME_REF;
+import static com.android.SdkConstants.STYLE_RESOURCE_PREFIX;
import static com.android.SdkConstants.TAG_ITEM;
import static com.android.SdkConstants.TAG_RESOURCES;
+import static com.android.SdkConstants.TAG_STYLE;
+import static com.android.SdkConstants.TOOLS_URI;
import static com.android.utils.SdkUtils.endsWithIgnoreCase;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.objectweb.asm.ClassReader.SKIP_DEBUG;
@@ -39,6 +51,7 @@ import com.android.resources.ResourceType;
import com.android.tools.lint.checks.ResourceUsageModel;
import com.android.tools.lint.checks.ResourceUsageModel.Resource;
import com.android.tools.lint.checks.StringFormatDetector;
+import com.android.tools.lint.detector.api.LintUtils;
import com.android.utils.AsmUtils;
import com.android.utils.Pair;
import com.android.utils.XmlUtils;
@@ -1173,27 +1186,227 @@ public class ResourceUsageAnalyzer {
return null;
}
+ @Nullable
+ Resource getResourceFromUrl(@NonNull String possibleUrlReference) {
+ ResourceUrl url = ResourceUrl.parse(possibleUrlReference);
+ if (url != null && !url.framework) {
+ return addResource(url.type, LintUtils.getFieldName(url.name), null);
+ }
+ return null;
+ }
+
+ /**
+ * Records resource declarations and usages within an XML resource file
+ *
+ * @param folderType the type of resource file
+ * @param node the root node to start the recursive search from
+ * @param from a referencing context, if any.
+ */
+ // Override from parent ResourceUsageModel to fix <style> analysis bugs.
+ // TODO(apell): remove this override once the packaged version of ResourceUsageModel includes
+ // these fixes. See inline comments for location of fixes.
@Override
- public void recordResourceReferences(ResourceFolderType folderType, Node node, Resource from) {
- super.recordResourceReferences(folderType, node, from);
- // The parent class does not consider id declarations in xml files to also be uses, which is
- // wrong. Fix that behavior here by adding a reference to any id declarations.
- if (from != null && node.getNodeType() == Node.ELEMENT_NODE) {
- NamedNodeMap attributes = ((Element) node).getAttributes();
- for (int i = 0; i < attributes.getLength(); i++) {
- Attr attr = (Attr) attributes.item(i);
- if (attr.getValue().startsWith(SdkConstants.PREFIX_RESOURCE_REF)
- && SdkConstants.ATTR_ID.equals(attr.getLocalName())
- && SdkConstants.ANDROID_URI.equals(attr.getNamespaceURI())) {
- ResourceUrl url = ResourceUrl.parse(attr.getValue());
- if (url != null) {
- Resource resource = getResource(url.type, url.name);
- if (resource != null) {
+ public void recordResourceReferences(
+ @NonNull ResourceFolderType folderType, @NonNull Node node, @Nullable Resource from) {
+ short nodeType = node.getNodeType();
+ if (nodeType == Node.ELEMENT_NODE) {
+ Element element = (Element) node;
+ if (from != null) {
+ NamedNodeMap attributes = element.getAttributes();
+ for (int i = 0, n = attributes.getLength(); i < n; i++) {
+ Attr attr = (Attr) attributes.item(i);
+
+ // Ignore tools: namespace attributes, unless it's
+ // a keep attribute
+ if (TOOLS_URI.equals(attr.getNamespaceURI())) {
+ recordToolsAttributes(attr);
+ // Skip all other tools: attributes
+ continue;
+ }
+
+ String value = attr.getValue();
+ if (!(value.startsWith(PREFIX_RESOURCE_REF) || value.startsWith(PREFIX_THEME_REF))) {
+ continue;
+ }
+ ResourceUrl url = ResourceUrl.parse(value);
+ if (url != null && !url.framework) {
+ Resource resource;
+ if (url.create) {
+ resource = declareResource(url.type, url.name, attr);
+ // The parent class does not consider id declarations in xml files to also be uses,
+ // which causes problems with the public.xml file. Modify that behavior here by
+ // adding a reference to any id declarations.
+ // Fix (see method comment): retain this modification when removing override.
from.addReference(resource);
+ } else {
+ resource = addResource(url.type, url.name, null);
+ from.addReference(resource);
+ }
+ } else if (value.startsWith("@{")) {
+ // Data binding expression: there could be multiple references here
+ int length = value.length();
+ int index = 2; // skip @{
+ while (true) {
+ index = value.indexOf('@', index);
+ if (index == -1) {
+ break;
+ }
+ // Find end of (potential) resource URL: first non resource URL character
+ int end = index + 1;
+ while (end < length) {
+ char c = value.charAt(end);
+ if (!(Character.isJavaIdentifierPart(c)
+ || c == '_'
+ || c == '.'
+ || c == '/'
+ || c == '+')) {
+ break;
+ }
+ end++;
+ }
+ url = ResourceUrl.parse(value.substring(index, end));
+ if (url != null && !url.framework) {
+ Resource resource;
+ if (url.create) {
+ resource = declareResource(url.type, url.name, attr);
+ } else {
+ resource = addResource(url.type, url.name, null);
+ }
+ from.addReference(resource);
+ }
+
+ index = end;
+ }
+ }
+ }
+
+ // Android Wear. We *could* limit ourselves to only doing this in files
+ // referenced from a manifest meta-data element, e.g.
+ // <meta-data android:name="com.google.android.wearable.beta.app"
+ // android:resource="@xml/wearable_app_desc"/>
+ // but given that that property has "beta" in the name, it seems likely
+ // to change and therefore hardcoding it for that key risks breakage
+ // in the future.
+ if ("rawPathResId".equals(element.getTagName())) {
+ StringBuilder sb = new StringBuilder();
+ NodeList children = node.getChildNodes();
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node child = children.item(i);
+ if (child.getNodeType() == Element.TEXT_NODE
+ || child.getNodeType() == Element.CDATA_SECTION_NODE) {
+ sb.append(child.getNodeValue());
}
}
+ if (sb.length() > 0) {
+ Resource resource = getResource(ResourceType.RAW, sb.toString().trim());
+ from.addReference(resource);
+ }
}
+ } else {
+ // Look for keep attributes everywhere else since they don't require a source
+ recordToolsAttributes(element.getAttributeNodeNS(TOOLS_URI, ATTR_KEEP));
+ recordToolsAttributes(element.getAttributeNodeNS(TOOLS_URI, ATTR_DISCARD));
+ recordToolsAttributes(element.getAttributeNodeNS(TOOLS_URI, ATTR_SHRINK_MODE));
}
+
+ if (folderType == ResourceFolderType.VALUES) {
+
+ Resource definition = null;
+ ResourceType type = getResourceType(element);
+ if (type != null) {
+ String name = getFieldName(element);
+ if (type == ResourceType.PUBLIC) {
+ String typeName = element.getAttribute(ATTR_TYPE);
+ if (!typeName.isEmpty()) {
+ type = ResourceType.getEnum(typeName);
+ if (type != null) {
+ definition = declareResource(type, name, element);
+ definition.setPublic(true);
+ }
+ }
+ } else {
+ definition = declareResource(type, name, element);
+ }
+ }
+ if (definition != null) {
+ from = definition;
+ }
+
+ String tagName = element.getTagName();
+ if (TAG_STYLE.equals(tagName)) {
+ if (element.hasAttribute(ATTR_PARENT)) {
+ String parent = element.getAttribute(ATTR_PARENT);
+ // Fix (see method comment): don't treat empty parent tag the same as extending
+ // builtin theme.
+ if (parent.startsWith(ANDROID_STYLE_RESOURCE_PREFIX)
+ || parent.startsWith(PREFIX_ANDROID)) {
+ // Extending a builtin theme: treat these as used
+ if (definition != null) {
+ markReachable(definition);
+ }
+ } else if (!parent.isEmpty()) {
+ String parentStyle = parent;
+ if (!parentStyle.startsWith(STYLE_RESOURCE_PREFIX)) {
+ parentStyle = STYLE_RESOURCE_PREFIX + parentStyle;
+ }
+ Resource ps = getResourceFromUrl(LintUtils.getFieldName(parentStyle));
+ if (ps != null && definition != null) {
+ // Fix (see method comment): don't create parent to child reference.
+ definition.addReference(ps);
+ }
+ }
+ } else {
+ // Implicit parent styles by name
+ String name = getFieldName(element);
+ while (true) {
+ int index = name.lastIndexOf('_');
+ if (index != -1) {
+ name = name.substring(0, index);
+ Resource ps =
+ getResourceFromUrl(STYLE_RESOURCE_PREFIX + LintUtils.getFieldName(name));
+ if (ps != null && definition != null) {
+ // Fix (see method comment): don't create parent to child reference.
+ definition.addReference(ps);
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ if (TAG_ITEM.equals(tagName)) {
+ // In style? If so the name: attribute can be a reference
+ if (element.getParentNode() != null
+ && element.getParentNode().getNodeName().equals(TAG_STYLE)) {
+ String name = element.getAttributeNS(ANDROID_URI, ATTR_NAME);
+ if (!name.isEmpty() && !name.startsWith("android:")) {
+ Resource resource = getResource(ResourceType.ATTR, name);
+ if (definition == null) {
+ Element style = (Element) element.getParentNode();
+ definition = getResource(style);
+ if (definition != null) {
+ from = definition;
+ definition.addReference(resource);
+ }
+ }
+ }
+ }
+ }
+ }
+ } else if (nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE) {
+ String text = node.getNodeValue().trim();
+ // Why are we calling getFieldName here? That doesn't make sense! for styles I guess
+ Resource textResource = getResourceFromUrl(LintUtils.getFieldName(text));
+ if (textResource != null && from != null) {
+ from.addReference(textResource);
+ }
+ }
+
+ NodeList children = node.getChildNodes();
+ for (int i = 0, n = children.getLength(); i < n; i++) {
+ Node child = children.item(i);
+ recordResourceReferences(folderType, child, from);
}
}
}