View Javadoc

1   /*
2    * Copyright 2004 Sun Microsystems, Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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 &&                             // ensure it has a getter method
135                         pReadMethod.getDeclaringClass()!=Object.class && // filter Object.class getter methods
136                         pReadMethod.getParameterTypes().length==0) {     // filter getter methods that take parameters
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                     //NEW
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!=null) ? eValue.toString() : "null";
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                     //NEW
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!=null) ? cValue.toString() : "null";
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