001: /*
002: * Copyright 2004 Sun Microsystems, Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: *
016: */
017: package com.sun.syndication.feed.impl;
018:
019: import java.beans.PropertyDescriptor;
020: import java.lang.reflect.Array;
021: import java.lang.reflect.Method;
022: import java.util.Collection;
023: import java.util.Iterator;
024: import java.util.Map;
025: import java.util.Stack;
026: import java.io.Serializable;
027:
028: /**
029: * Provides deep <b>Bean</b> toString support.
030: * <p>
031: * It works on all read/write properties, recursively. It support all primitive types, Strings, Collections,
032: * ToString objects and multi-dimensional arrays of any of them.
033: * <p>
034: * @author Alejandro Abdelnur
035: *
036: */
037: public class ToStringBean implements Serializable {
038: private static final ThreadLocal PREFIX_TL = new ThreadLocal() {
039: public Object get() {
040: Object o = super .get();
041: if (o == null) {
042: o = new Stack();
043: set(o);
044: }
045: return o;
046: }
047: };
048:
049: private static final Object[] NO_PARAMS = new Object[0];
050:
051: private Class _beanClass;
052: private Object _obj;
053:
054: /**
055: * Default constructor.
056: * <p>
057: * To be used by classes extending ToStringBean only.
058: * <p>
059: * @param beanClass indicates the class to scan for properties, normally an interface class.
060: *
061: */
062: protected ToStringBean(Class beanClass) {
063: _beanClass = beanClass;
064: _obj = this ;
065: }
066:
067: /**
068: * Creates a ToStringBean to be used in a delegation pattern.
069: * <p>
070: * For example:
071: * <p>
072: * <code>
073: * public class Foo implements ToString {
074: *
075: * public String toString(String prefix) {
076: * ToStringBean tsb = new ToStringBean(this);
077: * return tsb.toString(prefix);
078: * }
079: *
080: * public String toString() {
081: * return toString("Foo");
082: * }
083: *
084: * }
085: * </code>
086: * <p>
087: * @param beanClass indicates the class to scan for properties, normally an interface class.
088: * @param obj object bean to create String representation.
089: *
090: */
091: public ToStringBean(Class beanClass, Object obj) {
092: _beanClass = beanClass;
093: _obj = obj;
094: }
095:
096: /**
097: * Returns the String representation of the bean given in the constructor.
098: * <p>
099: * 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
107: .peek());
108: String prefix;
109: if (tsInfo == null) {
110: String className = _obj.getClass().getName();
111: prefix = className
112: .substring(className.lastIndexOf(".") + 1);
113: } else {
114: prefix = tsInfo[0];
115: tsInfo[1] = prefix;
116: }
117: return toString(prefix);
118: }
119:
120: /**
121: * Returns the String representation of the bean given in the constructor.
122: * <p>
123: * @param prefix to use for bean properties.
124: * @return bean object String representation.
125: *
126: */
127: private String toString(String prefix) {
128: StringBuffer sb = new StringBuffer(128);
129: try {
130: PropertyDescriptor[] pds = BeanIntrospector
131: .getPropertyDescriptors(_beanClass);
132: if (pds != null) {
133: for (int i = 0; i < pds.length; i++) {
134: String pName = pds[i].getName();
135: Method pReadMethod = pds[i].getReadMethod();
136: if (pReadMethod != null
137: && // ensure it has a getter method
138: pReadMethod.getDeclaringClass() != Object.class
139: && // filter Object.class getter methods
140: pReadMethod.getParameterTypes().length == 0) { // filter getter methods that take parameters
141: Object value = pReadMethod.invoke(_obj,
142: NO_PARAMS);
143: printProperty(sb, prefix + "." + pName, value);
144: }
145: }
146: }
147: } catch (Exception ex) {
148: sb.append("\n\nEXCEPTION: Could not complete "
149: + _obj.getClass() + ".toString(): "
150: + ex.getMessage() + "\n");
151: }
152: return sb.toString();
153: }
154:
155: private void printProperty(StringBuffer sb, String prefix,
156: Object value) {
157: if (value == null) {
158: sb.append(prefix).append("=null\n");
159: } else if (value.getClass().isArray()) {
160: printArrayProperty(sb, prefix, value);
161: } else if (value instanceof Map) {
162: Map map = (Map) value;
163: Iterator i = map.entrySet().iterator();
164: if (i.hasNext()) {
165: while (i.hasNext()) {
166: Map.Entry me = (Map.Entry) i.next();
167: String ePrefix = prefix + "[" + me.getKey() + "]";
168: Object eValue = me.getValue();
169:
170: //NEW
171: String[] tsInfo = new String[2];
172: tsInfo[0] = ePrefix;
173: Stack stack = (Stack) PREFIX_TL.get();
174: stack.push(tsInfo);
175: String s = (eValue != null) ? eValue.toString()
176: : "null";
177: stack.pop();
178: if (tsInfo[1] == null) {
179: sb.append(ePrefix).append("=").append(s)
180: .append("\n");
181: } else {
182: sb.append(s);
183: }
184: }
185: } else {
186: sb.append(prefix).append("=[]\n");
187: }
188: } else if (value instanceof Collection) {
189: Collection collection = (Collection) value;
190: Iterator i = collection.iterator();
191: if (i.hasNext()) {
192: int c = 0;
193: while (i.hasNext()) {
194: String cPrefix = prefix + "[" + (c++) + "]";
195: Object cValue = i.next();
196:
197: //NEW
198: String[] tsInfo = new String[2];
199: tsInfo[0] = cPrefix;
200: Stack stack = (Stack) PREFIX_TL.get();
201: stack.push(tsInfo);
202: String s = (cValue != null) ? cValue.toString()
203: : "null";
204: stack.pop();
205: if (tsInfo[1] == null) {
206: sb.append(cPrefix).append("=").append(s)
207: .append("\n");
208: } else {
209: sb.append(s);
210: }
211: }
212: } else {
213: sb.append(prefix).append("=[]\n");
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: } else {
225: sb.append(s);
226: }
227: }
228: }
229:
230: private void printArrayProperty(StringBuffer sb, String prefix,
231: 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: }
|