/* ====================================================================
* The LateralNZ Software License, Version 1.0
*
* Copyright (c) 2003 LateralNZ. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by
* LateralNZ (http://www.lateralnz.org/) and other third parties."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "LateralNZ" must not be used to endorse or promote
* products derived from this software without prior written
* permission. For written permission, please
* contact oss@lateralnz.org.
*
* 5. Products derived from this software may not be called "Panther",
* or "Lateral" or "LateralNZ", nor may "PANTHER" or "LATERAL" or
* "LATERALNZ" appear in their name, without prior written
* permission of LateralNZ.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of LateralNZ. For more
* information on Lateral, please see http://www.lateralnz.com/ or
* http://www.lateralnz.org
*
*/
package org.lateralnz.common.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.security.MessageDigest;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* common string utility functions
*
* @author J R Briggs
*/
public final class StringUtils implements Constants {
private static MessageDigest md5 = null;
private static MessageDigest sha = null;
private static HashMap patternCache = new HashMap();
private static final String MD5 = "MD5";
private static final String SHA = "SHA";
private static final char[] hexChars = new char[] {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
private static final String[][] HTML_PATTERNS = {
{ "&", "&"},
{ "<", "<" },
{ ">", ">" },
{ "\\ \\; ", " " },
{ "'", "'" },
{ "\"", """ },
{ "\n", "<br />" },
{ "\n ", "<br /> " }
};
private static final String[][] ESCAPES = { { "\n", "\\n" },
{ "\r", "\\r" },
{ "\"", "\\\"" },
{ "\t", "\\t" } };
private static final String[][] REV_ESCAPES = { { "\\n", "\n" },
{ "\\r", "\r" },
{ "\\\"", "\"" },
{ "\\t", "\t" },
{ "\\'", "'" }};
private static final String AMP_HASH = "&#";
private static Pattern[] strpatterns = new Pattern[HTML_PATTERNS.length];
static {
try {
for (int i = 0; i < HTML_PATTERNS.length; i++) {
strpatterns[i] = getPattern(HTML_PATTERNS[i][0]);
}
}
catch (PatternSyntaxException pse) {
pse.printStackTrace();
}
}
/**
* count the occurrences of a character in a string
*/
public static final int countOccurrences(String s, char c) {
int total = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == c) {
total++;
}
}
return total;
}
public static final int countOccurrences(StringBuffer sb, char c) {
int total = 0;
for (int i = 0; i < sb.length(); i++) {
if (sb.charAt(i) == c) {
total++;
}
}
return total;
}
public static final String degzip(byte[] b) throws IOException {
GZIPInputStream gis = null;
try {
gis = new GZIPInputStream(new ByteArrayInputStream(b));
int offset = 0;
int len = 1024;
int read;
int total = 0;
byte[] buf = new byte[len];
while ((read = gis.read(buf, offset, len)) != -1) {
total += read;
if (read < len) {
break;
}
else {
byte[] tmp = new byte[buf.length + len];
System.arraycopy(buf, 0, tmp, 0, buf.length);
offset = buf.length;
buf = tmp;
}
}
return new String(buf, 0, total);
}
finally {
IOUtils.close(gis);
}
}
/**
* escape a string
*/
public static final String escape(String s) {
StringBuffer sb = new StringBuffer(s);
for (int i = 0; i < ESCAPES.length; i++) {
replace(sb, ESCAPES[i][0], ESCAPES[i][1]);
}
return sb.toString();
}
/**
* return a list of regular expression groups given a string and a regex grouping pattern
*/
public static final List findRegex(String s, String pattern) {
ArrayList rtn = new ArrayList();
Pattern p = getPattern(pattern);
Matcher matcher = p.matcher(s);
while (matcher.find()) {
int groups = matcher.groupCount();
for (int i = 1; i <= groups; i++) {
rtn.add(matcher.group(i));
}
}
return rtn;
}
/**
* a quick and dirty wrapper over MessageFormat.format, so we don't need
* to import it into JSPs as well as org.lateralnz.util.*
*/
public static final String format(String pattern, Object[] arguments) {
if (isEmpty(pattern)) {
return EMPTY;
}
else {
return MessageFormat.format(pattern, arguments);
}
}
/**
* a wrapper for MessageFormat.format that takes a single argument
* rather than an array
*/
public static final String format(String pattern, String argument) {
return format(pattern, new Object[]{ argument });
}
public static final String fromArray(String[] s, String delim) {
if (s == null || s.length < 1) {
return EMPTY;
}
else {
return fromArray(s, delim, 0, s.length);
}
}
/**
* turn an array of strings into a single delimited string
* @param s the array of strings to use
* @param delim the delimiter to use between each value
* @param start the start position of the array
* @param len the number of elements to use from the array
* @returns a delimited string
*/
public static final String fromArray(String[] s, String delim, int start, int len) {
StringBuffer sb = new StringBuffer();
if (s != null && start < s.length && len > 0) {
for (int i = 0, j = start; i < len && j < s.length; i++, j++) {
sb.append(s[j]);
if (j < s.length-1 && i < len-1) {
sb.append(delim);
}
}
}
return sb.toString();
}
/**
* turn a list of strings into a single delimited string
*/
public static final String fromList(List l, String delim) {
StringBuffer sb = new StringBuffer();
Iterator iter = l.iterator();
while (iter.hasNext()) {
sb.append(iter.next());
if (iter.hasNext()) {
sb.append(delim);
}
}
return sb.toString();
}
/**
* create a string based upon the contents of a map. The string will be constructed
* as key=value[DELIM]key=value[DELIM]..., where [DELIM] is specified in the call to
* this method. For example:
* fromMap(m, ',')
* would return: key1=value1,key2=value2,key3=value3,etc
*/
public static final String fromMap(Map map, char delim) {
StringBuffer sb = new StringBuffer();
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
String key = (String)iter.next();
String value = (String)map.get(key);
if (sb.length() > 0) {
sb.append(delim);
}
sb.append(key).append(EQUALS).append(value);
}
return sb.toString();
}
/**
* parse a delimited string returning the element at the specified index
* @param s the string to parse
* @param delim the delimiters
* @param index the index value to return
* @returns a string value
*/
public static final String getFromDelimitedString(String s, String delim, int index) {
String rtn = null;
StringTokenizer st = new StringTokenizer(s, delim);
int counter = 0;
while (st.hasMoreTokens() && counter <= index) {
if (counter == index) {
rtn = st.nextToken();
}
else {
st.nextToken();
}
counter++;
}
return rtn;
}
public static final Pattern getPattern(String pattern) throws PatternSyntaxException {
if (patternCache.containsKey(pattern)) {
return (Pattern)patternCache.get(pattern);
}
else {
Pattern pat = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
patternCache.put(pattern, pat);
return pat;
}
}
/**
* create a random string of a specified length
*/
public static String getRandomString(int len) {
char[] stringchars = new char[len];
for (int i = 0; i < len; i++) {
int index = (int)((Math.random() * 25 + 65) + 0.5);
if (Math.random() > 0.5) {
index += 32;
}
stringchars[i] = (char)index;
}
return new String(stringchars);
}
/**
* given a string and the start and end of a tag return the contents of the tag.
* For example, given the string: "hello this <!-- is a test -->" and a tag start
* of "<!--" and a tag end of "-->", this method should return " is a test "
*/
public static final String getTagValue(String s, String tagStart, String tagEnd) {
if (isEmpty(s)) {
return EMPTY;
}
else {
int start = s.indexOf(tagStart);
if (start < 0) {
return EMPTY;
}
else {
start += tagStart.length();
}
int end = -1;
if (!StringUtils.isEmpty(tagEnd)) {
end = s.indexOf(tagEnd, start);
}
if (end >= 0) {
return s.substring(start, end);
}
else {
return s.substring(start);
}
}
}
public static final byte[] gzip(String s) throws IOException {
ByteArrayOutputStream baos;
GZIPOutputStream gos = null;
try {
baos = new ByteArrayOutputStream();
gos = new GZIPOutputStream(baos);
byte[] b = s.getBytes();
gos.write(b, 0, b.length);
gos.finish();
return baos.toByteArray();
}
finally {
IOUtils.close(gos);
}
}
/**
* return true if a string is null or empty (in other words equals(\"\"))
*/
public static final boolean isEmpty(String s) {
if (s == null || s.equals(EMPTY)) {
return true;
}
else {
return false;
}
}
/**
* return true if an object is null, or it is a string and empty
*/
public static final boolean isEmpty(Object o) {
if (o == null || (o instanceof String && o.equals(EMPTY))) {
return true;
}
else {
return false;
}
}
/**
* check for equality between two strings, taking nulls into account
*/
public static final boolean isEqual(String s1, String s2) {
if ((isEmpty(s1) && !isEmpty(s2)) || (!isEmpty(s1) && isEmpty(s2))) {
return false;
}
else if (isEmpty(s1) && isEmpty(s2)) {
return true;
}
else {
return s1.equals(s2);
}
}
/**
* Check if a string is numeric (digit chars only)
*/
public static final boolean isNumeric(String s) {
if (isEmpty(s)) {
return false;
}
for (int i = 0; i < s.length(); i++) {
if (!Character.isDigit(s.charAt(i))) {
return false;
}
}
return true;
}
/**
* if a string is null, then return the specified replacement
* @param s the string to check for null
* @param replacement the string to return if s is null
*/
public static final String isNull(String s, String replacement) {
return (s == null ? replacement : s);
}
/**
* left pad a string with a certain character so that it equals the specified
* length
*/
public static final String lpad(String text, char pad, int length) {
return pad(text, pad, length, true);
}
/**
* return true if a particular string matches the regular expression
* @param s the string to check
* @param pattern the regular expression to look for
*/
public static final boolean matches(String s, String pattern) throws PatternSyntaxException {
Pattern p = getPattern(pattern);
Matcher matcher = p.matcher(s);
return matcher.matches();
}
/**
* actual padding method
*/
private static final String pad(String text, char pad, int length, boolean left) {
int textlen = text.length();
if (textlen > length) {
return text;
}
StringBuffer sb;
if (left) {
sb = new StringBuffer();
}
else {
sb = new StringBuffer(text);
}
int len = length - textlen;
for (int i = 0; i < len; i++) {
sb.append(pad);
}
if (left) {
sb.append(text);
}
return sb.toString();
}
/**
* read the contents of a file and return as a string
*/
public static final String readFromFile(String filename) {
File f = new File(filename);
if (f.exists() && f.canRead()) {
FileReader fr = null;
try {
fr = new FileReader(f);
return readFrom(fr);
}
catch (Exception e) {
e.printStackTrace();
}
finally {
IOUtils.close(fr);
}
}
else {
System.err.println(filename + " does not exist or is not readable");
}
return EMPTY;
}
/**
* read the contents of the specified reader
*/
public static final String readFrom(Reader r) {
try {
StringBuffer sb = new StringBuffer();
BufferedReader in = new BufferedReader(r);
char[] array = new char[2048];
int num;
while ((num = in.read(array)) != -1) {
sb.append(array, 0, num);
}
in.close();
return sb.toString();
}
catch (Exception e) { }
return EMPTY;
}
/**
* read the contents of the specified reader up to the specified size
*/
public static final String readFrom(Reader r, int size) {
try {
StringBuffer sb = new StringBuffer();
BufferedReader in = new BufferedReader(r);
char[] array = new char[size];
in.read(array);
String rtn = new String(array);
return rtn;
}
catch (Exception e) {
e.printStackTrace();
return EMPTY;
}
}
/**
* remove all occurences of a list of characters from a string. for example:
* <pre>
* remove("This is a test", " ");
* </pre>
* should return "Thisisatest".
* @param s the string to remove characters from
* @param chars the list of characters to remove
* @returns a new string with chars removed
*/
public static final String remove(String s, String chars) {
if (s == null) {
return EMPTY;
}
else {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (chars.indexOf(c) < 0) {
sb.append(c);
}
}
return sb.toString();
}
}
/**
* replace all references of a string in a stringbuffer with the contents
* of a replacement string
*/
public static final void replace(StringBuffer sb, String oldstr, String newstr) {
if (sb.length() == 0 || isEmpty(oldstr)) {
return;
}
else if (newstr == null) {
newstr = EMPTY;
}
int len = oldstr.length();
int nlen = newstr.length();
int start = 0;
int occ = 0;
while (occ != -1) {
occ = sb.indexOf(oldstr, start);
if (occ != -1) {
sb.replace(occ, occ + len, newstr);
start = occ + nlen;
}
}
}
/**
* replace all occurrences of a string with the contents of another string
* @param s the string to search
* @param oldstr the string pattern to look for
* @param newstr the string pattern to replace with
*/
public static final String replace(String s, String oldstr, String newstr) {
if (isEmpty(s)) {
return s;
}
StringBuffer sb = new StringBuffer(s);
replace(sb, oldstr, newstr);
return sb.toString();
}
public static final String replaceChars(String s, String chars, char newChar) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (chars.indexOf(c) >= 0) {
sb.append(newChar);
}
else {
sb.append(c);
}
}
return sb.toString();
}
/**
* replace all references to a 'tag' within a string. A tag is a section of text
* defined with a start string and and end string.
* For example:
* <pre>
* replaceTag("this is a test <!-- this is a test --> blah blah", "<!--", "-->", "");
* </pre>
* Would be expected to return "this is a test blah blah"
*/
public static final String replaceTag(String s, String tagStart, String tagEnd, String replace) {
if (s.length() == 0 || isEmpty(tagStart) || isEmpty(tagEnd)) {
return EMPTY;
}
else if (replace == null) {
replace = EMPTY;
}
StringBuffer sb = new StringBuffer(s);
int nlen = replace.length();
int elen = tagEnd.length();
int start = 0;
int occ = 0;
while (occ != -1) {
occ = sb.indexOf(tagStart, start);
if (occ != -1) {
int occend = sb.indexOf(tagEnd, occ) + elen;
sb.replace(occ, occend, replace);
start = occ + nlen;
}
}
return sb.toString();
}
/**
* right pad a string with a character so that the length is the same as that
* specified by the length param
*/
public static final String rpad(String text, char pad, int length) {
return pad(text, pad, length, false);
}
/**
* split a string based upon a regular expression pattern
* For example: splitRegex("a,b,c,d,e", ",")
* would return a list containing a, b, c, d, and e as the elements
*/
public static final List splitRegex(String s, String pattern) throws PatternSyntaxException {
// Pattern p = getPattern(pattern);
String[] tmp = s.split(pattern);
List l = java.util.Arrays.asList(tmp);
return new ArrayList(l);
}
/**
* strip all occurrences of characters in a string from a specified string
* @param s the string to search
* @param chars a string of characters to remove from s
*/
public static final String strip(String s, String chars) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (chars.indexOf(c) < 0) {
sb.append(c);
}
}
return sb.toString();
}
/**
* strip leading a trailing spaces from a string based upon line delimiters
*/
public static final String stripLTSpaces(String s) {
Pattern lpattern = getPattern("^\\s+");
Pattern rpattern = getPattern("\\s+$");
Matcher matcher = lpattern.matcher(s);
s = matcher.replaceAll(EMPTY);
matcher = rpattern.matcher(s);
String rtn = matcher.replaceAll(EMPTY);
return rtn;
}
/**
* turn a string into an array of strings (delimiters are not returned as part of the array)
* @param s the string to convert
* @param delim the delimiters to use in conversion
*/
public static final String[] toArray(String s, String delim) {
return toArray(s, delim, false);
}
/**
* turn a string into a string array based upon a delimiter
* @param s the string to convert
* @param delim the delimiters to use in conversion
* @param returnDelimiters if true, then delimiters are included in the array
*/
public static final String[] toArray(String s, String delim, boolean returnDelims) {
StringTokenizer st = new StringTokenizer(s, delim, returnDelims);
String[] rtn = new String[st.countTokens()];
for (int i = 0; i < rtn.length; i++) {
rtn[i] = st.nextToken();
}
st = null;
return rtn;
}
/**
* makes sure that a directory filename ends with the file separator.
* eg: /usr/local/lib becomes /usr/local/lib/
*/
public static final String toDirectory(String filename) {
if (isEmpty(filename)) {
return EMPTY;
}
else if (filename.endsWith(FILE_SEPARATOR)) {
return filename;
}
else {
return filename + FILE_SEPARATOR;
}
}
public static final String toHex(byte b) {
int i = b & 0xff;
int h0 = i & 0xf;
int h1 = (i >>> 4) & 0xf;
char[] c = new char[2];
c[0] = hexChars[h1];
c[1] = hexChars[h0];
return new String(c);
}
public static final String toHex(byte[] b) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < b.length; i++) {
sb.append(toHex(b[i]));
}
return sb.toString();
}
/**
* convert a string into an 'HTML-ready' string, handling special characters and newlines
*/
public static final String toHTML(String s) {
return toHTML(s, true);
}
/**
* convert a string into an 'HTML-ready' string, handling special characters.
* This will convert newlines to html breaks if specified
*/
public static final String toHTML(String s, boolean includeBR) {
Matcher matcher;
int end = strpatterns.length-2;
if (includeBR) {
end = strpatterns.length;
}
for (int i = 0; i < end; i++) {
matcher = strpatterns[i].matcher(s);
s = matcher.replaceAll(HTML_PATTERNS[i][1]);
}
return s;
}
/**
* chop a string up into tokens based upon the delimiter and add
* the tokens to a list
*/
public static final List toList(String s, String delim, List list) {
if (isEmpty(s)) {
s = EMPTY;
}
StringTokenizer st = new StringTokenizer(s, delim);
while (st.hasMoreTokens()) {
list.add(st.nextToken());
}
st = null;
return list;
}
/**
* turn a string into a list based upon a delimiter
*/
public static final List toList(String s, String delim) {
ArrayList list = new ArrayList();
return toList(s, delim, list);
}
/**
* turn a string into a map of param=value objects based upon a delimiter.
* The string should be something like: "a=100,b=hello,c=a203" where the delimiter
* is obviously ","
*/
public static final Map toMap(String s, String delim) {
HashMap hm = new HashMap();
toMap(hm, s, delim);
return hm;
}
/**
* turn a string into a map of param=value objects based upon a delimiter and
* using the specified map object for the result
*/
public static final void toMap(Map m, String s, String delim) {
if (!isEmpty(s)) {
StringTokenizer st = new StringTokenizer(s, delim);
while (st.hasMoreTokens()) {
String token = st.nextToken();
int pos = token.indexOf(EQUALS);
if (pos >= 0) {
String key = token.substring(0, pos);
String val;
if (pos < token.length()-1) {
val = token.substring(pos+1);
}
else {
val = EMPTY;
}
m.put(key, val);
}
else {
m.put(s, EMPTY);
}
}
}
}
/**
* take a string that contains escaped values \\n \\t \\r and returns it with
* the actual escape codes (\n, \t, \r)
*/
public static final String unescape(String s) {
StringBuffer sb = new StringBuffer(s);
for (int i = 0; i < REV_ESCAPES.length; i++) {
replace(sb, REV_ESCAPES[i][0], REV_ESCAPES[i][1]);
}
return sb.toString();
}
/**
* unencode a string containing html escape codes (in the form &#...;)
*/
public static final String unencode(String s) throws NumberFormatException {
StringBuffer sb = new StringBuffer(s);
int pos = 0;
int endpos;
String tmp;
while ((pos = sb.indexOf(AMP_HASH, pos)) >= 0) {
endpos = sb.indexOf(SEMICOLON, pos);
if (endpos >= 0) {
tmp = sb.substring(pos+2, endpos);
int i = Integer.parseInt(tmp);
sb.replace(pos, endpos+1, EMPTY + (char)i);
}
pos++;
}
return sb.toString();
}
private static final String toDigest(String s, MessageDigest md) throws Exception {
md.update(s.getBytes());
StringBuffer sb = new StringBuffer();
byte[] b = md.digest();
for (int i = 0; i < b.length; i++) {
sb.append(toHex(b[i]));
}
return sb.toString();
/*
StringReader sr = new StringReader(new String(md.digest()));
int c;
StringBuffer sb = new StringBuffer();
while ((c = sr.read()) != -1) {
String hexString = Integer.toHexString(c);
if ( hexString.length() == 1 ) {
sb.append(ZERO).append(hexString);
}
else {
sb.append(hexString);
}
}
return sb.toString();*/
}
/**
* return an MD5 digest of a specified string
*/
public static final String toMD5Digest(String s) throws Exception {
if (md5 == null) {
synchronized (StringUtils.class) {
if (md5 == null) {
md5 = MessageDigest.getInstance(MD5);
}
}
}
MessageDigest md5c = (MessageDigest)md5.clone();
return toDigest(s, md5c);
}
public static final String toSHADigest(String s) throws Exception {
if (sha == null) {
synchronized (StringUtils.class) {
if (sha == null) {
sha = MessageDigest.getInstance(SHA);
}
}
}
MessageDigest shac = (MessageDigest)sha.clone();
return toDigest(s, shac);
}
}
|