/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><method name></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