aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/AndroidXmlFormattingStrategy.java22
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/XmlFormatPreferences.java12
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/DosLineEndingsFix.java64
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java13
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFix.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/TypoFix.java2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/formatting/AndroidXmlFormattingStrategyTest.java331
-rw-r--r--lint/libs/lint_api/src/com/android/tools/lint/detector/api/Location.java7
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java9
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/DosLineEndingDetector.java114
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/DosLineEndingDetectorTest.java49
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/crcrlf.xml14
-rw-r--r--lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/crcrlf_ignore.xml19
13 files changed, 646 insertions, 12 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/AndroidXmlFormattingStrategy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/AndroidXmlFormattingStrategy.java
index 9c29077..35735dc 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/AndroidXmlFormattingStrategy.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/AndroidXmlFormattingStrategy.java
@@ -27,6 +27,7 @@ import static org.eclipse.wst.xml.core.internal.regions.DOMRegionContext.XML_TAG
import static org.eclipse.wst.xml.core.internal.regions.DOMRegionContext.XML_TAG_OPEN;
import com.android.SdkConstants;
+import com.android.annotations.VisibleForTesting;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AdtUtils;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities;
@@ -360,7 +361,8 @@ public class AndroidXmlFormattingStrategy extends ContextBasedFormattingStrategy
* adjusted (for example to make the edit smaller if the beginning and/or end is
* identical, and so on)
*/
- private static ReplaceEdit createReplaceEdit(IStructuredDocument document, int replaceStart,
+ @VisibleForTesting
+ static ReplaceEdit createReplaceEdit(IDocument document, int replaceStart,
int replaceEnd, String formatted, XmlFormatPreferences prefs) {
// If replacing a node somewhere in the middle, start the replacement at the
// beginning of the current line
@@ -399,7 +401,7 @@ public class AndroidXmlFormattingStrategy extends ContextBasedFormattingStrategy
if (c == '\n') {
beginsWithNewline = true;
break;
- } else if (!Character.isWhitespace(c)) {
+ } else if (!Character.isWhitespace(c)) { // \r is whitespace so is handled correctly
break;
}
}
@@ -411,6 +413,9 @@ public class AndroidXmlFormattingStrategy extends ContextBasedFormattingStrategy
replaceStart = prevNewlineIndex;
}
prevNewlineIndex = index;
+ if (index > 0 && document.getChar(index - 1) == '\r') {
+ prevNewlineIndex--;
+ }
} else if (!Character.isWhitespace(c)) {
break;
}
@@ -423,16 +428,16 @@ public class AndroidXmlFormattingStrategy extends ContextBasedFormattingStrategy
}
// Search forwards too
- prevNewlineIndex = -1;
+ int nextNewlineIndex = -1;
try {
int max = document.getLength();
for (index = replaceEnd; index < max; index++) {
char c = document.getChar(index);
if (c == '\n') {
- if (prevNewlineIndex != -1) {
- replaceEnd = prevNewlineIndex + 1;
+ if (nextNewlineIndex != -1) {
+ replaceEnd = nextNewlineIndex + 1;
}
- prevNewlineIndex = index;
+ nextNewlineIndex = index;
} else if (!Character.isWhitespace(c)) {
break;
}
@@ -440,7 +445,6 @@ public class AndroidXmlFormattingStrategy extends ContextBasedFormattingStrategy
} catch (BadLocationException e) {
AdtPlugin.log(e, null);
}
-
boolean endsWithNewline = false;
for (int i = formatted.length() - 1; i >= 0; i--) {
char c = formatted.charAt(i);
@@ -452,8 +456,8 @@ public class AndroidXmlFormattingStrategy extends ContextBasedFormattingStrategy
}
}
- if (prefs.removeEmptyLines && prevNewlineIndex != -1 && endsWithNewline) {
- replaceEnd = prevNewlineIndex + 1;
+ if (prefs.removeEmptyLines && nextNewlineIndex != -1 && endsWithNewline) {
+ replaceEnd = nextNewlineIndex + 1;
}
// Figure out how much of the before and after strings are identical and narrow
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/XmlFormatPreferences.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/XmlFormatPreferences.java
index 04441fd..05c8a7f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/XmlFormatPreferences.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/formatting/XmlFormatPreferences.java
@@ -15,6 +15,7 @@
*/
package com.android.ide.eclipse.adt.internal.editors.formatting;
+import com.android.annotations.NonNull;
import com.android.annotations.VisibleForTesting;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
import com.android.ide.eclipse.adt.internal.preferences.AttributeSortOrder;
@@ -67,6 +68,7 @@ public class XmlFormatPreferences {
*
* @return an {@link XmlFormatPreferences} object
*/
+ @NonNull
public static XmlFormatPreferences create() {
XmlFormatPreferences p = new XmlFormatPreferences();
AdtPrefs prefs = AdtPrefs.getPrefs();
@@ -80,6 +82,16 @@ public class XmlFormatPreferences {
return p;
}
+ /**
+ * Returns a new preferences object initialized with the defaults
+ *
+ * @return an {@link XmlFormatPreferences} object
+ */
+ @NonNull
+ static XmlFormatPreferences defaults() {
+ return new XmlFormatPreferences();
+ }
+
// The XML module settings do not have a public API. We should replace this with JDT
// settings anyway since that's more likely what users have configured and want applied
// to their XML files
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/DosLineEndingsFix.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/DosLineEndingsFix.java
new file mode 100644
index 0000000..9a5456b
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/DosLineEndingsFix.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.
+ */
+package com.android.ide.eclipse.adt.internal.lint;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+
+/** Quickfix for correcting line endings in the file */
+class DosLineEndingsFix extends LintFix {
+
+ protected DosLineEndingsFix(String id, IMarker marker) {
+ super(id, marker);
+ }
+
+ @Override
+ public boolean needsFocus() {
+ return false;
+ }
+
+ @Override
+ public boolean isCancelable() {
+ return false;
+ }
+
+ @Override
+ public String getDisplayString() {
+ return "Fix line endings";
+ }
+
+ @Override
+ public void apply(IDocument document) {
+ char next = 0;
+ for (int i = document.getLength() - 1; i >= 0; i--) {
+ try {
+ char c = document.getChar(i);
+ if (c == '\r' && next != '\n') {
+ document.replace(i, 1, "\n"); //$NON-NLS-1$
+ }
+ next = c;
+ } catch (BadLocationException e) {
+ AdtPlugin.log(e, null);
+ return;
+ }
+ }
+
+ deleteMarker();
+ }
+}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java
index ab850e1..18d72db 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/EclipseLintClient.java
@@ -531,6 +531,9 @@ public class EclipseLintClient extends LintClient implements IDomParser {
*/
private static Pair<Integer, Integer> adjustOffsets(IDocument doc, int startOffset,
int endOffset) {
+ int originalStart = startOffset;
+ int originalEnd = endOffset;
+
if (doc != null) {
while (endOffset > startOffset && endOffset < doc.getLength()) {
try {
@@ -552,6 +555,9 @@ public class EclipseLintClient extends LintClient implements IDomParser {
char c = doc.getChar(lineEnd);
if (c == '\n' || c == '\r') {
endOffset = lineEnd;
+ if (endOffset > 0 && doc.getChar(endOffset - 1) == '\r') {
+ endOffset--;
+ }
break;
}
} catch (BadLocationException e) {
@@ -562,6 +568,13 @@ public class EclipseLintClient extends LintClient implements IDomParser {
}
}
+ if (startOffset >= endOffset) {
+ // Selecting nothing (for example, for the mangled CRLF delimiter issue selecting
+ // just the newline)
+ // In that case, use the real range
+ return Pair.of(originalStart, originalEnd);
+ }
+
return Pair.of(startOffset, endOffset);
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFix.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFix.java
index 0b074bb..feb6bb5 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFix.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/LintFix.java
@@ -21,6 +21,7 @@ import com.android.annotations.Nullable;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.tools.lint.checks.AccessibilityDetector;
import com.android.tools.lint.checks.DetectMissingPrefix;
+import com.android.tools.lint.checks.DosLineEndingDetector;
import com.android.tools.lint.checks.HardcodedValuesDetector;
import com.android.tools.lint.checks.InefficientWeightDetector;
import com.android.tools.lint.checks.ManifestOrderDetector;
@@ -170,6 +171,7 @@ abstract class LintFix implements ICompletionProposal {
sFixes.put(UseCompoundDrawableDetector.ISSUE.getId(),
UseCompoundDrawableDetectorFix.class);
sFixes.put(TypoDetector.ISSUE.getId(), TypoFix.class);
+ sFixes.put(DosLineEndingDetector.ISSUE.getId(), DosLineEndingsFix.class);
// ApiDetector.UNSUPPORTED is provided as a marker resolution rather than
// a quick assistant (the marker resolution adds a suitable @TargetApi annotation)
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/TypoFix.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/TypoFix.java
index 4358410..8a83364 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/TypoFix.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/lint/TypoFix.java
@@ -105,7 +105,7 @@ final class TypoFix extends DocumentFix {
String message = mMarker.getAttribute(IMarker.MESSAGE, "");
String typo = TypoDetector.getTypo(message);
List<String> replacements = TypoDetector.getSuggestions(message);
- if (!replacements.isEmpty() && typo != null) {
+ if (replacements != null && !replacements.isEmpty() && typo != null) {
List<LintFix> allFixes = new ArrayList<LintFix>(replacements.size());
for (String replacement : replacements) {
TypoFix fix = new TypoFix(mId, mMarker);
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/formatting/AndroidXmlFormattingStrategyTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/formatting/AndroidXmlFormattingStrategyTest.java
new file mode 100644
index 0000000..4fe2a7f
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/formatting/AndroidXmlFormattingStrategyTest.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.
+ */
+package com.android.ide.eclipse.adt.internal.editors.formatting;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.Document;
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.ReplaceEdit;
+
+import junit.framework.TestCase;
+
+@SuppressWarnings("javadoc")
+public class AndroidXmlFormattingStrategyTest extends TestCase {
+ // In the given before document, replace in the range replaceStart to replaceEnd
+ // the formatted string, and assert that it's identical to the given after string
+ private void check(String before, int replaceStart, int replaceEnd, String formatted,
+ String expected, XmlFormatPreferences prefs)
+ throws MalformedTreeException, BadLocationException {
+ Document document = new Document();
+ document.set(before);
+ ReplaceEdit edit = AndroidXmlFormattingStrategy.createReplaceEdit(document, replaceStart,
+ replaceEnd, formatted, prefs);
+ assertNotNull(edit);
+ edit.apply(document);
+ String contents = document.get();
+ // Ensure that we don't have any mangled CRLFs
+ char prev = 0;
+ boolean haveCrlf = false;
+ for (int i = 0, n = contents.length(); i < n; i++) {
+ char c = contents.charAt(i);
+ if (c == '\r') {
+ haveCrlf = true;
+ }
+ if (!(c != '\r' || prev != '\r')) {
+ fail("Mangled document: Found adjacent \\r's starting at " + i
+ + ": " + contents.substring(i - 1, Math.min(contents.length(), i + 10))
+ + "...");
+ }
+ if (haveCrlf && c == '\n' && prev != '\r') {
+ fail("Mangled document: In a CRLF document, found \\n without preceeding \\r");
+ }
+
+ prev = c;
+ }
+
+ assertEquals(expected, contents);
+ }
+
+ // In the given before document, replace the range indicated by [ and ] with the given
+ // formatted string, and assert that it's identical to the given after string
+ private void check(String before, String insert, String expected, XmlFormatPreferences prefs)
+ throws MalformedTreeException, BadLocationException {
+ int replaceStart = before.indexOf('[');
+ assertTrue(replaceStart != -1);
+ before = before.substring(0, replaceStart) + before.substring(replaceStart + 1);
+
+ int replaceEnd = before.indexOf(']');
+ assertTrue(replaceEnd != -1);
+ before = before.substring(0, replaceEnd) + before.substring(replaceEnd + 1);
+
+ check(before, replaceStart, replaceEnd, insert, expected, prefs);
+ }
+
+ public void test1() throws Exception {
+ check(
+ // Before
+ "<root>\n" +
+ "[ <element/>\n" +
+ " <second/>\n" +
+ "]\n" +
+ "</root>\n",
+
+ // Insert
+ " <element/>\n" +
+ " <second/>\n",
+
+ // After
+ "<root>\n" +
+ " <element/>\n" +
+ " <second/>\n" +
+ "\n" +
+ "</root>\n",
+
+ XmlFormatPreferences.defaults());
+ }
+
+ public void test2() throws Exception {
+ XmlFormatPreferences prefs = XmlFormatPreferences.defaults();
+ prefs.removeEmptyLines = true;
+
+ check(
+ // Before
+ "<root>\n" +
+ "\n" +
+ "\n" +
+ "[ <element/>\n" +
+ " <second/>\n" +
+ "]\n" +
+ "\n" +
+ "\n" +
+ "</root>\n",
+
+ // Insert
+ " <element/>\n" +
+ " <second/>\n",
+
+ // After
+ "<root>\n" +
+ " <element/>\n" +
+ " <second/>\n" +
+ "</root>\n",
+
+ prefs);
+ }
+
+ public void test3() throws Exception {
+ XmlFormatPreferences prefs = XmlFormatPreferences.defaults();
+ prefs.removeEmptyLines = true;
+
+ check(
+ // Before
+ "<root>\n" +
+ "\n" +
+ "\n" +
+ " [<element/>\n" +
+ " <second/>]\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "</root>\n",
+
+ // Insert
+ " <element/>\n" +
+ " <second/>",
+
+ // After
+ "<root>\n" +
+ " <element/>\n" +
+ " <second/>\n" +
+ "</root>\n",
+
+ prefs);
+ }
+
+ public void test4() throws Exception {
+ check(
+ "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+ " xmlns:tools=\"http://schemas.android.com/tools\"\n" +
+ " android:layout_width=\"match_parent\"\n" +
+ " android:layout_height=\"match_parent\" >\n" +
+ "\n" +
+ " [<TextView\n" +
+ " android:layout_width=\"wrap_content\"\n" +
+ " android:layout_height=\"wrap_content\"\n" +
+ " android:layout_centerHorizontal=\"true\"\n" +
+ " android:layout_centerVertical=\"true\"\n" +
+ " android:text=\"foo\"\n" +
+ " tools:context=\".MainActivity\" />]\n" +
+ "\n" +
+ "</RelativeLayout>\n",
+
+ // Insert
+ "\n" +
+ " <TextView\n" +
+ " android:layout_width=\"wrap_content\"\n" +
+ " android:layout_height=\"wrap_content\"\n" +
+ " android:layout_centerHorizontal=\"true\"\n" +
+ " android:layout_centerVertical=\"true\"\n" +
+ " android:text=\"foo\"\n" +
+ " tools:context=\".MainActivity\" />\n",
+
+ // After
+ "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+ " xmlns:tools=\"http://schemas.android.com/tools\"\n" +
+ " android:layout_width=\"match_parent\"\n" +
+ " android:layout_height=\"match_parent\" >\n" +
+ "\n" +
+ " <TextView\n" +
+ " android:layout_width=\"wrap_content\"\n" +
+ " android:layout_height=\"wrap_content\"\n" +
+ " android:layout_centerHorizontal=\"true\"\n" +
+ " android:layout_centerVertical=\"true\"\n" +
+ " android:text=\"foo\"\n" +
+ " tools:context=\".MainActivity\" />\n" +
+ "\n" +
+ "</RelativeLayout>\n",
+
+ XmlFormatPreferences.defaults());
+ }
+
+ public void testCrLf1() throws Exception {
+ check(
+ // Before
+ "<root>\r\n" +
+ "[ <element/>\r\n" +
+ " <second/>\r\n" +
+ "]\r\n" +
+ "</root>\r\n",
+
+ // Insert
+ " <element/>\r\n" +
+ " <second/>\r\n",
+
+ // After
+ "<root>\r\n" +
+ " <element/>\r\n" +
+ " <second/>\r\n" +
+ "\r\n" +
+ "</root>\r\n",
+
+ XmlFormatPreferences.defaults());
+ }
+
+ public void testCrLf2() throws Exception {
+ XmlFormatPreferences prefs = XmlFormatPreferences.defaults();
+ prefs.removeEmptyLines = true;
+
+ check(
+ // Before
+ "<root>\r\n" +
+ "\r\n" +
+ "\r\n" +
+ "[ <element/>\r\n" +
+ " <second/>\r\n" +
+ "]\r\n" +
+ "\r\n" +
+ "\r\n" +
+ "</root>\r\n",
+
+ // Insert
+ " <element/>\r\n" +
+ " <second/>\r\n",
+
+ // After
+ "<root>\r\n" +
+ " <element/>\r\n" +
+ " <second/>\r\n" +
+ "</root>\r\n",
+
+ prefs);
+ }
+
+ public void testCrLf3() throws Exception {
+ XmlFormatPreferences prefs = XmlFormatPreferences.defaults();
+ prefs.removeEmptyLines = true;
+
+ check(
+ // Before
+ "<root>\r\n" +
+ "\r\n" +
+ "\r\n" +
+ " [<element/>\r\n" +
+ " <second/>]\r\n" +
+ "\r\n" +
+ "\r\n" +
+ "\r\n" +
+ "</root>\r\n",
+
+ // Insert
+ " <element/>\r\n" +
+ " <second/>",
+
+ // After
+ "<root>\r\n" +
+ " <element/>\r\n" +
+ " <second/>\r\n" +
+ "</root>\r\n",
+
+ prefs);
+ }
+
+
+ public void testCrlf4() throws Exception {
+ check(
+ "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n" +
+ " xmlns:tools=\"http://schemas.android.com/tools\"\r\n" +
+ " android:layout_width=\"match_parent\"\r\n" +
+ " android:layout_height=\"match_parent\" >\r\n" +
+ "\r\n" +
+ " [<TextView\r\n" +
+ " android:layout_width=\"wrap_content\"\r\n" +
+ " android:layout_height=\"wrap_content\"\r\n" +
+ " android:layout_centerHorizontal=\"true\"\r\n" +
+ " android:layout_centerVertical=\"true\"\r\n" +
+ " android:text=\"foo\"\r\n" +
+ " tools:context=\".MainActivity\" />]\r\n" +
+ "\r\n" +
+ "</RelativeLayout>\r\n",
+
+ // Insert
+ "\r\n" +
+ " <TextView\r\n" +
+ " android:layout_width=\"wrap_content\"\r\n" +
+ " android:layout_height=\"wrap_content\"\r\n" +
+ " android:layout_centerHorizontal=\"true\"\r\n" +
+ " android:layout_centerVertical=\"true\"\r\n" +
+ " android:text=\"foo\"\r\n" +
+ " tools:context=\".MainActivity\" />\r\n",
+
+ // After
+ "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n" +
+ " xmlns:tools=\"http://schemas.android.com/tools\"\r\n" +
+ " android:layout_width=\"match_parent\"\r\n" +
+ " android:layout_height=\"match_parent\" >\r\n" +
+ "\r\n" +
+ " <TextView\r\n" +
+ " android:layout_width=\"wrap_content\"\r\n" +
+ " android:layout_height=\"wrap_content\"\r\n" +
+ " android:layout_centerHorizontal=\"true\"\r\n" +
+ " android:layout_centerVertical=\"true\"\r\n" +
+ " android:text=\"foo\"\r\n" +
+ " tools:context=\".MainActivity\" />\r\n" +
+ "\r\n" +
+ "</RelativeLayout>\r\n",
+
+ XmlFormatPreferences.defaults());
+ }
+}
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Location.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Location.java
index f574189..183e7c1 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Location.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/Location.java
@@ -235,6 +235,7 @@ public class Location {
Position start = null;
int line = 0;
int lineOffset = 0;
+ char prev = 0;
for (int offset = 0; offset <= size; offset++) {
if (offset == startOffset) {
start = new DefaultPosition(line, offset - lineOffset, offset);
@@ -246,8 +247,14 @@ public class Location {
char c = contents.charAt(offset);
if (c == '\n') {
lineOffset = offset + 1;
+ if (prev != '\r') {
+ line++;
+ }
+ } else if (c == '\r') {
line++;
+ lineOffset = offset + 1;
}
+ prev = c;
}
return Location.create(file);
}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
index aafcd5c..22a6c9d 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
@@ -55,7 +55,7 @@ public class BuiltinIssueRegistry extends IssueRegistry {
private static final List<Issue> sIssues;
static {
- final int initialCapacity = 120;
+ final int initialCapacity = 121;
List<Issue> issues = new ArrayList<Issue>(initialCapacity);
issues.add(AccessibilityDetector.ISSUE);
@@ -102,6 +102,7 @@ public class BuiltinIssueRegistry extends IssueRegistry {
issues.add(TranslationDetector.MISSING);
issues.add(HardcodedValuesDetector.ISSUE);
issues.add(Utf8Detector.ISSUE);
+ issues.add(DosLineEndingDetector.ISSUE);
issues.add(ProguardDetector.WRONGKEEP);
issues.add(ProguardDetector.SPLITCONFIG);
issues.add(PxUsageDetector.PX_ISSUE);
@@ -306,7 +307,7 @@ public class BuiltinIssueRegistry extends IssueRegistry {
// to give a hint to the user that some fixes don't require manual work
if (sAdtFixes == null) {
- sAdtFixes = new HashSet<Issue>(20);
+ sAdtFixes = new HashSet<Issue>(25);
sAdtFixes.add(InefficientWeightDetector.INEFFICIENT_WEIGHT);
sAdtFixes.add(AccessibilityDetector.ISSUE);
sAdtFixes.add(InefficientWeightDetector.BASELINE_WEIGHTS);
@@ -327,6 +328,10 @@ public class BuiltinIssueRegistry extends IssueRegistry {
sAdtFixes.add(UseCompoundDrawableDetector.ISSUE);
sAdtFixes.add(ApiDetector.UNSUPPORTED);
sAdtFixes.add(TypoDetector.ISSUE);
+ sAdtFixes.add(ManifestOrderDetector.ALLOW_BACKUP);
+ sAdtFixes.add(MissingIdDetector.ISSUE);
+ sAdtFixes.add(TranslationDetector.MISSING);
+ sAdtFixes.add(DosLineEndingDetector.ISSUE);
}
return sAdtFixes.contains(issue);
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/DosLineEndingDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/DosLineEndingDetector.java
new file mode 100644
index 0000000..c2e735c
--- /dev/null
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/DosLineEndingDetector.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+package com.android.tools.lint.checks;
+
+import com.android.annotations.NonNull;
+import com.android.tools.lint.detector.api.Category;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.LayoutDetector;
+import com.android.tools.lint.detector.api.Location;
+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 org.w3c.dom.Document;
+
+/**
+ * Checks that the line endings in DOS files are consistent
+ */
+public class DosLineEndingDetector extends LayoutDetector {
+ /** Detects mangled DOS line ending documents */
+ public static final Issue ISSUE = Issue.create(
+ "MangledCRLF", //$NON-NLS-1$
+ "Checks that files with DOS line endings are consistent",
+
+ "On Windows, line endings are typically recorded as carriage return plus " +
+ "newline: \\r\\n.\n" +
+ "\n" +
+ "This detector looks for invalid line endings with repeated carriage return " +
+ "characters (without newlines). Previous versions of the ADT plugin could " +
+ "accidentally introduce these into the file, and when editing the file, the " +
+ "editor could produce confusing visual artifacts.",
+
+ Category.CORRECTNESS,
+ 2,
+ Severity.ERROR,
+ DosLineEndingDetector.class,
+ Scope.RESOURCE_FILE_SCOPE)
+ .setMoreInfo("https://bugs.eclipse.org/bugs/show_bug.cgi?id=375421"); //$NON-NLS-1$
+
+ /** Constructs a new {@link DosLineEndingDetector} */
+ public DosLineEndingDetector() {
+ }
+
+ @Override
+ public @NonNull Speed getSpeed() {
+ return Speed.NORMAL;
+ }
+
+ @Override
+ public void visitDocument(@NonNull XmlContext context, @NonNull Document document) {
+ String contents = context.getContents();
+ if (contents == null) {
+ return;
+ }
+
+ // We could look for *consistency* and complain if you mix \n and \r\n too,
+ // but that isn't really a problem (most editors handle it) so let's
+ // not complain needlessly.
+
+ char prev = 0;
+ for (int i = 0, n = contents.length(); i < n; i++) {
+ char c = contents.charAt(i);
+ if (c == '\r' && prev == '\r') {
+ String message = "Incorrect line ending: found carriage return (\\r) without " +
+ "corresponding newline (\\n)";
+
+ // Mark the whole line as the error range, since pointing just to the
+ // line ending makes the error invisible in IDEs and error reports etc
+ // Find the most recent non-blank line
+ boolean blankLine = true;
+ for (int index = i - 2; index < i; index++) {
+ char d = contents.charAt(index);
+ if (!Character.isWhitespace(d)) {
+ blankLine = false;
+ }
+ }
+
+ int lineBegin = i;
+ for (int index = i - 2; index >= 0; index--) {
+ char d = contents.charAt(index);
+ if (d == '\n') {
+ lineBegin = index + 1;
+ if (!blankLine) {
+ break;
+ }
+ } else if (!Character.isWhitespace(d)) {
+ blankLine = false;
+ }
+ }
+
+ int lineEnd = Math.min(contents.length(), i + 1);
+ Location location = Location.create(context.file, contents, lineBegin, lineEnd);
+ context.report(ISSUE, document.getDocumentElement(), location, message, null);
+ return;
+ }
+ prev = c;
+ }
+ }
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/DosLineEndingDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/DosLineEndingDetectorTest.java
new file mode 100644
index 0000000..3682420
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/DosLineEndingDetectorTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+package com.android.tools.lint.checks;
+
+import com.android.tools.lint.detector.api.Detector;
+
+@SuppressWarnings("javadoc")
+public class DosLineEndingDetectorTest extends AbstractCheckTest {
+ @Override
+ protected Detector getDetector() {
+ return new DosLineEndingDetector();
+ }
+
+ public void test() throws Exception {
+ assertEquals(
+ "res/layout/crcrlf.xml:4: Error: Incorrect line ending: found carriage return (\\r) without corresponding newline (\\n) [MangledCRLF]\n" +
+ " android:layout_height=\"match_parent\" >\r\n" +
+ "^\n" +
+ "1 errors, 0 warnings\n",
+ lintProject("res/layout/crcrlf.xml"));
+ }
+
+ public void testIgnore() throws Exception {
+ assertEquals(
+ "No warnings.",
+ lintProject("res/layout/crcrlf_ignore.xml"));
+ }
+
+ public void testNegative() throws Exception {
+ // Make sure we don't get warnings for a correct file
+ assertEquals(
+ "No warnings.",
+ lintProject("res/layout/layout1.xml"));
+ }
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/crcrlf.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/crcrlf.xml
new file mode 100644
index 0000000..d029725
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/crcrlf.xml
@@ -0,0 +1,14 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:text="Hello"
+ tools:context=".MainActivity" />
+
+</RelativeLayout>
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/crcrlf_ignore.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/crcrlf_ignore.xml
new file mode 100644
index 0000000..680a765
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/crcrlf_ignore.xml
@@ -0,0 +1,19 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:ignore="MangledCRLF" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:text="@string/app_name"
+ tools:context=".MainActivity" />
+
+
+
+
+
+</RelativeLayout>