/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.errorprone.fixes;
import com.sun.tools.javac.tree.EndPosTable;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.StringReader;
import java.util.HashSet;
import java.util.Set;
/**
* Represents the corrected source which we think was intended, by applying a Fix. This
* is used to generate the "Did you mean?" snippet in the error message.
*
* @author alexeagle@google.com (Alex Eagle)
*/
public class AppliedFix {
private final String snippet;
private final boolean isRemoveLine;
private AppliedFix(String snippet, boolean isRemoveLine) {
this.snippet = snippet;
this.isRemoveLine = isRemoveLine;
}
public CharSequence getNewCodeSnippet() {
return snippet;
}
public boolean isRemoveLine() {
return isRemoveLine;
}
public static class Applier {
private final CharSequence source;
private final EndPosTable endPositions;
public Applier(CharSequence source, EndPosTable endPositions) {
this.source = source;
this.endPositions = endPositions;
}
/**
* Applies the suggestedFix to the source. Returns null if applying the fix results in no
* change to the source, or a change only to imports.
*/
public AppliedFix apply(Fix suggestedFix) {
StringBuilder replaced = new StringBuilder(source);
Set<Integer> modifiedLines = new HashSet<>();
for (Replacement repl : suggestedFix.getReplacements(endPositions)) {
replaced.replace(repl.startPosition(), repl.endPosition(), repl.replaceWith());
// Find the line number(s) being modified
// TODO: this could be more efficient
try {
LineNumberReader lineNumberReader =
new LineNumberReader(new StringReader(source.toString()));
lineNumberReader.skip(repl.startPosition());
modifiedLines.add(lineNumberReader.getLineNumber());
} catch (IOException e) {
// impossible since source is in-memory
}
}
// Not sure this is really the right behavior, but otherwise we can end up with an infinite
// loop below.
if (modifiedLines.isEmpty()) {
return null;
}
LineNumberReader lineNumberReader =
new LineNumberReader(new StringReader(replaced.toString()));
String snippet = null;
boolean isRemoveLine = false;
try {
while(!modifiedLines.contains(lineNumberReader.getLineNumber())) {
lineNumberReader.readLine();
}
// TODO: this is over-simplified; need a failing test case
snippet = lineNumberReader.readLine().trim();
// snip comment from line
if (snippet.contains("//")) {
snippet = snippet.substring(0, snippet.indexOf("//")).trim();
}
if (snippet.isEmpty()) {
isRemoveLine = true;
snippet = "to remove this line";
}
} catch (IOException e) {
// impossible since source is in-memory
}
return new AppliedFix(snippet, isRemoveLine);
}
}
public static Applier fromSource(CharSequence source, EndPosTable endPositions) {
return new Applier(source, endPositions);
}
}