1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.sun.syndication.feed.impl;
18
19 import java.beans.PropertyDescriptor;
20 import java.lang.reflect.Array;
21 import java.lang.reflect.Method;
22 import java.util.Collection;
23 import java.util.Iterator;
24 import java.util.Map;
25 import java.util.Stack;
26 import java.io.Serializable;
27
28 /***
29 * Provides deep <b>Bean</b> toString support.
30 * <p>
31 * It works on all read/write properties, recursively. It support all primitive types, Strings, Collections,
32 * ToString objects and multi-dimensional arrays of any of them.
33 * <p>
34 * @author Alejandro Abdelnur
35 *
36 */
37 public class ToStringBean implements Serializable {
38 private static final ThreadLocal PREFIX_TL = new ThreadLocal() {
39 public Object get() {
40 Object o = super.get();
41 if (o==null) {
42 o = new Stack();
43 set(o);
44 }
45 return o;
46 }
47 };
48
49 private static final Object[] NO_PARAMS = new Object[0];
50
51 private Class _beanClass;
52 private Object _obj;
53
54 /***
55 * Default constructor.
56 * <p>
57 * To be used by classes extending ToStringBean only.
58 * <p>
59 * @param beanClass indicates the class to scan for properties, normally an interface class.
60 *
61 */
62 protected ToStringBean(Class beanClass) {
63 _beanClass = beanClass;
64 _obj = this;
65 }
66
67 /***
68 * Creates a ToStringBean to be used in a delegation pattern.
69 * <p>
70 * For example:
71 * <p>
72 * <code>
73 * public class Foo implements ToString {
74 *
75 * public String toString(String prefix) {
76 * ToStringBean tsb = new ToStringBean(this);
77 * return tsb.toString(prefix);
78 * }
79 *
80 * public String toString() {
81 * return toString("Foo");
82 * }
83 *
84 * }
85 * </code>
86 * <p>
87 * @param beanClass indicates the class to scan for properties, normally an interface class.
88 * @param obj object bean to create String representation.
89 *
90 */
91 public ToStringBean(Class beanClass,Object obj) {
92 _beanClass = beanClass;
93 _obj = obj;
94 }
95
96 /***
97 * Returns the String representation of the bean given in the constructor.
98 * <p>
99 * It uses the Class name as the prefix.
100 * <p>
101 * @return bean object String representation.
102 *
103 */
104 public String toString() {
105 Stack stack = (Stack) PREFIX_TL.get();
106 String[] tsInfo = (String[]) ((stack.isEmpty()) ? null : stack.peek());
107 String prefix;
108 if (tsInfo==null) {
109 String className = _obj.getClass().getName();
110 prefix = className.substring(className.lastIndexOf(".")+1);
111 }
112 else {
113 prefix = tsInfo[0];
114 tsInfo[1] = prefix;
115 }
116 return toString(prefix);
117 }
118
119 /***
120 * Returns the String representation of the bean given in the constructor.
121 * <p>
122 * @param prefix to use for bean properties.
123 * @return bean object String representation.
124 *
125 */
126 private String toString(String prefix) {
127 StringBuffer sb = new StringBuffer(128);
128 try {
129 PropertyDescriptor[] pds = BeanIntrospector.getPropertyDescriptors(_beanClass);
130 if (pds!=null) {
131 for (int i=0;i<pds.length;i++) {
132 String pName = pds[i].getName();
133 Method pReadMethod = pds[i].getReadMethod();
134 if (pReadMethod!=null &&
135 pReadMethod.getDeclaringClass()!=Object.class &&
136 pReadMethod.getParameterTypes().length==0) {
137 Object value = pReadMethod.invoke(_obj,NO_PARAMS);
138 printProperty(sb,prefix+"."+pName,value);
139 }
140 }
141 }
142 }
143 catch (Exception ex) {
144 sb.append("\n\nEXCEPTION: Could not complete "+_obj.getClass()+".toString(): "+ex.getMessage()+"\n");
145 }
146 return sb.toString();
147 }
148
149 private void printProperty(StringBuffer sb,String prefix,Object value) {
150 if (value==null) {
151 sb.append(prefix).append("=null\n");
152 }
153 else
154 if (value.getClass().isArray()) {
155 printArrayProperty(sb,prefix,value);
156 }
157 else
158 if (value instanceof Map) {
159 Map map = (Map) value;
160 Iterator i = map.entrySet().iterator();
161 if (i.hasNext()) {
162 while (i.hasNext()) {
163 Map.Entry me = (Map.Entry) i.next();
164 String ePrefix = prefix+"["+me.getKey()+"]";
165 Object eValue = me.getValue();
166
167
168 String[] tsInfo = new String[2];
169 tsInfo[0] = ePrefix;
170 Stack stack = (Stack) PREFIX_TL.get();
171 stack.push(tsInfo);
172 String s = eValue.toString();
173 stack.pop();
174 if (tsInfo[1]==null) {
175 sb.append(ePrefix).append("=").append(s).append("\n");
176 }
177 else {
178 sb.append(s);
179 }
180 }
181 }
182 else {
183 sb.append(prefix).append("=[]\n");
184 }
185 }
186 else
187 if (value instanceof Collection) {
188 Collection collection = (Collection) value;
189 Iterator i = collection.iterator();
190 if (i.hasNext()) {
191 int c = 0;
192 while (i.hasNext()) {
193 String cPrefix = prefix+"["+(c++)+"]";
194 Object cValue = i.next();
195
196
197 String[] tsInfo = new String[2];
198 tsInfo[0] = cPrefix;
199 Stack stack = (Stack) PREFIX_TL.get();
200 stack.push(tsInfo);
201 String s = cValue.toString();
202 stack.pop();
203 if (tsInfo[1]==null) {
204 sb.append(cPrefix).append("=").append(s).append("\n");
205 }
206 else {
207 sb.append(s);
208 }
209 }
210 }
211 else {
212 sb.append(prefix).append("=[]\n");
213 }
214 }
215 else {
216 String[] tsInfo = new String[2];
217 tsInfo[0] = prefix;
218 Stack stack = (Stack) PREFIX_TL.get();
219 stack.push(tsInfo);
220 String s = value.toString();
221 stack.pop();
222 if (tsInfo[1]==null) {
223 sb.append(prefix).append("=").append(s).append("\n");
224 }
225 else {
226 sb.append(s);
227 }
228 }
229 }
230
231 private void printArrayProperty(StringBuffer sb, String prefix,Object array) {
232 int length = Array.getLength(array);
233 for (int i=0;i<length;i++) {
234 Object obj = Array.get(array,i);
235 printProperty(sb,prefix+"["+i+"]",obj);
236 }
237 }
238
239 }
240