Skip to content

Commit eb25f9a

Browse files
committed
[IO-670] refine IOUtils.contentEquals(Reader, Reader)
1 parent c54bf68 commit eb25f9a

File tree

4 files changed

+887
-8
lines changed

4 files changed

+887
-8
lines changed

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

Lines changed: 83 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import java.util.function.Consumer;
5252

5353
import org.apache.commons.io.function.IOConsumer;
54+
import org.apache.commons.io.input.buffer.LineEndUnifiedBufferedReader;
5455
import org.apache.commons.io.output.AppendableWriter;
5556
import org.apache.commons.io.output.ByteArrayOutputStream;
5657
import org.apache.commons.io.output.NullOutputStream;
@@ -916,16 +917,90 @@ public static boolean contentEqualsIgnoreEOL(final Reader reader1, final Reader
916917
if (reader1 == null ^ reader2 == null) {
917918
return false;
918919
}
919-
final BufferedReader br1 = toBufferedReader(reader1);
920-
final BufferedReader br2 = toBufferedReader(reader2);
921920

922-
String line1 = br1.readLine();
923-
String line2 = br2.readLine();
924-
while (line1 != null && line1.equals(line2)) {
925-
line1 = br1.readLine();
926-
line2 = br2.readLine();
921+
final LineEndUnifiedBufferedReader bufferedInput1;
922+
if (reader1 instanceof LineEndUnifiedBufferedReader) {
923+
bufferedInput1 = (LineEndUnifiedBufferedReader) reader1;
924+
} else {
925+
bufferedInput1 = new LineEndUnifiedBufferedReader(reader1);
927926
}
928-
return Objects.equals(line1, line2);
927+
928+
final LineEndUnifiedBufferedReader bufferedInput2;
929+
if (reader2 instanceof LineEndUnifiedBufferedReader) {
930+
bufferedInput2 = (LineEndUnifiedBufferedReader) reader2;
931+
} else {
932+
bufferedInput2 = new LineEndUnifiedBufferedReader(reader2);
933+
}
934+
935+
/*
936+
* We use this variable to mark if last char be '\n'.
937+
* Because "a" and "a\n" is thought contentEqualsIgnoreEOL,
938+
* but "\n" and "\n\n" is thought not contentEqualsIgnoreEOL.
939+
*/
940+
boolean justNewLine = true;
941+
942+
int currentChar1;
943+
int currentChar2;
944+
945+
while (true) {
946+
currentChar1 = bufferedInput1.peek();
947+
currentChar2 = bufferedInput2.peek();
948+
949+
if (currentChar1 == EOF) {
950+
if (currentChar2 == EOF) {
951+
return true;
952+
} else {
953+
if (!justNewLine) {
954+
return inputOnlyHaveCRLForEOF( bufferedInput2, currentChar2);
955+
}
956+
return false;
957+
}
958+
} else if (currentChar2 == EOF) {
959+
if (!justNewLine) {
960+
return inputOnlyHaveCRLForEOF(bufferedInput1, currentChar1);
961+
}
962+
return false;
963+
}
964+
if (currentChar1 != currentChar2) {
965+
return false;
966+
}
967+
justNewLine = currentChar1 == '\n';
968+
bufferedInput1.eat();
969+
bufferedInput2.eat();
970+
}
971+
}
972+
973+
/**
974+
* private function used only in contentEqualsIgnoreEOL.
975+
* used in contentEqualsIgnoreEOL to detect whether a input only have CRLF or EOF.
976+
* @param input input reader
977+
* @param currentChar current peek char of input
978+
* @return true/false
979+
* @throws IOException by input.read(), not me.
980+
* @see #contentEqualsIgnoreEOL(Reader, Reader)
981+
*/
982+
private static boolean inputOnlyHaveCRLForEOF(LineEndUnifiedBufferedReader input, int currentChar) throws IOException {
983+
984+
/*
985+
* logically there should be some code like
986+
*
987+
* if (char1 == EOF) {
988+
* return true;
989+
* }
990+
*
991+
* here.
992+
*
993+
* But actually, if this input's read() is EOF, then we will not invoke this function at all.
994+
* So the check is deleted.
995+
*
996+
* You can go contentEqualsIgnoreEOL for details.
997+
*/
998+
999+
if (currentChar == '\n') {
1000+
input.eat();
1001+
return input.read() == EOF;
1002+
}
1003+
return false;
9291004
}
9301005

9311006
/**

0 commit comments

Comments
 (0)