package org.yajul.reflection; import org.yajul.util.ReflectionUtil; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.TimeZone; import java.util.logging.Level; import java.util.logging.Logger; /** * Prints the fields of an object into a string buffer. * <br> * User: jdavis * Date: Oct 21, 2003 * Time: 7:46:24 PM * * @author jdavis */ public class FieldPrinter { /** * The logger for this class. */ private static Logger log = Logger.getLogger(FieldPrinter.class.getName()); private static Class[] NO_PARAMETERS = new Class[0]; private static Object[] NO_ARGUMENTS = new Object[0]; private StringBuilder sb; private DateFormat df; public FieldPrinter() { this(new StringBuilder()); } /** * Creates a new object field printer, that will append * to the supplied string builder. * * @param sb The StringBuilder to append to. */ public FieldPrinter(StringBuilder sb) { this.sb = sb; df = new SimpleDateFormat("yyyyMMMdd HH:mm:ss zz"); df.setTimeZone(TimeZone.getDefault()); } /** * Appends all of the fields of the object to the string buffer. * * @param o - The object to print. */ public void append(Object o) { if (o == null) { sb.append("null"); return; } // Write the class name and a square bracket. Class c = o.getClass(); sb.append(c.getName()); sb.append("["); Field[] fields = c.getFields(); Method[] methods = c.getMethods(); // Assume first we're going to get publically accessible fields in the object's class. boolean usingFields = true; int length = fields.length; // If there are 0 accessible fields, switch to looking for 'getFoo()' methods if (length == 0) { length = methods.length; usingFields = false; } // tracks how many valid methods we've output so far (to help with knowing when to insert ", " between them) int validMethodCount = 0; // for methods, only drill in if it's a valid 'getter' boolean validAttribute; // Print out each item. for (int i = 0; i < length; i++) { Class attributeType = null; Object attributeValue = null; try { // assign the Class and Object of the attribute from a Field or Method depending on what we're looking at if (usingFields) { // publically-accessible fields are always valid validAttribute = true; if (i > 0) sb.append(", "); sb.append(fields[i].getName()); sb.append("="); attributeType = fields[i].getType(); attributeValue = fields[i].get(o); } // check the method to see if the name starts with 'get' else { // assume this method not a 'getter' validAttribute = false; // if it begins with 'get' or 'is' & has 0 parameters, use it if (ReflectionUtil.isPropertyGetter(methods[i])) { validAttribute = true; if (validMethodCount > 0) sb.append(", "); validMethodCount++; sb.append(methods[i].getName()); sb.append("="); attributeType = methods[i].getReturnType(); attributeValue = methods[i].invoke(o); } } // If this is a valid attribute, process if (validAttribute) { appendField(attributeValue, attributeType); } // if validAttribute } catch (IllegalAccessException e) { log.log(Level.WARNING, "Unexpected: " + e.getMessage(), e); sb.append("<error!>"); } catch (InvocationTargetException e) { log.log(Level.WARNING, "Unexpected: " + e.getMessage(), e); sb.append("<error!>"); } } // for i = 0 to length sb.append("]"); } public static String toString(Object o) { FieldPrinter fp = new FieldPrinter(); fp.append(o); return fp.toString(); } public String toString() { return sb.toString(); } private void appendField(Object attributeValue, Class attributeType) throws IllegalAccessException { // If the value is null, just use 'null'. if (attributeValue == null) sb.append("null"); // If the value is a primitive or a number, just append it. else if (attributeType.isPrimitive() || Number.class.isAssignableFrom(attributeType)) sb.append(attributeValue); // Use UTC format for dates. else if (java.util.Date.class.isAssignableFrom(attributeType)) sb.append(df.format((java.util.Date) attributeValue)); // Print strings in quotes. else if (String.class.isAssignableFrom(attributeType)) appendValue(attributeValue, "'", "'", sb); // Print arrays in brackets. else if (attributeType.isArray()) appendValue(attributeValue, "{", "}", sb); // Print objects nested in vertical bars. else appendNestedObject(attributeType, attributeValue); } private void appendNestedObject(Class attributeType, Object attributeValue) throws IllegalAccessException { String stringValue = null; // If the class defines 'toString()', then use that. Method m = null; //noinspection EmptyCatchBlock try { //noinspection unchecked m = attributeType.getDeclaredMethod("toString", NO_PARAMETERS); } catch (NoSuchMethodException ignore) { } if (m != null) { try { stringValue = (String) m.invoke(attributeValue, NO_ARGUMENTS); } catch (InvocationTargetException ite) { log.log(Level.WARNING, "Unexpected: " + ite.getMessage(), ite); stringValue = null; } } if (stringValue != null) appendValue(stringValue, "#", "#", sb); // Otherwise, recurse... else { sb.append("|"); append(attributeValue); sb.append("|"); } } private static void appendValue(Object fieldValue, String startDelim, String endDelim, StringBuilder sb) { sb.append(startDelim); sb.append(fieldValue); sb.append(endDelim); } }