1818
1919import static com .google .common .base .Preconditions .checkArgument ;
2020
21+ import com .google .common .base .Supplier ;
22+ import com .google .common .base .Suppliers ;
2123import com .sun .tools .javac .tree .EndPosTable ;
2224import java .io .IOException ;
2325import java .io .LineNumberReader ;
2426import java .io .StringReader ;
2527import java .util .HashSet ;
28+ import java .util .NavigableMap ;
2629import java .util .Set ;
30+ import java .util .TreeMap ;
31+ import java .util .regex .Matcher ;
32+ import java .util .regex .Pattern ;
2733import javax .annotation .Nullable ;
2834
2935/**
@@ -52,10 +58,12 @@ public boolean isRemoveLine() {
5258 public static class Applier {
5359 private final CharSequence source ;
5460 private final EndPosTable endPositions ;
61+ private final Supplier <NavigableMap <Integer , Integer >> lineOffsets ;
5562
5663 public Applier (CharSequence source , EndPosTable endPositions ) {
5764 this .source = source ;
5865 this .endPositions = endPositions ;
66+ this .lineOffsets = Suppliers .memoize (() -> lineOffsets (source .toString ()));
5967 }
6068
6169 /**
@@ -80,15 +88,7 @@ public AppliedFix apply(Fix suggestedFix) {
8088 replaced .replace (repl .startPosition (), repl .endPosition (), repl .replaceWith ());
8189
8290 // Find the line number(s) being modified
83- // TODO: this could be more efficient
84- try {
85- LineNumberReader lineNumberReader =
86- new LineNumberReader (new StringReader (source .toString ()));
87- lineNumberReader .skip (repl .startPosition ());
88- modifiedLines .add (lineNumberReader .getLineNumber ());
89- } catch (IOException e ) {
90- // impossible since source is in-memory
91- }
91+ modifiedLines .add (lineOffsets .get ().floorEntry (repl .startPosition ()).getValue ());
9292 }
9393
9494 // Not sure this is really the right behavior, but otherwise we can end up with an infinite
@@ -138,4 +138,20 @@ private static Set<Replacement> descending(Set<Replacement> set) {
138138 public static Applier fromSource (CharSequence source , EndPosTable endPositions ) {
139139 return new Applier (source , endPositions );
140140 }
141+
142+ private static final Pattern NEWLINE = Pattern .compile ("\\ R" );
143+
144+ /** Returns the start offsets of the lines in the input. */
145+ private static NavigableMap <Integer , Integer > lineOffsets (String input ) {
146+ NavigableMap <Integer , Integer > lines = new TreeMap <>();
147+ int line = 0 ;
148+ int idx = 0 ;
149+ lines .put (idx , line ++);
150+ Matcher matcher = NEWLINE .matcher (input );
151+ while (matcher .find (idx )) {
152+ idx = matcher .end ();
153+ lines .put (idx , line ++);
154+ }
155+ return lines ;
156+ }
141157}
0 commit comments