package er.extensions.foundation; import org.apache.commons.lang3.StringUtils; import org.junit.Before; import org.junit.Test; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSMutableArray; import er.erxtest.ERXTestCase; public class ERXStringUtilitiesTest extends ERXTestCase { /** * Represents a simple encapsulation of two strings and their expected * Levenshtein distance. */ private class LevenshteinExample { /** * First string */ public String s1; /** * Second string */ public String s2; /** * Levenshtein distance between {@code s1} and {@code s2} */ public int d; /** * Constructor * * @param s1 * first string * @param s2 * second string * @param d * Levenshtein distance */ public LevenshteinExample(String s1, String s2, int d) { this.s1 = s1; this.s2 = s2; this.d = d; } } /** * An array of known strings and distances */ private NSArray<LevenshteinExample> levs; @Before public void setUp() throws Exception { // Set up the levs array NSMutableArray<LevenshteinExample> l = new NSMutableArray<>(); // When the values are the same, the distance is zero. l.add(new LevenshteinExample("", "", 0)); l.add(new LevenshteinExample("1", "1", 0)); l.add(new LevenshteinExample("12", "12", 0)); l.add(new LevenshteinExample("123", "123", 0)); l.add(new LevenshteinExample("1234", "1234", 0)); l.add(new LevenshteinExample("12345", "12345", 0)); l.add(new LevenshteinExample("password", "password", 0)); // When one of the values is empty, the distance is the length of the // other value. l.add(new LevenshteinExample("", "1", 1)); l.add(new LevenshteinExample("", "12", 2)); l.add(new LevenshteinExample("", "123", 3)); l.add(new LevenshteinExample("", "1234", 4)); l.add(new LevenshteinExample("", "12345", 5)); l.add(new LevenshteinExample("", "password", 8)); l.add(new LevenshteinExample("1", "", 1)); l.add(new LevenshteinExample("12", "", 2)); l.add(new LevenshteinExample("123", "", 3)); l.add(new LevenshteinExample("1234", "", 4)); l.add(new LevenshteinExample("12345", "", 5)); l.add(new LevenshteinExample("password", "", 8)); // Whenever a single character is inserted or removed, the distance is // one. l.add(new LevenshteinExample("password", "1password", 1)); l.add(new LevenshteinExample("password", "p1assword", 1)); l.add(new LevenshteinExample("password", "pa1ssword", 1)); l.add(new LevenshteinExample("password", "pas1sword", 1)); l.add(new LevenshteinExample("password", "pass1word", 1)); l.add(new LevenshteinExample("password", "passw1ord", 1)); l.add(new LevenshteinExample("password", "passwo1rd", 1)); l.add(new LevenshteinExample("password", "passwor1d", 1)); l.add(new LevenshteinExample("password", "password1", 1)); l.add(new LevenshteinExample("password", "assword", 1)); l.add(new LevenshteinExample("password", "pssword", 1)); l.add(new LevenshteinExample("password", "pasword", 1)); l.add(new LevenshteinExample("password", "pasword", 1)); l.add(new LevenshteinExample("password", "passord", 1)); l.add(new LevenshteinExample("password", "passwrd", 1)); l.add(new LevenshteinExample("password", "passwod", 1)); l.add(new LevenshteinExample("password", "passwor", 1)); // Whenever a single character is replaced, the distance is one. l.add(new LevenshteinExample("password", "Xassword", 1)); l.add(new LevenshteinExample("password", "pXssword", 1)); l.add(new LevenshteinExample("password", "paXsword", 1)); l.add(new LevenshteinExample("password", "pasXword", 1)); l.add(new LevenshteinExample("password", "passXord", 1)); l.add(new LevenshteinExample("password", "passwXrd", 1)); l.add(new LevenshteinExample("password", "passwoXd", 1)); l.add(new LevenshteinExample("password", "passworX", 1)); // If characters are taken off the front and added to the back and all // of // the characters are unique, then the distance is two times the number // of // characters shifted, until you get halfway (and then it becomes easier // to shift from the other direction). l.add(new LevenshteinExample("12345678", "23456781", 2)); l.add(new LevenshteinExample("12345678", "34567812", 4)); l.add(new LevenshteinExample("12345678", "45678123", 6)); l.add(new LevenshteinExample("12345678", "56781234", 8)); l.add(new LevenshteinExample("12345678", "67812345", 6)); l.add(new LevenshteinExample("12345678", "78123456", 4)); l.add(new LevenshteinExample("12345678", "81234567", 2)); // If all the characters are unique and the values are reversed, then // the // distance is the number of characters for an even number of // characters, // and one less for an odd number of characters (since the middle // character will stay the same). l.add(new LevenshteinExample("12", "21", 2)); l.add(new LevenshteinExample("123", "321", 2)); l.add(new LevenshteinExample("1234", "4321", 4)); l.add(new LevenshteinExample("12345", "54321", 4)); l.add(new LevenshteinExample("123456", "654321", 6)); l.add(new LevenshteinExample("1234567", "7654321", 6)); l.add(new LevenshteinExample("12345678", "87654321", 8)); // The rest of these are miscellaneous interesting examples. They will // be illustrated using the following key: // = (the characters are equal) // + (the character is inserted) // - (the character is removed) // # (the character is replaced) // Mississippi // ippississiM // -=##====##=+ --> 6 l.add(new LevenshteinExample("Mississippi", "ippississiM", 6)); // eieio // oieie // #===# --> 2 l.add(new LevenshteinExample("eieio", "oieie", 2)); // brad+angelina // bra ngelina // ===+++======= --> 3 l.add(new LevenshteinExample("brad+angelina", "brangelina", 3)); // test international chars // ?e?uli?ka // e?uli?ka // -======== --> 1 l.add(new LevenshteinExample("?e?uli?ka", "e?uli?ka", 1)); levs = l.immutableClone(); } @Test public void testMaskStringWithCharacter() { String result; String arg = "0123456789"; result = ERXStringUtilities.maskStringWithCharacter(arg, '*', 0, -1); assertEquals("*********9", result); result = ERXStringUtilities.maskStringWithCharacter(arg, '*', 0, arg.length()); assertEquals("**********", result); result = ERXStringUtilities.maskStringWithCharacter(arg, '*', 0, -4); assertEquals("******6789", result); result= ERXStringUtilities.maskStringWithCharacter(arg, '*', 0, 5); assertEquals("*****56789", result); result= ERXStringUtilities.maskStringWithCharacter(arg, '*', 2, 5); assertEquals("01***56789", result); result = ERXStringUtilities.maskStringWithCharacter(arg, '*', -5, 9); assertEquals("01234****9", result); result = ERXStringUtilities.maskStringWithCharacter(arg, '*', -5, -5); assertEquals("0123456789", result); result = ERXStringUtilities.maskStringWithCharacter(arg, '*', -5, 5); assertEquals("0123456789", result); result = ERXStringUtilities.maskStringWithCharacter(arg, '*', -6, -5); assertEquals("0123*56789", result); result = ERXStringUtilities.maskStringWithCharacter("Visa 4111111111111111", '*', 5, -4); assertEquals("Visa ************1111", result); } @Test public void testMaskStringWithCharacter2() { try { //Illegal arguments. endIndex < beginIndex ERXStringUtilities.maskStringWithCharacter("0123456789", '*', 6, 5); fail("expected StringIndexOutOfBoundsException"); } catch (StringIndexOutOfBoundsException e) { // ignore } } @Test public void testMaskStringWithCharacter3() { try { //Illegal arguments. endIndex < beginIndex ERXStringUtilities.maskStringWithCharacter("0123456789", '*', 6, 11); fail("expected StringIndexOutOfBoundsException"); } catch (StringIndexOutOfBoundsException e) { // ignore } } @Test public void testMaskStringWithCharacter4() { try { //Illegal arguments. endIndex < beginIndex ERXStringUtilities.maskStringWithCharacter("0123456789", '*', 11, 12); fail("expected StringIndexOutOfBoundsException"); } catch (StringIndexOutOfBoundsException e) { // ignore } } /** * Tests {@link StringUtils#getLevenshteinDistance(String, String)}. */ @Test public void testDistance() { for (LevenshteinExample l : levs) { assertEquals(l.d, StringUtils.getLevenshteinDistance(l.s1, l.s2), 0.00001); } } /** * Tests {@link StringUtils#getLevenshteinDistance(String, String)}. */ @Test public void testLevenshteinDistance() { for (LevenshteinExample l : levs) { assertEquals(l.d, StringUtils.getLevenshteinDistance(l.s1, l.s2)); } } @Test public void testSafeIdentifierName() { String safeJavaIdentifierStart = "IamSafe"; String safeJavaIdentifierStartWithUnsafeChars = "Iam Nearly+Safe"; String unsafeJavaIdentifierStart = "0safe"; String nullIdentifierStart = null; String emptyIdentifierStart = ""; String prefix = "prefix"; char replacement = '_'; String resultWithSafeStart = ERXStringUtilities.safeIdentifierName(safeJavaIdentifierStart, prefix, replacement); String resultWithSafeStartUnsafeContent = ERXStringUtilities.safeIdentifierName(safeJavaIdentifierStartWithUnsafeChars, prefix, replacement); String resultWithUnsafeStart = ERXStringUtilities.safeIdentifierName(unsafeJavaIdentifierStart, prefix, replacement); String resultWithNullStart = ERXStringUtilities.safeIdentifierName(nullIdentifierStart, prefix, replacement); String resultWithEmptyStart = ERXStringUtilities.safeIdentifierName(emptyIdentifierStart, prefix, replacement); assertEquals(safeJavaIdentifierStart, resultWithSafeStart); assertNotSame(safeJavaIdentifierStartWithUnsafeChars, resultWithSafeStartUnsafeContent); assertEquals("Expected 2 replacements for unsafe characters", 2, resultWithSafeStartUnsafeContent.replaceAll("[^_]", "").length()); assertFalse("Did not expect prefix as identifier starts with safe character.", resultWithSafeStartUnsafeContent.contains(prefix)); assertNotSame(unsafeJavaIdentifierStart, resultWithUnsafeStart); assertFalse("Expected no replacement for unsafe characters", resultWithUnsafeStart.contains("_")); assertTrue("Did expect prefix as identifier starts with unsafe character.", resultWithUnsafeStart.contains(prefix)); assertNotSame(nullIdentifierStart, resultWithNullStart); assertTrue("Did expect 'null' as identifier was null.", resultWithNullStart.contains("null")); assertTrue("Did expect prefix as identifier was null.", resultWithNullStart.contains(prefix)); assertNotSame(emptyIdentifierStart, resultWithEmptyStart); assertEquals(prefix, resultWithEmptyStart); } }