aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2011-12-19 17:06:27 -0800
committerTor Norbye <tnorbye@google.com>2011-12-19 17:10:11 -0800
commit76d1bf760b373427be686b7d925b9b35abb526d2 (patch)
tree0221917b61cf8adfcc4435a78bec47cfac54691c
parent3d73211da767ae07698d12db1f6d6f8a0b0ed7bd (diff)
downloadsdk-76d1bf760b373427be686b7d925b9b35abb526d2.zip
sdk-76d1bf760b373427be686b7d925b9b35abb526d2.tar.gz
sdk-76d1bf760b373427be686b7d925b9b35abb526d2.tar.bz2
Fix Lint false positive for mixed XML/PNG Bitmap resource
This changeset fixes this issue: http://code.google.com/p/android/issues/detail?id=23214 The icon detector makes sure that if an icon appears in a given -dpi folder, it appears in all the other -dpi folders as well (except for -nodpi). However, in the set comparison it would use the full filename (including file extension), which is not correct since it's okay to have an .9.png in one folder and a .gif in another (and as reported in this bug, an .xml file). This changeset also associates a file location with folder warnings for a missing density folder (it uses the res folder), which made various unit tests need updates since the results sort differently. Change-Id: I7f09772f3a54683a9d0c5c5d93ae2499707f533a
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/client/api/DefaultSdkInfo.java2
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java15
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/IconDetector.java93
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/IconDetectorTest.java39
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/detector/api/LintUtilsTest.java6
5 files changed, 141 insertions, 14 deletions
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/client/api/DefaultSdkInfo.java b/lint/libs/lint_api/src/com/android/tools/lint/client/api/DefaultSdkInfo.java
index 7ca3fab..0392a6f 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/client/api/DefaultSdkInfo.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/client/api/DefaultSdkInfo.java
@@ -179,7 +179,7 @@ class DefaultSdkInfo extends SdkInfo {
PARENTS.put("MultiAutoCompleteTextView", //$NON-NLS-1$
"AutoCompleteTextView"); //$NON-NLS-1$
- assert PARENTS.size() == CLASS_COUNT;
+ assert PARENTS.size() == CLASS_COUNT : PARENTS.size();
/*
// Check that all widgets lead to the root view
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java
index 186a5f3..4dbcbb3 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintUtils.java
@@ -115,6 +115,21 @@ public class LintUtils {
}
/**
+ * Returns the basename of the given filename, unless it's a dot-file such as ".svn".
+ *
+ * @param fileName the file name to extract the basename from
+ * @return the basename (the filename without the file extension)
+ */
+ public static String getBaseName(String fileName) {
+ int extension = fileName.indexOf('.');
+ if (extension > 0) {
+ return fileName.substring(0, extension);
+ } else {
+ return fileName;
+ }
+ }
+
+ /**
* Returns the children elements of the given node
*
* @param node the parent node
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/IconDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/IconDetector.java
index 5f7adcb..56941bb 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/IconDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/IconDetector.java
@@ -44,7 +44,6 @@ import com.android.tools.lint.detector.api.Scope;
import com.android.tools.lint.detector.api.Severity;
import com.android.tools.lint.detector.api.Speed;
import com.android.tools.lint.detector.api.XmlContext;
-import com.google.common.collect.Sets;
import com.google.common.io.Files;
import org.w3c.dom.Element;
@@ -300,7 +299,7 @@ public class IconDetector extends Detector implements Detector.XmlScanner {
Set<String> names = new HashSet<String>(files.length);
for (File f : files) {
String name = f.getName();
- if (hasBitmapExtension(name)) {
+ if (isDrawableFile(name)) {
names.add(f.getName());
}
}
@@ -325,9 +324,10 @@ public class IconDetector extends Detector implements Detector.XmlScanner {
}
}
- private static boolean hasBitmapExtension(String name) {
+ private static boolean isDrawableFile(String name) {
// endsWith(name, DOT_PNG) is also true for endsWith(name, DOT_9PNG)
- return endsWith(name, DOT_PNG)|| endsWith(name, DOT_JPG) || endsWith(name, DOT_GIF);
+ return endsWith(name, DOT_PNG)|| endsWith(name, DOT_JPG) || endsWith(name, DOT_GIF) ||
+ endsWith(name, DOT_XML);
}
// This method looks for duplicates in the assets. This uses two pieces of information
@@ -740,7 +740,7 @@ public class IconDetector extends Detector implements Detector.XmlScanner {
if (missing.size() > 0 ) {
context.report(
ICON_MISSING_FOLDER,
- null /* location */,
+ Location.create(res),
String.format("Missing density variation folders in %1$s: %2$s",
context.getProject().getDisplayPath(res),
LintUtils.formatList(missing, missing.size())),
@@ -764,7 +764,7 @@ public class IconDetector extends Detector implements Detector.XmlScanner {
String folderName = folder.getName();
if (!isNoDpiFolder(folder)) {
assert DENSITY_PATTERN.matcher(folderName).matches();
- Set<String> overlap = Sets.intersection(noDpiNames, entry.getValue());
+ Set<String> overlap = nameIntersection(noDpiNames, entry.getValue());
inBoth.addAll(overlap);
for (String name : overlap) {
files.add(new File(folder, name));
@@ -811,8 +811,10 @@ public class IconDetector extends Detector implements Detector.XmlScanner {
}
Set<String> names = entry.getValue();
if (names.size() != allNames.size()) {
- List<String> delta =
- new ArrayList<String>(Sets.difference(allNames, names));
+ List<String> delta = new ArrayList<String>(nameDifferences(allNames, names));
+ if (delta.size() == 0) {
+ continue;
+ }
Collections.sort(delta);
String foundIn = "";
if (delta.size() == 1) {
@@ -842,6 +844,81 @@ public class IconDetector extends Detector implements Detector.XmlScanner {
}
}
+ /**
+ * Compute the difference in names between a and b. This is not just
+ * Sets.difference(a, b) because we want to make the comparisons <b>without
+ * file extensions</b> and return the result <b>with</b>..
+ */
+ private Set<String> nameDifferences(Set<String> a, Set<String> b) {
+ Set<String> names1 = new HashSet<String>(a.size());
+ for (String s : a) {
+ names1.add(LintUtils.getBaseName(s));
+ }
+ Set<String> names2 = new HashSet<String>(b.size());
+ for (String s : b) {
+ names2.add(LintUtils.getBaseName(s));
+ }
+
+ names1.removeAll(names2);
+
+ if (names1.size() > 0) {
+ // Map filenames back to original filenames with extensions
+ Set<String> result = new HashSet<String>(names1.size());
+ for (String s : a) {
+ if (names1.contains(LintUtils.getBaseName(s))) {
+ result.add(s);
+ }
+ }
+ for (String s : b) {
+ if (names1.contains(LintUtils.getBaseName(s))) {
+ result.add(s);
+ }
+ }
+
+ return result;
+ }
+
+ return Collections.emptySet();
+ }
+
+ /**
+ * Compute the intersection in names between a and b. This is not just
+ * Sets.intersection(a, b) because we want to make the comparisons <b>without
+ * file extensions</b> and return the result <b>with</b>.
+ */
+ private Set<String> nameIntersection(Set<String> a, Set<String> b) {
+ Set<String> names1 = new HashSet<String>(a.size());
+ for (String s : a) {
+ names1.add(LintUtils.getBaseName(s));
+ }
+ Set<String> names2 = new HashSet<String>(b.size());
+ for (String s : b) {
+ names2.add(LintUtils.getBaseName(s));
+ }
+
+ names1.retainAll(names2);
+
+ if (names1.size() > 0) {
+ // Map filenames back to original filenames with extensions
+ Set<String> result = new HashSet<String>(names1.size());
+ for (String s : a) {
+ if (names1.contains(LintUtils.getBaseName(s))) {
+ result.add(s);
+ }
+ }
+ for (String s : b) {
+ if (names1.contains(LintUtils.getBaseName(s))) {
+ result.add(s);
+ }
+ }
+
+ return result;
+ }
+
+
+ return Collections.emptySet();
+ }
+
private static boolean isNoDpiFolder(File file) {
return file.getName().contains("-nodpi");
}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/IconDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/IconDetectorTest.java
index 7c105ad..d6da23d 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/IconDetectorTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/IconDetectorTest.java
@@ -27,11 +27,12 @@ public class IconDetectorTest extends AbstractCheckTest {
public void test() throws Exception {
assertEquals(
- "Warning: Missing density variation folders in res: drawable-xhdpi\n" +
"drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: sample_icon.gif (found in drawable-mdpi)\n" +
"drawable/ic_launcher.png: Warning: The ic_launcher.png icon has identical contents in the following configuration folders: drawable-mdpi, drawable\n" +
"ic_launcher.png: Warning: Found bitmap drawable res/drawable/ic_launcher.png in densityless folder\n" +
+ "res: Warning: Missing density variation folders in res: drawable-xhdpi\n" +
"sample_icon.gif: Warning: Using the .gif format for bitmaps is discouraged",
+
lintProject(
"res/drawable/ic_launcher.png",
"res/drawable/ic_launcher.png=>res/drawable-mdpi/ic_launcher.png",
@@ -44,9 +45,10 @@ public class IconDetectorTest extends AbstractCheckTest {
public void test2() throws Exception {
assertEquals(
- "Warning: Missing density variation folders in res: drawable-mdpi, drawable-xhdpi\n" +
"drawable-hdpi/other.9.png: Warning: The following unrelated icon files have identical contents: appwidget_bg.9.png, other.9.png\n" +
- "drawable-hdpi/unrelated.png: Warning: The following unrelated icon files have identical contents: ic_launcher.png, unrelated.png",
+ "drawable-hdpi/unrelated.png: Warning: The following unrelated icon files have identical contents: ic_launcher.png, unrelated.png\n" +
+ "res: Warning: Missing density variation folders in res: drawable-mdpi, drawable-xhdpi",
+
lintProject(
"res/drawable-hdpi/unrelated.png",
"res/drawable-hdpi/appwidget_bg.9.png",
@@ -58,9 +60,10 @@ public class IconDetectorTest extends AbstractCheckTest {
public void testNoDpi() throws Exception {
assertEquals(
- "Warning: Missing density variation folders in res: drawable-hdpi, drawable-xhdpi\n" +
"drawable-xlarge-nodpi-v11/frame.png: Warning: The frame.png icon has identical contents in the following configuration folders: drawable-mdpi, drawable-nodpi, drawable-xlarge-nodpi-v11\n" +
- "frame.png: Warning: The following images appear in both -nodpi and in a density folder: frame.png",
+ "frame.png: Warning: The following images appear in both -nodpi and in a density folder: frame.png\n" +
+ "res: Warning: Missing density variation folders in res: drawable-hdpi, drawable-xhdpi",
+
lintProject(
"res/drawable-mdpi/frame.png",
"res/drawable-nodpi/frame.png",
@@ -81,4 +84,30 @@ public class IconDetectorTest extends AbstractCheckTest {
"res/drawable-mdpi/frame.png=>res/drawable-nodpi/file1.png",
"res/drawable-mdpi/frame.png=>res/drawable-nodpi/file2.png"));
}
+
+ public void testNoDpiMix() throws Exception {
+ assertEquals(
+ "drawable-mdpi/frame.xml: Warning: The following images appear in both -nodpi and in a density folder: frame.png, frame.xml\n" +
+ "res: Warning: Missing density variation folders in res: drawable-hdpi, drawable-xhdpi",
+
+ lintProject(
+ "res/drawable-mdpi/frame.png",
+ "res/drawable/states.xml=>res/drawable-nodpi/frame.xml"));
+ }
+
+
+ public void testMixedFormat() throws Exception {
+ // Test having a mixture of .xml and .png resources for the same name
+ // Make sure we don't get:
+ // drawable-hdpi: Warning: Missing the following drawables in drawable-hdpi: f.png (found in drawable-mdpi)
+ // drawable-xhdpi: Warning: Missing the following drawables in drawable-xhdpi: f.png (found in drawable-mdpi)
+ assertEquals(
+ "No warnings.",
+
+ lintProject(
+ "res/drawable-mdpi/frame.png=>res/drawable-mdpi/f.png",
+ "res/drawable/states.xml=>res/drawable-hdpi/f.xml",
+ "res/drawable/states.xml=>res/drawable-xhdpi/f.xml"));
+ }
+
} \ No newline at end of file
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/detector/api/LintUtilsTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/detector/api/LintUtilsTest.java
index ba3df3b..6f585b0 100644
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/detector/api/LintUtilsTest.java
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/detector/api/LintUtilsTest.java
@@ -46,6 +46,12 @@ public class LintUtilsTest extends TestCase {
assertFalse(LintUtils.isXmlFile(new File("xml.png")));
}
+ public void testGetBasename() throws Exception {
+ assertEquals("foo", LintUtils.getBaseName("foo.png"));
+ assertEquals("foo", LintUtils.getBaseName("foo.9.png"));
+ assertEquals(".foo", LintUtils.getBaseName(".foo"));
+ }
+
public void testEditDistance() {
assertEquals(0, LintUtils.editDistance("kitten", "kitten"));