aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2012-07-17 13:57:56 -0700
committerandroid code review <noreply-gerritcodereview@google.com>2012-07-17 13:57:56 -0700
commit87895eaf760548f45c319ea31abb55d1ba2f5b26 (patch)
tree9babffb5a4af6e57aa0064bacf0ef8e2df570a2b
parent805a2139aa441afd94bb82bf0f753b1489686524 (diff)
parent2f4fa1c6142bd7b5bdf8f449e3f58529a9ecd2d1 (diff)
downloadsdk-87895eaf760548f45c319ea31abb55d1ba2f5b26.zip
sdk-87895eaf760548f45c319ea31abb55d1ba2f5b26.tar.gz
sdk-87895eaf760548f45c319ea31abb55d1ba2f5b26.tar.bz2
Merge "Detect incorrect state lists"
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java54
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/StateListDetectorTest.java11
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/drawable/states3.xml49
3 files changed, 101 insertions, 13 deletions
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java
index d7f0d99..c08b0c6 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/StateListDetector.java
@@ -23,6 +23,7 @@ import com.android.resources.ResourceFolderType;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LintUtils;
+import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.ResourceXmlDetector;
import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
@@ -34,7 +35,11 @@ import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
* Checks for unreachable states in an Android state list definition
@@ -53,6 +58,8 @@ public class StateListDetector extends ResourceXmlDetector {
StateListDetector.class,
Scope.RESOURCE_FILE_SCOPE);
+ private static final String STATE_PREFIX = "state_"; //$NON-NLS-1$
+
/** Constructs a new {@link StateListDetector} */
public StateListDetector() {
}
@@ -77,18 +84,23 @@ public class StateListDetector extends ResourceXmlDetector {
Element root = document.getDocumentElement();
if (root != null && root.getTagName().equals("selector")) { //$NON-NLS-1$
List<Element> children = LintUtils.getChildren(root);
- for (int i = 0; i < children.size() - 1; i++) {
+ Map<Element, Set<String>> states =
+ new HashMap<Element, Set<String>>(children.size());
+
+ for (int i = 0; i < children.size(); i++) {
Element child = children.get(i);
- boolean hasState = false;
NamedNodeMap attributes = child.getAttributes();
+ Set<String> stateNames = new HashSet<String>(attributes.getLength());
+ states.put(child, stateNames);
+
for (int j = 0; j < attributes.getLength(); j++) {
Attr attribute = (Attr) attributes.item(j);
- if (attribute.getLocalName() == null) {
+ String name = attribute.getLocalName();
+ if (name == null) {
continue;
}
- if (attribute.getLocalName().startsWith("state_")) {
- hasState = true;
- break;
+ if (name.startsWith(STATE_PREFIX)) {
+ stateNames.add(name + '=' + attribute.getValue());
} else {
String namespaceUri = attribute.getNamespaceURI();
if (namespaceUri != null && namespaceUri.length() > 0 &&
@@ -97,14 +109,34 @@ public class StateListDetector extends ResourceXmlDetector {
// This could be a state, see
// http://code.google.com/p/android/issues/detail?id=22339
// so don't flag this one.
- hasState = true;
+ stateNames.add(attribute.getName() + '=' + attribute.getValue());
}
}
}
- if (!hasState) {
- context.report(ISSUE, child, context.getLocation(child),
- String.format("No android:state_ attribute found on <item> %1$d, later states not reachable",
- i), null);
+ }
+
+ // See if for each state, any subsequent state fully contains all the same
+ // state requirements
+
+ for (int i = 0; i < children.size() - 1; i++) {
+ Element prev = children.get(i);
+ Set<String> prevStates = states.get(prev);
+ assert prevStates != null : prev;
+ for (int j = i + 1; j < children.size(); j++) {
+ Element current = children.get(j);
+ Set<String> currentStates = states.get(current);
+ assert currentStates != null : current;
+ if (currentStates.containsAll(prevStates)) {
+ Location location = context.getLocation(current);
+ Location secondary = context.getLocation(prev);
+ secondary.setMessage("Earlier item which masks item");
+ location.setSecondary(secondary);
+ context.report(ISSUE, current, location, String.format(
+ "This item is unreachable because a previous item (item #%1$d) is a more general match than this one",
+ i + 1), null);
+ // Don't keep reporting errors for all the remaining cases in this file
+ return;
+ }
}
}
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/StateListDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/StateListDetectorTest.java
index b0994fe..9176773 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/StateListDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/StateListDetectorTest.java
@@ -27,8 +27,8 @@ public class StateListDetectorTest extends AbstractCheckTest {
public void testStates() throws Exception {
assertEquals(
- "states.xml:2: Warning: No android:state_ attribute found on <item> 0, " +
- "later states not reachable",
+ "drawable/states.xml:3: Warning: This item is unreachable because a previous item (item #1) is a more general match than this one\n" +
+ "=> drawable/states.xml:2: Earlier item which masks item",
lintProject("res/drawable/states.xml"));
}
@@ -37,4 +37,11 @@ public class StateListDetectorTest extends AbstractCheckTest {
"No warnings.",
lintProject("res/drawable/states2.xml"));
}
+
+ public void testStates3() throws Exception {
+ assertEquals(
+ "drawable/states3.xml:24: Warning: This item is unreachable because a previous item (item #1) is a more general match than this one\n" +
+ "=> drawable/states3.xml:18: Earlier item which masks item",
+ lintProject("res/drawable/states3.xml"));
+ }
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/drawable/states3.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/drawable/states3.xml
new file mode 100644
index 0000000..c3fc533
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/drawable/states3.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_checked="false" android:state_window_focused="false"
+ android:drawable="@drawable/btn_star_big_off" />
+ <item android:state_checked="true" android:state_window_focused="false"
+ android:drawable="@drawable/btn_star_big_on" />
+ <item android:state_checked="true" android:state_window_focused="false"
+ android:state_enabled="false" android:drawable="@drawable/btn_star_big_on_disable" />
+ <item android:state_checked="false" android:state_window_focused="false"
+ android:state_enabled="false" android:drawable="@drawable/btn_star_big_off_disable" />
+
+ <item android:state_checked="true" android:state_pressed="true"
+ android:drawable="@drawable/btn_star_big_on_pressed" />
+ <item android:state_checked="false" android:state_pressed="true"
+ android:drawable="@drawable/btn_star_big_off_pressed" />
+
+ <item android:state_checked="true" android:state_focused="true"
+ android:drawable="@drawable/btn_star_big_on_selected" />
+ <item android:state_checked="false" android:state_focused="true"
+ android:drawable="@drawable/btn_star_big_off_selected" />
+
+ <item android:state_checked="true" android:state_focused="true" android:state_enabled="false"
+ android:drawable="@drawable/btn_star_big_on_disable_focused" />
+ <item android:state_checked="true" android:state_focused="false" android:state_enabled="false"
+ android:drawable="@drawable/btn_star_big_on_disable" />
+
+ <item android:state_checked="false" android:state_focused="true" android:state_enabled="false"
+ android:drawable="@drawable/btn_star_big_off_disable_focused" />
+ <item android:state_checked="false" android:state_focused="false" android:state_enabled="false"
+ android:drawable="@drawable/btn_star_big_off_disable" />
+
+ <item android:state_checked="false" android:drawable="@drawable/btn_star_big_off" />
+ <item android:state_checked="true" android:drawable="@drawable/btn_star_big_on" />
+</selector> \ No newline at end of file