[Util] Add StringUtils.toSimpleSingleLine

Change-Id: I233e62fdd133796a3de221fca57c88b47439ed7d
diff --git a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/util/MessageUtils.java b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/util/MessageUtils.java
index 5151301..ec4d216 100644
--- a/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/util/MessageUtils.java
+++ b/ecommons/org.eclipse.statet.ecommons.uimisc/src/org/eclipse/statet/ecommons/ui/util/MessageUtils.java
@@ -35,60 +35,104 @@
 		return LegacyActionTools.removeMnemonics(label);
 	}
 	
-	public static String escapeForFormText(final String text) {
-		final StringBuilder escaped= new StringBuilder(text.length());
-		ITERATE_CHARS : for (int i= 0; i < text.length(); i++) {
-			final char c= text.charAt(i);
-			switch (c) {
-			case '<':
-				escaped.append("&lt;"); //$NON-NLS-1$
-				continue ITERATE_CHARS;
-			case '>':
-				escaped.append("&gt;"); //$NON-NLS-1$
-				continue ITERATE_CHARS;
-			case '&':
-				escaped.append("&amp;"); //$NON-NLS-1$
-				continue ITERATE_CHARS;
-			case '"':
-				escaped.append("&quot;"); //$NON-NLS-1$
-				continue ITERATE_CHARS;
-			case '\'':
-				escaped.append("&apos;"); //$NON-NLS-1$
-				continue ITERATE_CHARS;
-			default:
-				escaped.append(c);
-				continue ITERATE_CHARS;
-			}
-		}
-		return escaped.toString();
-	}
 	
 	public static String escapeForTooltip(final CharSequence text) {
 		return AMPERSAND_PATTERN.matcher(text).replaceAll(AMPERSAND_TOOLTIP_REPLACEMENT);
 	}
 	
 	public static String escapeForMenu(final String text) {
-		final StringBuilder escaped= new StringBuilder(text.length());
-		ITERATE_CHARS : for (int i= 0; i < text.length(); i++) {
-			final char c= text.charAt(i);
+		final int length= text.length();
+		StringBuilder escaped= null;
+		int idxWritten= 0;
+		ITERATE_CHARS : for (int idx= 0; idx < length; ) {
+			final char c= text.charAt(idx);
+			final String replacement;
 			switch (c) {
+			
 			case '&':
-				escaped.append("&&"); //$NON-NLS-1$
-				continue ITERATE_CHARS;
+				replacement= "&&"; //$NON-NLS-1$
+				break;
 			case '\t':
-				escaped.append("  "); //$NON-NLS-1$
-				continue ITERATE_CHARS;
 			case '\n':
-				escaped.append("  "); //$NON-NLS-1$
-				continue ITERATE_CHARS;
+				replacement= "  "; //$NON-NLS-1$
+				break;
+			
 			default:
-				escaped.append(c);
+				idx++;
 				continue ITERATE_CHARS;
 			}
+			
+			if (escaped == null) {
+				escaped= new StringBuilder(length + 16);
+			}
+			if (idx > idxWritten) {
+				escaped.append(text, idxWritten, idx);
+			}
+			escaped.append(replacement);
+			idxWritten= ++idx;
+			continue ITERATE_CHARS;
+		}
+		
+		if (escaped == null) {
+			return text;
+		}
+		if (length > idxWritten) {
+			escaped.append(text, idxWritten, length);
 		}
 		return escaped.toString();
 	}
 	
+	public static String escapeForFormText(final String text) {
+		final int length= text.length();
+		StringBuilder escaped= null;
+		int idxWritten= 0;
+		ITERATE_CHARS : for (int idx= 0; idx < length; ) {
+			final char c= text.charAt(idx);
+			final String replacement;
+			switch (c) {
+			
+			case '<':
+				replacement= "&lt;"; //$NON-NLS-1$
+				break;
+			case '>':
+				replacement= "&gt;"; //$NON-NLS-1$
+				break;
+			case '&':
+				replacement= "&amp;"; //$NON-NLS-1$
+				break;
+			case '"':
+				replacement= "&quot;"; //$NON-NLS-1$
+				break;
+			case '\'':
+				replacement= "&apos;"; //$NON-NLS-1$
+				break;
+			
+			default:
+				idx++;
+				continue ITERATE_CHARS;
+			}
+			
+			if (escaped == null) {
+				escaped= new StringBuilder(length + 16);
+			}
+			if (idx > idxWritten) {
+				escaped.append(text, idxWritten, idx);
+			}
+			escaped.append(replacement);
+			idxWritten= ++idx;
+			continue ITERATE_CHARS;
+		}
+		
+		if (escaped == null) {
+			return text;
+		}
+		if (length > idxWritten) {
+			escaped.append(text, idxWritten, length);
+		}
+		return escaped.toString();
+	}
+	
+	
 	private static final String URL_DELIMITERS= TextProcessor.getDefaultDelimiters() + ":@?-"; //$NON-NLS-1$
 	
 	public static String processURLPart(final String url) {
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 46e91f0..a4f2e25 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
@@ -698,4 +698,33 @@
 				StringUtils.firstMatchOfAny(new StringBuilder("foobarabcd"), newList("abc", null)) );
 	}
 	
+	
+	@Test
+	public void toSimpleSingleLine() {
+		assertEquals("foobarabcd",
+				StringUtils.toSimpleSingleLine("foobarabcd") );
+		assertEquals("",
+				StringUtils.toSimpleSingleLine("") );
+		assertEquals("foobarabcd",
+				StringUtils.toSimpleSingleLine("foobarabcd\n") );
+		assertEquals("foobar  abcd",
+				StringUtils.toSimpleSingleLine("foobar\tabcd") );
+		assertEquals("foobar \u2014 abcd",
+				StringUtils.toSimpleSingleLine("foobar\nabcd") );
+		assertEquals("foobar",
+				StringUtils.toSimpleSingleLine("foobar\fabcd") );
+		assertEquals("foobarabcd",
+				StringUtils.toSimpleSingleLine("foobar\u0000abcd") );
+		
+		assertEquals("foo  barabcd",
+				StringUtils.toSimpleSingleLine("foo\tbar\u0000\u0000abcd\n") );
+	}
+	
+	@Test
+	@SuppressWarnings("null")
+	public void toSimpleSingleLine_argCheck() {
+		assertThrows(NullPointerException.class, () ->
+				StringUtils.toSimpleSingleLine((String)null) );
+	}
+	
 }
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 f0abe50..98945e5 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
@@ -370,6 +370,130 @@
 	}
 	
 	
+	private static final String STOP= new String("STOP");
+	
+	public static String toSimpleSingleLine(final String text) {
+		final int length= text.length();
+		StringBuilder escaped= null;
+		int idxWritten= 0;
+		ITERATE_CHARS : for (int idx= 0; idx < length; ) {
+			final char c= text.charAt(idx);
+			final String replacement;
+			switch (c) {
+			
+			case 0x00:
+			case 0x01:
+			case 0x02:
+			case 0x03:
+			case 0x04:
+			case 0x05:
+			case 0x06:
+			case 0x07:
+			case 0x08:
+//			case 0x09: HT
+//			case 0x0A: LF
+			case 0x0B:
+//			case 0x0C: FF
+			case 0x0E:
+			case 0x0F:
+			case 0x10:
+			case 0x11:
+			case 0x12:
+			case 0x13:
+			case 0x14:
+			case 0x15:
+			case 0x16:
+			case 0x17:
+			case 0x18:
+			case 0x1B:
+			case 0x1C:
+			case 0x1E:
+			case 0x1F:
+			case 0x7F:
+			case 0x80:
+			case 0x81:
+			case 0x82:
+			case 0x83:
+			case 0x84:
+			case 0x85:
+			case 0x86:
+			case 0x87:
+			case 0x88:
+			case 0x89:
+			case 0x8A:
+			case 0x8B:
+			case 0x8C:
+			case 0x8D:
+			case 0x8E:
+			case 0x8F:
+			case 0x90:
+			case 0x91:
+			case 0x92:
+			case 0x93:
+			case 0x94:
+			case 0x95:
+			case 0x96:
+			case 0x97:
+			case 0x98:
+			case 0x99:
+			case 0x9A:
+			case 0x9B:
+			case 0x9C:
+			case 0x9D:
+			case 0x9E:
+			case 0x9F:
+				replacement= null;
+				break;
+			
+			case '\t': // HT
+				replacement= "  "; //$NON-NLS-1$
+				break;
+			case '\n': // LF
+				replacement= (idx < length - 1) ? " \u2014 " : STOP; //$NON-NLS-1$
+				break;
+				
+			case '\f': // FF
+				replacement= STOP;
+				break;
+			
+			default:
+				idx++;
+				continue ITERATE_CHARS;
+			}
+			
+			if (replacement == STOP) {
+				if (escaped == null) {
+					return text.substring(0, idx);
+				}
+				if (idx > idxWritten) {
+					escaped.append(text, idxWritten, idx);
+				}
+				return escaped.toString();
+			}
+			
+			if (escaped == null) {
+				escaped= new StringBuilder(length + 16);
+			}
+			if (idx > idxWritten) {
+				escaped.append(text, idxWritten, idx);
+			}
+			if (replacement != null) {
+				escaped.append(replacement);
+			}
+			idxWritten= ++idx;
+			continue ITERATE_CHARS;
+		}
+		
+		if (escaped == null) {
+			return text;
+		}
+		if (length > idxWritten) {
+			escaped.append(text, idxWritten, length);
+		}
+		return escaped.toString();
+	}
+	
+	
 	private StringUtils() {
 	}