Skip to content

Commit 2b92efc

Browse files
committed
[IO-670] refine IOUtils.contentEquals(Reader, Reader)
1 parent e014cb5 commit 2b92efc

File tree

4 files changed

+911
-8
lines changed

4 files changed

+911
-8
lines changed

src/main/java/org/apache/commons/io/IOUtils.java

Lines changed: 88 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import org.apache.commons.io.function.IOTriFunction;
6767
import org.apache.commons.io.input.CharSequenceReader;
6868
import org.apache.commons.io.input.QueueInputStream;
69+
import org.apache.commons.io.input.buffer.LineEndUnifiedBufferedReader;
6970
import org.apache.commons.io.output.AppendableWriter;
7071
import org.apache.commons.io.output.ByteArrayOutputStream;
7172
import org.apache.commons.io.output.NullOutputStream;
@@ -1029,14 +1030,46 @@ private static boolean contentEquals(final Stream<?> stream1, final Stream<?> st
10291030
}
10301031

10311032
// TODO Consider making public
1032-
private static boolean contentEqualsIgnoreEOL(final BufferedReader reader1, final BufferedReader reader2) {
1033-
if (reader1 == reader2) {
1034-
return true;
1035-
}
1036-
if (reader1 == null || reader2 == null) {
1037-
return false;
1033+
private static boolean contentEqualsIgnoreEOL(
1034+
final LineEndUnifiedBufferedReader bufferedInput1,
1035+
final LineEndUnifiedBufferedReader bufferedInput2
1036+
) throws UncheckedIOException {
1037+
/*
1038+
* We use this variable to mark if last char be '\n'.
1039+
* Because "a" and "a\n" is thought contentEqualsIgnoreEOL,
1040+
* but "\n" and "\n\n" is thought not contentEqualsIgnoreEOL.
1041+
*/
1042+
boolean justNewLine = true;
1043+
1044+
int currentChar1;
1045+
int currentChar2;
1046+
1047+
while (true) {
1048+
currentChar1 = bufferedInput1.peek();
1049+
currentChar2 = bufferedInput2.peek();
1050+
1051+
if (currentChar1 == EOF) {
1052+
if (currentChar2 == EOF) {
1053+
return true;
1054+
} else {
1055+
if (!justNewLine) {
1056+
return inputOnlyHaveCRLForEOF(bufferedInput2, currentChar2);
1057+
}
1058+
return false;
1059+
}
1060+
} else if (currentChar2 == EOF) {
1061+
if (!justNewLine) {
1062+
return inputOnlyHaveCRLForEOF(bufferedInput1, currentChar1);
1063+
}
1064+
return false;
1065+
}
1066+
if (currentChar1 != currentChar2) {
1067+
return false;
1068+
}
1069+
justNewLine = currentChar1 == '\n';
1070+
bufferedInput1.eat();
1071+
bufferedInput2.eat();
10381072
}
1039-
return contentEquals(reader1.lines(), reader2.lines());
10401073
}
10411074

10421075
/**
@@ -1062,7 +1095,54 @@ public static boolean contentEqualsIgnoreEOL(final Reader reader1, final Reader
10621095
if (reader1 == null || reader2 == null) {
10631096
return false;
10641097
}
1065-
return contentEqualsIgnoreEOL(toBufferedReader(reader1), toBufferedReader(reader2));
1098+
1099+
final LineEndUnifiedBufferedReader bufferedInput1;
1100+
if (reader1 instanceof LineEndUnifiedBufferedReader) {
1101+
bufferedInput1 = (LineEndUnifiedBufferedReader) reader1;
1102+
} else {
1103+
bufferedInput1 = new LineEndUnifiedBufferedReader(reader1);
1104+
}
1105+
1106+
final LineEndUnifiedBufferedReader bufferedInput2;
1107+
if (reader2 instanceof LineEndUnifiedBufferedReader) {
1108+
bufferedInput2 = (LineEndUnifiedBufferedReader) reader2;
1109+
} else {
1110+
bufferedInput2 = new LineEndUnifiedBufferedReader(reader2);
1111+
}
1112+
return contentEqualsIgnoreEOL(bufferedInput1, bufferedInput2);
1113+
}
1114+
1115+
/**
1116+
* private function used only in contentEqualsIgnoreEOL.
1117+
* used in contentEqualsIgnoreEOL to detect whether a input only have CRLF or EOF.
1118+
* @param input input reader
1119+
* @param currentChar current peek char of input
1120+
* @return true/false
1121+
* @throws IOException by input.read(), not me.
1122+
* @see #contentEqualsIgnoreEOL(Reader, Reader)
1123+
*/
1124+
private static boolean inputOnlyHaveCRLForEOF(LineEndUnifiedBufferedReader input, int currentChar) throws UncheckedIOException {
1125+
1126+
/*
1127+
* logically there should be some code like
1128+
*
1129+
* if (char1 == EOF) {
1130+
* return true;
1131+
* }
1132+
*
1133+
* here.
1134+
*
1135+
* But actually, if this input's read() is EOF, then we will not invoke this function at all.
1136+
* So the check is deleted.
1137+
*
1138+
* You can go contentEqualsIgnoreEOL for details.
1139+
*/
1140+
1141+
if (currentChar == '\n') {
1142+
input.eat();
1143+
return input.read() == EOF;
1144+
}
1145+
return false;
10661146
}
10671147

10681148
/**

0 commit comments

Comments
 (0)