1 /***
2 *
3 */
4 package ar.com.epere4.java2excel.parser.impl;
5
6 import java.beans.PropertyDescriptor;
7 import java.util.Collection;
8 import java.util.Iterator;
9 import java.util.Stack;
10
11 import org.apache.commons.beanutils.PropertyUtilsBean;
12 import org.apache.commons.collections.map.ListOrderedMap;
13 import org.apache.commons.lang.Validate;
14 import org.apache.commons.lang.builder.ToStringBuilder;
15 import org.apache.commons.lang.builder.ToStringStyle;
16
17 import ar.com.epere4.java2excel.parser.BodyDtd;
18 import ar.com.epere4.java2excel.parser.PropertyDtd;
19 import ar.com.epere4.util.collections.MapValuesIterator;
20 import ar.com.epere4.util.lang.ArrayUtils;
21 import ar.com.epere4.util.lang.Types;
22
23 /***
24 * @author epere4
25 *
26 */
27 public class BodyDtdImpl implements BodyDtd {
28 /*** This contains the property includeExtraLevel. */
29 private int includeExtraLevel;
30
31 /***
32 * This is the default value for {@link #getTypesToExcludeInAutoComplete()}.
33 */
34 public static final Class[] DEFAULT_TYPES_TO_EXCLUDE_IN_AUTO_COMPLETE = {
35 Collection.class, Class.class };
36
37 /***
38 * These represents the classes or interfaces to exclude in
39 * the {@link #completeProperties(Class)} method.
40 * Properties of this types will not be followed.
41 */
42 private Class[] typesToExcludeInAutoComplete =
43 DEFAULT_TYPES_TO_EXCLUDE_IN_AUTO_COMPLETE;
44
45 /***
46 * List of type {@link PropertyDtd}.
47 */
48 private ListOrderedMap properties = new ListOrderedMap();
49
50 /***
51 * {@inheritDoc}
52 *
53 * @see BodyDtd#addProperty(PropertyDtd)
54 * @since 18/02/2006
55 */
56 public void addProperty(final PropertyDtd property) {
57 properties.put(property.getExpression(), property);
58 }
59
60 /***
61 * {@inheritDoc}
62 *
63 * @see BodyDtd#propertiesIterator()
64 */
65 public Iterator propertiesIterator() {
66 return new MapValuesIterator(properties.mapIterator());
67 }
68
69 /***
70 * {@inheritDoc}
71 *
72 * @see java.lang.Object#toString()
73 */
74 public String toString() {
75 StringBuffer sb = new StringBuffer();
76 sb.append(super.toString());
77 sb.append("\n");
78 sb.append(ToStringBuilder.reflectionToString(this,
79 ToStringStyle.MULTI_LINE_STYLE));
80 sb.append("[\n");
81 int i = 0;
82 for (Iterator iter = propertiesIterator(); iter.hasNext(); i++) {
83 PropertyDtd property = (PropertyDtd) iter.next();
84 sb.append("[").append(i).append("]");
85 sb.append(property.toString());
86 sb.append('\n');
87 }
88 sb.append("]\n");
89 return sb.toString();
90 }
91
92 /***
93 * {@inheritDoc}
94 *
95 * @see BodyDtd#getIncludeExtraLevel()
96 * @since 18/02/2006
97 */
98 public int getIncludeExtraLevel() {
99 return includeExtraLevel;
100 }
101
102 /***
103 * @param includeExtraLevel
104 * The includeExtraLevel to set.
105 * @since 18/02/2006
106 */
107 public void setIncludeExtraLevel(final int includeExtraLevel) {
108 Validate.isTrue(includeExtraLevel >= -1,
109 "includeExtraLevel must be >= -1: ", includeExtraLevel);
110 this.includeExtraLevel = includeExtraLevel;
111 }
112
113 /***
114 * {@inheritDoc}
115 *
116 * @see ar.com.epere4.java2excel.parser.BodyDtd
117 * #completeProperties(java.lang.Class)
118 * @since 20/02/2006
119 */
120 public void completeProperties(final Class beanClass) {
121 completeProperties(beanClass, getIncludeExtraLevel(), new Stack());
122 setIncludeExtraLevel(-1);
123 }
124
125 /***
126 * This is the method that does the work for
127 * {@link #completeProperties(Class)}.
128 *
129 * @param beanClass
130 * the class where properties will be looked for.
131 * @param currLevel
132 * the number of levels we need to examine.
133 * @param propertyStack
134 * the stack of property names. See
135 * {@link #makePropertyOgnl(Stack)} for more details of what this
136 * is for.
137 * @since 20/02/2006
138 */
139 private void completeProperties(final Class beanClass, final int currLevel,
140 final Stack propertyStack) {
141 if (currLevel < 0) {
142 return;
143 }
144 PropertyDescriptor[] descriptors = new PropertyUtilsBean()
145 .getPropertyDescriptors(beanClass);
146 for (int i = 0; i < descriptors.length; i++) {
147 PropertyDescriptor descriptor = descriptors[i];
148
149 propertyStack.push(descriptor.getName());
150
151 Class propClass = descriptor.getPropertyType();
152 if (hasToFollowThisProperty(propClass)) {
153 if (Types.isNaturalJavaClass(propClass)
154 || Types.isNativeJavaPrimitive(propClass)) {
155 PropertyDtdImpl property = new PropertyDtdImpl();
156 property.setExpression(makePropertyOgnl(propertyStack));
157 addProperty(property);
158 } else {
159 completeProperties(propClass, currLevel - 1, propertyStack);
160 }
161 }
162
163 propertyStack.pop();
164 }
165 }
166
167 /***
168 * This converts a stack of property names into an ognl expression that
169 * would recover the property the stack is holding. <br/>If the stack has:
170 * <code>
171 * TOP ----- name3
172 * ----- name2
173 * ----- name1
174 * BOTTOM -- name0
175 * </code>
176 * Then, this method would return <code>name0.name1.name2.name3</code>.
177 *
178 * @param propertyStack
179 * the stack where the property names are stored (type
180 * {@link String}).
181 * @return the full ognl expression that recovers the property from an
182 * object.
183 * @since 20/02/2006
184 */
185 private String makePropertyOgnl(final Stack propertyStack) {
186 StringBuffer sb = new StringBuffer();
187 for (Iterator iter = propertyStack.iterator(); iter.hasNext();) {
188 String property = (String) iter.next();
189 sb.append(property);
190 if (iter.hasNext()) {
191 sb.append('.');
192 }
193 }
194 return sb.toString();
195 }
196
197 /***
198 * @param clazz the class to be analyzed if followed or not.
199 * @return true if a property of type clazz should be followed
200 * in the {@link #completeProperties(Class)} method.
201 * @since 20/02/2006
202 */
203 private boolean hasToFollowThisProperty(final Class clazz) {
204 for (int i = 0; i < getTypesToExcludeInAutoComplete().length; i++) {
205 Class excluded = getTypesToExcludeInAutoComplete()[i];
206 if (excluded.isAssignableFrom(clazz)) {
207 return false;
208 }
209 }
210 return true;
211 }
212
213 /***
214 * These represents the classes or interfaces to exclude in
215 * the {@link #completeProperties(Class)} method.
216 * Properties of this types will not be followed.
217 *
218 * @return Returns the typesToExcludeInAutoComplete.
219 * @since 21/02/2006
220 */
221 public Class[] getTypesToExcludeInAutoComplete() {
222 return typesToExcludeInAutoComplete;
223 }
224
225 /***
226 * @see #getTypesToExcludeInAutoComplete()
227 * @param typesToExcludeInAutoComplete
228 * The typesToExcludeInAutoComplete to set.
229 * @since 21/02/2006
230 */
231 public void setTypesToExcludeInAutoComplete(
232 final Class[] typesToExcludeInAutoComplete) {
233 if (typesToExcludeInAutoComplete == null) {
234 this.typesToExcludeInAutoComplete = new Class[0];
235 } else {
236 this.typesToExcludeInAutoComplete = typesToExcludeInAutoComplete;
237 }
238 }
239
240 /***
241 * Adds the specified types to what
242 * {@link #getTypesToExcludeInAutoComplete()} will return.
243 *
244 * @param typesToAdd the types to add.
245 * @since 22/02/2006
246 */
247 public void addTypesToExcludeInAutoComplete(
248 final Class[] typesToAdd) {
249 if (typesToAdd == null) {
250 return;
251 }
252 Class[] destination = (Class[]) ArrayUtils.concatArrays(
253 getTypesToExcludeInAutoComplete(),
254 typesToAdd, Class.class);
255
256
257
258
259
260
261 setTypesToExcludeInAutoComplete(destination);
262 }
263
264
265 }