Проблема
Я пишу пользовательское правило пуха (используя этот пример ) для поиска строк дублирования, которые объявлены с использованием уникальной строковой переменной.Например, в strings.xml
,
<string name="cancel">Cancel</string>
<string name="activity_cancel_text">Cancel</string>
Ожидание Показать lint
предупреждение при добавлении activity_cancel_text
в strings.xml
Подход - при первом открытии strings.xml
разработчиком, прочитать весь файл и сохранить хэш-карту в виде key
= "Cancel"
и value
= "cancel"
- посетить каждый элемент, то есть строковое значение (Cancel
в строке № 2 в этом случае), и если ключ существует на карте, выведите предупреждение о проблеме с lint)
Вопрос Необходимо прочитать strings.xml до того, как visitElement()
будетназывается.Как это сделать?
Документация скудная и отладка трудна даже при модульном тестировании (точка останова никогда не срабатывает).Так что, какой бы прогресс я ни добился, это путем проб и ошибок после прочтения десятков блогов.Чтобы проверить мои изменения, я устанавливаю пользовательскую банку lint в ~/.android/lint
и вносю изменения в тестовый проект Android для наблюдения за предупреждениями lint.
StringDupeDetector.java
package com.example.lint.checks;
import com.android.SdkConstants;
import com.android.resources.ResourceFolderType;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
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;
import com.android.tools.lint.detector.api.XmlContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.w3c.dom.Element;
import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class StringDupeDetector extends ResourceXmlDetector {
public StringDupeDetector() {}
public static final Issue ISSUE = Issue.create(
// ID: used in @SuppressLint warnings etc
"StringDupe",
// Title -- shown in the IDE's preference dialog, as category headers in the
// Analysis results window, etc
"String Dupe",
// Full explanation of the issue; you can use some markdown markup such as
// `monospace`, *italic*, and **bold**.
"This check highlights string/color/dimen literals in code which duplicates " +
"the string resources. Blah blah blah.\n" +
"\n" +
"Another paragraph here.\n",
Category.CORRECTNESS,
6,
Severity.ERROR,
new Implementation(
StringDupeDetector.class,
Scope.RESOURCE_FILE_SCOPE));
private static final Map<String, String> stringResourceMap = new HashMap<>(); // string, stringId
private static final Map<String, Location> violationsMap = new HashMap(); // string, location
private static final String ISSUE_MSG_FORMAT = "WARNING! %s is already defined as %s.";
@Nullable
@Override
public Collection<String> getApplicableElements() {
return Collections.singletonList(SdkConstants.TAG_STRING);
}
@Override
public boolean appliesTo(ResourceFolderType folderType) {
return folderType == ResourceFolderType.VALUES;
}
@Override
public void visitElement(@NotNull XmlContext context, @NotNull Element element) {
final String string = element.getTextContent();
final String stringId = element.getAttribute(SdkConstants.ATTR_NAME);
if (stringResourceMap.containsKey(string)) {
// found a dupe!
violationsMap.put(string, context.getLocation(element));
context.report(ISSUE, element, context.getLocation(element), String.format(ISSUE_MSG_FORMAT, string, stringId));
} else {
stringResourceMap.put(string, stringId);
}
}
}