[WIP] Working fix for <Line, Offset> mapping to support any type of line separators in the same source file && other minor fixes
This commit is contained in:
@@ -7,14 +7,12 @@ package net.sourceforge.pmd.util.document;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
@@ -36,26 +34,42 @@ public class DocumentFile implements Document, Closeable {
|
||||
private final BufferedReader reader;
|
||||
private int currentPosition = 0;
|
||||
|
||||
private final Path temporaryPath = Files.createTempFile("pmd-", null);
|
||||
private final Writer writer = new FileWriter(temporaryPath.toFile());
|
||||
private final Path temporaryPath = Files.createTempFile("pmd-", ".tmp");
|
||||
private final Writer writer = Files.newBufferedWriter(temporaryPath, StandardCharsets.UTF_8);
|
||||
|
||||
public DocumentFile(final File file) throws IOException {
|
||||
reader = new BufferedReader(new FileReader(file));
|
||||
reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8);
|
||||
this.filePath = file.getAbsolutePath();
|
||||
mapLinesToOffsets();
|
||||
}
|
||||
|
||||
private void mapLinesToOffsets() throws IOException {
|
||||
try (Scanner scanner = new Scanner(filePath)) {
|
||||
try (Scanner scanner = new Scanner(Paths.get(filePath))) {
|
||||
int currentGlobalOffset = 0;
|
||||
|
||||
while (scanner.hasNextLine()) {
|
||||
lineToOffset.add(currentGlobalOffset);
|
||||
currentGlobalOffset += scanner.nextLine().length();
|
||||
currentGlobalOffset += getLineLengthWithLineSeparator(scanner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sums the line length without the line separation and the characters which matched the line separation pattern
|
||||
* @param scanner the scanner from which to read the line's length
|
||||
* @return the length of the line with the line separator.
|
||||
*/
|
||||
private int getLineLengthWithLineSeparator(final Scanner scanner) {
|
||||
int lineLength = scanner.nextLine().length();
|
||||
final String lineSeparationMatch = scanner.match().group(1);
|
||||
|
||||
if (lineSeparationMatch != null) {
|
||||
lineLength += lineSeparationMatch.length();
|
||||
}
|
||||
|
||||
return lineLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(int beginLine, int beginColumn, final String textToInsert) {
|
||||
try {
|
||||
@@ -135,7 +149,10 @@ public class DocumentFile implements Document, Closeable {
|
||||
}
|
||||
reader.close();
|
||||
writer.close();
|
||||
Files.copy(temporaryPath, Paths.get(filePath), StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
if (!temporaryPath.toFile().renameTo(new File(filePath))) {
|
||||
throw new IOException("Fixed file could not be renamed. Original path = " + filePath);
|
||||
}
|
||||
temporaryPath.toFile().delete();
|
||||
}
|
||||
|
||||
@@ -146,4 +163,8 @@ public class DocumentFile implements Document, Closeable {
|
||||
writer.write(line);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Integer> getLineToOffset() {
|
||||
return lineToOffset;
|
||||
}
|
||||
}
|
||||
|
@@ -12,15 +12,15 @@ import java.util.Objects;
|
||||
|
||||
public class DocumentOperationsApplierForNonOverlappingRegions {
|
||||
|
||||
private static final Comparator<DocumentOperation> COMPARATOR = new DocumentOperationNonOverlappingRegionsComparator();
|
||||
|
||||
private final Document document;
|
||||
private final List<DocumentOperation> operations;
|
||||
private final Comparator<DocumentOperation> comparator;
|
||||
|
||||
private boolean applied;
|
||||
|
||||
public DocumentOperationsApplierForNonOverlappingRegions(final Document document) {
|
||||
this.document = Objects.requireNonNull(document);
|
||||
comparator = new DocumentOperationNonOverlappingRegionsComparator();
|
||||
operations = new ArrayList<>();
|
||||
applied = false;
|
||||
}
|
||||
@@ -39,7 +39,7 @@ public class DocumentOperationsApplierForNonOverlappingRegions {
|
||||
}
|
||||
|
||||
private int getIndexForDocumentOperation(final DocumentOperation documentOperation) {
|
||||
int potentialIndex = Collections.binarySearch(operations, documentOperation, comparator);
|
||||
int potentialIndex = Collections.binarySearch(operations, documentOperation, COMPARATOR);
|
||||
|
||||
if (potentialIndex < 0) {
|
||||
return ~potentialIndex;
|
||||
@@ -53,7 +53,7 @@ public class DocumentOperationsApplierForNonOverlappingRegions {
|
||||
}
|
||||
|
||||
private boolean areSiblingsEqual(final int index) {
|
||||
return comparator.compare(operations.get(index), operations.get(index + 1)) == 0;
|
||||
return COMPARATOR.compare(operations.get(index), operations.get(index + 1)) == 0;
|
||||
}
|
||||
|
||||
public void apply() {
|
||||
@@ -65,7 +65,7 @@ public class DocumentOperationsApplierForNonOverlappingRegions {
|
||||
}
|
||||
}
|
||||
|
||||
private class DocumentOperationNonOverlappingRegionsComparator implements Comparator<DocumentOperation> {
|
||||
private static class DocumentOperationNonOverlappingRegionsComparator implements Comparator<DocumentOperation> {
|
||||
|
||||
@Override
|
||||
public int compare(final DocumentOperation o1, final DocumentOperation o2) {
|
||||
@@ -73,7 +73,7 @@ public class DocumentOperationsApplierForNonOverlappingRegions {
|
||||
final RegionByLine r2 = Objects.requireNonNull(o2).getRegionByLine();
|
||||
|
||||
final int comparison;
|
||||
if (areInsertOperations(r1, r2)) {
|
||||
if (areInsertOperationsAtTheSameOffset(r1, r2)) {
|
||||
comparison = 0;
|
||||
} else if (doesFirstRegionEndBeforeSecondRegionBegins(r1, r2)) {
|
||||
comparison = -1;
|
||||
@@ -85,7 +85,7 @@ public class DocumentOperationsApplierForNonOverlappingRegions {
|
||||
return comparison;
|
||||
}
|
||||
|
||||
private boolean areInsertOperations(final RegionByLine r1, final RegionByLine r2) {
|
||||
private boolean areInsertOperationsAtTheSameOffset(final RegionByLine r1, final RegionByLine r2) {
|
||||
return r1.getBeginLine() == r2.getBeginLine() && r1.getBeginColumn() == r2.getBeginColumn()
|
||||
&& r1.getBeginLine() == r1.getEndLine() && r1.getBeginColumn() == r1.getEndColumn();
|
||||
}
|
||||
|
@@ -6,22 +6,21 @@ package net.sourceforge.pmd.util.document;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public class DocumentFileTest {
|
||||
|
||||
private static final String FILE_PATH = "psvm.java";
|
||||
@@ -35,11 +34,6 @@ public class DocumentFileTest {
|
||||
temporaryFile = temporaryFolder.newFile(FILE_PATH);
|
||||
}
|
||||
|
||||
@After
|
||||
public void deleteTemporaryFiles() {
|
||||
temporaryFile.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertAtStartOfTheFileShouldSucceed() throws IOException {
|
||||
writeContentToTemporaryFile("static void main(String[] args) {}");
|
||||
@@ -218,8 +212,59 @@ public class DocumentFileTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lineToOffsetMappingWithLineFeedShouldSucceed() throws IOException {
|
||||
final String code = "public static int main(String[] args) {" + '\n'
|
||||
+ "int var;" + '\n'
|
||||
+ "}";
|
||||
writeContentToTemporaryFile(code);
|
||||
|
||||
final List<Integer> expectedLineToOffset = new ArrayList<>();
|
||||
expectedLineToOffset.add(0);
|
||||
expectedLineToOffset.add(40);
|
||||
expectedLineToOffset.add(49);
|
||||
|
||||
try (DocumentFile documentFile = new DocumentFile(temporaryFile)) {
|
||||
assertEquals(expectedLineToOffset, documentFile.getLineToOffset());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lineToOffsetMappingWithCarriageReturnFeedLineFeedShouldSucceed() throws IOException {
|
||||
final String code = "public static int main(String[] args) {" + "\r\n"
|
||||
+ "int var;" + "\r\n"
|
||||
+ "}";
|
||||
writeContentToTemporaryFile(code);
|
||||
|
||||
final List<Integer> expectedLineToOffset = new ArrayList<>();
|
||||
expectedLineToOffset.add(0);
|
||||
expectedLineToOffset.add(41);
|
||||
expectedLineToOffset.add(51);
|
||||
|
||||
try (DocumentFile documentFile = new DocumentFile(temporaryFile)) {
|
||||
assertEquals(expectedLineToOffset, documentFile.getLineToOffset());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lineToOffsetMappingWithMixedLineSeparatorsShouldSucceed() throws IOException {
|
||||
final String code = "public static int main(String[] args) {" + "\r\n"
|
||||
+ "int var;" + "\n"
|
||||
+ "}";
|
||||
writeContentToTemporaryFile(code);
|
||||
|
||||
final List<Integer> expectedLineToOffset = new ArrayList<>();
|
||||
expectedLineToOffset.add(0);
|
||||
expectedLineToOffset.add(41);
|
||||
expectedLineToOffset.add(50);
|
||||
|
||||
try (DocumentFile documentFile = new DocumentFile(temporaryFile)) {
|
||||
assertEquals(expectedLineToOffset, documentFile.getLineToOffset());
|
||||
}
|
||||
}
|
||||
|
||||
private void writeContentToTemporaryFile(final String content) throws IOException {
|
||||
try (FileWriter writer = new FileWriter(temporaryFile)) {
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(temporaryFile.toPath(), StandardCharsets.UTF_8)) {
|
||||
writer.write(content);
|
||||
}
|
||||
}
|
||||
|
@@ -15,7 +15,6 @@ import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -36,11 +35,6 @@ public class DocumentOperationsApplierForNonOverlappingRegionsWithDocumentFileTe
|
||||
temporaryFile = temporaryFolder.newFile(FILE_PATH);
|
||||
}
|
||||
|
||||
@After
|
||||
public void deleteTemporaryFiles() {
|
||||
temporaryFile.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertAtStartOfTheDocumentShouldSucceed() throws IOException {
|
||||
writeContentToTemporaryFile("static void main(String[] args) {}");
|
||||
|
Reference in New Issue
Block a user