View Javadoc

1   /*
2    * Copyright 2004-2005, 2008 the original author or authors.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License. You may obtain a copy of
6    * 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, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
15   */
16  package net.sf.morph.transform.converters;
17  
18  import java.util.Arrays;
19  import java.util.Locale;
20  import java.util.Set;
21  
22  import net.sf.morph.transform.Converter;
23  import net.sf.morph.util.ClassUtils;
24  import net.sf.morph.util.ContainerUtils;
25  import net.sf.morph.util.MutableInteger;
26  import net.sf.morph.util.TransformerUtils;
27  
28  /**.
29   * <p>
30   * Creates a String representation of an object that is useful for debugging.
31   * This class is threadsafe and will not enter an infinite loop, even if
32   * displaying the information in a cyclic graph of objects.
33   * </p>
34   * 
35   * @author Matt Sgarlata
36   * @since Feb 15, 2005
37   */
38  public class ObjectToPrettyTextConverter extends BaseToPrettyTextConverter {
39  	/** Default types using <code>toString()</code> */
40  	public static final Class[] DEFAULT_TYPES_USING_TO_STRING = new Class[] {
41  		String.class, Long.class, Integer.class, Short.class, Character.class,
42  		Byte.class, Double.class, Float.class, Boolean.class, Long.TYPE,
43  		Integer.TYPE, Short.TYPE, Character.TYPE, Byte.TYPE, Double.TYPE,
44  		Float.TYPE, Boolean.TYPE, StringBuffer.class
45  	};
46  
47  	/** Default levels */
48  	public static final int DEFAULT_LEVELS = 1;
49  
50  	private int levels = DEFAULT_LEVELS;
51  	private Converter containerToPrettyTextConverter;
52  	private Converter beanToPrettyTextConverter;
53  	private static ThreadLocal currentLevelThreadLocal = new ThreadLocal() {
54  		/**
55  		 * {@inheritDoc}
56  		 */
57  		protected Object initialValue() {
58  			return new MutableInteger(-1);
59  		}
60  	};
61  	private Set typesUsingToString;
62  
63  	/**
64  	 * Create a new ObjectToPrettyTextConverter.
65  	 */
66  	public ObjectToPrettyTextConverter() {
67  		super();
68  		setTypesUsingToString(DEFAULT_TYPES_USING_TO_STRING);
69  	}
70  
71  	/**
72  	 * {@inheritDoc}
73  	 */
74  	protected Object convertImpl(Class destinationClass, Object source, Locale locale) throws Exception {
75  		MutableInteger currentLevel = (MutableInteger) currentLevelThreadLocal.get();
76  		currentLevel.value++;
77  
78  		try {
79  			// if we aren't down too many levels in the object graph
80  			if (currentLevel.value < levels) {
81  				if (source == null) {
82  					return "null";
83  				}
84  				if (getTypesUsingToStringInternal().contains(source.getClass())) {
85  					return source.toString(); 
86  				}
87  				if (TransformerUtils.isTransformable(getContainerToPrettyTextConverter(),
88  					destinationClass, ClassUtils.getClass(source))) {
89  					return getContainerToPrettyTextConverter().convert(destinationClass, source, locale);
90  				}
91  				if (TransformerUtils.isTransformable(getBeanToPrettyTextConverter(),
92  					destinationClass, ClassUtils.getClass(source))) {
93  					return getBeanToPrettyTextConverter().convert(destinationClass, source, locale);
94  				}
95  			}
96  		}
97  		catch (Exception e) {
98  			if (log.isErrorEnabled()) {
99  				log.error("Error occurred while attempting to create a formatted text representation of source " + source, e);
100 			}
101 		}
102 		finally {
103 			currentLevel.value--;
104 		}
105 		return getToTextConverter().convert(destinationClass, source, locale);
106 	}
107 
108 	/**
109 	 * {@inheritDoc}
110 	 */
111 	protected Class[] getSourceClassesImpl() throws Exception {
112 		Set candidates = ContainerUtils.createOrderedSet();
113 		candidates.addAll(Arrays.asList(getContainerToPrettyTextConverter().getSourceClasses()));
114 		candidates.addAll(Arrays.asList(getBeanToPrettyTextConverter().getSourceClasses()));
115 		candidates.addAll(Arrays.asList(getToTextConverter().getSourceClasses()));
116 		return (Class[]) candidates.toArray(new Class[candidates.size()]);
117 	}
118 
119 	/**
120 	 * Get the levels
121 	 * @return int
122 	 */
123 	public int getLevels() {
124 		return levels;
125 	}
126 
127 	/**
128 	 * Set the levels.
129 	 * @param levels
130 	 */
131 	public void setLevels(int levels) {
132 		this.levels = levels;
133 	}
134 
135 	/**
136 	 * Get the converter used to convert beans to pretty text.
137 	 * @return Converter
138 	 */
139 	public Converter getBeanToPrettyTextConverter() {
140 		if (beanToPrettyTextConverter == null) {
141 			BeanToPrettyTextConverter converter = new BeanToPrettyTextConverter();
142 			converter.setToTextConverter(this);
143 			setBeanToPrettyTextConverter(converter);
144 		}
145 		return beanToPrettyTextConverter;
146 	}
147 
148 	/**
149 	 * Set the converter used to convert beans to pretty text.
150 	 * @param beanToTextConverter
151 	 */
152 	public void setBeanToPrettyTextConverter(Converter beanToTextConverter) {
153 		this.beanToPrettyTextConverter = beanToTextConverter;
154 	}
155 
156 	/**
157 	 * Get the converter used to convert containers to pretty text.
158 	 * @return Converter
159 	 */
160 	public Converter getContainerToPrettyTextConverter() {
161 		if (containerToPrettyTextConverter == null) {
162 			ContainerToPrettyTextConverter converter = new ContainerToPrettyTextConverter();
163 			converter.setToTextConverter(this);
164 			setContainerToPrettyTextConverter(converter);
165 		}
166 		return containerToPrettyTextConverter;
167 	}
168 
169 	/**
170 	 * Set the converter used to convert containers to pretty text.
171 	 * @param containerToTextConverter
172 	 */
173 	public void setContainerToPrettyTextConverter(Converter containerToTextConverter) {
174 		this.containerToPrettyTextConverter = containerToTextConverter;
175 	}
176 
177 	/**
178 	 * Get the types using <code>toString()</code>.
179 	 * @return Set<Class>
180 	 */
181 	protected Set getTypesUsingToStringInternal() {
182 		// make sure the set is initialized
183 		if (typesUsingToString == null) getTypesUsingToString();
184 		return typesUsingToString;
185 	}
186 
187 	/**
188 	 * Get the types using <code>toString()</code>.
189 	 * @return Class[]
190 	 */
191 	public Class[] getTypesUsingToString() {
192 		if (typesUsingToString == null) {
193 			setTypesUsingToString(DEFAULT_TYPES_USING_TO_STRING);
194 		}
195 		return (Class[]) typesUsingToString.toArray(new Class[typesUsingToString.size()]);
196 	}
197 
198 	/**
199 	 * Set the types using <code>toString()</code>.
200 	 * @param typesUsingToString
201 	 */
202 	public void setTypesUsingToString(Class[] typesUsingToString) {
203 		Set s = ContainerUtils.createOrderedSet();
204 		s.addAll(Arrays.asList(typesUsingToString));
205 		this.typesUsingToString = s;
206 	}
207 }