Skip to content

Commit

Permalink
renamed uses of toString() to toRawString() to emphasise that the…
Browse files Browse the repository at this point in the history
… caller wants the actual line separator "as-is" and without any additional escaping; added human-readable "descriptions" to the LineEnding enums; progress on LineEndingProcessingProvider.java -- now functional and tests passing;
  • Loading branch information
MysterAitch committed May 26, 2020
1 parent ede3a21 commit df5605c
Show file tree
Hide file tree
Showing 13 changed files with 271 additions and 140 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package com.github.javaparser;

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Modifier;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.printer.lexicalpreservation.LexicalPreservingPrinter;
import com.github.javaparser.utils.LineEnding;
import org.junit.jupiter.api.Test;

import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;

public class LineEndingProcessorTest {

// TODO: Add more tests outside the "happy path" (e.g. mixed EOL, no EOL, etc.)

/*
* This test case must prevent an UnsupportedOperation Removed throwed by LexicalPreservation when we try to replace an expression
*/
public void doTest(LineEnding eol) {

final String original = "" +
" public class Foo { //comment" + eol.toRawString() +
" private String a;" + eol +
" private String b;" + eol +
" private String c;" + eol +
" private String d;" + eol +
" }";

// Note: Expect the platform's EOL character when printing
String expected = "" +
" public class Foo { //comment" + eol +
" private String newField;" + eol +
" " + eol +
" private String a;" + eol +
" private String b;" + eol +
" private String c;" + eol +
" private String d;" + eol +
" }";


CompilationUnit cu = StaticJavaParser.parse(original);
LexicalPreservingPrinter.setup(cu);

// create a new field declaration
VariableDeclarator variable = new VariableDeclarator(new ClassOrInterfaceType("String"), "newField");
FieldDeclaration fd = new FieldDeclaration(new NodeList(Modifier.privateModifier()), variable);
Optional<ClassOrInterfaceDeclaration> cd = cu.findFirst(ClassOrInterfaceDeclaration.class);

// add the new variable
cd.get().getMembers().addFirst(fd);

// should be printed like this
System.out.println("\n\nOriginal:\n" + original);
System.out.println("\n\nExpected:\n" + expected);

// but the result is
final String actual = LexicalPreservingPrinter.print(cu);
System.out.println("\n\nActual:\n" + actual);


// The LineEndingProcessingProvider sets the line ending to the root node.
// Child nodes should then "inherit" then line ending style.
LineEnding lineEnding_cu = cu.getLineEndingStyle();
LineEnding lineEnding_fd = fd.getLineEndingStyle();

System.out.println("lineEnding_cu.describe() = " + lineEnding_cu.describe());
System.out.println("lineEnding_fd.describe() = " + lineEnding_fd.describe());

// Assert that it has been detected and injected correctly.
LineEnding detectedLineEnding = LineEnding.detect(actual);
assertEquals(eol, detectedLineEnding);
assertEquals(eol, lineEnding_cu);
assertEquals(eol, lineEnding_fd);

// The line ending data is injected at the root node, thus should only exist there.
assertTrue(cu.containsData(Node.LINE_ENDING_KEY), "Expected the processor provider to have set the data on the root node.");
assertFalse(fd.containsData(Node.LINE_ENDING_KEY), "Expected the line ending value to have been inherited, not set directly");

}

@Test
public void testWithCr() {
doTest(LineEnding.CR);
}

@Test
public void testWithCrLf() {
doTest(LineEnding.CRLF);
}

@Test
public void testWithLf() {
doTest(LineEnding.LF);
}


// TODO: Test for textblocks

}
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public void doTest(LineEnding eol) {
LineEnding detectedLineEnding = LineEnding.detect(actual);

assertFalse(detectedLineEnding.equals(LineEnding.MIXED));
assertEquals(eol.escaped(), detectedLineEnding.escaped());
assertEquals(eol.toEscapedString(), detectedLineEnding.toEscapedString());

assertEquals(normaliseNewlines(expected), normaliseNewlines(actual));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ class LineEndingTest {

@Test
void escaped() {
assertEquals("\\r", LineEnding.CR.escaped());
assertEquals("\\n", LineEnding.LF.escaped());
assertEquals("\\r\\n", LineEnding.CRLF.escaped());
assertEquals("\\r", LineEnding.CR.toEscapedString());
assertEquals("\\n", LineEnding.LF.toEscapedString());
assertEquals("\\r\\n", LineEnding.CRLF.toEscapedString());
}

@Test
Expand Down Expand Up @@ -69,12 +69,12 @@ void detect() {

@Test
void testToString() {
assertEquals("\r", LineEnding.CR.toString());
assertEquals("\n", LineEnding.LF.toString());
assertEquals("\r\n", LineEnding.CRLF.toString());
assertEquals("\r", LineEnding.CR.toRawString());
assertEquals("\n", LineEnding.LF.toRawString());
assertEquals("\r\n", LineEnding.CRLF.toRawString());

// Note that this represents an "arbitrary" line ending -- this test is to highlight any time that it changes.
assertEquals("\n", LineEnding.ARBITRARY.toString());
assertEquals("\n", LineEnding.ARBITRARY.toRawString());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public static String readResource(String resourceName, LineEnding lineEnding) {
final StringBuilder builder = new StringBuilder(4096);
String line;
while ((line = br.readLine()) != null) {
builder.append(line).append(lineEnding.toString());
builder.append(line).append(lineEnding.toRawString());
}
return builder.toString();
}
Expand Down Expand Up @@ -299,9 +299,9 @@ public static void assertEqualsString(String expected, String actual, String mes
expected,
actual,
message + String.format(" -- failed due to line separator differences -- Expected: %s, but actual: %s (system eol: %s)",
LineEnding.detect(expected).escaped(),
LineEnding.detect(actual).escaped(),
LineEnding.SYSTEM.escaped()
LineEnding.detect(expected).toEscapedString(),
LineEnding.detect(actual).toEscapedString(),
LineEnding.SYSTEM.toEscapedString()
)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,8 @@

import com.github.javaparser.utils.LineEnding;

import javax.sound.sampled.Line;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

Expand All @@ -36,19 +32,19 @@
*/
public class LineEndingProcessingProvider implements Provider {

private static final char LF = '\n';

private static final char CR = '\r';

private static final char BACKSLASH = '\\';

private static final int EOF = -1;

private static final int INITIAL_BUFFER_SIZE = 2048;
private static final int DEFAULT_BUFFER_SIZE = 2048;

private char[] _data;
/**
* The "other" provider which we are wrapping around / reading from.
*/
private final Provider _input;

private Map<LineEnding, Integer> eolCounts = new HashMap<>();
/**
* The buffer that we're storing data within.
*/
private final char[] _data;

/**
* The number of characters in {@link #_data}.
Expand All @@ -60,23 +56,31 @@ public class LineEndingProcessingProvider implements Provider {
*/
private int _pos = 0;

private Provider _input;
private final Map<LineEnding, Integer> eolCounts = new HashMap<>();

/**
* Creates a {@link LineEndingProcessingProvider}.
*/
public LineEndingProcessingProvider(Provider input) {
this(INITIAL_BUFFER_SIZE, input);
this(DEFAULT_BUFFER_SIZE, input);
}

/**
* Creates a {@link LineEndingProcessingProvider}.
*/
public LineEndingProcessingProvider(int bufferSize, Provider input) {
_input = input;
_data = new char[bufferSize];
}

@Override
public void close() throws IOException {
_input.close();
}

private int fillBuffer() throws IOException {
_pos = 0;
int direct = _input.read(_data, 0, _data.length);
if (direct != 0) {
_len = direct;
}
return direct;
}

public LineEnding getDetectedLineEnding() {
return LineEnding.getLineEnding(
eolCounts.getOrDefault(LineEnding.CR, 0),
Expand All @@ -85,10 +89,30 @@ public LineEnding getDetectedLineEnding() {
);
}

private boolean isBufferEmpty() {
return _pos >= _len;
}

/**
* Retrieves the next un-escaped character from the buffered {@link #_input}.
*
* @return The next character or {@code -1} if no more input is available.
*/
private int nextBufferedChar() throws IOException {
while (isBufferEmpty()) {
int direct = fillBuffer();
if (direct < 0) {
return EOF;
}
}
return _data[_pos++];
}

@Override
public int read(char[] buffer, final int offset, int len) throws IOException {
int pos = offset;
int stop = offset + len;
LineEnding previousLineSeparator = null;
while (pos < stop) {
int ch = nextBufferedChar();
if (ch < 0) {
Expand All @@ -99,11 +123,31 @@ public int read(char[] buffer, final int offset, int len) throws IOException {
break;
}
} else {
Optional<LineEnding> lookup = LineEnding.lookup(String.valueOf(ch));
lookup.ifPresent(lineEnding -> {
eolCounts.putIfAbsent(lineEnding, 0);
eolCounts.put(lineEnding, eolCounts.get(lineEnding) + 1);
});
String str = String.valueOf((char) ch);
Optional<LineEnding> lookup = LineEnding.lookup(str);

if (lookup.isPresent()) {
LineEnding lineSeparator = lookup.get();

// Track the number of times this character is found..
eolCounts.putIfAbsent(lineSeparator, 0);
eolCounts.put(lineSeparator, eolCounts.get(lineSeparator) + 1);

// Handle line separators of length two (specifically CRLF)
// TODO: Make this more generic than just CRLF (e.g. track the previous char rather than the previous line separator
if (lineSeparator == LineEnding.LF) {
if (previousLineSeparator == LineEnding.CR) {
eolCounts.putIfAbsent(LineEnding.CRLF, 0);
eolCounts.put(LineEnding.CRLF, eolCounts.get(LineEnding.CRLF) + 1);
}
}

// If "this" (current) char <strong>is</strong> a line separator, set the next loop's "previous" to this
previousLineSeparator = lineSeparator;
} else {
// If "this" (current) char <strong>is not</strong> a line separator, set the next loop's "previous" to null
previousLineSeparator = null;
}

// Move to next character
buffer[pos++] = (char) ch;
Expand All @@ -112,39 +156,4 @@ public int read(char[] buffer, final int offset, int len) throws IOException {
return pos - offset;
}

@Override
public void close() throws IOException {
_input.close();
}


/**
* Retrieves the next un-escaped character from the buffered {@link #_input}.
*
* @return The next character or {@code -1} if no more input is available.
*/
private int nextBufferedChar() throws IOException {
while (isBufferEmpty()) {
int direct = fillBuffer();
if (direct < 0) {
return EOF;
}
}
return _data[_pos++];
}

private boolean isBufferEmpty() {
return _pos >= _len;
}

private int fillBuffer() throws IOException {
_pos = 0;
int direct = _input.read(_data, 0, _data.length);
if (direct != 0) {
_len = direct;
}
return direct;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.github.javaparser.ast.validator.*;
import com.github.javaparser.printer.lexicalpreservation.LexicalPreservingPrinter;
import com.github.javaparser.resolution.SymbolResolver;
import com.github.javaparser.utils.LineEnding;
import com.github.javaparser.version.Java10PostProcessor;
import com.github.javaparser.version.Java11PostProcessor;
import com.github.javaparser.version.Java12PostProcessor;
Expand Down Expand Up @@ -204,8 +205,10 @@ public void process(ParseResult<? extends Node> result, ParserConfiguration conf
if (isRetainOriginalLineEnding()) {
result.getResult().ifPresent(
rootNode -> {
LineEnding detectedLineEnding = _lineEndingProcessingProvider.getDetectedLineEnding();

// Set the line ending on the root node
rootNode.setData(Node.LINE_ENDING_KEY, _lineEndingProcessingProvider.getDetectedLineEnding());
rootNode.setData(Node.LINE_ENDING_KEY, detectedLineEnding);

// // Set the line ending on all children of the root node -- FIXME: Should ignore """textblocks"""
// rootNode.findAll(Node.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ public final Node setBlockComment(String comment) {
public final String toString() {
if (containsData(LINE_ENDING_KEY)) {
LineEnding lineEnding = getLineEndingStyleOrDefault(LineEnding.SYSTEM);
toStringPrettyPrinterConfiguration.setEndOfLineCharacter(lineEnding.toString());
toStringPrettyPrinterConfiguration.setEndOfLineCharacter(lineEnding.toRawString());
}
return new PrettyPrinter(toStringPrettyPrinterConfiguration).print(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ static CsmElement newline() {
}

static CsmElement newline(LineEnding lineEnding) {
return new CsmToken(eolTokenKind(lineEnding), lineEnding.toString());
return new CsmToken(eolTokenKind(lineEnding), lineEnding.toRawString());
}

static CsmElement none() {
Expand Down
Loading

0 comments on commit df5605c

Please sign in to comment.