View Javadoc

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; // nothing to add
251         }
252         Class[] destination = (Class[]) ArrayUtils.concatArrays(
253                 getTypesToExcludeInAutoComplete(),
254                 typesToAdd, Class.class);
255 //        Class[] original = getTypesToExcludeInAutoComplete();
256 //        Class[] destination = new Class[original.length
257 //                + typesToAdd.length];
258 //        System.arraycopy(original, 0, destination, 0, original.length);
259 //        System.arraycopy(typesToAdd, 0, destination,
260 //                original.length, typesToAdd.length);
261         setTypesToExcludeInAutoComplete(destination);
262     }
263     
264     
265 }