View Javadoc

1   package org.yajul.util;
2   
3   
4   import org.slf4j.Logger;
5   import org.slf4j.LoggerFactory;
6   
7   import java.lang.reflect.Field;
8   import java.lang.reflect.InvocationTargetException;
9   import java.lang.reflect.Method;
10  import java.text.DateFormat;
11  import java.text.SimpleDateFormat;
12  import java.util.TimeZone;
13  
14  /***
15   * Prints the fields of an object into a string buffer.
16   * <br>
17   * User: jdavis
18   * Date: Oct 21, 2003
19   * Time: 7:46:24 PM
20   *
21   * @author jdavis
22   */
23  public class FieldPrinter {
24      /***
25       * The logger for this class.
26       */
27      private static Logger log = LoggerFactory.getLogger(FieldPrinter.class);
28  
29      private static Class[] NO_PARAMETERS = new Class[0];
30      private static Object[] NO_ARGUMENTS = new Object[0];
31  
32      private StringBuilder sb;
33      private DateFormat df;
34  
35      public FieldPrinter() {
36          this(new StringBuilder());
37      }
38  
39      /***
40       * Creates a new object field printer, that will append
41       * to the supplied string builder.
42       *
43       * @param sb The StringBuilder to append to.
44       */
45      public FieldPrinter(StringBuilder sb) {
46          this.sb = sb;
47          df = new SimpleDateFormat("yyyyMMMdd HH:mm:ss zz");
48          df.setTimeZone(TimeZone.getDefault());
49      }
50  
51      /***
52       * Appends all of the fields of the object to the string buffer.
53       *
54       * @param o - The object to print.
55       */
56      public void append(Object o) {
57          if (o == null) {
58              sb.append("null");
59              return;
60          }
61  
62          // Write the class name and a square bracket.
63          Class c = o.getClass();
64          sb.append(c.getName());
65          sb.append("[");
66  
67          Field[] fields = c.getFields();
68          Method[] methods = c.getMethods();
69  
70          // Assume first we're going to get publically accessible fields in the object's class.
71          boolean usingFields = true;
72          int length = fields.length;
73  
74          // If there are 0 accessible fields, switch to looking for 'getFoo()' methods
75          if (length == 0) {
76              length = methods.length;
77              usingFields = false;
78          }
79  
80          // tracks how many valid methods we've output so far (to help with knowing when to insert ", " between them)
81          int validMethodCount = 0;
82          // for methods, only drill in if it's a valid 'getter'
83          boolean validAttribute;
84  
85          // Print out each item.
86          for (int i = 0; i < length; i++) {
87              Class attributeType = null;
88              Object attributeValue = null;
89  
90              try {
91                  // assign the Class and Object of the attribute from a Field or Method depending on what we're looking at
92                  if (usingFields) {
93                      // publically-accessible fields are always valid
94                      validAttribute = true;
95                      if (i > 0)
96                          sb.append(", ");
97                      sb.append(fields[i].getName());
98                      sb.append("=");
99  
100                     attributeType = fields[i].getType();
101                     attributeValue = fields[i].get(o);
102                 }
103                 // check the method to see if the name starts with 'get'
104                 else {
105                     // assume this method not a 'getter'
106                     validAttribute = false;
107                     // if it begins with 'get' or 'is' & has 0 parameters, use it
108                     if (ReflectionUtil.isPropertyGetter(methods[i])) {
109                         validAttribute = true;
110                         if (validMethodCount > 0)
111                             sb.append(", ");
112                         validMethodCount++;
113                         sb.append(methods[i].getName());
114                         sb.append("=");
115 
116                         attributeType = methods[i].getReturnType();
117                         attributeValue = methods[i].invoke(o);
118                     }
119                 }
120 
121                 // If this is a valid attribute, process
122                 if (validAttribute) {
123                     appendField(attributeValue, attributeType);
124                 } // if validAttribute
125             }
126             catch (IllegalAccessException e) {
127                 log.warn("Unexpected: " + e.getMessage(), e);
128                 sb.append("<error!>");
129             }
130             catch (InvocationTargetException e) {
131                 log.warn("Unexpected: " + e.getMessage(), e);
132                 sb.append("<error!>");
133             }
134         } // for i = 0 to length
135         sb.append("]");
136     }
137 
138     public static String toString(Object o) {
139         FieldPrinter fp = new FieldPrinter();
140         fp.append(o);
141         return fp.toString();
142     }
143 
144     public String toString() {
145         return sb.toString();
146     }
147 
148     private void appendField(Object attributeValue, Class attributeType) throws IllegalAccessException {
149         // If the value is null, just use 'null'.
150         if (attributeValue == null)
151             sb.append("null");
152             // If the value is a primitive or a number, just append it.
153         else if (attributeType.isPrimitive() || Number.class.isAssignableFrom(attributeType))
154             sb.append(attributeValue);
155             // Use UTC format for dates.
156         else if (java.util.Date.class.isAssignableFrom(attributeType))
157             sb.append(df.format((java.util.Date) attributeValue));
158             // Print strings in quotes.
159         else if (String.class.isAssignableFrom(attributeType))
160             appendValue(attributeValue, "'", "'", sb);
161             // Print arrays in brackets.
162         else if (attributeType.isArray())
163             appendValue(attributeValue, "{", "}", sb);
164             // Print objects nested in vertical bars.
165         else
166             appendNestedObject(attributeType, attributeValue);
167     }
168 
169     private void appendNestedObject(Class attributeType, Object attributeValue) throws IllegalAccessException {
170         String stringValue = null;
171         // If the class defines 'toString()', then use that.
172         Method m = null;
173         //noinspection EmptyCatchBlock
174         try {
175             m = attributeType.getDeclaredMethod("toString", NO_PARAMETERS);
176         }
177         catch (NoSuchMethodException ignore) {
178         }
179         if (m != null) {
180             try {
181                 stringValue = (String) m.invoke(attributeValue, NO_ARGUMENTS);
182             }
183             catch (InvocationTargetException ite) {
184                 log.warn("Unexpected: " + ite.getMessage(), ite);
185                 stringValue = null;
186             }
187         }
188         if (stringValue != null)
189             appendValue(stringValue, "#", "#", sb);
190             // Otherwise, recurse...
191         else {
192             sb.append("|");
193             append(attributeValue);
194             sb.append("|");
195         }
196     }
197 
198     private static void appendValue(Object fieldValue, String startDelim, String endDelim, StringBuilder sb) {
199         sb.append(startDelim);
200         sb.append(fieldValue);
201         sb.append(endDelim);
202     }
203 }
204