/*******************************************************************************
* Copyright (c) 2009 MATERNA Information & Communications. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html. For further
* project-related information visit http://www.ws4d.org. The most recent
* version of the JMEDS framework can be obtained from
* http://sourceforge.net/projects/ws4d-javame.
******************************************************************************/
package org.ws4d.java.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.TimeZone;
import java.util.Vector;
import org.ws4d.java.constants.Specialchars;
import org.ws4d.java.structures.ArrayList;
import org.ws4d.java.structures.HashMap;
import org.ws4d.java.structures.List;
/**
*
*/
public final class StringUtil {
private static String[] encList = { "UTF-8", "ISO-8859-1" };
/*
* Some stuff defined by the RFC 822 (5. DATE AND TIME SPECIFICATION)
*/
public static final String[] day = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
public static final String[] month = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
public static final String[][] zone = { { "UT", "0" }, { "GMT", "0" }, { "EST", "-5" }, { "EDT", "-4" }, { "CST", "-6" }, { "CDT", "-5" }, { "MST", "-7" }, { "MDT", "-6" }, { "PST", "-8" }, { "PDT", "-7" }, { "Z", "0" }, { "A", "-1" }, { "M", "-12" }, { "N", "+1" }, { "Y", "+12" }, { "J", "0" } };
/**
* When set to <code>true</code>, most of the framework classes will use
* fully qualified class names within their <code>toString()</code> methods.
* Otherwise (when <code>false</code>), class names will be shorten up by
* means of {@link #simpleClassName(String)}.
*/
private static final boolean USE_LONG_CLASS_NAMES = false;
private static final String[] EMPTY_STRING_ARRAY = new String[0];
public static boolean equalsIgnoreCase(String s1, String s2) {
if (s1 == s2) {
return true;
}
return s1 != null && s2 != null && s1.length() == s2.length() && s1.regionMatches(true, 0, s2, 0, s1.length());
}
public static int lastIndexOf(String what, String within) {
if (what == null || within == null || what.length() == 0) {
return -1;
}
int i = 0;
int lastI = -1;
while ((i = within.indexOf(what, i)) != -1) {
lastI = i++;
}
return lastI;
}
public static final String getStringEncoding() {
return encList[0];
}
/**
* Splits a string at a given separator char in substrings and returns these
* elements in a string array.
*
* @param s The string to split.
* @param c The separator char.
* @return The array containing the substrings.
*/
public static String[] split(String s, char c) {
if (s == null) return null;
Vector v = new Vector();
int idx;
int begin = 0;
while (true) {
idx = s.indexOf(c, begin);
if (idx == -1) {
v.addElement(s.substring(begin));
break;
} else {
v.addElement(s.substring(begin, idx));
}
begin = idx + 1;
}
String[] result = new String[v.size()];
for (int i = 0; i < result.length; i++) {
result[i] = (String) v.elementAt(i);
}
return result;
}
/**
* Splits a string at a given separator char in substrings and returns these
* elements in a string array.
*
* @param s The string to split.
* @param separator The separator char.
* @return The array containing the substrings.
*/
public static String[] split(String s, String separator) {
if (s == null) return null;
Vector v = stringToVector(s, separator);
String[] result = new String[v.size()];
for (int i = 0; i < result.length; i++) {
result[i] = (String) v.elementAt(i);
}
return result;
}
public static String[] splitAtWhitespace(String s) {
if (s == null) {
return null;
}
if ("".equals(s = s.trim())) {
return EMPTY_STRING_ARRAY;
}
List l = new ArrayList();
int len = s.length();
int firstNonWsIdx = -1;
int i;
for (i = 0; i < len; i++) {
char c = s.charAt(i);
switch (c) {
/*
* according to XML Schema Part 2, Section 4.3.6, which itself
* refers to XML 1.0 (Second Edition), whitespace is defined as
* arbitrary sequences of one of the characters #x9 (tab), #xA
* (line feed), #xD (carriage return) or #x20 (space)
*/
case ('\t'):
case ('\n'):
case ('\r'):
case (' '): {
if (firstNonWsIdx != -1) {
l.add(s.substring(firstNonWsIdx, i));
firstNonWsIdx = -1;
}
break;
}
default: {
if (firstNonWsIdx == -1) {
firstNonWsIdx = i;
}
break;
}
}
}
// last chunk
if (firstNonWsIdx != -1) {
l.add(s.substring(firstNonWsIdx));
}
return (String[]) l.toArray(new String[l.size()]);
}
/**
* Converts text with spaces into <code>Vector</code>.
*
* @param s The text to convert.
* @return <code>Vector</code> containing the text parts.
*/
public static Vector stringToVector(String s, String separator) {
Vector v = new Vector();
int idx;
int begin = 0;
while (true) {
idx = s.indexOf(separator, begin);
if (idx == -1) {
v.addElement(s.substring(begin));
break;
} else {
v.addElement(s.substring(begin, idx));
}
begin = idx + separator.length();
}
return v;
}
/**
* Concats the elements of a vector to a string (calls toString of each
* element) separated by given string. Returns null if no vector is given,
* returns "" if no elements are in the vector.
*
* @param v Vector to work on.
* @param separator The string which should be used as separator.
* @return The built string.
*/
public static String vectorToString(Vector v, String separator) {
if (v == null) {
return null;
}
if (separator == null) {
separator = "";
}
String s = "";
for (Enumeration enu = v.elements(); enu.hasMoreElements();) {
s += enu.nextElement().toString() + separator;
}
s.trim();
return s;
}
/**
* Encodes an given URL.
*
* @param url the URL to encode.
* @return the encoded URL.
*/
public static String encodeURL(String url) {
/*
* This encoding was done according to
* http://www.blooberry.com/indexdot/html/topics/urlencoding.htm
*/
if (url != null) {
StringBuffer buffer = new StringBuffer();
int i = 0;
while (i < url.length()) {
int b = url.charAt(i++);
boolean encoded = false;
// get control characters encoded
if (b >= 0x00 && b <= 0x1F || b == 0x7F) {
buffer.append("%");
if (b <= 0xf) buffer.append("0");
buffer.append(Integer.toHexString(b));
encoded = true;
}
// get non-ascii characters encoded
if (b >= 0x80 && b <= 0xFF) {
buffer.append("%");
if (b <= 0xf) buffer.append("0");
buffer.append(Integer.toHexString(b));
encoded = true;
}
// TODO: get reserved URL characters encoded
// switch (b) {
// case 0x24:
// case 0x26:
// case 0x2B:
// case 0x2F:
// case 0x3A:
// case 0x3B:
// case 0x3D:
// case 0x3F:
// case 0x40:
// case 0x21:
// buffer.append("%");
// if (b <= 0xf) buffer.append("0");
// buffer.append(Integer.toHexString(b));
// encoded = true;
// break;
// }
// get unsafe characters encoded
switch (b) {
case 0x20: // Space
case 0x22: // Quotation marks
case 0x3C: // Less Than
case 0x3E: // Greater Than
case 0x23: // Pound
case 0x25: // Percent
case 0x7B: // Left Curly Brace
case 0x7D: // Right Curly Brace
case 0x7C: // Vertical Bar/Pipe
case 0x5C: // Backslash
case 0x5E: // Caret
case 0x7E: // Tilde
case 0x5B: // Left Square Bracket
case 0x5D: // Right Square Bracket
case 0x60: // Grave Accent
buffer.append("%");
if (b <= 0xf) buffer.append("0");
buffer.append(Integer.toHexString(b));
encoded = true;
break;
}
if (!encoded) {
buffer.append((char) b);
}
}
return buffer.toString();
}
return null;
}
/**
* Decodes a String in <code>application/x-www-form-urlencoded</code> format
* as specified in <a
* href="http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1">
* Chapter Forms</a> of the HTML 4.01 Specification. Uses default character
* encoding.
*
* @param url String to decode.
* @return Decoded String.
* @throws UnsupportedEncodingException
*/
public static String decodeURL(String url) {
try {
return decodeURL(url, getStringEncoding());
} catch (UnsupportedEncodingException e) {
Log.printStackTrace(e);
}
return url;
}
/**
* Decodes a String in <code>application/x-www-form-urlencoded</code> format
* as specified in <a
* href="http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1">
* Chapter Forms</a> of the HTML 4.01 Specification.
*
* @param url String to decode.
* @param encoding Encoding.
* @return Decoded String.
* @throws UnsupportedEncodingException
*/
public static String decodeURL(String url, String encoding) throws UnsupportedEncodingException {
if (encoding.length() == 0) {
throw new UnsupportedEncodingException("StringUtil.decodeURL: No encoding specified");
}
int length = url.length();
int i = 0;
char c;
for (; i < length; i++) {
c = url.charAt(i);
if (c == '+' || c == '%') {
break;
}
}
if (i == length) {
return url;
}
StringBuffer mainBuffer = new StringBuffer(length);
char[] charArray = new char[i];
url.getChars(0, i, charArray, 0);
mainBuffer.append(charArray);
byte[] byteBuffer = null;
while (i < length) {
c = url.charAt(i);
if (c == '%') {
try {
if (byteBuffer == null) {
byteBuffer = new byte[(length - i) / 3];
}
int pos = 0;
while (((i + 2) < length) && (c == '%')) {
byteBuffer[pos++] = (byte) Integer.parseInt(url.substring(i + 1, i + 3), 16);
i += 3;
if (i < length) {
c = url.charAt(i);
}
}
if ((i < length) && (c == '%')) {
throw new IllegalArgumentException("StringUtil.decodeURL: Incomplete percent pattern");
}
mainBuffer.append(new String(byteBuffer, 0, pos, encoding));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("StringUtil.decodeURL: Unsupported character in percent pattern (" + e.getMessage() + ")");
}
} else if (c == '+') {
mainBuffer.append(' ');
i++;
} else {
mainBuffer.append(c);
i++;
}
}
return mainBuffer.toString();
}
/**
* Depending on the value of {@link #USE_LONG_CLASS_NAMES} returns either
* the fully qualified class name of <code>clazz</code> or the result of
* calling {@link #simpleClassName(Class)} on it.
*
* @param clazz the class to return a formatted name of
* @return either the fully qualified or the simple class name of argument
* <code>clazz</code>
* @see #USE_LONG_CLASS_NAMES
* @see #simpleClassName(Class)
*/
public static String formatClassName(Class clazz) {
return formatClassName(clazz.getName());
}
/**
* Depending on the value of {@link #USE_LONG_CLASS_NAMES} returns either
* the same String given in argument <code>qualifiedClassName</code> or the
* result of calling {@link #simpleClassName(String)} on it.
*
* @param qualifiedClassName a fully qualified Java class name
* @return either the same String as <code>qualifiedClassName</code> or the
* simple class name
* @see #USE_LONG_CLASS_NAMES
* @see #simpleClassName(String)
*/
public static String formatClassName(String qualifiedClassName) {
return USE_LONG_CLASS_NAMES ? qualifiedClassName : simpleClassName(qualifiedClassName);
}
/**
* Returns only the substring after the last dot character
* ("e;."e;), within the name of <code>clazz</code>, i.e.
* <code>Object</code> instead of <code>java.lang.Object</code>.
*
* @param clazz the class to return the simple name of
* @return the simple class name
*/
public static String simpleClassName(Class clazz) {
return simpleClassName(clazz.getName());
}
/**
* Given a fully qualified class name (like <code>java.lang.Object</code>),
* returns only the substring after the last dot character
* ("e;."e;), i.e. <code>Object</code> instead of
* <code>java.lang.Object</code>.
*
* @param qualifiedClassName a fully qualified Java class name
* @return the simple class name
*/
public static String simpleClassName(String qualifiedClassName) {
if (qualifiedClassName == null || "".equals(qualifiedClassName)) {
return qualifiedClassName;
}
int idx = qualifiedClassName.lastIndexOf('.');
return idx == -1 ? qualifiedClassName : qualifiedClassName.substring(idx + 1);
}
/**
* Converts the given Vector to String array
*
* @param strings The Vector to convert.
* @return The array containing the Vector parts.
*/
public static String[] toStringArray(Vector strings) {
String[] result = new String[strings.size()];
for (int i = 0; i < strings.size(); i++) {
result[i] = strings.elementAt(i).toString();
}
return result;
}
/**
* Deletes the Line Feed (10) and Carriage Return (13) fields of the given
* String.
*
* @param inStr The String to work on.
* @return The string without Line Feed and Carriage Return.
*/
public static String chomp(String inStr) {
if (inStr == null || "".equals(inStr)) {
return inStr;
}
char lastChar = inStr.charAt(inStr.length() - 1);
if (lastChar == 13) {
// if the string ends with Carriage Return
return inStr.substring(0, inStr.length() - 1);
} else if (lastChar == 10) {
// if the string ends with Line Feed
String tmp = inStr.substring(0, inStr.length() - 1);
if ("".equals(tmp)) {
return tmp;
}
lastChar = tmp.charAt(tmp.length() - 1);
// if the string also contains Carriage Return
if (lastChar == 13) {
return tmp.substring(0, tmp.length() - 1);
} else {
return tmp;
}
} else {
return inStr;
}
}
/**
* Parses options from a <code>String</code>. The options are defined as
* "-name <properties>" divided by a blank.
*
* @param args the <code>String</code> to parse the options from.
* @return <code>Map</code> containing the options properties.
*/
public static HashMap parseStringOptions(String args) {
HashMap options = new HashMap();
if (args == null || args.length() == 0) return options;
while (args != null && args.length() > 0) {
int min = args.indexOf(Specialchars.MINUS);
int sp = args.indexOf(Specialchars.SP, min);
String key = args.substring(min + 1, sp);
min = args.indexOf(Specialchars.MINUS, sp + 1);
if (min == -1) {
String properties = args.substring(sp + 1).trim();
if (key.length() > 0 && properties.length() > 0) options.put(key, properties);
return options;
}
String properties = args.substring(sp, min).trim();
if (key.length() > 0 && properties.length() > 0) options.put(key, properties);
int remove = key.length() + properties.length() + 2;
if (remove > args.length()) {
args = null;
} else {
args = args.substring(remove, args.length()).trim();
}
}
return options;
}
/**
* Parses properties from a <code>String</code>. The properties are defined
* as "name=value" pairs divided by a blank.
*
* @param args the <code>String</code> to parse the properties from.
* @return <code>Map</code> containing the properties.
*/
public static HashMap parseStringProperties(String args) {
HashMap options = new HashMap();
if (args == null || args.length() == 0) return options;
while (args != null && args.length() > 0) {
int eq = args.indexOf(Specialchars.EQ);
String key = args.substring(0, eq);
int eqN = args.indexOf(Specialchars.EQ, eq + 1);
if (eqN == -1) {
String value = args.substring(eq + 1, args.length());
if (key.length() > 0 && value.length() > 0) options.put(key, value);
return options;
}
int last = args.lastIndexOf(Specialchars.SP, eqN - 1);
if (last == -1) last = args.lastIndexOf(Specialchars.SP, args.length());
if (last == -1) last = args.length();
String value = args.substring(eq + 1, last);
if (key.length() > 0 && value.length() > 0) options.put(key, value);
int remove = key.length() + value.length() + 2;
if (remove > args.length()) {
args = null;
} else {
args = args.substring(remove, args.length());
}
}
return options;
}
/**
* Prints a stream to System.out.
*
* @param in the stream to print.
*/
public static void printStream(InputStream in) {
if (in == null) {
if (Log.isDebug()) {
Log.debug("No input stream set.", Log.DEBUG_LAYER_FRAMEWORK);
return;
}
}
byte[] buffer = new byte[8192];
try {
while (in.available() > 0) {
int len = in.read(buffer);
for (int i = 0; i < len; i++) {
System.out.print((char) buffer[i]);
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Creates a String from an array of int.
*
* @param array the array to show.
* @return the String.
*/
public static String arrayToStringInt(int[] array) {
StringBuffer sBuf = new StringBuffer();
for (int i = 0; i < array.length; i++) {
sBuf.append(array[i]);
if (i < array.length - 1) {
sBuf.append(",");
}
}
return sBuf.toString();
}
/**
* Creates a String from an array of byte.
*
* @param array the array to show.
* @return the String.
*/
public static String arrayToStringByte(byte[] array) {
StringBuffer sBuf = new StringBuffer();
for (int i = 0; i < array.length; i++) {
sBuf.append(array[i]);
if (i < array.length - 1) {
sBuf.append(",");
}
}
return sBuf.toString();
}
/**
* Creates a String form a array of long.
*
* @param array the array to show.
* @return the String.
*/
public static String arrayToStringLong(long[] array) {
StringBuffer sBuf = new StringBuffer();
for (int i = 0; i < array.length; i++) {
sBuf.append(array[i]);
if (i < array.length - 1) {
sBuf.append(",");
}
}
return sBuf.toString();
}
public static long getHTTPDateAsLong(String httpDate) {
/*
* RFC 822 (5. DATE AND TIME SPECIFICATION)
*/
boolean hasDay = (httpDate.indexOf(',') > 0);
String day = null;
if (hasDay) {
day = httpDate.substring(0, 3);
httpDate = httpDate.substring(5, httpDate.length());
}
// dd mm yy hh:mm:ss zzz
String[] slice = StringUtil.split(httpDate, ' ');
String[] time = StringUtil.split(slice[3], ':');
int ddD = Integer.valueOf(slice[0]).intValue();
String mmDS = slice[1];
int mmD = 0;
for (int i = 0; i < month.length; i++) {
if (month[i].equals(mmDS)) {
mmD = i;
break;
}
}
int yyD = Integer.valueOf(slice[2]).intValue();
int hhT = Integer.valueOf(time[0]).intValue();
int mmT = Integer.valueOf(time[1]).intValue();
int ssT = 0;
if (time.length > 2) {
ssT = Integer.valueOf(time[2]).intValue();
}
String zzzS = slice[4];
TimeZone tz = TimeZone.getTimeZone(zzzS);
Calendar c = Calendar.getInstance(tz);
c.set(Calendar.DAY_OF_MONTH, ddD);
c.set(Calendar.MONTH, mmD);
c.set(Calendar.YEAR, yyD);
c.set(Calendar.MINUTE, mmT);
c.set(Calendar.HOUR_OF_DAY, hhT);
c.set(Calendar.SECOND, ssT);
c.set(Calendar.MILLISECOND, 0);
return c.getTime().getTime();
}
public static String getHTTPDate(long date) {
/*
* RFC 822 (5. DATE AND TIME SPECIFICATION)
*/
Date d = new Date(date);
Calendar c = Calendar.getInstance();
c.setTime(d);
TimeZone tz = c.getTimeZone();
int addHG = tz.getRawOffset() / 1000 / 3600;
int addMG = tz.getRawOffset() / 1000 / 60 % 60;
String addHGS = getInt(addHG);
String addMGS = getInt(addMG);
String add = addHGS + addMGS;
int dI = c.get(Calendar.DAY_OF_WEEK);
int ddD = c.get(Calendar.DAY_OF_MONTH);
String mmD = month[c.get(Calendar.MONTH)];
int yyD = c.get(Calendar.YEAR);
int hhT = c.get(Calendar.HOUR_OF_DAY);
int mmT = c.get(Calendar.MINUTE);
int ssT = c.get(Calendar.SECOND);
String result = day[dI - 1] + ", " + ddD + " " + mmD + " " + yyD + " " + getInt(hhT) + ":" + getInt(mmT) + ":" + getInt(ssT) + " GMT+" + add;
return result;
}
private static String getInt(int i) {
String r = "";
if (i < 10) {
r = "0" + String.valueOf(i);
} else {
r = String.valueOf(i);
}
return r;
}
}