package com.twitter.common.tools;
import java.util.Locale;
import javax.tools.Diagnostic;
import javax.tools.Diagnostic.Kind;
import javax.tools.DiagnosticListener;
import com.twitter.common.tools.DiagnosticFilters.DiagnosticFilter;
import com.twitter.common.tools.DiagnosticFilters.Treatment;
/**
* A DiagnosticListener that supports filtering and promotion or demotion of diagnostics.
*
* <p>By default no diagnostics are filtered or otherwise rank-adjusted, use
* {@link #setFilter(DiagnosticFilter)} to change this.
*
* @param <T> The type of diagnostic this listener can handle.
*/
abstract class FilteredDiagnosticListener<T> implements DiagnosticListener<T> {
/**
* A diagnostic wrapper that replaces the wrapped diagnostic's {@link Diagnostic#getKind() kind}.
*/
class FilteredDiagnostic implements Diagnostic<T> {
private final Kind filteredKind;
private final Diagnostic<? extends T> delegate;
FilteredDiagnostic(Kind filteredKind, Diagnostic<? extends T> delegate) {
this.filteredKind = filteredKind;
this.delegate = delegate;
}
@Override
public Kind getKind() {
return filteredKind;
}
@Override
public T getSource() {
return delegate.getSource();
}
@Override
public long getPosition() {
return delegate.getPosition();
}
@Override
public long getStartPosition() {
return delegate.getStartPosition();
}
@Override
public long getEndPosition() {
return delegate.getEndPosition();
}
@Override
public long getLineNumber() {
return delegate.getLineNumber();
}
@Override
public long getColumnNumber() {
return delegate.getColumnNumber();
}
@Override
public String getCode() {
return delegate.getCode();
}
@Override
public String getMessage(Locale locale) {
return delegate.getMessage(locale);
}
}
private volatile DiagnosticFilter<? super T> filter = DiagnosticFilters.STRAIGHT_MAPPING;
/**
* Set the filter to use for all subsequent reporting.
*
* @param filter The filter to use for all subsequent reporting.
*/
void setFilter(final DiagnosticFilter<? super T> filter) {
if (filter == null) {
throw new NullPointerException("The filter cannot be null.");
}
this.filter = new DiagnosticFilter<T>() {
@Override public Treatment categorize(Diagnostic<? extends T> diagnostic) {
Treatment treatment = filter.categorize(diagnostic);
if (Treatment.PASS != treatment) {
return treatment;
}
// Backstop with a mapping guaranteed not to ignore or pass
return DiagnosticFilters.STRAIGHT_MAPPING.categorize(diagnostic);
}
};
}
@Override
public final void report(Diagnostic<? extends T> diagnostic) {
Treatment treatment = filter.categorize(diagnostic);
switch (treatment) {
case IGNORE:
break;
case NOTE:
reportOn(new FilteredDiagnostic(Kind.NOTE, diagnostic));
break;
case WARNING:
reportOn(new FilteredDiagnostic(Kind.WARNING, diagnostic));
break;
case MANDATORY_WARNING:
reportOn(new FilteredDiagnostic(Kind.MANDATORY_WARNING, diagnostic));
break;
case ERROR:
reportOn(new FilteredDiagnostic(Kind.ERROR, diagnostic));
break;
case OTHER:
default:
reportOn(new FilteredDiagnostic(Kind.OTHER, diagnostic));
}
}
/**
* Subclasses should override and handle reporting of the given diagnostic.
*
* @param diagnostic The diagnostic to report.
*/
protected abstract void reportOn(Diagnostic<? extends T> diagnostic);
}