Add string utils to core (#3738)

* Add utils to core

Signed-off-by: lsiepel <leosiepel@gmail.com>
pull/3845/head
lsiepel 2023-10-15 20:35:15 +02:00 committed by GitHub
parent ae85096e53
commit 4001161810
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 432 additions and 0 deletions

View File

@ -0,0 +1,322 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.core.util;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* Static utility methods that are helpful when dealing with strings.
*
* @author Leo Siepel - Initial contribution
*/
@NonNullByDefault
public class StringUtils {
/**
* If a newline char exists at the end of the line, it is removed
*
* <pre>
* Util.chomp(null) = null
* Util.chomp("") = ""
* Util.chomp("abc \r") = "abc "
* Util.chomp("abc\n") = "abc"
* Util.chomp("abc\r\n") = "abc"
* Util.chomp("abc\r\n\r\n") = "abc\r\n"
* Util.chomp("abc\n\r") = "abc\n"
* Util.chomp("abc\n\rabc") = "abc\n\rabc"
* Util.chomp("\r") = ""
* Util.chomp("\n") = ""
* Util.chomp("\r\n") = ""
* </pre>
*
* @param str the input String to escape, may be null
* @return the chomped string, may be null
*/
public static @Nullable String chomp(final @Nullable String str) {
if (str == null || str.isEmpty()) {
return str;
}
if (str.endsWith("\r\n")) {
return str.substring(0, str.length() - 2);
} else if (str.endsWith("\r") || str.endsWith("\n")) {
return str.substring(0, str.length() - 1);
} else {
return str;
}
}
/**
* Simple method to escape XML special characters in String.
* There are five XML special characters which needs to be escaped:
*
* <pre>
* & - &amp;
* < - &lt;
* > - &gt;
* " - &quot;
* ' - &apos;
* </pre>
*
* @param str the input xml as String to escape, may be null
* @return the escaped xml as String, may be null
*/
public static @Nullable String escapeXml(@Nullable String str) {
if (str == null || str.isEmpty()) {
return str;
}
str = str.replace("&", "&amp;");
str = str.replace("<", "&lt;");
str = str.replace(">", "&gt;");
str = str.replace("\"", "&quot;");
str = str.replace("'", "&apos;");
return str;
}
/**
* Capitalizes a String changing the first character to title case.
* No other characters are changed.
*
* <pre>
* "" => ""
* "cat" => "Cat"
* "cAt" => "CAt"
* "'cat'" => "'cat'"
* </pre>
*
* @param val the String to capitalize, may be null
* @return the capitalized String, may be null
*/
public static @Nullable String capitalize(@Nullable String str) {
if (str == null || str.isEmpty()) {
return str;
}
StringBuilder sb = new StringBuilder(str);
sb.setCharAt(0, Character.toUpperCase(str.charAt(0)));
return sb.toString();
}
/**
* Capitalizes words in the string. Only the first char of every word is capitalized, other are set to lowercase.
* Words are recognized by an underscore delimiter.
*
* <pre>
* "openHAB_is_cool" => "Openhab_Is_Cool"
* "foobar_Example" => "Foobar_Example"
* </pre>
*
* @param str the String to capitalize, may be null
* @return the capitalized String, may be null
*/
public static @Nullable String capitalizeByUnderscore(@Nullable String str) {
if (str == null || str.isEmpty()) {
return str;
}
final String delimiter = "_";
StringBuilder capitalizedFully = new StringBuilder();
for (String splitStr : str.split(delimiter)) {
if (splitStr.length() > 0) {
capitalizedFully.append(splitStr.substring(0, 1).toUpperCase());
}
if (splitStr.length() > 1) {
capitalizedFully.append(splitStr.substring(1).toLowerCase());
}
capitalizedFully.append(delimiter);
}
capitalizedFully.setLength(Math.max(capitalizedFully.length() - 1, 0));
return capitalizedFully.toString();
}
/**
* Capitalizes words in the string. Only the first char of every word is capitalized, the rest is left as is.
* Words are recognized by any whitespace.
*
* <pre>
* "openHAB is cool" => "OpenHAB Is Cool"
* "foobar Example" => "Foobar Example"
* </pre>
*
* @param str the String to capitalize, may be null
* @return the capitalized String, may be null
*/
public static @Nullable String capitalizeByWhitespace(@Nullable String str) {
if (str == null || str.isEmpty()) {
return str;
}
StringBuilder processed = new StringBuilder();
for (String splitted : str.split("\\s+")) {
if (splitted.length() > 1) {
processed.append(splitted.substring(0, 1).toUpperCase());
processed.append(splitted.substring(1));
} else {
processed.append(splitted.toUpperCase());
}
processed.append(" ");
}
processed.setLength(Math.max(processed.length() - 1, 0));
return processed.toString();
}
/**
* Pads the string from the left
*
* <pre>
* padLeft("9", 4, "0") => "0009"
* padLeft("3112", 12, "*") => "********3112"
* padLeft("openHAB", 4, "*") => "openHAB"
* </pre>
*
* @param str the String to pad, may be null
* @param minSize the minimum String size to return
* @param padString the String to add when padding
* @return the padded String
*/
public static String padLeft(@Nullable String str, int minSize, String padString) {
if (str == null) {
str = "";
}
return String.format("%" + minSize + "s", str).replace(" ", padString);
}
/**
* Creates a random string
*
* @param length the length of the String to return
* @param charset the characters to use to create the String
* @return the random String
*/
public static String getRandomString(int length, String charset) {
StringBuilder sb = new StringBuilder(length);
SecureRandom secureRandom = new SecureRandom();
for (int i = 0; i < length; i++) {
final int index = secureRandom.nextInt(charset.length());
sb.append(charset.charAt(index));
}
return sb.toString();
}
/**
* Creates a random string with [A-Zaz] characters
*
* @param length the length of the String to return
* @return the random String
*/
public static String getRandomAlphabetic(int length) {
return StringUtils.getRandomString(length, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvxyz");
}
/**
* Creates a random string with only hex characters
*
* @param length the length of the String to return
* @return the random String
*/
public static String getRandomHex(int length) {
return StringUtils.getRandomString(length, "0123456789ABCDEF");
}
/**
* Creates a random string with [A-Za-z0-9] characters
*
* @param length the length of the String to return
* @return the random String
*/
public static String getRandomAlphanumeric(int length) {
return StringUtils.getRandomString(length, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvxyz");
}
/**
* Splits the string by character type into an array
*
* <pre>
* "ab de fg" => "ab", " ", "de", " ", "fg";
* "ab de fg" => "ab", " ", "de", " ", "fg";
* "ab:cd:ef" => "ab", ":", "cd", ":", "ef";
* "number5" => "number", "5"
* "fooBar" => "foo", "Bar"
* "OHRules" => "OH", "Rules"
* </pre>
*
* @param str the input String to split, may be null
* @return the splitted String
*/
public static String[] splitByCharacterType(@Nullable String str) {
if (str == null || str.isBlank()) {
return new String[0];
}
List<String> cache = new ArrayList<>();
char[] inputAsCharArray = str.toCharArray();
int prevType = Character.getType(inputAsCharArray[0]);
int prevTypeStart = 0;
for (int i = prevTypeStart + 1; i < inputAsCharArray.length; i++) {
int curType = Character.getType(inputAsCharArray[i]);
if (prevType == curType) {
continue;
}
if (curType == Character.LOWERCASE_LETTER && prevType == Character.UPPERCASE_LETTER) {
int tmpStart = i - 1;
if (tmpStart != prevTypeStart) {
cache.add(new String(inputAsCharArray, prevTypeStart, tmpStart - prevTypeStart));
prevTypeStart = tmpStart;
}
} else {
cache.add(new String(inputAsCharArray, prevTypeStart, i - prevTypeStart));
prevTypeStart = i;
}
prevType = curType;
}
cache.add(new String(inputAsCharArray, prevTypeStart, inputAsCharArray.length - prevTypeStart));
return cache.toArray(String[]::new);
}
/**
* Simple method to un escape XML special characters in String.
* There are five XML Special characters which needs to be escaped:
*
* <pre>
* & => &amp;
* < => &lt;
* > => &gt;
* " => &quot;
* ' => &apos;
* </pre>
*
* @param input the input xml as String to unescape, may be null
* @return the unescaped xml as String, may be null
*/
public static @Nullable String unEscapeXml(@Nullable String str) {
if (str == null || str.isEmpty()) {
return str;
}
str = str.replace("&amp;", "&");
str = str.replace("&lt;", "<");
str = str.replace("&gt;", ">");
str = str.replace("&quot;", "\"");
str = str.replace("&apos;", "'");
return str;
}
}

View File

@ -0,0 +1,110 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.core.util;
import static org.junit.jupiter.api.Assertions.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
/**
* The {@link StringUtils} class defines some static string utility methods
*
* @author Leo Siepel - Initial contribution
*/
@NonNullByDefault
public class StringUtilsTest {
@Test
public void chompTest() {
assertEquals("", StringUtils.chomp(""));
assertEquals(null, StringUtils.chomp(null));
assertEquals("abc ", StringUtils.chomp("abc \r"));
assertEquals("abc", StringUtils.chomp("abc\n"));
assertEquals("abc", StringUtils.chomp("abc\r\n"));
assertEquals("abc\r\n", StringUtils.chomp("abc\r\n\r\n"));
assertEquals("abc\n", StringUtils.chomp("abc\n\r"));
assertEquals("abc\n\rabc", StringUtils.chomp("abc\n\rabc"));
assertEquals("", StringUtils.chomp("\r"));
assertEquals("", StringUtils.chomp("\n"));
assertEquals("", StringUtils.chomp("\r\n"));
}
@Test
public void escapeXmlTest() {
assertEquals(null, StringUtils.escapeXml(null));
assertEquals(" ", StringUtils.escapeXml(" "));
assertEquals("invalidxml", StringUtils.escapeXml("invalidxml"));
assertEquals("&lt;xmlExample&gt;&amp;&lt;/xmlExample&gt;", StringUtils.escapeXml("<xmlExample>&</xmlExample>"));
assertEquals("&lt;xmlExample&gt;&quot;&lt;/xmlExample&gt;",
StringUtils.escapeXml("<xmlExample>\"</xmlExample>"));
assertEquals("&lt;xmlExample&gt;&apos;&lt;/xmlExample&gt;",
StringUtils.escapeXml("<xmlExample>\'</xmlExample>"));
}
@Test
public void capitalizeTest() {
assertEquals(null, StringUtils.capitalize(null));
assertEquals(" ", StringUtils.capitalize(" "));
assertEquals("Cat", StringUtils.capitalize("cat"));
assertEquals("CAt", StringUtils.capitalize("cAt"));
assertEquals("'cat'", StringUtils.capitalize("'cat'"));
}
@Test
public void capitalizeAllWordsTest() {
assertEquals(null, StringUtils.capitalizeByUnderscore(null));
assertEquals("Openhab_Is_Cool", StringUtils.capitalizeByUnderscore("openHAB_is_cool"));
assertEquals("Foobar_Example", StringUtils.capitalizeByUnderscore("foobar_Example"));
assertEquals("'another_Test'", StringUtils.capitalizeByUnderscore("'another_test'"));
}
@Test
public void capitalizeWordsTest() {
assertEquals("OpenHAB Is Cool", StringUtils.capitalizeByWhitespace("openHAB is cool"));
assertEquals("Foobar Example", StringUtils.capitalizeByWhitespace("foobar Example"));
assertEquals("'another Test'", StringUtils.capitalizeByWhitespace("'another test'"));
}
@Test
public void getRandomString() {
String randomstring = StringUtils.getRandomString(10000, "123");
assertEquals(6666, randomstring.replace("1", "").length(), 333,
"randomString does not equaly (<5% delta) use all characters in set");
}
@Test
public void padLeft() {
assertEquals("000000", StringUtils.padLeft("", 6, "0"));
assertEquals("000000", StringUtils.padLeft(null, 6, "0"));
assertEquals("000teststr", StringUtils.padLeft("teststr", 10, "0"));
assertEquals("AAAAAAp3RF@CT", StringUtils.padLeft("p3RF@CT", 13, "A"));
assertEquals("nopaddingshouldhappen", StringUtils.padLeft("nopaddingshouldhappen", 21, "x"));
assertEquals("LongerStringThenMinSize", StringUtils.padLeft("LongerStringThenMinSize", 10, "x"));
}
@Test
public void splitByCharacterType() {
assertArrayEquals(new String[0], StringUtils.splitByCharacterType(null));
assertArrayEquals(new String[0], StringUtils.splitByCharacterType(""));
assertArrayEquals(new String[] { "ab", " ", "de", " ", "fg" }, StringUtils.splitByCharacterType("ab de fg"));
assertArrayEquals(new String[] { "ab", " ", "de", " ", "fg" },
StringUtils.splitByCharacterType("ab de fg"));
assertArrayEquals(new String[] { "ab", ":", "cd", ":", "ef" }, StringUtils.splitByCharacterType("ab:cd:ef"));
assertArrayEquals(new String[] { "number", "5" }, StringUtils.splitByCharacterType("number5"));
assertArrayEquals(new String[] { "foo", "Bar" }, StringUtils.splitByCharacterType("fooBar"));
assertArrayEquals(new String[] { "foo", "200", "Bar" }, StringUtils.splitByCharacterType("foo200Bar"));
assertArrayEquals(new String[] { "ASF", "Rules" }, StringUtils.splitByCharacterType("ASFRules"));
}
}