package com.ibm.jactors.utils;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.TreeMap;
import org.w3c.dom.NodeList;
import com.ibm.jactors.logging.DefaultLogger;
import com.ibm.jactors.logging.Logger;
/**
* Assorted utility functions (sort of a kitchen sink).
* Some methods used but core to Actor implementation.
* Additional unused methods may be included and should be ignored.
*
* @author bfeigenb
*
*/
public class Utils {
public static final String _OUT_QUALIFIER = "_out.";
public static final String UTF8_ENCODING_NAME = "UTF-8";
public static final String MIME_WILDCARD_SUFFIX = "/*";
public static DateFormat hhmmsstttTimeFormat = new SimpleDateFormat(
"hh-mm-ss.SSS");
public static DateFormat yyyymmddDateFormat = new SimpleDateFormat(
"yyyy-MM-dd");
public static SimpleDateFormat dashOnlyDateFormat = new SimpleDateFormat(
"yyyy-MM-dd'T'hh-mm-ss-SSS");
public static DateFormat standardFullDateFormat = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss.SSSZ");
public static DateFormat dateTimeInstance = DateFormat.getDateTimeInstance(
DateFormat.SHORT, DateFormat.MEDIUM);
public static final String FORM_URLENCODED_CONTENT_TYPE = "application/x-www-form-urlencoded";
/** Set to enable debugging. */
public static final String MTP_DEBUG_PROPERTY = "com.ibm.haac.mtp.debug";
private static final int LOG_RETENSION_LENGTH = 1 * 1000 * 1000;
public static final Logger logger = DefaultLogger.getDefaultInstance();
/** File to log connection into. Placed in current temp directory. */
public static final String HTTP_CONNECTIONS_LOG_FILE = "connections.log";
public static final int SECOND_MILLIS = 1000;
public static final int MINUTE_MILLIS = 60 * SECOND_MILLIS;
public static final int HOUR_MILLIS = 60 * MINUTE_MILLIS;
public static final int DAY_MILLIS = 24 * HOUR_MILLIS;
protected static final int CHAR_BUFFER_LENGTH = 1024;
/** US.IBM.COM suffix. */
public static final String US_IBM_COM = "us.ibm.com";
/** Carriage Return character as a string. */
public static final String CR = "\r";
/** Line Feed character as a string. */
public static final String LF = "\n";
/** Form Feed character as a string. */
public static final String FF = "\f";
/** Tab character as a string. */
public static final String TAB = "\t";
/** Tab character as a string. */
public static final String SPACE = " ";
public static String capitalizeFirst(String s) {
if (!isEmpty(s)) {
s = Character.toUpperCase(s.charAt(0)) + s.substring(1);
}
return s;
}
public static boolean isNull(Object o) {
return o == null;
}
/** Test if array is null or empty. */
public static boolean isEmpty(byte[] ba) {
return ba == null || ba.length == 0;
}
/** Test if array is null or empty. */
public static boolean isEmpty(Object[] oa) {
return oa == null || oa.length == 0;
}
/** Test if string is null or empty. */
public static boolean isEmpty(CharSequence s) {
return s == null || s.length() == 0;
}
/** Test if string is null or empty. */
public static boolean isEmpty(Appendable s) {
int length = -1;
if (s != null) {
Method m = null;
Class<? extends Appendable> sclass = s.getClass();
try {
m = sclass.getMethod("size");
} catch (NoSuchMethodException e1) {
try {
m = sclass.getMethod("length");
} catch (NoSuchMethodException e2) {
try {
m = sclass.getMethod("getLength");
} catch (NoSuchMethodException e3) {
}
}
}
if (m != null) {
try {
length = (Integer) m.invoke(s);
} catch (Exception e) {
}
}
}
return s == null || length == 0;
}
/** Test if node list is null or empty. */
public static boolean isEmpty(NodeList nl) {
return nl == null || nl.getLength() == 0;
}
/** Test if map is null or empty. */
public static boolean isEmpty(Map<?, ?> m) {
return m == null || m.size() == 0;
}
/** Test if collection is null or empty. */
public static boolean isEmpty(Collection<?> c) {
return c == null || c.size() == 0;
}
public static boolean isEmptyOrNull(String s) {
return isEmpty(s) || s.trim().equalsIgnoreCase("null");
}
/** Make null string have some value. */
public static String fixNull(String s) {
return s != null ? s : "Unknown";
}
public static String formatString(String s) {
return !isEmpty(s) ? s : "";
}
/** Truncate a text string to 60 characters. Replace newlines with '~'. */
public static String truncateText(String text) {
return truncateText(text, 60);
}
/** Truncate a text string to length characters. Replace newlines with '~'. */
public static String truncateText(String text, int length) {
if (!isEmpty(text)) {
text = truncate(text, length);
text = text.replace('\r', '~').replace('\n', '~');
}
return text;
}
/** Truncate a text string to 100 characters. */
public static String truncate(Object s) {
return s != null ? truncate(s.toString(), 100) : "null";
}
/** Remove repeated whitespace (including newlines) */
public static String removeMultipleSpaces(String s) {
return removeMultipleSpaces(s, ' ');
}
/** Remove repeated whitespace (including newlines) */
public static String removeMultipleSpaces(String s, char nlReplace) {
if (!isEmpty(s)) {
// s = s.replace('\n', nlReplace);
s = s.replaceAll("\\s\\s+", " ");
}
return s;
}
/** Truncate a text string to size characters. */
public static String truncate(String s, int size) {
if (!isEmpty(s)) {
s = removeMultipleSpaces(s);
if (s.length() > size) {
int leadLength = size / 2;
int tailLength = size / 2;
s = s.substring(0, leadLength) + " ... "
+ s.substring(s.length() - tailLength);
}
}
return s;
}
/** Remove lead text before "<HTML" start. */
public static String trimDoctype(String contents) {
if (!isEmpty(contents)) {
int posn = contents.toLowerCase().indexOf("<html");
if (posn > 0) {
contents = contents.substring(posn);
}
}
return contents;
}
/** Remove content after a ';'. */
public static String stripAfterSemicolon(String type) {
return stripAfter(type, ';');
}
/** Remove content after a delimiter character. */
public static String stripAfter(String type, char c) {
if (!isEmpty(type)) {
int posn = type.indexOf(c);
if (posn > 0) {
type = type.substring(0, posn);
}
}
return type;
}
public static String[] splitAndTrim(String s) {
return splitAndTrim(s, ",|;");
}
public static String[] splitAndTrim(String s, String delim) {
String[] sa = null;
if (s != null) {
sa = s.split(delim);
for (int i = 0; i < sa.length; i++) {
sa[i] = sa[i].trim();
}
}
return sa != null ? sa : new String[0];
}
/** Test if all strings are not empty/null. */
public static boolean areAllNotEmpty(String... values) {
boolean res = true;
for (String v : values) {
res &= !isEmpty(v);
}
return res;
}
/** Test if any strings are not empty/null. */
public static boolean areAnyNotEmpty(String... values) {
boolean res = false;
for (String v : values) {
res |= !isEmpty(v);
}
return res;
}
/** Trim a potentially null string. */
public static String safeTrim(String s) {
return s != null ? s.trim() : s;
}
/**
* Convert a name to camelCase format.
*
* @param name
* @return
*/
public static String camelCaseName(String name) {
return isEmpty(name) ? "" : Character.toLowerCase(name.charAt(0))
+ name.substring(1);
}
/**
* Does the string look like JSON.
*
* @param s
* @return
*/
public static boolean isJsonArray(String s) {
return isJson(s, '[', ']');
}
/**
* Does the string look like JSON.
*
* @param s
* @return
*/
public static boolean isJsonObject(String s) {
return isJson(s, '{', '}');
}
private static boolean isJson(String s, char b, char e) {
boolean res = false;
if (s != null) {
s = s.trim();
if (s.length() >= 2) {
res = s.charAt(0) == e && s.charAt(s.length() - 1) == e;
}
}
return res;
}
/** Test objects for null or (if a string) "null". */
public static boolean areAllNull(Object[] oa) {
boolean res = true;
if (oa != null) {
for (Object o : oa) {
res = o == null || "null".equals(o);
}
}
return res;
}
public static String toList(Object[] oa) {
return oa != null ? Arrays.asList(oa).toString() : null;
}
public static String defValue(String text, String def) {
if (isEmpty(text)) {
text = def;
}
return text;
}
public static void printf(String format, Object... args) {
synchronized (System.out) {
System.out.printf(format, args);
}
}
@Retention(RetentionPolicy.RUNTIME)
public @interface ForToString {
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
public @interface ForEquals {
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
public @interface ForCompareTo {
String value() default "";
}
@Override
public String toString() {
return toString(this);
}
/** Reflectively implement toString() using ForToString annotations. */
public String toString(Object target) {
Map<String, Object> xfields = getMineAndParentFields(ForToString.class,
target);
return formatToString(xfields, target);
}
public static boolean useShortClassName = true;
protected String formatToString(Map<String, Object> xfields, Object target) {
StringBuilder sb = new StringBuilder();
String cname = useShortClassName ? target.getClass().getSimpleName()
: target.getClass().getName();
if (isEmpty(cname)) {
cname = target.getClass().getName();
}
sb.append(cname);
sb.append('[');
if (!isEmpty(xfields)) {
for (String key : xfields.keySet()) {
if (sb.charAt(sb.length() - 1) != '[') {
sb.append(',');
}
Object v = xfields.get(key);
sb.append(key);
sb.append('=');
if (v != null) {
if (v.getClass().isArray()) {
Object[] va = new Object[] { v };
v = Arrays.deepToString(va);
}
}
sb.append(v);
}
}
sb.append(']');
sb.append('@');
sb.append(Integer.toHexString(System.identityHashCode(this))
.toUpperCase());
return sb.toString();
}
@Override
/** Reflectively implement equals() using ForEquals annotations. */
public boolean equals(Object that) {
boolean res = that != null;
if (res) {
res = this.getClass().isAssignableFrom(that.getClass());
}
if (res) {
res = testEquals(that);
}
return res;
}
protected boolean testEquals(Object that) {
boolean res = true;
Map<String, Object> fields1 = getMineAndParentFields(ForEquals.class,
this);
Map<String, Object> fields2 = getMineAndParentFields(ForEquals.class,
that);
if (!isEmpty(fields1) && fields2.size() >= fields1.size()) {
for (String key : fields1.keySet()) {
Object v1 = fields1.get(key);
Object v2 = fields2.get(key);
if (v1 != null) {
if (v2 != null) {
res &= v1.equals(v2);
} else {
res = false;
}
} else {
if (v2 != null) {
res = false;
} else {
// res &= true;
}
}
if (!res) {
break;
}
}
} else {
res = super.equals(that);
}
return res;
}
@SuppressWarnings("rawtypes")
/** Reflectively implement compareTo() using ForCompareTo annotations. */
public int compareToOther(Object that) {
int res = that != null ? (this == that ? 0 : 1) : -1;
if (that != null) {
if (this instanceof Comparable && that instanceof Comparable
&& this.getClass().isAssignableFrom(that.getClass())) {
res = testCompareTo((Comparable) this, (Comparable) that);
}
}
return res;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
protected int testCompareTo(Comparable o1, Comparable o2) {
int res = 0;
Map<String, Object> fields1 = getMineAndParentFields(
ForCompareTo.class, o1);
Map<String, Object> fields2 = getMineAndParentFields(
ForCompareTo.class, o2);
if (!isEmpty(fields1) && fields2.size() >= fields1.size()) {
for (String key : fields1.keySet()) {
Object v1 = fields1.get(key);
Object v2 = fields2.get(key);
if (v1 != null) {
if (v2 != null) {
if (v1 instanceof Comparable
&& v2 instanceof Comparable) {
res = ((Comparable) v1).compareTo((Comparable) v2);
} else {
throw new IllegalArgumentException(
safeFormat(
"both arguments must be Comparables: (%s vs. %s)",
v1.getClass(), v2.getClass()));
}
} else {
res = 1;
}
} else {
if (v2 != null) {
res = -1;
} else {
// res = 0;
}
}
if (res != 0) {
break;
}
}
} else {
res = o1.compareTo(o2);
}
return res;
}
@Override
/** Reflectively implement hashCode() using ForEquals annotations. */
public int hashCode() {
int res = 31;
Map<String, Object> fields = getMineAndParentFields(ForEquals.class,
this);
if (!isEmpty(fields)) {
for (String key : fields.keySet()) {
Object v = fields.get(key);
if (v != null) {
res ^= v.hashCode();
}
}
} else {
res = super.hashCode();
}
return res;
}
protected Map<String, Object> getMineAndParentFields(
Class<? extends Annotation> targetAnnoation, Object target) {
Map<String, Object> fields = new TreeMap<String, Object>();
Class<? extends Object> clazz = target.getClass();
// getFieldValues(clazz, clazz.getFields(), fields);
while (clazz != null) {
getFieldValues(targetAnnoation, clazz, clazz.getDeclaredFields(),
fields, target);
clazz = clazz.getSuperclass();
}
return fields;
}
protected void getFieldValues(Class<? extends Annotation> targetAnnoation,
Class<? extends Object> clazz, Field[] fields,
Map<String, Object> xfields, Object target) {
for (Field field : fields) {
// printf("getFieldValues: class=%s, field=%s%n", clazz, field);
if (!Modifier.isStatic(field.getModifiers())) {
getFieldValue(targetAnnoation, xfields, field, target);
}
}
}
protected void getFieldValue(Class<? extends Annotation> targetAnnoation,
Map<String, Object> xfields, Field field, Object target) {
Annotation anno = field.getAnnotation(targetAnnoation);
if (anno != null) {
try {
boolean acc = field.isAccessible();
try {
field.setAccessible(true);
Object o = field.get(target);
String name = null;
Method value = anno.annotationType().getMethod("value");
if (value != null) {
name = (String) value.invoke(anno);
}
if (isEmpty(name)) {
name = field.getName();
}
xfields.put(name, o);
} finally {
field.setAccessible(acc);
}
} catch (Exception e) {
e.printStackTrace(); // temp
}
}
}
/** Safely implement sleep(). */
public static void sleep(long millis) {
if (millis >= 0) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
// e.printStackTrace(System.out);
}
}
}
/** Remove matching " or ' from a string. */
public String trimQuotes(String s) {
if (!isEmpty(s)) {
if (s.startsWith("\"") && s.endsWith("\"") || s.startsWith("'")
&& s.endsWith("'")) {
s = s.substring(1, s.length() - 1);
}
}
return s;
}
public static String safeFormat(String format, Object... args) {
return String.format(format, args);
}
}