/Users/lyon/j4p/src/classUtils/pack/util/ObjectLister.java

1    package classUtils.pack.util; 
2     
3    import java.lang.reflect.InvocationTargetException; 
4    import java.lang.reflect.Method; 
5    import java.util.Collection; 
6    import java.util.Enumeration; 
7    import java.util.HashMap; 
8    import java.util.Iterator; 
9    import java.util.List; 
10   import java.util.Map; 
11   import java.util.ArrayList; 
12    
13   /** 
14    * A class to list the elements of an array or a collection/enumeration into a string, each element 
15    * divided by a separtor, by invoking either the <tt>toString()</tt> method 
16    * or a given method with no parameters (and returning a String). 
17    * <p> 
18    * The default {@link #list(java.lang.Object[]) list(array)} method 
19    * uses <tt>toString</tt> to describe each object; the {@link #list(java.lang.Object[], java.lang.String)  
20    * list(array, method name)} method attempts to locate a method with signature 
21    * <pre> 
22    *  public String <i>&lt;method name&gt;</i>() 
23    * </pre> 
24    * in each object in the array, and invokes it to describe each object. 
25    * <p> 
26    * For example, for the array <code>String [] array { "Hello", "World" }</code> 
27    * invoking <code>new ObjectLister().list(array);</code> will produce  
28    * <code>"Hello, World"</code>. 
29    * <p> 
30    * For the array <code>Thread [] array = { new Thread("Thread 1"),  
31    * new Thread("Thread 2") }</code>, invoking  
32    * <code>new ObjectLister().list(array, "getName");</code> will produce 
33    * <code>"Thread 1, Thread 2"</code>. 
34    *  
35    * @author Cristiano Sadun 
36    */ 
37   public class ObjectLister { 
38    
39       private static ObjectLister defaultInstance = new ObjectLister(); 
40    
41       private String separator; 
42       private boolean useToStringIfNotFound; 
43        
44       /** 
45        * The default separator sequence ", " 
46        */ 
47       public static final String DEFAULT_SEPARATOR = ", "; 
48    
49       // Chache of Class -> Map(name, Method) 
50       private Cache clsToMethodsCache = new Cache(3); 
51    
52       /** 
53        * Constructor for ObjectLister. 
54        *  
55        * @param separator the separator to use 
56        * @param useToStringIfNotFound if <b>true</b>, when {@link #list(java.lang.Object[], java.lang.String)  
57        * list(array, method name)} is invoked, <tt>toString()</tt> will be used if the given method 
58        * is not found in an object in the array to list. If <b>false</b> an exception will be raised. 
59        */ 
60       public ObjectLister(String separator, boolean useToStringIfNotFound) { 
61           this.separator = separator; 
62           this.useToStringIfNotFound = useToStringIfNotFound; 
63       } 
64        
65       /** 
66        * Constructor for ObjectLister, which uses the default sequence {@link #DEFAULT_SEPARATOR} as 
67        * separator. 
68        *  
69        * @param useToStringIfNotFound if <b>true</b>, when {@link #list(java.lang.Object[], java.lang.String)  
70        * list(array, method name)} is invoked, <tt>toString()</tt> will be used if the given method 
71        * is not found in an object in the array to list. If <b>false</b> an exception will be raised. 
72        */ 
73       public ObjectLister(boolean useToStringIfNotFound) { 
74           this(DEFAULT_SEPARATOR, useToStringIfNotFound); 
75       } 
76    
77       /** 
78        * Constructor for ObjectLister. When {@link #list(java.lang.Object[], java.lang.String)  
79        * list(array, method name)} is invoked, <tt>toString()</tt> will be used if the given method 
80        * is not found in an object in the array to list. 
81        *  
82        * @param separator the separator to use 
83        */ 
84       public ObjectLister(String separator) { 
85           this(separator, true); 
86       } 
87    
88       /** 
89        * Constructor for ObjectLister, which uses the default sequence {@link #DEFAULT_SEPARATOR} as 
90        * separator. When {@link #list(java.lang.Object[], java.lang.String)  
91        * list(array, method name)} is invoked, <tt>toString()</tt> will be used if the given method 
92        * is not found in an object in the array to list. 
93        */ 
94       public ObjectLister() { 
95           this(DEFAULT_SEPARATOR); 
96       } 
97    
98       /** 
99        * Return a String containing a list of objects in the array, 
100       * obtained invoking the given method name on each element 
101       * in the array. 
102       * <p> 
103       * The method must return a String and have no parameters. 
104       * @return a String containing a list of objects in the array 
105       * @param array the array to list 
106       * @param methodToUse the name of the method to use 
107       * @exception RuntimeException if a method with the given name, 
108       * which returns a String and has no parameter is not available in the objects 
109       * of the array. 
110       */ 
111      public String list(Object[] array, String methodToUse) { 
112          StringBuffer sb = new StringBuffer(); 
113          Object[] params = new Object[0]; 
114          for (int i = 0; i < array.length; i++) { 
115              if (array[i]==null) continue; 
116              try { 
117                  Method m = findMethod(array[i], methodToUse); 
118                  String s = (String) m.invoke(array[i], params); 
119                  sb.append("\""); 
120                  sb.append(s); 
121                  sb.append("\""); 
122   
123              } catch (NoSuchMethodException e) { 
124                  if (useToStringIfNotFound) 
125                      sb.append(array[i].toString()); 
126                  else 
127                      throw new RuntimeException(e); 
128              } catch (InvocationTargetException e) { 
129                  if (useToStringIfNotFound) 
130                      sb.append(array[i].toString()); 
131                  else 
132                      throw new RuntimeException(e); 
133              } catch (IllegalAccessException e) { 
134                  throw new RuntimeException( 
135                      "\"" + methodToUse + "()\" exists in "+array[i].getClass().getName()+", but can't be accessed", 
136                      e); 
137              } 
138              if (i < array.length - 1) 
139                  sb.append(separator); 
140          } 
141          return sb.toString(); 
142      } 
143       
144      /** 
145       * Find a public String <name>() method for the given object. 
146       *  
147       * @param object the object in whose class to look for 
148       * @param methodName  
149       * @return Method the found method 
150       */ 
151      private Method findMethod(Object object, String methodName) 
152          throws NoSuchMethodException { 
153   
154          Class cls = object.getClass(); 
155          synchronized (this) { 
156              Cache methodsCache; 
157              if ((methodsCache = (Cache) clsToMethodsCache.get(cls)) == null) { 
158                  methodsCache = new Cache(3); 
159                  clsToMethodsCache.put(cls, methodsCache); 
160              } 
161              Method method; 
162              if ((method = (Method) methodsCache.get(methodName)) == null) { 
163                  method = cls.getMethod(methodName, new Class[0]); 
164                  if (method.getReturnType() != String.class) 
165                      throw new NoSuchMethodException( 
166                          "\"" 
167                              + methodName 
168                              + "()\" exists, but has not String return type"); 
169                  methodsCache.put(methodName, method); 
170              } 
171              return method; 
172          } 
173      } 
174   
175      /** 
176       * Invoke {@link #list(java.lang.Object[], java.lang.String) list()} using 
177       * the <tt>toString()</tt> method. 
178       *  
179       * @param  array to be listed 
180       * @return a string listing the array 
181       */ 
182      public String list(Object[] array) { 
183          return list(array, "toString"); 
184      } 
185       
186      /** 
187       * Return a String containing a list of objects in the collection, 
188       * obtained invoking the given method name on each element 
189       * in the collection. 
190       * <p> 
191       * The method must return a String and have no parameters. 
192       * @return a String containing a list of objects in the collection 
193       * @param coll the collection to list 
194       * @param methodToUse the name of the method to use 
195       * @exception RuntimeException if a method with the given name, 
196       * which returns a String and has no parameter is not available in the objects 
197       * of the collection. 
198       */ 
199      public String list(Collection coll, String methodToUse) { 
200          Object [] array = new Object[coll.size()]; 
201          coll.toArray(array); 
202          return list(array, methodToUse); 
203      } 
204       
205      /** 
206       * Invoke {@link #list(java.util.Collection, java.lang.String) list()} using 
207       * the <tt>toString()</tt> method. 
208       *  
209       * @param  coll to be listed 
210       * @return a string listing the collection 
211       */ 
212      public String list(Collection coll) { 
213          return list(coll, "toString"); 
214      } 
215       
216      /** 
217       * Return a String containing a list of objects in the enumeration, 
218       * obtained invoking the given method name on each element 
219       * in the enumeration. 
220       * <p> 
221       * The method must return a String and have no parameters. 
222       * @return a String containing a list of objects in the enumeration 
223       * @param e the enumeration to list 
224       * @param methodToUse the name of the method to use 
225       * @exception RuntimeException if a method with the given name, 
226       * which returns a String and has no parameter is not available in the objects 
227       * of the enumeration. 
228       */ 
229      public String list(Enumeration e, String methodToUse) { 
230          Object [] array = enumerate(e); 
231          return list(array, methodToUse); 
232      } 
233       
234      /** 
235       * Invoke {@link #list(java.util.Enumeration, java.lang.String) list()} using 
236       * the <tt>toString()</tt> method. 
237       *  
238       * @param e the enumeration to be listed 
239       * @return a string listing the enumeration 
240       */ 
241      public String list(Enumeration e) { 
242          return list(e, "toString"); 
243      } 
244       
245      private class Entry { 
246          Object key; 
247          Object value; 
248          String methodToUse; 
249           
250          public Entry(Object key, Object value, String methodToUse) { 
251              this.key=key; 
252              this.value=value; 
253              this.methodToUse=methodToUse; 
254          } 
255           
256          public String toString() { 
257              return key.toString()+" = "+list(new Object[] { value }, methodToUse); 
258          } 
259           
260      } 
261   
262      /** 
263       * Return a String containing a list of values in the map, 
264       * obtained invoking the given method name on each element 
265       * in the map. 
266       * <p> 
267       * The method must return a String and have no parameters. 
268       * @return a String containing a list of values in the map 
269       * @param map the map to list 
270       * @param methodToUse the name of the method to use 
271       * @exception RuntimeException if a method with the given name, 
272       * which returns a String and has no parameter is not available in the objects 
273       * of the map. 
274       */  
275      public String list(Map map, String methodToUse) { 
276          Entry [] entries = new Entry[map.keySet().size()]; 
277          int c=0; 
278          for(Iterator i=map.keySet().iterator();i.hasNext();) { 
279              Object key=i.next(); 
280              Object value=map.get(key); 
281              entries[c++]=new Entry(key, value, methodToUse); 
282          } 
283          return list(entries, methodToUse); 
284      } 
285       
286      /** 
287       * Invoke {@link #list(java.util.Map, java.lang.String) list()} using 
288       * the <tt>toString()</tt> method. 
289       *  
290       * @param  map to be listed 
291       * @return a string listing the map 
292       */  
293      public String list(Map map) { 
294          return list(map, "toString"); 
295      } 
296   
297      /** 
298       * Returns the useToStringIfNotFound. 
299       * @return boolean 
300       */ 
301      public boolean isUseToStringIfNotFound() { 
302          return useToStringIfNotFound; 
303      } 
304   
305      /** 
306       * Sets the useToStringIfNotFound. 
307       * @param useToStringIfNotFound The useToStringIfNotFound to set 
308       */ 
309      public void setUseToStringIfNotFound(boolean useToStringIfNotFound) { 
310          this.useToStringIfNotFound = useToStringIfNotFound; 
311      } 
312   
313      /** 
314       * A test method 
315       */ 
316      public static void main(String args[]) { 
317          Object[] array = new Object[3]; 
318          array[0] = "Hello world"; 
319          array[1] = new Thread("Test Thread 2"); 
320          array[2] = new Object[0]; 
321           
322          Map map = new HashMap(); 
323          map.put("Key 1", array[0]); 
324          map.put("Key 2", array[1]); 
325          map.put("Key 3", array[2]); 
326   
327          System.out.println(new ObjectLister().list(map)); 
328      } 
329   
330      /** 
331       * Returns the default instance, which uses {@link #DEFAULT_SEPARATOR}. 
332       *  
333       * @return ObjectLister the default object lister. 
334       */ 
335      public static ObjectLister getInstance() { 
336          return defaultInstance; 
337      } 
338   
339      private static Object [] enumerate(Enumeration e) { 
340          List l = new ArrayList(); 
341          while(e.hasMoreElements()) l.add(e.nextElement()); 
342          return l.toArray(); 
343      } 
344   
345  } 
346