[Util] Add StringUtils.collectLines with unit tests

Change-Id: Iaee6b71c27a55195e8b9031644d1a0f12cec11e1
diff --git a/jcommons/org.eclipse.statet.jcommons.util-tests/src/org/eclipse/statet/jcommons/util/StringUtilsTest.java b/jcommons/org.eclipse.statet.jcommons.util-tests/src/org/eclipse/statet/jcommons/util/StringUtilsTest.java
index a4f2e25..5c76b1e 100644
--- a/jcommons/org.eclipse.statet.jcommons.util-tests/src/org/eclipse/statet/jcommons/util/StringUtilsTest.java
+++ b/jcommons/org.eclipse.statet.jcommons.util-tests/src/org/eclipse/statet/jcommons/util/StringUtilsTest.java
@@ -14,6 +14,7 @@
 
 package org.eclipse.statet.jcommons.util;
 
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -24,6 +25,8 @@
 import static org.eclipse.statet.jcommons.collections.ImCollections.emptyList;
 import static org.eclipse.statet.jcommons.collections.ImCollections.newList;
 
+import java.util.ArrayList;
+
 import org.junit.jupiter.api.Test;
 
 import org.eclipse.statet.jcommons.lang.NonNullByDefault;
@@ -700,6 +703,50 @@
 	
 	
 	@Test
+	public void lines() {
+		final var lines= new ArrayList<String>();
+		
+		lines.clear();
+		StringUtils.collectLines("1234567890\nABCDEFGHIJ\r\nabcdefghij", lines);
+		assertArrayEquals(lines.toArray(),
+				new String[] { "1234567890", "ABCDEFGHIJ", "abcdefghij" } );
+		
+		lines.clear();
+		StringUtils.collectLines("1234567890\n\nabcdefghij", lines);
+		assertArrayEquals(lines.toArray(),
+				new String[] { "1234567890", "", "abcdefghij" } );
+		
+		lines.clear();
+		StringUtils.collectLines("\nABCDEFGHIJ\nabcdefghij", lines);
+		assertArrayEquals(lines.toArray(),
+				new String[] { "", "ABCDEFGHIJ", "abcdefghij" } );
+		
+		lines.clear();
+		StringUtils.collectLines("1234567890\nABCDEFGHIJ\r\n", lines);
+		assertArrayEquals(lines.toArray(),
+				new String[] { "1234567890", "ABCDEFGHIJ" } );
+		
+	}
+	
+	@Test
+	public void lines_empty() {
+		final var lines= new ArrayList<String>();
+		
+		lines.clear();
+		StringUtils.collectLines("", lines);
+		assertArrayEquals(lines.toArray(),
+				new String[] { } );
+	}
+	
+	@Test
+	@SuppressWarnings("null")
+	public void splitLines_argCheck() {
+		assertThrows(NullPointerException.class, () ->
+				StringUtils.collectLines(null, new ArrayList<>()) );
+	}
+	
+	
+	@Test
 	public void toSimpleSingleLine() {
 		assertEquals("foobarabcd",
 				StringUtils.toSimpleSingleLine("foobarabcd") );
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/util/StringUtils.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/util/StringUtils.java
index df721d2..09e91dc 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/util/StringUtils.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/util/StringUtils.java
@@ -16,6 +16,10 @@
 
 import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
 import org.eclipse.statet.jcommons.collections.ImCollection;
 import org.eclipse.statet.jcommons.lang.NonNullByDefault;
 import org.eclipse.statet.jcommons.lang.Nullable;
@@ -370,6 +374,51 @@
 	}
 	
 	
+	/**
+	 * Adds the lines of a string without its line delimiters to the specified list.
+	 * 
+	 * @param s the text
+	 * @param lines list the lines are added to
+	 */
+	public static void collectLines(final String s, final Collection<String> lines) {
+		final int n= s.length();
+		int idx= 0;
+		int lineStartIdx= 0;
+		while (idx < n) {
+			switch (s.charAt(idx)) {
+			case '\r':
+				lines.add(s.substring(lineStartIdx, idx));
+				idx++;
+				if (idx < n && s.charAt(idx) == '\n') {
+					idx++;
+				}
+				lineStartIdx= idx;
+				continue;
+			case '\n':
+				lines.add(s.substring(lineStartIdx, idx));
+				idx++;
+				if (idx < n && s.charAt(idx) == '\r') {
+					idx++;
+				}
+				lineStartIdx= idx;
+				continue;
+			default:
+				idx++;
+				continue;
+			}
+		}
+		if (lineStartIdx < n) {
+			lines.add(s.substring(lineStartIdx, n));
+		}
+	}
+	
+	public static List<String> linesToList(final String s) {
+		final List<String> lines= new ArrayList<>(2 + s.length() / 20);
+		collectLines(s, lines);
+		return lines;
+	}
+	
+	
 	private static final String STOP= new String("STOP");
 	
 	public static String toSimpleSingleLine(final String text) {