Android пользовательские линта для дубликатов строк - PullRequest
1 голос
/ 30 апреля 2019

Проблема

Я пишу пользовательское правило пуха (используя этот пример ) для поиска строк дублирования, которые объявлены с использованием уникальной строковой переменной.Например, в 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);
        }
    }
}
...