View Javadoc

1   
2   package org.weda.store.impl;
3   
4   import java.util.ArrayList;
5   import java.util.Collection;
6   import java.util.Collections;
7   import java.util.HashMap;
8   import java.util.LinkedList;
9   import java.util.List;
10  import java.util.ListIterator;
11  import java.util.Map;
12  import java.util.Map.Entry;
13  import java.util.Set;
14  import java.util.TreeSet;
15  import org.apache.commons.logging.Log;
16  import org.apache.commons.logging.LogFactory;
17  import org.weda.enhance.InjectHivemindObject;
18  import org.weda.enhance.InjectMessages;
19  import org.weda.message.Messages;
20  import org.weda.property.PropertyValue;
21  import org.weda.converter.ValueTypeConverter;
22  import org.weda.store.ObjectSource.Mode;
23  import org.weda.store.ObjectSourceConstraint;
24  import org.weda.store.ObjectSourceException;
25  import org.weda.store.ObjectSourceListener;
26  import org.weda.store.ObjectSourceRegistry;
27  import org.weda.store.DetailObjectSourceInfo;
28  import org.weda.store.ObjectStoreException;
29  import org.weda.store.Query;
30  import org.weda.store.QueryFilter;
31  import org.weda.store.ObjectSource;
32  import org.weda.property.ObjectDescriptorRegistry;
33  import org.weda.store.ObjectStore;
34  import org.weda.store.QueryFilterElement;
35  import org.weda.store.QueryFilterElement.ExpressionType;
36  import org.weda.store.QueryFilterElement.OperatorType;
37  import org.weda.store.QueryFilterException;
38  import org.weda.action.impl.AbstractActionContainer;
39  import org.weda.common.ChangeType;
40  import org.weda.store.RowsChangeEvent;
41  
42  /**
43   *
44   * @author tim
45   */
46  public class ObjectSourceImpl 
47          extends AbstractActionContainer 
48          implements ObjectSource, Cloneable, java.io.Serializable
49  {
50      public final static int DEFAULT_PAGE_SIZE=20;
51      private final static Log log = LogFactory.getLog(ObjectSourceImpl.class);
52      //дружественый доступ необходим для корректного клонирования объекта
53      List<ObjectSourceListener> listeners;
54      private RowsChangeEvent rowsChangeEvent;
55      
56      private Mode mode;
57      private Mode previousMode;
58      private boolean creatingNewRow;
59      private List<Object[]> rows = new ArrayList<Object[]>(100);
60      
61      private boolean readOnly = false;
62      private String name;    
63      private Query query;
64  //    private Map<String, DetailObjectSourceInfo> 
65  //            detailInfos = 
66  //                new HashMap<String, DetailObjectSourceInfo>();
67      private List<DetailObjectSourceInfo> detailInfos;
68      private Map<String, ObjectSource> detailDataSources = 
69              new HashMap<String, ObjectSource>();
70      private Map<String, ObjectSourceConstraint> detailConstraints =
71              new HashMap<String, ObjectSourceConstraint>();
72      private Map<String, ObjectSourceConstraint> constraints = 
73              new HashMap<String, ObjectSourceConstraint>();
74      private Map<String, Boolean> removeConstraintFilter = 
75              new HashMap<String, Boolean>();
76      private Map<String, QueryFilterElement> constraintFilters = 
77              new HashMap<String, QueryFilterElement>();
78      private Map<String, Integer> baseClassGetterIds;    
79      
80      @InjectHivemindObject()
81      private static ObjectDescriptorRegistry objectDescriptorRegistry;
82      @InjectHivemindObject private static ObjectStore objectStore;
83      @InjectHivemindObject private static PropertyValue propertyValue;
84      @InjectHivemindObject private static ValueTypeConverter converter;
85      @InjectHivemindObject()
86      private static ObjectSourceRegistry dataSourceRegistry;
87      @InjectMessages private Messages messages;
88      
89      private QueryFilter queryFilter;
90      private int pageNumber = 0;
91      private int pageSize = DEFAULT_PAGE_SIZE;
92      private Class baseClass;
93      private int baseClassPosition;
94      private String baseClassAlias;
95      private SummaryRowImpl summaryRow;
96      private ObjectSet selectedObjectSet;
97  //    private Messages allMessages;
98  //    private Messages messages;
99      private boolean detailConstraintsActivated = false;
100     private boolean modificationApplied = false;
101     private int selectedRow = -1;
102     private Set<Integer> selectedRows = new TreeSet();
103     
104     public void init() throws ObjectSourceException {
105         try{
106             setActionContainerName(name);
107             mode = Mode.CLOSED;
108 //            messages = 
109 //                    new MessagesSubset(allMessages, this.getClass().getName());
110             if (queryFilter==null)
111                 queryFilter = new QueryFilterImpl();
112             ObjectAlias alias = 
113                     new ObjectAlias(getBaseClassAlias(), getBaseClass());
114             queryFilter.setDefaultObjectAlias(alias);
115 //            for (DetailObjectSourceInfo detailInfo: detailInfos.values()){
116             if (detailInfos!=null){
117                 for (DetailObjectSourceInfo detailInfo: detailInfos){
118                     String masterProperty = detailInfo.getMasterPropertyName();
119                     if (masterProperty!=null){
120                         Integer getterId = 
121                                 propertyValue.compileGetter(
122                                     baseClass, masterProperty);
123                         if (baseClassGetterIds==null)
124                             baseClassGetterIds = new HashMap<String, Integer>();
125                         baseClassGetterIds.put(masterProperty, getterId);
126                     }
127                 }
128             }
129 //            queryFilter.setMessages(
130 //                    new MessagesSubset(
131 //                        allMessages, QueryFilter.class.getName()));
132             queryFilter.init();
133             if (summaryRow != null){
134                 summaryRow.setObjectSource(this);
135                 summaryRow.init();
136             }
137         }catch(Exception e){
138             throw new ObjectSourceException(
139                 String.format(
140                     "Can't initialize dataSource (%s)"
141                     , getName())
142                 , e);
143         }
144     }    
145     
146     public void addDetailInfo(
147             DetailObjectSourceInfo detailObjectSourceInfo) 
148     {
149 //        detailInfos.put(
150 //                detailDataSourceInfo.getName(), detailDataSourceInfo);
151         if (detailInfos==null)
152             detailInfos = new LinkedList<DetailObjectSourceInfo>();
153         detailInfos.add(detailObjectSourceInfo);
154     }   
155     
156     public void addConstraint(ObjectSourceConstraint constraint)
157         throws ObjectSourceException
158     {
159         try{
160             constraints.put(constraint.getPropertyName(), constraint);
161             //проверим наличие фильтра
162 
163             QueryFilterElementImpl filter = (QueryFilterElementImpl)
164                     getQueryFilter().getStaticFilterElement(
165                         getBaseClassAlias()+"."+constraint.getPropertyName());
166             if (filter!=null)
167                 removeConstraintFilter.put(constraint.getPropertyName(), false);
168             else {
169                 removeConstraintFilter.put(constraint.getPropertyName(), true);
170                 filter = new QueryFilterElementImpl();
171                 filter.setObjectAlias(getBaseClassAlias());
172                 filter.setUseAliasAsProperty(false);
173                 filter.setProperty(constraint.getPropertyName());
174                 getQueryFilter().addStaticFilterElement(filter);
175                 getQueryFilter().initQueryFilterElement(filter, true);
176             }
177             filter.setOperator("=");
178             filter.setValue(constraint.getValue());
179             filter.setExpressionType(ExpressionType.OPERATOR);
180             filter.setOperatorType(OperatorType.SIMPLE);
181             constraintFilters.put(constraint.getPropertyName(), filter);
182         }catch(QueryFilterException e){
183             throw new ObjectSourceException(
184                 String.format(
185                     "Can't add constraint (propertyName: %s, value: %s) " +
186                     "to dataSource (name: %s)"
187                     , constraint.getPropertyName()
188                     , constraint.getValue().toString()
189                     , getName()));
190         }
191     }
192     
193     public void removeConstraint(ObjectSourceConstraint constraint){
194         constraints.remove(constraint.getPropertyName());
195         QueryFilterElement filter = 
196                 constraintFilters.remove(constraint.getPropertyName());
197         if (   filter!=null 
198             && removeConstraintFilter.get(constraint.getPropertyName()))
199         {
200             getQueryFilter().getStaticFilterElements().remove(filter);
201         }
202     }
203 
204     public String getName() {
205         return name;
206     }
207     
208     public void setName(String name){
209         this.name=name;
210     }
211     
212     public void open() throws ObjectSourceException{
213         if (mode == Mode.CLOSED) {
214             try{
215                 rows.clear();
216                 createRefreshRowsChangingEvent();
217                 fireRowsChangingEvent(rowsChangeEvent);
218                 rows = objectStore.queryAllRows(this);
219                 selectedRows.clear();
220                 createRefreshRowsChangeEvent();
221                 fireRowsChangeEvent(rowsChangeEvent);
222                 if (log.isDebugEnabled())
223                     log.debug(formLogMessage("selected "+rows.size()+" rows"));
224                 mode = Mode.VIEW;
225             }catch(Exception e){
226                 throw new ObjectSourceException(
227                         messages.format("OpenError", getName()), e);
228             }            
229         }
230     }
231 
232     public void refresh() throws ObjectSourceException {
233         try{
234             Mode curMode = getMode();
235             close();
236             open();
237             if (curMode==Mode.SELECT)
238                 setMode(curMode);
239         }catch(Exception e){
240             throw new ObjectSourceException(
241                     messages.format("RefreshError", getName()), e);
242         }
243     }
244     
245     public void refresh(Object baseClassObject) throws ObjectSourceException{
246         if (baseClassObject == null)
247             throw new ObjectSourceException(
248                     "Parameter baseClassObject can not be null");
249         if (!getBaseClass().isAssignableFrom(baseClassObject.getClass()))
250             throw new ObjectSourceException(
251                     String.format(
252                         "%s is not the same or super class for %s"
253                         , getBaseClass().getName()
254                         , baseClassObject.getClass().getName()));
255         else{
256             try{
257                 close();
258                 createRefreshRowsChangingEvent();
259                 fireRowsChangingEvent(rowsChangeEvent);
260                 refreshNewRow(baseClassObject);
261                 createRefreshRowsChangeEvent();
262                 fireRowsChangeEvent(rowsChangeEvent);
263                 mode = Mode.VIEW;
264             }catch(Exception e){
265                 throw new ObjectSourceException(
266                         messages.format("RefreshError", name));
267             }
268         }
269     }
270     
271     public void close() throws ObjectSourceException {
272         if (mode != Mode.CLOSED){
273             selectedRows.clear();
274             selectedRow = -1;
275             rows.clear();
276             mode = Mode.CLOSED;
277         }
278     }    
279     
280     public Query getQuery() {
281         return query;
282     }
283 
284     public void setQuery(Query query) {
285         this.query = query;
286     }
287 
288     public QueryFilter getQueryFilter() {
289         return queryFilter;
290     }
291 
292     public void setQueryFilter(QueryFilter queryFilter) {
293         this.queryFilter = queryFilter;
294     }
295 
296     public PropertyValue getPropertyValue() {
297         return propertyValue;
298     }
299 
300     private String formLogMessage(String message){
301         return getName()+">> "+message;
302     }
303 
304     public int getRowCount() {
305         return rows.size();
306     }
307 
308     public void selectRow(int row) {
309         selectedRow = row;
310         selectedRows.add(row);
311     }
312 
313     public boolean isRowSelected(int row) {
314         return selectedRows.contains(row);
315     }
316 
317     public void deselectRow(int row) {
318         selectedRows.remove(row);
319     }
320     
321     public Class getBaseClass() {
322         return baseClass;
323     }
324 
325     public void setBaseClass(Class baseClass) {
326         this.baseClass = baseClass;
327     }
328 
329     public int getBaseClassPosition() {
330         return baseClassPosition;
331     }
332 
333     public void setBaseClassPosition(int baseClassPosition) {
334         this.baseClassPosition = baseClassPosition;
335     }
336 
337     public ObjectSet getSelectedObjectSet() {
338         return selectedObjectSet;
339     }
340     
341     public void createNewRow() throws ObjectSourceException {
342         try{
343             if (Mode.EDIT == getMode())
344                 throw new ObjectSourceException(
345                         messages.getMessage("AlreadyInEditMode"));
346             if (Mode.CLOSED == getMode())
347                 setMode(Mode.VIEW);
348             Object obj = getBaseClass().newInstance();
349             selectedObjectSet = new ObjectSet(
350                     getBaseClass(), new Object[]{obj}, propertyValue);
351             if (constraints.size()>0)
352                 for (ObjectSourceConstraint constraint: constraints.values())
353                     selectedObjectSet.setValue(
354                         constraint.getPropertyName(), constraint.getValue());
355             objectStore.beginTransaction();
356             objectStore.beginTransaction();
357             setPreviousMode(getMode());
358             setMode(Mode.EDIT);
359             creatingNewRow=true;
360             modificationApplied = false;
361         }catch (Exception e){
362             selectedObjectSet = null;
363             if (Mode.EDIT == getMode())
364                 cancelModification();
365             throw new ObjectSourceException(
366                 messages.format("CreateNewRowError", getName())
367                 , e);            
368         }
369     }
370     
371     public void removeSelectedRows() throws ObjectSourceException {
372         try{
373             objectStore.beginTransaction();
374             createRowsChangeEvent(ChangeType.DELETE);
375             fireRowsChangingEvent(rowsChangeEvent);
376             for (Integer row: selectedRows){
377                 Object obj = rows.get(row)[baseClassPosition];
378                 objectStore.delete(obj);                
379             }
380             objectStore.commit();
381             int i=0;
382             for (Integer row: selectedRows)
383                 rows.remove(row.intValue()-i++);
384             selectedRows.clear();
385             selectedRow = -1;
386             fireRowsChangeEvent(rowsChangeEvent);
387         }catch(Exception e){
388             try{
389                 objectStore.rollback();
390             }catch(ObjectStoreException e2){
391                 throw new ObjectSourceException(
392                     messages.format(
393                         "FatalSaveModificationError", getName()), e);                
394             }
395             throw new ObjectSourceException(
396                     messages.format("SaveModificationError", getName()), e);
397         }
398     }
399 
400     public void beginModification() throws ObjectSourceException {
401         try{
402             if (Mode.EDIT == getMode())
403                 throw new ObjectSourceException(
404                         messages.getMessage("AlreadyInEditMode"));
405             if (selectedRows.size()>0){
406                 Object[] objects = new Object[selectedRows.size()];
407                 int i=0;
408                 for (int pos: selectedRows){
409                     objects[i]=rows.get(pos)[baseClassPosition];
410                     i++;
411                 }
412                 selectedObjectSet=
413                     new ObjectSet(getBaseClass(), objects, propertyValue);
414                 objectStore.beginTransaction();
415                 objectStore.beginTransaction();
416                 setPreviousMode(getMode());
417                 setMode(Mode.EDIT);
418                 for (Object obj: objects)
419                     objectStore.lock(obj);
420                 if (selectedRows.size()==1){
421                     activateDetailConstraints(
422                             selectedObjectSet.getObjects()[0], false);
423                 }
424                 creatingNewRow=false;
425                 modificationApplied = false;
426                 createRowsChangeEvent(ChangeType.UPDATE);
427             }
428         }catch(Exception e){
429             selectedObjectSet = null;
430             if (Mode.EDIT == getMode())
431                 cancelModification();
432             throw new ObjectSourceException(
433                 messages.format("BeginModificationError", getName())
434                 , e);
435         }
436     }
437 
438     public void saveModification() throws ObjectSourceException {
439         if (log.isDebugEnabled())
440             log.debug("Saving datasource modifications");
441         try{
442             if (!getMode().equals(Mode.EDIT))
443                 throw new ObjectSourceException(
444                         messages.getMessage("NotInEditMode"));            
445             Object[] objects = selectedObjectSet.getObjects();
446             //
447             if (creatingNewRow){
448                 createNewRowChangeEvent(objects[0]);
449                 fireRowsChangingEvent(rowsChangeEvent);
450             }else {
451                 fireRowsChangingEvent(rowsChangeEvent);
452             }
453             //
454             for (Object obj: objects)
455                 objectStore.save(obj);
456             if (creatingNewRow){
457                 objectStore.flushUpdates();
458                 refreshNewRow(objects[0]);
459             }else
460                 objectStore.flushUpdates();
461             objectStore.commit();
462             objectStore.commit();
463             selectedObjectSet=null;
464             if (isDetailConstraintsActivated())
465                 deactivateDetailConstraints();
466             setMode(getPreviousMode());
467             fireRowsChangeEvent(rowsChangeEvent);
468             rowsChangeEvent = null;
469             if (log.isDebugEnabled())
470                 log.debug("Modifications successfully saved");
471         }catch(Exception e){
472 //            try{
473 //                objectStore.rollback();
474 //                objectStore.beginTransaction();
475 //            }catch(ObjectStoreException e2){
476 //                throw new ObjectSourceException(
477 //                    messages.format(
478 //                        "FatalSaveModificationError", getName()), e);                
479 //            }
480             throw new ObjectSourceException(
481                     messages.format("SaveModificationError", getName()), e);
482         }
483     }
484     
485     private void refreshNewRow(Object newObject)
486         throws ObjectSourceException
487     {
488         try{
489             getQueryFilter().disableFilterElements();
490             try{
491                 QueryFilterElementImpl filter = 
492                         new QueryFilterElementImpl();
493                 filter.setObjectAlias(getBaseClassAlias());
494                 filter.setUseAliasAsProperty(true);
495                 filter.setOperator("=");
496                 filter.setValue(newObject);
497                 filter.setExpressionType(ExpressionType.OPERATOR);
498                 filter.setOperatorType(OperatorType.SIMPLE);
499                 getQueryFilter().addFilterElement(filter);
500                 try{
501                     getQueryFilter().initQueryFilterElement(filter, false);
502                     List<Object[]> newRow = 
503                             objectStore.queryAllRows(this);
504                     int insertPosition = 0;
505                     rows.add(insertPosition, newRow.get(0));
506                     selectedRows.clear();
507                     //selectedRows.add(insertPosition);
508                     selectRow(insertPosition);
509                 }finally{
510                     getQueryFilter().removeFilterElement(filter);
511                 }
512             }finally{
513                 getQueryFilter().enableFilterElements();
514             }
515         }catch (Exception e){
516             throw new ObjectSourceException(messages.format(
517                     "RefreshNewRowError", getName()), e);
518         }
519     }
520     
521     public void cancelModification() throws ObjectSourceException {
522         if (log.isDebugEnabled())
523             log.debug("Canceling datasource modifications");
524         try{
525             if (!getMode().equals(Mode.EDIT))
526                 throw new ObjectSourceException(
527                         messages.getMessage("NotInEditMode"));
528             objectStore.rollback();
529             if (selectedObjectSet!=null && !creatingNewRow)
530                 for (Object obj: selectedObjectSet.getObjects())
531                     objectStore.refresh(obj);
532             objectStore.rollback();
533             selectedObjectSet=null;
534             if (isDetailConstraintsActivated())
535                 deactivateDetailConstraints();
536             setMode(getPreviousMode());
537             rowsChangeEvent = null;
538         }catch(Exception e){
539             throw new ObjectSourceException(
540                     messages.format("CancelModificationError", getName())
541                     , e);
542         }
543     }
544     
545     public boolean isModificationApplied() {
546         return modificationApplied;
547     }
548 
549     public void applyModification() throws ObjectSourceException {
550         try{
551             //objectStore.flushUpdates();
552             for (Object obj: selectedObjectSet.getObjects())
553                 objectStore.save(obj);
554             modificationApplied = true;
555             if (   (!detailConstraintsActivated && creatingNewRow)
556                 || baseClassGetterIds!=null)
557             {
558                 activateDetailConstraints(
559                         selectedObjectSet.getObjects()[0]
560                         , baseClassGetterIds!=null);
561             }
562         }catch(ObjectStoreException e){
563             throw new ObjectSourceException(
564                 messages.format("ApplyModificationError", getName())
565                 , e);
566         }
567     }
568     
569     private void resolveDetailDataSources() throws Exception {
570         for (DetailObjectSourceInfo info: detailInfos)
571             detailDataSources.put(
572                 info.getName()
573                 , dataSourceRegistry.getObjectSource(info.getName()));
574     }
575     
576     public Set<Class> getDisabledActions() {
577         return null;
578     }
579 
580     public boolean hasSelectedRows() {
581         return selectedRows.size()>0;
582     }
583     
584     public Set<Integer> getSelectedRows(){
585         return selectedRows;
586     }
587     
588     public int getSelectedRow(){
589         return selectedRow;
590     }
591 
592     public int getSelectedRowCount() {
593         return selectedRows.size();
594     }
595 
596     private Mode getPreviousMode(){
597         return previousMode;
598     }
599     
600     private void setPreviousMode(Mode mode){
601         previousMode = mode;
602     }
603 
604     public Object[] getRowAt(int row) {
605         return rows.get(row);
606     }
607 
608     public Mode getMode() {
609         return mode;
610     }
611 
612     public void setMode(Mode mode) {
613         this.mode = mode;
614     }
615 
616     public String getBaseClassAlias() {
617         return baseClassAlias;
618     }
619 
620     public void setBaseClassAlias(String baseClassAlias) {
621         this.baseClassAlias = baseClassAlias;
622     }
623 
624     public boolean isReadOnly() {
625         return readOnly;
626     }
627 
628     public void setReadOnly(boolean readOnly) {
629         this.readOnly = readOnly;
630     }
631     
632     public ObjectSourceRegistry getDataSourceRegistry(){
633         return dataSourceRegistry;
634     }
635 
636     public void activateDetailConstraints(
637             Object masterObject, boolean masterPropertiesOnly) 
638         throws ObjectSourceException
639     {
640         if (detailInfos==null)
641             return;
642         DetailObjectSourceInfo lastDetailInfo = null;
643         try{
644             resolveDetailDataSources();
645             try{
646                 ObjectSourceConstraintImpl c = null;
647 //                for (Entry<String, DetailObjectSourceInfo> entry: 
648 //                            detailInfos.entrySet())
649                 for (DetailObjectSourceInfo detailInfo: detailInfos)
650                 {
651                     lastDetailInfo = detailInfo;
652                     String masterProperty = detailInfo.getMasterPropertyName();
653                     if (masterProperty==null && masterPropertiesOnly)
654                         continue;
655                     c = new ObjectSourceConstraintImpl();
656                     c.setPropertyName(detailInfo.getPropertyName());
657                     if (masterProperty==null)
658                         c.setValue(masterObject);
659                     else{
660                         if (masterObject!=null)
661                             c.setValue(
662                                 propertyValue.getValue(
663                                     masterObject
664                                     , baseClassGetterIds.get(masterProperty)));
665                     }
666                     ObjectSource detailDataSource = 
667                             detailDataSources.get(detailInfo.getName());
668                     detailDataSource.close();
669                     detailDataSource.addConstraint(c);
670                     if (detailInfo.isRefreshOnActivate() && c.getValue()!=null)
671                         detailDataSource.refresh();
672                     detailConstraints.put(detailInfo.getName(), c);
673                 }
674                 detailConstraintsActivated = true;
675             }catch(ObjectSourceException e){
676                 deactivateDetailConstraints();
677                 throw new ObjectSourceException(
678                     String.format(
679                         "Can't add constraint for property (%s) in detail " +
680                         "objectSource (%s)"
681                         , lastDetailInfo.getPropertyName()
682                         , lastDetailInfo.getName())
683                     , e);
684             }
685         }catch(Exception e){
686             throw new ObjectSourceException(
687                 String.format(
688                     "Can't activate detail constaints from master objectSource " +
689                     "(%s)"
690                     , getName())
691                 , e);
692         }
693     }
694 
695     public void deactivateDetailConstraints() throws ObjectSourceException {
696         if (detailInfos==null)
697             return;
698         ObjectSource detailDataSource = null;
699         for (Entry<String, ObjectSourceConstraint> entry: 
700             detailConstraints.entrySet())
701         {
702             detailDataSource = detailDataSources.get(entry.getKey());
703             detailDataSource.close();
704             detailDataSource.removeConstraint(entry.getValue());
705         }
706         detailConstraintsActivated = false;
707     }
708     
709     public boolean isDetailConstraintsActivated() {
710         return detailConstraintsActivated;
711     }
712 
713     public Collection<DetailObjectSourceInfo> getDetailInfos() {
714         return detailInfos;
715     }
716 
717     public Object clone() throws CloneNotSupportedException {
718         ObjectSourceImpl clone = new ObjectSourceImpl();
719         clone.setName(name);
720         clone.setBaseClass(baseClass);
721         clone.setBaseClassPosition(baseClassPosition);
722         clone.setBaseClassAlias(baseClassAlias);
723         clone.setReadOnly(readOnly);
724         clone.setQuery(query);
725         clone.listeners = listeners;
726         if (getDetailInfos()!=null){
727             for (DetailObjectSourceInfo detailInfo: getDetailInfos())
728                 clone.addDetailInfo(detailInfo);
729         }
730         clone.setQueryFilter((QueryFilter)queryFilter.clone());
731         if (summaryRow!=null)
732             clone.setSummaryRow((SummaryRowImpl)summaryRow.clone());
733         return clone;
734     }
735 
736     public synchronized void removeListener(ObjectSourceListener listener) {
737         if (listeners!=null)
738             listeners.remove(listener);
739     }
740 
741     public synchronized void addListener(ObjectSourceListener listener) {        
742         if (listeners==null)
743             listeners = 
744                     Collections.synchronizedList(
745                         new ArrayList<ObjectSourceListener>(5));
746         listeners.add(listener);
747     }
748     
749     private void createRowsChangeEvent(ChangeType changeType){
750         rowsChangeEvent = null;
751         if (listeners!=null && selectedRows.size()>0){
752             List<Object[]> changedRows = 
753                     new ArrayList<Object[]>(selectedRows.size());
754             for (int i: selectedRows)
755                 changedRows.add(rows.get(i));
756             rowsChangeEvent = new RowsChangeEventImpl(changeType, changedRows);
757         }
758     }
759     
760     private void createRefreshRowsChangeEvent(){
761         rowsChangeEvent = null;
762         if (listeners!=null && rows.size()>0)
763             rowsChangeEvent = 
764                     new RowsChangeEventImpl(
765                         ChangeType.REFRESH
766                         , Collections.unmodifiableList(rows));        
767     }
768     
769     private void createRefreshRowsChangingEvent(){
770         rowsChangeEvent = null;
771         if (listeners!=null)
772             rowsChangeEvent = 
773                     new RowsChangeEventImpl(
774                         ChangeType.REFRESH
775                         , Collections.unmodifiableList(rows));        
776     }
777     
778     private void createNewRowChangeEvent(Object baseObj){
779         rowsChangeEvent = null;
780         if (listeners!=null){
781             List<Object[]> rows = new ArrayList<Object[]>(1);
782             Object[] row = new Object[baseClassPosition+1];
783             row[baseClassPosition] = baseObj;
784             rows.add(row);
785             rowsChangeEvent = 
786                     new RowsChangeEventImpl(
787                         ChangeType.ADD
788                         , Collections.unmodifiableList(rows));        
789         }
790         
791     }
792     
793     private void fireRowsChangeEvent(RowsChangeEvent event) 
794         throws Exception
795     {
796         if (listeners!=null && event != null)
797             for (ObjectSourceListener listener: listeners)
798                 listener.rowsChanged(event);
799     }
800 
801     private void fireRowsChangingEvent(RowsChangeEvent event)
802         throws Exception
803     {
804         if (listeners!=null && event != null)
805             for (ObjectSourceListener listener: listeners)
806                 listener.rowsChanging(event);
807     }
808 
809     public SummaryRowImpl getSummaryRow() {
810         return summaryRow;
811     }
812 
813     public void setSummaryRow(SummaryRowImpl summaryRow) {
814         this.summaryRow = summaryRow;
815     }
816     
817 }