[String] Improve StringFactory

  - Add get(char)
  - Let all impl make use of cached interned strings for ASCII chars
  - Add unit tests

Change-Id: I1786fa927cfdc6feb65382b81b25e973de4d9a43
diff --git a/jcommons/org.eclipse.statet.jcommons.util-tests/src/org/eclipse/statet/jcommons/string/BasicStringFactoryTest.java b/jcommons/org.eclipse.statet.jcommons.util-tests/src/org/eclipse/statet/jcommons/string/BasicStringFactoryTest.java
new file mode 100644
index 0000000..c044773
--- /dev/null
+++ b/jcommons/org.eclipse.statet.jcommons.util-tests/src/org/eclipse/statet/jcommons/string/BasicStringFactoryTest.java
@@ -0,0 +1,116 @@
+/*=============================================================================#
+ # Copyright (c) 2021 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.jcommons.string;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullLateInit;
+
+import org.junit.jupiter.api.Test;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+
+@NonNullByDefault
+public class BasicStringFactoryTest {
+	
+	
+	protected StringFactory factory= nonNullLateInit();
+	
+	
+	public BasicStringFactoryTest() {
+	}
+	
+	
+	protected StringFactory create() {
+		return BasicStringFactory.INSTANCE;
+	}
+	
+	
+	@Test
+	public void get_CharSequence() {
+		this.factory= create();
+		final String s= this.factory.get(new StringBuilder("ab"));
+		assertEquals("ab", s);
+	}
+	
+	@Test
+	public void get_CharSequence_emtpy() {
+		this.factory= create();
+		final String s= this.factory.get(new StringBuilder(""));
+		assertTrue("" == s);
+	}
+	
+	@Test
+	public void get_CharSequence_asciiChar() {
+		this.factory= create();
+		final String s= this.factory.get(new StringBuilder("a"));
+		assertTrue("a" == s);
+	}
+	
+	
+	@Test
+	public void get_CharArrayString() {
+		this.factory= create();
+		final String s= this.factory.get(new CharArrayString("ab"));
+		assertEquals("ab", s);
+	}
+	
+	@Test
+	public void get_CharArrayString_emtpy() {
+		this.factory= create();
+		final String s= this.factory.get(new CharArrayString(""));
+		assertTrue("" == s);
+	}
+	
+	@Test
+	public void get_CharArrayString_asciiChar() {
+		this.factory= create();
+		final String s= this.factory.get(new CharArrayString("a"));
+		assertTrue("a" == s);
+	}
+	
+	
+	@Test
+	public void get_String() {
+		this.factory= create();
+		final String s= this.factory.get(new String("ab"));
+		assertEquals("ab", s);
+	}
+	
+	@Test
+	public void get_String_emtpy() {
+		this.factory= create();
+		final String s= this.factory.get(new String(""));
+		assertTrue("" == s);
+	}
+	
+	@Test
+	public void get_String_asciiChar() {
+		this.factory= create();
+		final String s= this.factory.get(new String("a"));
+		assertTrue("a" == s);
+	}
+	
+	
+	@Test
+	public void get_Char_asciiChar() {
+		this.factory= create();
+		final String s= this.factory.get('a');
+		assertTrue("a" == s);
+	}
+	
+}
diff --git a/jcommons/org.eclipse.statet.jcommons.util-tests/src/org/eclipse/statet/jcommons/string/CachedStringFactoryTest.java b/jcommons/org.eclipse.statet.jcommons.util-tests/src/org/eclipse/statet/jcommons/string/CachedStringFactoryTest.java
new file mode 100644
index 0000000..40214d1
--- /dev/null
+++ b/jcommons/org.eclipse.statet.jcommons.util-tests/src/org/eclipse/statet/jcommons/string/CachedStringFactoryTest.java
@@ -0,0 +1,59 @@
+/*=============================================================================#
+ # Copyright (c) 2021 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.jcommons.string;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
+
+@NonNullByDefault
+public class CachedStringFactoryTest extends BasicStringFactoryTest {
+	
+	
+	@Override
+	protected StringFactory create() {
+		return new CacheStringFactory(10);
+	}
+	
+	
+	@Test
+	public void get_CharSequence_cached() {
+		this.factory= create();
+		final String s= this.factory.get(new StringBuilder("ab"));
+		assertEquals("ab", s);
+		assertTrue(s == this.factory.get(new StringBuilder("ab")));
+	}
+	
+	@Test
+	public void get_CharArrayString_cached() {
+		this.factory= create();
+		final String s= this.factory.get(new CharArrayString("ab"));
+		assertEquals("ab", s);
+		assertTrue(s == this.factory.get(new CharArrayString("ab")));
+	}
+	
+	@Test
+	public void get_String_cached() {
+		this.factory= create();
+		final String s= this.factory.get(new String("ab"));
+		assertEquals("ab", s);
+		assertTrue(s == this.factory.get(new String("ab")));
+	}
+	
+}
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/string/BasicStringFactory.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/string/BasicStringFactory.java
index e09285c..6ab2bea 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/string/BasicStringFactory.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/string/BasicStringFactory.java
@@ -14,6 +14,7 @@
 
 package org.eclipse.statet.jcommons.string;
 
+import org.eclipse.statet.jcommons.lang.NonNull;
 import org.eclipse.statet.jcommons.lang.NonNullByDefault;
 
 
@@ -24,22 +25,68 @@
 	public static final BasicStringFactory INSTANCE= new BasicStringFactory();
 	
 	
+	static final int CHARTABLE_SIZE= 0x7F;
+	static final @NonNull String[] CHARTABLE;
+	static {
+		CHARTABLE= new @NonNull String[CHARTABLE_SIZE];
+		for (char i= 0; i < CHARTABLE_SIZE; i++) {
+			CHARTABLE[i]= String.valueOf(i).intern();
+		}
+	}
+	
+	
 	public BasicStringFactory() {
 	}
 	
 	
 	@Override
 	public String get(final CharSequence s) {
-		return s.toString();
+		switch (s.length()) {
+		case 0:
+			return ""; //$NON-NLS-1$
+		case 1:
+			return get(s.charAt(0));
+		default:
+			return s.toString();
+		}
 	}
 	
 	@Override
 	public String get(final CharArrayString s) {
-		return s.toString();
+		switch (s.length()) {
+		case 0:
+			return ""; //$NON-NLS-1$
+		case 1:
+			return get(s.charAt(0));
+		default:
+			return s.toString();
+		}
 	}
 	
 	@Override
 	public String get(final String s) {
+		switch (s.length()) {
+		case 0:
+			return ""; //$NON-NLS-1$
+		case 1:
+			return get(s.charAt(0), s);
+		default:
+			return s;
+		}
+	}
+	
+	@Override
+	public String get(final char c) {
+		if (c >= 0 && c < CHARTABLE_SIZE) {
+			return CHARTABLE[c];
+		}
+		return String.valueOf(c);
+	}
+	
+	private String get(final char c, final String s) {
+		if (c >= 0 && c < CHARTABLE_SIZE) {
+			return CHARTABLE[c];
+		}
 		return s;
 	}
 	
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/string/CacheStringFactory.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/string/CacheStringFactory.java
index 4138ca3..247c069 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/string/CacheStringFactory.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/string/CacheStringFactory.java
@@ -14,25 +14,22 @@
 
 package org.eclipse.statet.jcommons.string;
 
+import static org.eclipse.statet.jcommons.string.BasicStringFactory.CHARTABLE;
+import static org.eclipse.statet.jcommons.string.BasicStringFactory.CHARTABLE_SIZE;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
 
 /**
  * A cache of fixed size, interning string
  */
+@NonNullByDefault
 public class CacheStringFactory implements StringFactory {
 	
 	
 	private static final int HASHSET_SIZE= 0x400;
 	private static final int HASHSET_MASK= HASHSET_SIZE - 1; // bits true from right
 	
-	private static final int CHARTABLE_SIZE= 0x7F;
-	private static final String[] CHARTABLE;
-	static {
-		CHARTABLE= new String[CHARTABLE_SIZE];
-		for (char i= 0; i < CHARTABLE_SIZE; i++) {
-			CHARTABLE[i]= String.valueOf(i).intern();
-		}
-	}
-	
 	
 	private final String[] values;
 	
@@ -46,12 +43,12 @@
 	
 	
 	@Override
-	public String get(final CharArrayString s) {
+	public String get(final CharSequence s) {
 		switch (s.length()) {
 		case 0:
 			return ""; //$NON-NLS-1$
 		case 1:
-			return getChar(s.charAt(0));
+			return get(s.charAt(0));
 		default:
 			if (s.length() > this.maxCachedLength) {
 				return s.toString();
@@ -61,12 +58,12 @@
 	}
 	
 	@Override
-	public String get(final CharSequence s) {
+	public String get(final CharArrayString s) {
 		switch (s.length()) {
 		case 0:
 			return ""; //$NON-NLS-1$
 		case 1:
-			return getChar(s.charAt(0));
+			return get(s.charAt(0));
 		default:
 			if (s.length() > this.maxCachedLength) {
 				return s.toString();
@@ -81,7 +78,7 @@
 		case 0:
 			return ""; //$NON-NLS-1$
 		case 1:
-			return getChar(s.charAt(0), s);
+			return get(s.charAt(0), s);
 		default:
 			if (s.length() > this.maxCachedLength) {
 				return s;
@@ -91,7 +88,8 @@
 	}
 	
 	
-	private String getChar(final char c) {
+	@Override
+	public String get(final char c) {
 		if (c >= 0 && c < CHARTABLE_SIZE) {
 //			this.statCharmap++;
 			return CHARTABLE[c];
@@ -108,7 +106,7 @@
 		}
 	}
 	
-	private String getChar(final char c, final String s) {
+	private String get(final char c, final String s) {
 		if (c >= 0 && c < CHARTABLE_SIZE) {
 //			this.statCharmap++;
 			return CHARTABLE[c];
@@ -125,6 +123,7 @@
 		}
 	}
 	
+	
 	private int computeHash(final CharSequence s) {
 		int hashCode= 0;
 		int length= s.length();
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/string/StringFactory.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/string/StringFactory.java
index 4c72615..ed15322 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/string/StringFactory.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/string/StringFactory.java
@@ -24,10 +24,10 @@
 public interface StringFactory {
 	
 	
-	String get(CharArrayString s);
-	
 	String get(CharSequence s);
-	
+	String get(CharArrayString s);
 	String get(String s);
 	
+	String get(char c);
+	
 }