KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > fr > improve > struts > taglib > layout > datagrid > DatagridImpl


1 /*
2  * Created on 5 avr. 2004
3  *
4  * Copyright Improve SA 2004.
5  * All rights reserved.
6  */

7 package fr.improve.struts.taglib.layout.datagrid;
8
9 import java.beans.PropertyDescriptor JavaDoc;
10 import java.lang.reflect.InvocationTargetException JavaDoc;
11 import java.util.ArrayList JavaDoc;
12 import java.util.Collection JavaDoc;
13 import java.util.Collections JavaDoc;
14 import java.util.HashMap JavaDoc;
15 import java.util.HashSet JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.List JavaDoc;
18 import java.util.Map JavaDoc;
19 import java.util.Set JavaDoc;
20 import java.util.TreeMap JavaDoc;
21
22 import org.apache.commons.beanutils.BasicDynaBean;
23 import org.apache.commons.beanutils.BasicDynaClass;
24 import org.apache.commons.beanutils.DynaBean;
25 import org.apache.commons.beanutils.DynaClass;
26 import org.apache.commons.beanutils.DynaProperty;
27 import org.apache.commons.beanutils.PropertyUtils;
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30
31 /**
32  * <p>Implementation of a Datagrid.</p>
33  *
34  * <p>How it works:</p>
35  *
36  * <ul>
37  * <li>The data and dataclass are first set with the setData and setDataclass methods.</li>
38  * <li>The datagrid tag uses the getData method to get the data, build an iterator and display the datagrid.</li>
39  * <li>When the form is submitted, the set(String in_property, String in_index, Object in_value) method
40  * is called by BeanUtils to update the object properties and states.</li>
41  * <li>The use calls the different get methods of the Datagrid to get back the objects.
42  * </ul>
43  *
44  * @author jnribette
45  */

46 public class DatagridImpl extends Datagrid implements DynaBean {
47     /**
48      * Log object.
49      */

50     private static Log LOG = LogFactory.getLog(DatagridImpl.class);
51     
52     /**
53      * Initial data list
54      */

55     private List JavaDoc data;
56     
57     /**
58      * Added data.
59      */

60     private Map JavaDoc addedData;
61     
62     /**
63      * Updated data.
64      */

65     private Map JavaDoc modifiedData;
66     
67     /**
68      * Data with a specified state.
69      * The Map contains (key/value) entries in the form (state (String) / objects having this state (List))
70      */

71     private Map JavaDoc dataWithStates = new HashMap JavaDoc();
72     
73     /**
74      * Data with a specified state.
75      * The map contains (key/value) entries in the form (object index (Integer) / state (String))
76      */

77     private Map JavaDoc indexWithStates = new HashMap JavaDoc();
78     
79     /**
80      * List of objects that have been added and removed at the same time.
81      * Used to prevent to have the objects in both the added and removed lists.
82      */

83     private Set JavaDoc addedAndRemoved = new HashSet JavaDoc();
84     
85     /**
86      * Class of the data being manipulated.
87      */

88     private Class JavaDoc dataClass;
89     
90     /**
91      * Dynaclass.
92      * This dynaclass is created to have the same properties as the class "dataClass" plus a "dataState" property.
93      */

94     private DynaClass dataDynaClass;
95     
96     /**
97      * Set of editable boolean properties.
98      */

99     private Set JavaDoc booleans = new HashSet JavaDoc();
100     
101     /**
102      * Map of originals boolean values.
103      */

104     private Map JavaDoc booleanStates = new HashMap JavaDoc();
105     
106     
107     /**
108      * A new datagrid, whose data are not defined yet.
109      */

110     private static final int STATUS_NEW = 0;
111     
112     /**
113      * A datagrid whose data have just been set.
114      */

115     private static final int STATUS_SET = 1;
116     
117     /**
118      * A datagrid whose state has been updated by Struts.
119      */

120     private static final int STATUS_DIRTY = 2;
121     
122     /**
123      * A datagrid whose state has been read.
124      */

125     private static final int STATUS_READ = 3;
126     
127     /**
128      * Current status of the datagrid.
129      */

130     private int status = STATUS_NEW;
131     
132    
133     
134     /**
135      * Non public Constructor
136      */

137     DatagridImpl() {
138         
139     }
140     
141     // ----------------------------- PROTECTED METHODS ------------------------
142

143     /**
144      * Register an object in the modified Map.
145      * @param in_index the index of the object to modify.
146      * @return the object to modify.
147      */

148     protected Object JavaDoc getModifiedObject(int in_index) {
149         // Get the object in the initial object list.
150
Object JavaDoc o = data.get(in_index);
151         
152         // Put the object in the modified object map.
153
modifiedData.put(new Integer JavaDoc(in_index), o);
154         
155         // Return the object.
156
return o;
157     }
158     
159     /**
160      * Register an object in the new object Map.
161      *
162      * If the object state is REMOVED, the new object is not registered in the Map.
163      *
164      * @param in_index the index of the new object.
165      * @return the new object.
166      */

167     protected Object JavaDoc getAddedObject(int in_index) {
168         Integer JavaDoc lc_int = new Integer JavaDoc(in_index);
169         
170         // Check if an object had not been already registered for this index.
171
Object JavaDoc o = addedData.get(lc_int);
172         
173         if (o==null) {
174             if (dataClass==null) {
175                 throw new RuntimeException JavaDoc("dataClass property is not set");
176             }
177             try {
178                 // No object registered, create a new one.
179
o = dataClass.newInstance();
180             } catch (InstantiationException JavaDoc e) {
181                 throwException(e);
182             } catch (IllegalAccessException JavaDoc e) {
183                 throwException(e);
184             }
185             
186             // If the object was removed (not very likely but possible), do not register it into the list.
187
if (!addedAndRemoved.contains(lc_int)) {
188                 addedData.put(lc_int, o);
189             }
190         }
191         
192         // Return the new object.
193
return o;
194     }
195     
196     private void throwException(Exception JavaDoc e) {
197         LOG.error("Fail to create an object of class " + dataClass.getName(), e);
198         throw new RuntimeException JavaDoc("A " + e.getClass().getName() + " occured while creating an object of class " + dataClass.getName() + " : " + e.getMessage());
199     }
200     
201     /**
202      * Make sure an object has not been added and removed in the same transaction.
203      * @param in_index the object index
204      * @param in_value the object state
205      * @return true if the object has not been added and removed
206      */

207     protected boolean checkNotAddedAndRemoved(int in_index, Object JavaDoc in_value) {
208         if (in_index >= data.size() && Datagrid.REMOVED.equals(in_value)) {
209             Integer JavaDoc lc_int = new Integer JavaDoc(in_index);
210             addedData.remove(lc_int);
211             addedAndRemoved.add(lc_int);
212             return false;
213         } else {
214             return true;
215         }
216     }
217     
218     // ------------------------- PUBLIC METHODS USED BY BEANUTILS -------------
219

220     public DynaClass getDynaClass() {
221         return dataDynaClass;
222     }
223     
224     /**
225      * Update the datagrid when a form is submitted.
226      */

227     public void set(String JavaDoc in_propertyName, int in_index, Object JavaDoc in_value) {
228         moveToStatus(STATUS_DIRTY);
229         if (in_index < 0 ) {
230             // Illegal index.
231
throw new IllegalArgumentException JavaDoc("Negative in_index !");
232         }
233         
234         if ("dataState".equals(in_propertyName)) {
235             // Update the state of an object.
236
// Do nothing if the object has been both added and removed.
237
if (in_value!=null) {
238                 if (checkNotAddedAndRemoved(in_index, in_value)) {
239                     setDataState(in_index, in_value.toString());
240                 }
241             }
242             // Return : we don't want to register the object
243
// in the added or modified object map.
244
return;
245         }
246         
247         // Ok, the property of an object is being updated.
248
Object JavaDoc o;
249         if (in_index >= data.size()) {
250             // Addition, register the object has a new object.
251
o = getAddedObject(in_index);
252         } else {
253             if (booleans.contains(in_propertyName)) {
254                 // Special case for boolean properties :
255
// can not determinate if there has been a change or not.
256
o = data.get(in_index);
257             } else {
258                 // Update, register the object has a modified object.
259
if (in_value.equals(get(in_propertyName, in_index))) {
260                     // Do nothing if the property has not changed.
261
return;
262                 }
263                 o = getModifiedObject(in_index);
264             }
265         }
266         try {
267             // Set the property value.
268
PropertyUtils.setProperty(o, in_propertyName, in_value);
269         } catch (IllegalAccessException JavaDoc e) {
270             LOG.error("Could not set new object property value", e);
271         } catch (InvocationTargetException JavaDoc e) {
272             LOG.error("Could not set new object property value", e);
273         } catch (NoSuchMethodException JavaDoc e) {
274             LOG.error("Could not set new object property value", e);
275         }
276     }
277
278     /**
279      * Data display.
280      * Return the value of the property in_propertyName of the object at index in_index.
281      */

282     public Object JavaDoc get(String JavaDoc in_propertyName, int in_index) {
283         Object JavaDoc o = data.get(in_index);
284         Object JavaDoc ret = null;
285         try {
286             ret = PropertyUtils.getProperty(o, in_propertyName);
287         } catch (IllegalAccessException JavaDoc e) {
288             LOG.error("Could not get object property value", e);
289         } catch (InvocationTargetException JavaDoc e) {
290             LOG.error("Could not get object property value", e);
291         } catch (NoSuchMethodException JavaDoc e) {
292             LOG.error("Could not get object property value", e);
293         }
294         return ret;
295     }
296     
297     // ----------------------------- PUBLIC METHODS ---------------------------
298

299     /**
300      * Set the data.
301      */

302     public void setData(List JavaDoc in_list) {
303         data = in_list;
304         addedData = new TreeMap JavaDoc();
305         modifiedData = new HashMap JavaDoc();
306         dataWithStates.clear();
307         indexWithStates.clear();
308         addedAndRemoved.clear();
309         status = STATUS_SET;
310     }
311     
312     /**
313      * Set the dataclass.
314      * Build a Dynaclass from the class properties.
315      */

316     public void setDataClass(Class JavaDoc in_class) {
317         // Keep the class.
318
dataClass = in_class;
319         
320         // Build a fake DynaClass for this datagrid object.
321
PropertyDescriptor JavaDoc[] lc_descriptors = PropertyUtils.getPropertyDescriptors(in_class);
322         List JavaDoc lc_list = new ArrayList JavaDoc(lc_descriptors.length+1);
323         for (int i = 0; i < lc_descriptors.length; i++) {
324             PropertyDescriptor JavaDoc lc_descriptor = lc_descriptors[i];
325             if (lc_descriptor.getWriteMethod()!=null) {
326                 DynaProperty lc_property = new DynaProperty(lc_descriptor.getName(), lc_descriptor.getPropertyType());
327                 lc_list.add(lc_property);
328             }
329         }
330         
331         // Add a dataState property.
332
DynaProperty lc_property = new DynaProperty("dataState", String JavaDoc.class);
333         lc_list.add(lc_property);
334         
335         DynaProperty[] lc_properties = (DynaProperty[]) lc_list.toArray(new DynaProperty[lc_list.size()]);
336         dataDynaClass = new BasicDynaClass(in_class.getName(), BasicDynaBean.class, lc_properties);
337     }
338     
339     /**
340      * Set the an object state.
341      */

342     public void setDataState(int in_index, String JavaDoc in_state) {
343         if (in_state==null) {
344             throw new IllegalArgumentException JavaDoc("in_state null");
345         }
346         Integer JavaDoc lc_int = new Integer JavaDoc(in_index);
347         
348         // Clear the old state map.
349
String JavaDoc lc_oldState = (String JavaDoc) indexWithStates.get(lc_int);
350         if (lc_oldState!=null) {
351             Collection JavaDoc lc_list = (Collection JavaDoc) dataWithStates.get(lc_oldState);
352             if (lc_list!=null) {
353                 lc_list.remove(lc_int);
354             }
355         }
356         
357         // Update the new state map.
358
Collection JavaDoc lc_list = (Collection JavaDoc) dataWithStates.get(in_state);
359         if (lc_list==null) {
360             lc_list = new HashSet JavaDoc();
361             dataWithStates.put(in_state, lc_list);
362         }
363         lc_list.add(lc_int);
364         
365         // Keep the states of the line.
366
indexWithStates.put(lc_int, in_state);
367     }
368     
369     public Collection JavaDoc getData() {
370         Collection JavaDoc lc_collection = new ArrayList JavaDoc();
371         lc_collection.addAll(data);
372         lc_collection.addAll(addedData.values());
373         return lc_collection;
374     }
375
376     /**
377      * Return the deleted data.
378      */

379     public Collection JavaDoc getDeletedData() {
380         moveToStatus(STATUS_READ);
381         return getDataWithState(REMOVED);
382     }
383     
384     /**
385      * Return the object having the specified state.
386      */

387     public Collection JavaDoc getDataWithState(String JavaDoc in_state) {
388         moveToStatus(STATUS_READ);
389         
390         // Get the list of the number of the lines we are interested in.
391
Collection JavaDoc goodStateList = (Collection JavaDoc) dataWithStates.get(in_state);
392                 
393         if (goodStateList==null || goodStateList.isEmpty()) {
394             // No lines with the specified state.
395
return Collections.EMPTY_LIST;
396         } else {
397             // Ok, get each object in "data" whose index is specified in "goodStateList", and put it into a list.
398
ArrayList JavaDoc lc_list = new ArrayList JavaDoc();
399             Iterator JavaDoc lc_it = goodStateList.iterator();
400             Object JavaDoc o;
401             while (lc_it.hasNext()) {
402                 Integer JavaDoc lc_int = (Integer JavaDoc)lc_it.next();
403                 if (lc_int.intValue()<data.size()) {
404                      o = data.get(lc_int.intValue());
405                      lc_list.add(o);
406                 } else {
407                     o = addedData.get(lc_int);
408                     lc_list.add(o);
409                 }
410             }
411             return lc_list;
412         }
413     }
414     
415     /**
416      * Return the selected data.
417      */

418     public Collection JavaDoc getSelectedData() {
419         moveToStatus(STATUS_READ);
420         return getDataWithState(SELECTED);
421     }
422
423     /**
424      * Return the added data.
425      */

426     public Collection JavaDoc getAddedData() {
427         moveToStatus(STATUS_READ);
428         return addedData.values();
429     }
430
431     /**
432      * Return the modified data.
433      */

434     public Collection JavaDoc getModifiedData() {
435         moveToStatus(STATUS_READ);
436         return modifiedData.values();
437     }
438     
439     public String JavaDoc getObjectState(int in_index) {
440         String JavaDoc lc_state = (String JavaDoc) indexWithStates.get(new Integer JavaDoc(in_index));
441         return lc_state == null ? "" : lc_state;
442     }
443     
444     // -------------------------- Useless DynaBean interface -----------------------------//
445

446     /**
447      * Do nothing
448      */

449     public boolean contains(String JavaDoc name, String JavaDoc key) {
450         // Do nothing.
451
return false;
452     }
453
454     /**
455      * Do nothing
456      */

457     public Object JavaDoc get(String JavaDoc name, String JavaDoc key) {
458         // Do nothing
459
return null;
460     }
461
462     /**
463      * Do nothing
464      */

465     public Object JavaDoc get(String JavaDoc name) {
466         // Do nothing
467
return null;
468     }
469     
470     /**
471      * Do nothing
472      */

473     public void remove(String JavaDoc name, String JavaDoc key) {
474         // Do nothing
475
}
476     
477     /**
478      * Do nothing
479      */

480     public void set(String JavaDoc name, Object JavaDoc value) {
481         // Do nothing
482
}
483     
484     /**
485      * Do nothing
486      */

487     public void set(String JavaDoc name, String JavaDoc key, Object JavaDoc value) {
488         // Do nothing.
489
}
490     
491     /**
492      * Prepare for an update.
493      */

494     public void preUpdate() {
495         Iterator JavaDoc lc_it = booleans.iterator();
496         Boolean JavaDoc falseValue = Boolean.FALSE;
497         while (lc_it.hasNext()) {
498             String JavaDoc lc_booleanProperty = (String JavaDoc) lc_it.next();
499             Map JavaDoc objects = (Map JavaDoc) booleanStates.get(lc_booleanProperty);
500             if (objects==null) {
501                 objects = new HashMap JavaDoc();
502                 booleanStates.put(lc_booleanProperty, objects);
503             }
504             Iterator JavaDoc lc_elements = data.iterator();
505             while (lc_elements.hasNext()) {
506                 Object JavaDoc lc_element = lc_elements.next();
507                 try {
508                     objects.put(lc_element, PropertyUtils.getProperty(lc_element, lc_booleanProperty));
509                     PropertyUtils.setProperty(lc_element, lc_booleanProperty, falseValue);
510                 } catch (Exception JavaDoc e) {
511                     LOG.error("Fail to reset property " + lc_booleanProperty, e);
512                 }
513             }
514         }
515         status = STATUS_DIRTY;
516     }
517     
518     private void postUpdate() {
519         Iterator JavaDoc lc_it = booleans.iterator();
520         while (lc_it.hasNext()) {
521             String JavaDoc lc_booleanProperty = (String JavaDoc) lc_it.next();
522             Map JavaDoc objects = (Map JavaDoc) booleanStates.get(lc_booleanProperty);
523             Iterator JavaDoc lc_elements = data.iterator();
524             int i = 0;
525             while (lc_elements.hasNext()) {
526                 try {
527                     Object JavaDoc lc_element = lc_elements.next();
528                     Boolean JavaDoc oldValue = (Boolean JavaDoc) objects.get(lc_element);
529                     Boolean JavaDoc newValue = (Boolean JavaDoc) PropertyUtils.getProperty(lc_element, lc_booleanProperty);
530                     if (!oldValue.equals(newValue)) {
531                         getModifiedObject(i);
532                     }
533                 } catch (Exception JavaDoc e) {
534                     LOG.error("Fail to check property " + lc_booleanProperty, e);
535                 }
536                 i++;
537             }
538         }
539     }
540     
541     private void moveToStatus(int in_status) {
542         if (status!=in_status) {
543             switch (in_status) {
544                 case STATUS_DIRTY:
545                     preUpdate();
546                     break;
547                 case STATUS_READ:
548                     postUpdate();
549                     break;
550             }
551             status = in_status;
552         }
553         
554     }
555     
556     void addBooleanProperty(String JavaDoc in_property) {
557         booleans.add(in_property);
558     }
559 }
560
Popular Tags