Custom Lint для Java/Android Report, если мы найдем вызов класса без реализации его интерфейса
Я пишу специальный детектор для вождения. Я ухожу с урока Big Nerd Ranches по созданию пользовательского правила lint в Android (должно быть одинаковым для Java)
Я могу обнаружить событие, которое я хочу. Это вызвало конструктор класса. Однако из-за того, что мы проходим через Абстрактное дерево синтаксиса
Я не могу обнаружить реализацию обратного вызова. Я не уверен, как сказать Java, чтобы проверить файл и только сообщить, если он не смог найти событие. В этом случае реализация интерфейса. Поскольку я вижу только один лист за раз.
Как искать два вхождения, хранить места, где произошло каждое возникновение, а затем выполнять мою логику и сообщать соответственно?
package com.bignerdranch.linette.detectors;
import com.android.annotations.NonNull;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.JavaContext;
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.TextFormat;
import java.io.File;
import java.util.EnumSet;
import java.util.List;
import lombok.ast.AstVisitor;
import lombok.ast.Node;
/**
* Lint check for the usage of to-do statements
*/
public class CallBackDetector extends Detector implements Detector.JavaScanner {
private static final String FRAGMENT_MATCHER_STRING = "NoInternetDialogFragment()";
private static final String INTERFACE_MATCHER_STRING =
"NoInternetDialogFragment.NoInternetCallbackInterface";
private static final Class<? extends Detector> DETECTOR_CLASS = CallBackDetector.class;
private static final EnumSet<Scope> DETECTOR_SCOPE = Scope.JAVA_FILE_SCOPE;
private static final Implementation IMPLEMENTATION = new Implementation(
DETECTOR_CLASS,
DETECTOR_SCOPE
);
private static final String ISSUE_ID = "NoInternetDialogFragment";
private static final String ISSUE_DESCRIPTION =
"NoInternetDialogFragment Callback not detected";
private static final String ISSUE_EXPLANATION =
"When using NoInternetDialogFragment you must implement its' callback -- "
+ "NoInternetCallbackInterface";
private static final Category ISSUE_CATEGORY = Category.CORRECTNESS;
private static final int ISSUE_PRIORITY = 10;
private static final Severity ISSUE_SEVERITY = Severity.ERROR;
public static final Issue ISSUE = Issue.create(
ISSUE_ID,
ISSUE_DESCRIPTION,
ISSUE_EXPLANATION,
ISSUE_CATEGORY,
ISSUE_PRIORITY,
ISSUE_SEVERITY,
IMPLEMENTATION
);
/**
* Constructs a new {@link CallBackDetector} check
*/
public CallBackDetector() {
}
@Override
public boolean appliesTo(@NonNull Context context, @NonNull File file) {
return true;
}
@Override
public List<Class<? extends Node>> getApplicableNodeTypes() {
return null;
}
@Override
public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
String source = context.getContents();
// Check validity of source
if (source == null) {
return null;
}
if(source.indexOf(INTERFACE_MATCHER_STRING) >=0){
return null;
}
int index = source.indexOf(FRAGMENT_MATCHER_STRING);
for (int i = index; i >= 0; i = source.indexOf(FRAGMENT_MATCHER_STRING, i + 1)) {
Location location = Location.create(context.file, source, i,
i + FRAGMENT_MATCHER_STRING.length());
context.report(ISSUE, location, ISSUE.getBriefDescription(TextFormat.TEXT));
}
return null;
}
}
Ответы
Ответ 1
Возможно, если вы сохраните ссылки, которые вы найдете в качестве поля, и сообщите об этом в конце всех обходов синтаксиса:
Внутри createJavaVisitor
выполните проверку, как у вас сейчас, для использования конструктора. Вместо того, чтобы сообщать о проблеме на данный момент, создайте поле коллекции в CallBackDetector
, которое будет содержать ссылку на использование конструктора. Затем вы получите список всех используемых конструктором.
Теперь также внутри createJavaVisitor
выполните проверку для любого класса, реализующего интерфейс, который вы ищете, и добавьте его в другую коллекцию полей.
Вы можете переопределить метод afterCheckProject
(api здесь) в Detector
, чтобы узнать, когда он закончил. В этом методе выполните итерацию через ваши две коллекции и удалите любые элементы из обеих коллекций, которые являются подходящей парой (конструктор и интерфейс). Любые, оставшиеся в любой коллекции, являются вашими ошибками Lint, и вы можете добавить их в качестве проблем на этом этапе.