package com.twitter.common.tools;
import java.util.Locale;
import java.util.regex.Pattern;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
/**
* A utility for constructing {@link DiagnosticFilter DiagnosticFilters}.
*/
final class DiagnosticFilters {
/**
* Indicates the treatment that should be applied to a diagnostic.
* <ul>
* <li>{@link #IGNORE} indicates the diagnostic should be ignored.
* <li>{@link #PASS} ndicates a filter passes on categorizing a diagnostic.
* </ul>
*
* All other treatments map to a {@link Diagnostic.Kind} of the same name.
*/
public enum Treatment {
// Diagnostic.Kind equivalents
NOTE,
WARNING,
MANDATORY_WARNING,
ERROR,
OTHER,
/**
* Indicates a diagnostic should be dropped.
*/
IGNORE,
/**
* Indicates a {@link DiagnosticFilter} passes on treatment assignment to the next filter.
*/
PASS
}
/**
* A filter for diagnostics that can signal a diagnostic be skipped or alter its treatment by
* a downstream {@link javax.tools.DiagnosticListener}.
*
* @param <T>
*/
interface DiagnosticFilter<T> {
/**
* Reports the appropriate treatment for the given diagnostic.
*
* @param diagnostic The diagnostic to categorize.
* @return A suggested treatment for the diagnostic or {@link Treatment#PASS} to pass the
* categorization to the next filter.
*/
Treatment categorize(Diagnostic<? extends T> diagnostic);
}
/**
* A predicate that tests if values of type {@code T} are permitted to pass through to the guarded
* code.
*
* @param <T> The type of values the guard tests.
*/
interface Guard<T> {
/**
* Tests if the given item is permitted to pass through to some guarded code.
*
* @param item the item to test.
* @return {@code true} if the item is permitted to pass through to the guarded code.
*/
boolean permit(T item);
}
/**
* A filter that maps each {@link Diagnostic.Kind} straight to its obvious {@link Treatment}
* counterpart and never returns {@link Treatment#IGNORE} or {@link Treatment#PASS}.
*/
static final DiagnosticFilter<Object> STRAIGHT_MAPPING = new DiagnosticFilter<Object>() {
@Override public Treatment categorize(Diagnostic<?> diagnostic) {
Treatment treatment;
switch (diagnostic.getKind()) {
case NOTE:
treatment = Treatment.NOTE;
break;
case WARNING:
treatment = Treatment.WARNING;
break;
case MANDATORY_WARNING:
treatment = Treatment.MANDATORY_WARNING;
break;
case ERROR:
treatment = Treatment.ERROR;
break;
case OTHER:
default:
treatment = Treatment.OTHER;
}
return treatment;
}
};
private DiagnosticFilters() {
// utility
}
static <S> DiagnosticFilter<S> combine(
final Iterable<? extends DiagnosticFilter<? super S>> filters) {
return new DiagnosticFilter<S>() {
@Override public Treatment categorize(Diagnostic<? extends S> diagnostic) {
for (DiagnosticFilter<? super S> filter : filters) {
Treatment treatment = filter.categorize(diagnostic);
if (Treatment.PASS != treatment) {
return treatment;
}
}
return STRAIGHT_MAPPING.categorize(diagnostic);
}
};
}
static <T extends FileObject> DiagnosticFilter<T> guarded(final DiagnosticFilter<T> guarded,
final Guard<Diagnostic<? extends T>> guard) {
return new DiagnosticFilter<T>() {
@Override public Treatment categorize(Diagnostic<? extends T> diagnostic) {
return guard.permit(diagnostic) ? guarded.categorize(diagnostic) : Treatment.PASS;
}
};
}
static <T extends FileObject> DiagnosticFilter<T> ignorePathPrefixes(
final Iterable<String> pathPrefixes) {
return new DiagnosticFilter<T>() {
@Override public Treatment categorize(Diagnostic<? extends T> diagnostic) {
FileObject source = diagnostic.getSource();
if (source != null) {
for (String pathPrefix : pathPrefixes) {
if (source.toUri().getPath().startsWith(pathPrefix)) {
return Treatment.IGNORE;
}
}
}
return Treatment.PASS;
}
};
}
static <T extends FileObject> DiagnosticFilter<T> ignoreMessagesMatching(
final Iterable<Pattern> regexes) {
return new DiagnosticFilter<T>() {
@Override public Treatment categorize(Diagnostic<? extends T> diagnostic) {
String message = diagnostic.getMessage(Locale.getDefault());
for (Pattern regex : regexes) {
if (regex.matcher(message).matches()) {
return Treatment.IGNORE;
}
}
return Treatment.PASS;
}
};
}
}