@Override
    public void onDescribed(Description description) {
      SourceContext sourceContext = shipshapeContext.getSourceContext();

      // Create a TextRange to mark where the problem occurred.
      JCTree treeNode = (JCTree) description.node;
      Position.LineMap lineMap = compilationUnit.getLineMap();
      TextRange textRange =
          TextRange.newBuilder()
              .setStartLine(lineMap.getLineNumber(treeNode.getStartPosition()))
              .setStartColumn(lineMap.getColumnNumber(treeNode.getStartPosition()))
              .setEndLine(
                  lineMap.getLineNumber(treeNode.getEndPosition(compilationUnit.endPositions)))
              .setEndColumn(
                  lineMap.getColumnNumber(treeNode.getEndPosition(compilationUnit.endPositions)))
              .build();

      // Create a Note for this problem.
      Note.Builder noteBuilder =
          Note.newBuilder()
              .setDescription(description.getMessageWithoutCheckName())
              .setCategory(CATEGORY)
              .setSubcategory(description.checkName)
              .setLocation(
                  Location.newBuilder()
                      .setPath(path)
                      .setRange(textRange)
                      .setSourceContext(sourceContext));

      // If the source is available, create fix protos for the problem.
      if (source != null) {
        try {
          EncodingOffsetConverter encodingOffsetConverter =
              new EncodingOffsetConverter(source, Charset.forName(encoding));
          FixAndReplacementConverter translator =
              new FixAndReplacementConverter(
                  path, sourceContext, encodingOffsetConverter, compilationUnit);
          for (int i = 0; i < description.fixes.size(); i++) {
            Fix errorProneFix = description.fixes.get(i);
            NotesProto.Fix fix =
                translator.fromErrorProneFix(
                    errorProneFix,
                    String.format(
                        "Fix #%d for error-prone %s warning", i + 1, description.checkName));
            noteBuilder.addFix(fix);
          }
        } catch (UnsupportedEncodingException e) {
          logger.info("Cannot convert to byte index", shipshapeContext, CATEGORY);
        }
      }
      notes.add(noteBuilder.build());
    }
  @Override
  public ImmutableList<Note> analyzeFile(
      final ShipshapeContext shipshapeContext,
      final JavaCompilationDetails compilationDetails,
      final CompilationUnitTree file,
      String path)
      throws AnalyzerException {

    final ImmutableList.Builder<Note> notes = new ImmutableList.Builder<>();

    // Don't run error-prone if the compilation failed in any way.
    if (compilationDetails.hasCompileErrors() || compilationDetails.inBadCompilationState()) {
      return ImmutableList.of();
    }

    // TODO(ciera): Create an EnabledPredicate that uses a configuration file
    // and push that change upstream to errorprone.
    Scanner scanner = new ErrorProneScanner(ErrorProneScanner.EnabledPredicate.DEFAULT_CHECKS);

    JavacTask javacTask = compilationDetails.getJavac();
    Context context = ((JavacTaskImpl) javacTask).getContext();
    JCCompilationUnit compilationUnit = (JCCompilationUnit) file;

    CharSequence source = null;
    try {
      source = file.getSourceFile().getCharContent(false);
    } catch (IOException e) {
      logger.severe(e, "Unable to read source file " + path, shipshapeContext, CATEGORY);
    }
    NoteAdapter adapter =
        new NoteAdapter(
            notes,
            path,
            compilationUnit,
            shipshapeContext,
            source,
            compilationDetails.getEncoding());
    scanner.scan(file, new VisitorState(context, adapter));
    return notes.build();
  }
/** Uses the ErrorProne Scanner to create notes for the given CompilationUnit. */
public class ErrorProneAnalyzer extends JavacAnalyzer {
  private static final ShipshapeLogger logger = ShipshapeLogger.getLogger(ErrorProneAnalyzer.class);

  public static final String CATEGORY = "ErrorProne";

  @Override
  public String getCategory() {
    return CATEGORY;
  }

  @Override
  public ImmutableList<Note> analyzeFile(
      final ShipshapeContext shipshapeContext,
      final JavaCompilationDetails compilationDetails,
      final CompilationUnitTree file,
      String path)
      throws AnalyzerException {

    final ImmutableList.Builder<Note> notes = new ImmutableList.Builder<>();

    // Don't run error-prone if the compilation failed in any way.
    if (compilationDetails.hasCompileErrors() || compilationDetails.inBadCompilationState()) {
      return ImmutableList.of();
    }

    // TODO(ciera): Create an EnabledPredicate that uses a configuration file
    // and push that change upstream to errorprone.
    Scanner scanner = new ErrorProneScanner(ErrorProneScanner.EnabledPredicate.DEFAULT_CHECKS);

    JavacTask javacTask = compilationDetails.getJavac();
    Context context = ((JavacTaskImpl) javacTask).getContext();
    JCCompilationUnit compilationUnit = (JCCompilationUnit) file;

    CharSequence source = null;
    try {
      source = file.getSourceFile().getCharContent(false);
    } catch (IOException e) {
      logger.severe(e, "Unable to read source file " + path, shipshapeContext, CATEGORY);
    }
    NoteAdapter adapter =
        new NoteAdapter(
            notes,
            path,
            compilationUnit,
            shipshapeContext,
            source,
            compilationDetails.getEncoding());
    scanner.scan(file, new VisitorState(context, adapter));
    return notes.build();
  }

  /** A class to conver ErrorProne descriptions to Shipshape Notes. */
  private static class NoteAdapter implements DescriptionListener {
    private final ImmutableList.Builder<Note> notes;
    private final String path;
    private final JCCompilationUnit compilationUnit;
    private final ShipshapeContext shipshapeContext;
    @Nullable private final CharSequence source;
    private final String encoding;

    public NoteAdapter(
        ImmutableList.Builder<Note> notes,
        String path,
        JCCompilationUnit compilationUnit,
        ShipshapeContext shipshapeContext,
        @Nullable CharSequence source,
        String encoding) {
      this.notes = notes;
      this.path = path;
      this.compilationUnit = compilationUnit;
      this.shipshapeContext = shipshapeContext;
      this.source = source;
      this.encoding = Preconditions.checkNotNull(encoding);
    }

    @Override
    public void onDescribed(Description description) {
      SourceContext sourceContext = shipshapeContext.getSourceContext();

      // Create a TextRange to mark where the problem occurred.
      JCTree treeNode = (JCTree) description.node;
      Position.LineMap lineMap = compilationUnit.getLineMap();
      TextRange textRange =
          TextRange.newBuilder()
              .setStartLine(lineMap.getLineNumber(treeNode.getStartPosition()))
              .setStartColumn(lineMap.getColumnNumber(treeNode.getStartPosition()))
              .setEndLine(
                  lineMap.getLineNumber(treeNode.getEndPosition(compilationUnit.endPositions)))
              .setEndColumn(
                  lineMap.getColumnNumber(treeNode.getEndPosition(compilationUnit.endPositions)))
              .build();

      // Create a Note for this problem.
      Note.Builder noteBuilder =
          Note.newBuilder()
              .setDescription(description.getMessageWithoutCheckName())
              .setCategory(CATEGORY)
              .setSubcategory(description.checkName)
              .setLocation(
                  Location.newBuilder()
                      .setPath(path)
                      .setRange(textRange)
                      .setSourceContext(sourceContext));

      // If the source is available, create fix protos for the problem.
      if (source != null) {
        try {
          EncodingOffsetConverter encodingOffsetConverter =
              new EncodingOffsetConverter(source, Charset.forName(encoding));
          FixAndReplacementConverter translator =
              new FixAndReplacementConverter(
                  path, sourceContext, encodingOffsetConverter, compilationUnit);
          for (int i = 0; i < description.fixes.size(); i++) {
            Fix errorProneFix = description.fixes.get(i);
            NotesProto.Fix fix =
                translator.fromErrorProneFix(
                    errorProneFix,
                    String.format(
                        "Fix #%d for error-prone %s warning", i + 1, description.checkName));
            noteBuilder.addFix(fix);
          }
        } catch (UnsupportedEncodingException e) {
          logger.info("Cannot convert to byte index", shipshapeContext, CATEGORY);
        }
      }
      notes.add(noteBuilder.build());
    }
  }
}