com.storedobject.common.StringUtility Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of so-common Show documentation
Show all versions of so-common Show documentation
SO Common utility classes
/*
* Copyright 2018 Syam Pillai
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.storedobject.common;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.security.MessageDigest;
import java.text.NumberFormat;
import java.util.*;
/**
* String utility functions.
*
* @author Syam
*/
public class StringUtility {
/**
* Convert to lower case.
* @param s String array to be converted.
* @return Array
*/
public static String[] toLowerCase(String[] s) {
if(s == null) {
return s;
}
for(int i=0; i tagDuplicates(java.util.List s) {
int i, j, tag;
String t;
for(i = 1; i < s.size(); i++) {
if(s.get(i) == null) {
continue;
}
tag = 1;
t = s.get(i);
for(j = 0; j < i; j++) {
if(s.get(j) != null && s.get(j).equals(t)) {
++tag;
t = s.get(i) + " (" + tag + ")";
j = -1;
}
}
s.set(i, t);
}
return s;
}
/**
* Convert the list to a String array. Null values will be converted to empty strings.
* @param list List to be converted
* @return String array.
*/
public static String[] toArray(java.util.List list) {
String[] s = new String[list.size()];
int i = 0;
for(Object obj: list) {
s[i++] = obj == null ? "" : obj.toString();
}
return s;
}
/**
* Trim each element of a String array
* @param s String array to be trimmed
* @return Array
*/
public static String[] trim(String[] s) {
for(int i=0; i= 0) {
if(!Character.isWhitespace(s.charAt(i))) break;
--i;
}
++i;
return s.substring(0, i);
}
/**
* Left trim a string
* @param s String to be trimmed
* @return The result String
*/
public static String trimLeft(String s) {
int i;
for(i=0; i 0) {
x.append(s);
--times;
}
return x.toString();
}
/**
* Right pad a String with spaces
* @param s String to be padded. A null String is treated as "".
* @param length Length of the String after padding. Warning: The resulting String may be shorter than
* the passed, one depending on this parameter.
* @return The result String
*/
public static String padRight(String s, int length) {
return padRight(s, length, ' ');
}
/**
* Right pad a String with the padChar passed
* @param s String to be padded. A null String is treated as "".
* @param length Length of the String after padding. Warning: The resulting String may be shorter than
* the passed, one depending on this parameter.
* @param padChar The character to be used for padding.
* @return The result String
*/
public static String padRight(String s, int length, char padChar) {
return makeString(s, length, padChar, false);
}
/**
* Left pad a String with spaces
* @param s String to be padded. A null String is treated as "".
* @param length Length of the String after padding. Warning: The resulting String may be shorter than
* the passed, one depending on this parameter.
* @return The result String
*/
public static String padLeft(String s, int length) {
return padLeft(s, length, ' ');
}
/**
* Left pad a String with the padChar passed
* @param s String to be padded. A null String is treated as "".
* @param length Length of the String after padding. Warning: The resulting String may be shorter than
* the passed, one depending on this parameter.
* @param padChar The character to be used for padding.
* @return The result String
*/
public static String padLeft(String s, int length, char padChar) {
return makeString(s, length, padChar, true);
}
/**
* Make a String of spaces.
* @param length The length of the resulting String
* @return The result String
*/
public static String makeString(int length) {
return makeString("", length);
}
/**
* Make a new String by padding spaces to the String passed
* @param s Input string
* @param length Length of the String after padding. Warning: The resulting String may be shorter than
* the passed one, depending on this parameter.
* @return The result String
*/
public static String makeString(String s, int length) {
return makeString(s, length, false);
}
/**
* Make a new String by padding spaces to the String passed
* @param s Input string
* @param length Length of the String after padding. Warning: The resulting String may be shorter than
* the passed one, depending on this parameter.
* @param padLeft If passed "true", left padding will happen, otherwise padding will happen on the right
* @return The result String
*/
public static String makeString(String s, int length, boolean padLeft) {
return makeString(s, length, ' ', padLeft);
}
/**
* Make a new String by padding the padChar to the String passed
* @param s Input string
* @param length Length of the String after padding. Warning: The resulting String may be shorter than
* the passed one, depending on this parameter.
* @param padChar The character to be used for padding.
* @param padLeft If passed "true", left padding will happen, otherwise padding will happen on the right
* @return The result String
*/
public static String makeString(String s, int length, char padChar, boolean padLeft) {
if(length == 0) {
return "";
}
if(length < 0) {
return s;
}
if(s == null) {
s = "" + padChar;
}
if(s.length() > length) {
if(padLeft) {
return s.substring(s.length()-length);
}
return s.substring(0, length);
}
while(s.length() < length) {
if(padLeft) {
s = padChar + s;
} else {
s += padChar;
}
}
return s;
}
/**
* Smoothen a string by trimming and replacing double spaces with single space. Null will be converted to "". Also, output will be trimmed.
* @param s Input string
* @return Smoothened string
*/
public static String smoothen(String s) {
if(s == null) {
return "";
}
s = s.trim();
while(s.indexOf(" ") >= 0) {
s = s.replace(" ", " ");
}
return s.trim();
}
/**
* Pack a string by removing white spaces. Note: Null value is converted to "".
*
* @param s String to be packed.
* @return String value after removing all white spaces.
*/
public static String pack(String s) {
if(s == null) {
return "";
}
return s.replaceAll("\\s", "");
}
/**
* Pack all the strings in an array by removing white spaces. Note: Null values are converted to "".
*
* @param s String array to be packed.
* @return Converted array value after removing all white spaces from the elements.
*/
public static String[] pack(String[] s) {
if(s == null) {
return null;
}
for(int i = 0; i < s.length; i++) {
s[i] = pack(s[i]);
}
return s;
}
/**
* Remove null values from the array of Strings.
*
* @param s String array from which null values need to be removed.
* @return Converted array containing no null values. Array length may shrink because of the removals.
*/
public static String[] removeNulls(String[] s) {
if(s == null) {
return null;
}
int n = 0;
for(String t: s) {
if(t != null) {
++n;
}
}
String[] v = new String[n];
n = 0;
for(String t: s) {
if(t != null) {
v[n++] = t;
}
}
return v;
}
/**
* Convert a byte array into a String of hex values. Each is converted to 2 hex charaters.
* For example, StringUtility.toHex( new byte[ ] { 10, 17, 19, 4, 11 } ) will return "0A1113040B".
* @param bytes The byte array to be converted.
* @return The result String
*/
public static String toHex(byte[] bytes) {
String hex = "";
int b;
for(int i=0; i= 1440) {
time -= 1440;
}
String s;
int h, m;
h = time / 60;
if(h < 12) {
if(h == 0) {
h = 12;
}
s = " AM";
} else {
s = " PM";
if(h > 12) {
h -= 12;
}
}
m = time % 60;
return (h < 10 ? "0" : "") + h + ":" + (m < 10 ? "0" : "") + m + s;
}
private final static String[] propositions = new String[] {
"A", "After", "And", "As", "At", "Be", "Before", "By", "Down", "For", "From", "Of", "Off", "On", "Or", "Out", "Over", "In", "Not",
"Per", "The", "Till", "To", "Under", "Until", "Up", "With",
};
/**
* Make label by inserting a space before each capital letter. Also, all dash characters ("-", "_") are
* converted into " - ". Single quotes will be copied as such. If "as" keyword is found, the word sequences after that keyword will be used for
* generating the label.
* Examples: "ShortName" => "Short Name", "CountryName" => "Country Name", "CountryOfDestination as Shipment Country" => "Shipment Country",
* "KGB" => "KGB", "KGBAgent" => "KGB Agent", "eDocument" => "eDocument", "eGovernanceInitiative" => "eGovernance Initiative",
* "Document_PartI" => "Document - Part I.
* Also, class casting type constructs will be removed. Example: "(com.storedobject.core.Person)Employee" => "Employee".
* @param s String to be converted.
* @param lowerPropositions Whether to convert propositions to lower case or not.
* @return Converted string.
*/
public static String makeLabel(String s, boolean lowerPropositions) {
if(isWhite(s)) {
return s;
}
int i = s.toLowerCase().indexOf(" as ");
if(i > 0) {
String t = s.substring(i + 4).trim();
if(t.startsWith("/")) {
int p = t.indexOf(' ');
if(p > 0) {
s = t.substring(p + 1).trim();
} else {
s = s.substring(0, i).trim();
}
} else {
s = t;
}
return s;
}
if(s.startsWith(".")) {
s = s.substring(1);
}
if(s.startsWith("\"")) {
s = s.substring(1);
}
if(s.endsWith("\"")) {
s = s.substring(0, s.length() - 1);
}
if(isWhite(s) || s.contains(" ")) {
return s;
}
char c;
if(i < 0) { // No 'as'
i = s.indexOf('(');
int p = s.indexOf(')', i);
if(i >= 0 && p > i) {
String t;
boolean pack;
int j;
while(i >= 0 && p > i) {
t = s.substring(i + 1, p);
pack = true;
for(j = 0; pack && j < t.length(); j++) {
c = t.charAt(j);
pack = c == '.' || Character.isLetterOrDigit(c);
}
if(pack) {
s = s.replace("(" + t + ")", "");
} else {
i = p;
}
i = s.indexOf('(', i);
p = s.indexOf(')', i);
}
}
}
StringBuilder t = new StringBuilder();
i = 0;
while(Character.isWhitespace(s.charAt(i))) {
++i;
}
char pc;
c = s.charAt(i);
boolean wasSpace = !Character.isLetterOrDigit(c), prefix= false;
if(c == '_') {
c = '-';
} else {
if(!(s.length() > (i+1) && Character.isUpperCase(s.charAt(i + 1)))) {
c = Character.toUpperCase(c);
} else {
prefix = true;
}
}
t.append(c);
for(++i; i < s.length(); i++) {
pc = c;
c = s.charAt(i);
if(Character.isWhitespace(c)) {
wasSpace = true;
continue;
}
if(c == '\'') {
wasSpace = false;
t.append(c);
continue;
}
if(c == '_') {
c = '-';
}
if(!Character.isLetterOrDigit(c)) {
if(!(c == '.' || c == ',' || c == ';' || c == ')' || c == ']' || c == '}')) {
t.append(' ');
}
t.append(c);
wasSpace = true;
continue;
}
if(Character.isUpperCase(c)) {
if(prefix) {
prefix = false;
} else {
if(!Character.isUpperCase(pc)) {
if(!(pc == '(' || pc == '[' || pc == '{')) {
t.append(' ');
}
} else {
pc = s.length() > (i+1) ? s.charAt(i + 1) : 'X';
if(Character.isLetterOrDigit(pc) && !Character.isUpperCase(pc)) {
t.append(' ');
}
}
}
t.append(c);
wasSpace = false;
continue;
}
if(wasSpace) {
if(!(s.length() > (i+1) && Character.isUpperCase(s.charAt(i + 1)))) {
c = Character.toUpperCase(c);
} else {
prefix = true;
}
if(!(pc == '(' || pc == '[' || pc == '{')) {
t.append(' ');
}
t.append(c);
wasSpace = false;
} else {
t.append(c);
}
}
String label = t.toString();
if(!lowerPropositions) {
return label;
}
String r;
for(String proposition: propositions) {
r = " " + proposition + " ";
if(label.contains(r)) {
label = label.replace(r, r.toLowerCase());
}
if(label.endsWith(" " + proposition)) {
label = label.substring(0, label.length() - proposition.length()) + proposition.toLowerCase();
}
}
s = "In-house";
if(label.startsWith("In House")) {
label = s + label.substring(8);
}
if(label.endsWith(" in House")) {
label = label.substring(0, label.length() - 8) + s;
}
s = " " + s + " ";
label = label.replace(" in House ", s);
return label;
}
/**
* Make label by inserting a space before each capital letter. Also, all dash characters ("-", "_") are
* converted into " - ". Single quotes will be copied as such. If "as" keyword is found, the word sequences after that keyword will be used for
* generating the label. Propositions in the resultant string will be in lower case.
* Examples: "ShortName" => "Short Name", "CountryName" => "Country Name", "CountryOfDestination as Shipment Country" => "Shipment Country",
* "KGB" => "KGB", "KGBAgent" => "KGB Agent", "eDocument" => "eDocument", "eGovernanceInitiative" => "eGovernance Initiative",
* "Document_PartI" => "Document - Part I".
* Also, class casting type constructs will be removed. Example: "(com.storedobject.core.Person)Employee" => "Employee".
* @param s String to be converted.
* @return Converted string.
*/
public static String makeLabel(String s) {
return makeLabel(s, true);
}
/**
* Convert an integer list to a range string. For example, { 1, 2, 3, 5, 7, 8, 9 } will be converted to "1 - 3, 5, 7 - 9".
*
* @param list List of numbers to be converted.
* @return Range string
*/
public static String toRangeString(Collection list) {
int i = 0;
int[] n = new int[list.size()];
for(Integer v: list) {
n[i++] = v;
}
return toRangeString(n);
}
/**
* Convert an int array into a range string. For example, an array { 1, 2, 3, 5, 7, 8, 9 } will be converted to "1 - 3, 5, 7 - 9".
*
* @param n int array of numbers to be converted.
* @return Range string
*/
public static String toRangeString(int[] n) {
if(n == null || n.length == 0) {
return "";
}
Arrays.sort(n);
int p = n[0];
String s = "" + p;
for(int i=1; i= 0) {
s = s.replaceAll(" , ", ",");
}
while(s.indexOf(", ") >= 0) {
s = s.replaceAll(", ", ",");
}
while(s.indexOf(" ,") >= 0) {
s = s.replaceAll(" ,", ",");
}
while(s.indexOf(" - ") >= 0) {
s = s.replaceAll(" - ", "-");
}
while(s.indexOf("- ") >= 0) {
s = s.replaceAll("- ", "-");
}
while(s.indexOf(" -") >= 0) {
s = s.replaceAll(" -", "-");
}
if(s.length() == 0) {
return new int[0];
}
if(s.endsWith(",") || s.endsWith("-")) {
return null;
}
int i, r1, r2;
char c;
for(i=0; i v = new ArrayList();
String[] t = s.split(",");
for(i=0; i 0) {
r1 = Integer.parseInt(s.substring(0, s.indexOf('-')));
v.add(r1);
r2 = Integer.parseInt(s.substring(s.indexOf('-')+1));
while((++r1) < r2) {
v.add(r1);
}
v.add(r2);
} else {
v.add(Integer.parseInt(s));
}
} catch(Exception ignored) {
}
}
int[] n = new int[v.size()];
for(i=0; i= 0) {
++count;
++p;
}
return count;
}
/**
* Gets the number of occurrences of a given string in this string.
*
* @param text String to be scanned.
* @param pattern String to look for.
* @return Occurrences
*/
public static int getStringCount(String text, String pattern) {
if(text == null || pattern == null || pattern.length() > text.length()) {
return 0;
}
int p = 0, count = 0;
while((p = text.indexOf(pattern, p)) >= 0) {
++count;
p += pattern.length();
}
return count;
}
/**
* Replaces all occurrences of 'target' with 'replacement' outside the quoted portion (single or double quotes).
* @param text String to operated on.
* @param target Target sequence to replace
* @param replacement Replacement text
* @return Replaced string
*/
public static String replaceOutside(String text, CharSequence target, CharSequence replacement) {
if(!text.contains(target)) {
return text;
}
StringBuilder s = new StringBuilder();
int p = 0, n = 0;
char c, quote = 0;
while(n < text.length()) {
c = text.charAt(n);
if(quote == 0) {
if(c == '\'' || c == '"') {
quote = c;
if(p < n) {
s.append(text.substring(p, n).replace(target, replacement));
}
p = n;
}
} else {
if(c == quote) {
quote = 0;
s.append(text, p, n + 1);
p = n + 1;
}
}
n++;
}
if(p < n) {
s.append(text.substring(p, n).replace(target, replacement));
}
return s.toString();
}
/**
* Encode and return the string representation of the object passed. This encoding is useful for
* creating strings that can be used in XML texts. That is, it replaces characters such as "<", "&", ">" etc. the respective
* encoded values "<", "&", ">" etc.
*
* @param obj The object to convert.
* @return XML encoded string.
*/
public static String encodeForXML(Object obj) {
if(obj == null) {
return "";
}
String text = obj.toString();
if(text.indexOf('&') >= 0) {
text = text.replaceAll("&", "&");
}
if(text.indexOf('<') >= 0) {
text = text.replaceAll("<", "<");
}
if(text.indexOf('>') >= 0) {
text = text.replaceAll(">", ">");
}
if(text.indexOf('"') >= 0) {
text = text.replaceAll("\"", """);
}
if(text.indexOf('\'') >= 0) {
text = text.replaceAll("\'", "'");
}
return text;
}
/**
* Compute the modulus 11 check digit for the number passed.
*
* @param number Number
* @return Check digit. -1 is returned for invalid numbers.
*/
public static int modulus11(long number) {
return modulus11("" + number);
}
/**
* Compute the modulus 11 check digit for the alphanumeric string passed.
*
* @param alphanumeric Input
* @return Check digit. -1 is returned for invalid numbers and -2 is returned for invalid strings.
*/
public static int modulus11(String alphanumeric) {
if(alphanumeric == null) {
return -2;
}
if(alphanumeric.startsWith("-") || alphanumeric.startsWith("+")) {
alphanumeric = alphanumeric.substring(1);
}
if(alphanumeric.length() == 0) {
return -2;
}
char c;
int w = 2, d, total = 0;
for(int i = alphanumeric.length() - 1; i >= 0; i--) {
c = alphanumeric.charAt(i);
if(Character.isWhitespace(c)) {
d = 0;
} else if(Character.isDigit(c)) {
d = c - '0';
} else if(Character.isLetter(c)) {
c = Character.toUpperCase(c);
d = ((c - 'A') % 10) + 1;
} else {
return -2;
}
total += w * d;
if(++w == 8) {
w = 2;
}
}
total = 11 - (total % 11);
return total == 11 ? 0 : (total == 10 ? -1 : total);
}
/**
* Check the alphanumeric string passed to see if its ends with a modulus 11 check digit.
*
* @param alphanumeric Input
* @return True or false.
*/
public static boolean isModulus11(String alphanumeric) {
if(alphanumeric == null || alphanumeric.length() < 2) {
return false;
}
String s = alphanumeric.substring(0, alphanumeric.length() - 1);
return (s + modulus11(s)).equals(alphanumeric);
}
/**
* Check the number passed to see if its ends with a modulus 11 check digit.
*
* @param number Number
* @return True or false.
*/
public static boolean isModulus11(long number) {
return isModulus11("" + number);
}
/**
* Stringify the object
*
* @param object Object to be stringified.
* @return String obtained from object.toString(). If object is null or toString method generate errors zero-length string will return.
*/
public static String stringify(Object object) {
String s;
try {
s = object.toString();
return s == null ? "" : s;
} catch(Throwable ignore) {
}
return "";
}
/**
* Get a duplicate string array copy of the array passed. Each element from the source array will be stringified.
*
* @param objects Array of any object copy.
* @return Copy of the array as string array. If the input is null, output will also be null.
* Null elements in the source array will generate null element in the output array.
*/
public static String[] copy(Object[] objects) {
if(objects == null) {
return null;
}
String[] t = new String[objects.length];
int i = 0;
for(Object object: objects) {
t[i++] = object == null ? null : object.toString();
}
return t;
}
/**
* Get a char array from a String array.
*
* @param s Array to be converted.
* @return character array created by taking the first characters of the String array. For null values and values of length zeros,
* a space is set for the corresponding character values.
*/
public static char[] toCharArray(String[] s) {
if(s == null) {
return null;
}
char[] t = new char[s.length];
for(int i = 0; i < s.length; i++) {
t[i] = s[i] == null || s[i].length() == 0 ? ' ' : s[i].charAt(0);
}
return t;
}
/**
* Insert a new element at the given index.
*
* @param a Array for inserting the the new element.
* @param element Element to be inserted.
* @param index Index
* @return Resultant array with length greater than the length of the array passed.
*/
public static String[] insert(String[] a, String element, int index) {
if(a == null || index >= a.length) {
return append(a, element);
}
String[] t = new String[a.length + 1];
if(index >= t.length) {
index = t.length - 1;
}
t[index] = element;
if(a.length > 0) {
if(index > 0) {
System.arraycopy(a, 0, t, 0, index);
}
if(index < (t.length - 1)) {
System.arraycopy(a, index, t, index + 1, a.length - index);
}
}
return t;
}
/**
* Append a new element to an array.
*
* @param a Array to be increased.
* @param element Element to be added.
* @return Resultant array with length greater than the length of the array passed.
*
*/
public static String[] append(String[] a, String element) {
String[] t = new String[a == null ? 1 : a.length + 1];
if(a != null && a.length > 0) {
System.arraycopy(a, 0, t, 0, a.length);
}
t[t.length - 1] = element;
return t;
}
/**
* Remove the first instance of an element from an array.
*
* @param a Array.
* @param element Element to be removed.
* @return Resultant array.
*
*/
public static String[] remove(String[] a, String element) {
if(a == null) {
return null;
}
int i;
for(i = 0; i < a.length; i++) {
if(a[i].equals(element)) {
String[] t = new String[a.length - 1];
if(t.length == 0) {
return t;
}
System.arraycopy(a, 0, t, 0, i);
if(i == t.length) {
return t;
}
System.arraycopy(a, i + 1, t, i, t.length - i);
return t;
}
}
return a;
}
/**
* Remove all instances of an element from an array.
*
* @param a Array.
* @param element Element to be removed.
* @return Resultant array.
*
*/
public static String[] removeAll(String[] a, String element) {
if(element == null) {
return removeNulls(a);
}
while(indexOf(a, element) >= 0) {
a = remove(a, element);
}
return a;
}
/**
* Concatenate two strings.
*
* @param first First string.
* @param second Second string.
* @return Concatenated string.
*/
public static String[] concat(String[] first, String[] second) {
if(first == null && second == null) {
return new String[0];
}
if(first == null || first.length == 0) {
return second;
}
if(second == null || second.length == 0) {
return first;
}
String[] s = new String[first.length + second.length];
int i = 0;
for(String t: first) {
s[i++] = t;
}
for(String t: second) {
s[i++] = t;
}
return s;
}
/**
* Find the index of an element in an array.
*
* @param a Array.
* @param element Element to be searched.
* @return First index of the element. -1 is returned if the array is null or the element is not found.
*
*/
public static int indexOf(String[] a, String element) {
return indexOf(a, element, 0);
}
/**
* Find the index of an element in an array.
*
* @param a Array.
* @param element Element to be searched.
* @param from From index
* @return First index of the element. -1 is returned if the array is null or the element is not found.
*
*/
public static int indexOf(String[] a, String element, int from) {
return a == null ? -1 : indexOf(a, element, from, a.length);
}
/**
* Find the index of an element in an array.
*
* @param a Array.
* @param element Element to be searched.
* @param from From index
* @param to To index
* @return First index of the element. -1 is returned if the array is null or the element is not found.
*
*/
public static int indexOf(String[] a, String element, int from, int to) {
if(a == null) {
return -1;
}
if(from < 0) {
from = 0;
}
int i;
if(element == null) {
for(i = from; i < a.length; i++) {
if(i >= to) {
return -1;
}
if(a[i] == null) {
return i;
}
}
return -1;
}
for(i = from; i < a.length; i++) {
if(i >= to) {
return -1;
}
if(element.equals(a[i])) {
return i;
}
}
return -1;
}
/**
* Split the string at the given length into an array. New line characters will be treated
* as if splitting is needed at that position. An attempt is made to split at word boundaries
* if possible. "\r" (carriage return) characters will be removed.
*
* @param s String to split.
* @param length Length at which split happens.
* @return Array of strings containing each part of the string.
**/
public static String[] split(String s, int length) {
if(s == null) {
return new String[0];
}
if(s.indexOf('\r') >= 0) {
s = s.replace("\r", "");
}
int pos = s.indexOf('\n');
if(pos >= 0) {
return concat(split(s.substring(0, pos), length), split(s.substring(pos + 1), length));
}
s = s.trim();
if(s.length() <= length || length <= 0) {
return new String[] { s };
}
pos = length;
while(pos >= 0) {
if(Character.isWhitespace(s.charAt(pos))) {
return concat(new String[] { s.substring(0, pos).trim() }, split(s.substring(pos + 1), length));
}
--pos;
}
return concat(new String[] { s.substring(0, length) }, split(s.substring(length), length));
}
/**
* Parse the string array into "properties". This is useful to parse "command line" parameters.
*
* @param args Argument list as string array.
* @return Properties containing all the argument values passed as the array.
* @throws SOException if anything in the argument does not look like a "command line" parameter.
*/
public static Properties parseToProperties(String[] args) throws SOException {
Properties p = new MultipleKeysProperties();
if(args == null || args.length == 0) {
return p;
}
int i = 0;
try {
if(args.length == 1 && !args[0].startsWith("-")) {
String f = args[0];
InputStream in = IO.getInput(f);
if(f.endsWith(".XML") || f.endsWith(".xml")) {
p.loadFromXML(in);
} else {
p.load(in);
}
return p;
}
String key = null;
while(i < args.length) {
if(args[i].equals("-")) {
++i;
key = args[i];
p.put(key, "1");
} else if(args[i].startsWith("-")) {
key = args[i].substring(1);
p.put(key, "1");
} else {
if(key == null) {
throw new SOException("Invalid_Value '" + args[i] + "'");
}
p.put(key, args[i]);
key = null;
}
++i;
}
} catch(Exception e) {
if(i >= args.length) {
i = args.length - 1;
}
throw new SOException("Invalid_Value '" + args[i] + "'");
}
return p;
}
@SuppressWarnings("serial")
static class MultipleKeysProperties extends Properties {
@Override
public String getProperty(String key) {
if(key.indexOf(',') < 0) {
return super.getProperty(key);
}
String v;
String[] keys = key.split(",");
for(String k: keys) {
v = super.getProperty(k.trim());
if(v != null) {
return v;
}
}
return null;
}
@Override
public String getProperty(String key, String value) {
String v = getProperty(key);
return v == null ? value : v;
}
}
/**
* Find the Levenshtein distance between two strings.
*
* @param s First string
* @param t Second string
* @return Levenstein distance
*/
public static int distanceLevenshtein(String s, String t) {
if(s == null) {
s = "";
}
if(t == null) {
t = "";
}
s = s.toUpperCase();
t = t.toUpperCase();
int n = s.length(); // length of s
int m = t.length(); // length of t
if(n == 0) {
return m;
} else if(m == 0) {
return n;
}
int[] p = new int[n + 1]; //'previous' cost array, horizontally
int[] d = new int[n + 1]; // cost array, horizontally
int[] _d; //placeholder to assist in swapping p and d
// indexes into strings s and t
int i; // iterates through s
int j; // iterates through t
char t_j; // jth character of t
int cost; // cost
for (i = 0; i<=n; i++) {
p[i] = i;
}
for (j = 1; j<=m; j++) {
t_j = t.charAt(j-1);
d[0] = j;
for (i=1; i<=n; i++) {
cost = s.charAt(i-1) == t_j ? 0 : 1;
// minimum of cell to the left + 1, to the top + 1, diagonally left and up +cost
d[i] = Math.min(Math.min(d[i-1] + 1, p[i] + 1), p[i-1] + cost);
}
// copy current distance counts to 'previous row' distance counts
_d = p;
p = d;
d = _d;
}
// our last action in the above loop was to switch d and p, so p now
// actually has the most recent cost counts
return p[n];
}
public static String bitsValue(int value, String[] valueLabels) {
return bitsValue(value, new StringList(valueLabels));
}
public static String bitsValue(int value, StringList valueLabels) {
StringBuilder sb = new StringBuilder();
int i = 0;
while(value > 0 && i < valueLabels.size()) {
if((value & 1) == 1) {
if(sb.length() > 0) {
sb.append(" & ");
}
sb.append(valueLabels.get(i));
}
++i;
value >>= 1;
}
return sb.toString();
}
@SuppressWarnings("serial")
public static class List extends ArrayList {
public List(String[] array) {
if(array == null) {
return;
}
for(String t: array) {
add(t);
}
}
}
public static boolean same(CharSequence one, CharSequence two) {
int len = one.length();
if(len != two.length()) {
return false;
}
for(int i = 0; i < len; i++) {
if(one.charAt(i) != two.charAt(i)) {
return false;
}
}
return true;
}
/**
* Formats a double value as a numeric string with thousands separation. Up to 14 fractional digits will be considered.
*
* @param value Double value to be converted.
* @return Formatted value
*/
public static String format(double value) {
return format(value, -1, false);
}
/**
* Formats a double value as a numeric string. Up to 14 fractional digits will be considered.
*
* @param value Double value to be converted.
* @param separated True if thousands separation is needed in the output.
* @return Formatted value
*/
public static String format(double value, boolean separated) {
return format(value, -1, separated);
}
/**
* Formats a double value as a numeric string with thousands separation.
*
* @param value Double value to be converted.
* @param decimals Number of decimals required in the output string. Passing -1 causes all non-zero decimals up to
* 14 positions to be incorporated.
* @return Formatted value
*/
public static String format(double value, int decimals) {
return format(value, decimals, true);
}
private static final NumberFormat format = NumberFormat.getNumberInstance();
static {
format.setMaximumIntegerDigits(30);
format.setRoundingMode(RoundingMode.HALF_UP);
}
/**
* Formats a double value as a numeric string with thousands separation.
*
* @param value Double value to be converted.
* @param decimals Number of decimals required in the output string. Passing -1 causes all non-zero decimals up to
* 14 positions to be incorporated.
* @param separated True if thousands separation is needed in the output.
* @return Formatted value
*/
public static String format(double value, int decimals, boolean separated) {
boolean negative = value < 0;
if(negative) {
value = -value;
}
if(decimals < 0) {
format.setMinimumFractionDigits(decimals < -1 ? -decimals : 14);
format.setMaximumFractionDigits(decimals < -1 ? -decimals : 14);
} else {
format.setMinimumFractionDigits(decimals);
format.setMaximumFractionDigits(decimals);
}
String s = format.format(value);
if(decimals < 0) {
if(s.indexOf('.') >= 0) {
while(s.endsWith("0") && !s.endsWith(".0")) {
s = s.substring(0, s.length() - 1);
}
}
return (negative ? "-" : "") + format(s, s.endsWith(".0") ? 0 : s.length() - s.indexOf('.') - 1, separated);
}
return (negative ? "-" : "") + format(s, decimals, separated);
}
/**
* Formats a string as a numeric string with thousands separation.
*
* @param s String of digits (can contain a decimal point).
* @param decimals Number of decimals required in the output string.
* @param separated True if thousands separation is needed in the output.
* @return Formatted value
*/
public static String format(String s, int decimals, boolean separated) {
if(s == null) {
s = "0";
}
if(s.startsWith("+")) {
s = s.substring(1);
} else if(s.endsWith("+")) {
s = s.substring(0, s.length()-1);
}
int neg = 0;
if(s.startsWith("-")) {
neg = 1;
s = s.substring(1);
} else if(s.endsWith("-")) {
neg = 2;
s = s.substring(0, s.length()-1);
} else if(s.endsWith("DB")) {
neg = 3;
s = s.substring(0, s.length()-2).trim();
} else if(s.endsWith("CR")) {
neg = 4;
s = s.substring(0, s.length()-2).trim();
}
StringBuffer sb = new StringBuffer();
char c;
boolean decimalFound = false;
for(int i = 0; i < s.length(); i++) {
c = s.charAt(i);
if(c == '.') {
if(decimalFound) {
break;
}
decimalFound = true;
} else if(!Character.isDigit(c)) {
if(c == ',') {
continue;
}
break;
}
sb.append(c);
}
s = sb.toString();
int d = decimals;
int p = s.indexOf('.');
String decimalDigits = null;
if(d < 0 && p >= 0) {
decimalDigits = s.substring(p);
s = s.substring(0, p);
while(true) {
if(decimalDigits.length() <= 1) {
decimalDigits = null;
break;
}
if(decimalDigits.endsWith("0")) {
decimalDigits = decimalDigits.substring(0, decimalDigits.length() - 1);
} else {
break;
}
}
p = -1;
}
String t;
if(p >= 0) {
t = s.substring(p);
if(p > 0) {
s = s.substring(0, p);
} else {
s = "0";
}
} else {
t = ".";
}
if(d == 0) {
t = "";
} else {
d -= t.length();
++d;
while(d > 0) {
t += "0";
--d;
}
if(t.length() > (decimals+1)) {
t = t.substring(0, decimals+1);
}
}
while(separated) {
p = s.length() - 3;
if(p <= 0) {
break;
}
t = "," + s.substring(p) + t;
s = s.substring(0, p);
}
s += t;
if(decimalDigits != null) {
s += decimalDigits;
}
switch(neg) {
case 1:
s = "-" + s;
break;
case 2:
s += "-";
break;
case 3:
s += " DB";
break;
case 4:
s += " CR";
break;
}
return s;
}
private static String[] set1 = { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };
private static String[] set2 = { "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" };
private static String[] set3 = { "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" };
/**
* Internal function to convert a 3 digit number to words. It will return empty string for zero.
* @param digit A 3 digit number as string
* @return 3 digit number in words.
*/
private static String words3(String digit) {
while(digit.length() < 3) {
digit = "0" + digit;
}
char c0 = digit.charAt(0), c1 = digit.charAt(1), c2 = digit.charAt(2);
String v = "";
if(c0 != '0') {
v = set1[c0 - '1'] + " hundred";
if((c1 != '0') || (c2 != '0')) {
v += " and ";
}
}
if(c1 != '0') {
if(c1 == '1') {
v += set2[c2 - '0'];
return v;
} else {
v += set3[c1 - '2'];
}
if(c2 != '0') {
v += " " ;
}
}
if( c2 != '0' ) {
v += set1[c2 - '1'];
}
return v;
}
private final static String zero = "zero";
/**
* Converts a numeric value into words. Decimal portion and negative part will be ignored
* @param value Value
* @return Value in words
*/
public static String words(BigDecimal value) {
return value == null ? zero : words(value.toBigInteger());
}
/**
* Converts a numeric value into words. Negative part will be ignored
* @param value Value
* @return Value in words
*/
public static String words(BigInteger value) {
if(value == null) {
return zero;
}
if(value.signum() < 0) {
value = value.negate();
}
return words(value.toString());
}
/**
* Converts a numeric value into words in Indian format. Decimal portion and negative part will be ignored
* @param value Value
* @return Value in words in Indian format
*/
public static String wordsIndian(BigDecimal value) {
return value == null ? zero : wordsIndian(value.toBigInteger());
}
/**
* Converts a numeric value into words in Indian format. Decimal portion and negative part will be ignored
* @param value Value
* @return Value in words in Indian format
*/
public static String wordsIndian(BigInteger value) {
if(value == null) {
return zero;
}
if(value.signum() < 0) {
value = value.negate();
}
return wordsIndian(value.toString()).replace(",", "");
}
private final static String[] crores = new String[]{"", " thousand", " lakhs", " crores"};
private final static String[] millions = new String[]{"", " thousand", " million", " billion", " trillion",
" quadrillion", " quintillion", " sextillion", " septillion"};
/**
* Internal method to convert a numerical non-empty string to words.
* @param s Numerial non-empty string
* @return Words
*/
private static String words(String s) {
if(s.length() > (3 * millions.length)) {
int n = s.length() - (millions.length - 1) * 3;
return words(s.substring(0, n)) + millions[millions.length - 1] + ", " + words(s.substring(n));
}
if(s.equals("0")) {
return zero;
}
if(s.length() <= 3) {
return words3(s);
}
return words(s, 0);
}
private static String words(String s, int i) {
String w;
if(s.length() <= 3) {
w = words3(s);
return w.isEmpty() ? w : (w + millions[i]);
}
String p = words(s.substring(0, s.length() - 3), i + 1);
w = words(s.substring(s.length() - 3));
if(w.isEmpty()) {
return p;
}
return p + ", " + w + millions[i];
}
/**
* Internal method to convert a numerical non-empty string to words in Indian style.
* @param s Numerial non-empty string
* @return Words
*/
private static String wordsIndian(String s) {
if(s.length() > (3 * (crores.length - 1) + 2)) {
int n = s.length() - ((crores.length - 2) * 2 + 3);
return wordsIndian(s.substring(0, n)) + crores[crores.length - 1] + ", " + wordsIndian(s.substring(n));
}
if(s.equals("0")) {
return zero;
}
if(s.length() <= 3) {
return words3(s);
}
return wordsIndian(s, 0);
}
private static String wordsIndian(String s, int i) {
String w;
if(s.length() <= 2) {
w = words3(s);
return w.isEmpty() ? w : (w + crores[i]);
}
String p = wordsIndian(s.substring(0, s.length() - (i == 0 ? 3 : 2)), i + 1);
w = wordsIndian(s.substring(s.length() - (i == 0 ? 3 : 2)));
if(w.isEmpty()) {
return p;
}
return p + ", " + w + crores[i];
}
/**
* See if 2 character sequences contain same sequence of characters
*
* @param one First character sequence.
* @param two Second character sequence.
* @return True/False.
*/
public static boolean equals(CharSequence one, CharSequence two) {
if(one == null && two == null) {
return true;
}
if(one == null || two == null) {
return false;
}
int i = 0;
while(true) {
if(one.length() == i && two.length() == i) {
return true;
}
if(one.length() == i || two.length() == i) {
return false;
}
if(one.charAt(i) != two.charAt(i)) {
return false;
}
++i;
}
}
public static String getTrace(Throwable error) {
return SORuntimeException.getTrace(error);
}
public static String getTrace(Thread thread) {
return SORuntimeException.getTrace(thread);
}
public static String toString(Object message) {
if(message == null) {
return "";
}
if(message instanceof Double) {
String s = message.toString();
if(s.endsWith(".0") && s.length() > 2) {
s = s.substring(0, s.length() - 2);
}
return s;
}
if(message instanceof String) {
return (String)message;
}
if(message instanceof Displayable) {
return ((Displayable)message).toDisplay();
}
if(message instanceof java.util.Date) {
return DateUtility.format((java.util.Date)message);
}
if(message instanceof Boolean) {
return (Boolean) message ? "Yes" : "No";
}
if(message instanceof Throwable) {
if(!(message instanceof SOException || message instanceof SORuntimeException)) {
message = new SORuntimeException((Throwable)message);
}
}
return toString(message.toString());
}
public static String toString(InputStream stream) throws Exception {
return toString(IO.getReader(stream));
}
public static String toString(Reader reader) throws Exception {
StringCollector sc = new StringCollector(reader);
String s = sc.getString();
Exception e = sc.getException();
if(e != null) {
throw e;
}
return s;
}
private static class CSVField {
private String field;
private boolean more = false;
private int nextPosition;
}
private static CSVField nextField(String line, int p, boolean inside) throws Exception {
if(p >= line.length()) {
return null;
}
StringBuilder s = new StringBuilder();
char c;
while(true) {
if(p >= line.length()) {
break;
}
c = line.charAt(p++);
if(inside) {
if(c == '"') {
if(p >= line.length()) {
break;
}
c = line.charAt(p);
if(c == '"') {
++p;
} else {
inside = false;
break;
}
} else if(c == ',') {
if(p < line.length()) {
c = line.charAt(p);
}
if(p >= line.length() || c != ',') {
throw new SOException("Invalid comma at position " + p);
}
++p;
}
s.append(c);
continue;
}
if(c == '"' && s.length() == 0) {
inside = true;
continue;
} else if(c == ',') {
--p;
break;
}
s.append(c);
}
if(!inside && p < line.length() && line.charAt(p) != ',') {
throw new SOException("Comma expected at position " + p);
}
++p;
CSVField f = new CSVField();
f.field = s.toString();
f.nextPosition = p;
f.more = inside;
return f;
}
public static String[] getCSV(BufferedReader reader) throws Exception {
BufferedReader r = IO.get(reader);
String line = r.readLine();
if(line == null || line.length() == 0) {
return null;
}
ArrayList v = new ArrayList();
CSVField f;
int p, i = 0;
boolean more = false;
while(i < line.length()) {
f = nextField(line, i, more);
if(f == null) {
break;
}
i = f.nextPosition;
if(more) {
p = v.size() - 1;
v.set(p, v.get(p) + f.field);
} else {
v.add(f.field);
}
more = f.more;
if(!more) {
continue;
}
while(true) {
line = r.readLine();
if(line == null) {
throw new SOException("Line termination");
}
if(line.length() > 0) {
break;
}
p = v.size() - 1;
v.set(p, v.get(p) + "\n");
}
i = 0;
}
return v.size() == 0 ? null : toArray(v);
}
/**
* Replace variables in the source string with values returned by the filler. Variables in the source string are of the
* form ${VariableName}.
* A sample source may look like "My name is ${name} and my age is ${age}". Here, 'name' and 'age' are the variables and
* the values returned by filler.fill("name") and filler.fill("age") will be used to replace the variables in the string. So,
* the resulting string will look like "My name is Syam Pillai and my age is 40" if the return values are "Syam Pillai" and "40"
* respectively.
*
* @param source The source string.
* @param filler The filler logic.
* @return Result of substituting the variables in the source string with respective values returned by the filler logic.
*/
public static String fill(String source, StringFiller filler) {
if(filler == null) {
return source;
}
StringBuilder s = new StringBuilder();
StringTokenizer st = new StringTokenizer(source, "${}", true);
String t, v;
boolean expectVar = false;
while(st.hasMoreTokens()) {
t = st.nextToken();
if(t.equals("$")) {
if(expectVar) {
s.append('$');
}
expectVar = true;
if(st.hasMoreTokens()) {
t = st.nextToken();
} else {
break;
}
}
if(expectVar && t.equals("{")) {
if(st.hasMoreTokens()) {
v = st.nextToken();
if(v.equals("$")) {
s.append("${");
continue;
}
if(st.hasMoreTokens()) {
t = st.nextToken();
if(t.equals("}") && Character.isLetter(v.charAt(0)) && isLetterOrDigit(v.replace(":", "").replace(".", "").replace("-", ""))) {
s.append(filler.fill(v));
} else {
s.append("${").append(v).append(t);
}
} else {
s.append('$').append(t).append(v);
}
} else {
s.append('$').append(t);
}
expectVar = false;
} else {
if(expectVar) {
s.append('$');
expectVar = false;
}
s.append(t);
}
}
if(expectVar) {
s.append('$');
}
return s.toString();
}
public static > T collect(T list, String[] items) {
return collect(list, new StringList(items));
}
public static > T collect(T list, StringList items) {
list.addAll(items);
return list;
}
private static final char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/**
* MD5 encryption
*
* @param input Input string to encrypt
* @return Encrypted output in hex-coded form
*/
public static String md5(String input) {
byte[] source = null;
source = input.getBytes(IO.UTF8);
String result = null;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(source);
byte[] temp = md.digest();
char[] str = new char[16 * 2];
int k = 0;
for (int i = 0; i < 16; i++) {
byte byte0 = temp[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
result = new String(str);
} catch (Exception e) {
}
return result;
}
}