View Javadoc

1   /* (c) Copyright 2003 Caleigo AB, All rights reserved. 
2    * 
3    * This library is free software; you can redistribute it and/or
4    * modify it under the terms of the GNU Lesser General Public
5    * License as published by the Free Software Foundation; either
6    * version 2.1 of the License, or (at your option) any later version.
7    * 
8    * This library is distributed in the hope that it will be useful,
9    * but WITHOUT ANY WARRANTY; without even the implied warranty of
10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   * Lesser General Public License for more details.
12   * 
13   * You should have received a copy of the GNU Lesser General Public
14   * License along with this library; if not, write to the Free Software
15   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16   *  
17   */
18  
19  package org.caleigo.core;
20  
21  
22  import java.util.*;
23  
24  import org.caleigo.core.event.*;
25  import org.caleigo.core.exception.*;
26  
27  /*** <Description for ProxySelection>
28   *
29   * @author  Dennis Zikovic
30   * @version 1.00
31   *
32   *//* 
33   *
34   * WHEN        WHO               WHY & WHAT
35   * -----------------------------------------------------------------------------
36   * 2001-11-23  Dennis Zikovic    Creation
37   */
38  public class ProxySelection implements IProxySelection
39  {
40      // Data members ------------------------------------------------------------
41      private ISelection mRemoteSelection;
42      private IEntityDescriptor mEntityDescriptor;
43      
44      private transient RelayListener mEntityRelayListener;
45      private transient ISelectionListener mSelectionListener;
46      private transient IEntityListener mEntityListener;
47      private transient IEntityChangeListener mEntityChangeListener;
48      private transient IProxyListener mProxyListener;
49  
50      // Constructors ------------------------------------------------------------
51      
52      /*** Creates new ProxySelection
53       */
54      public ProxySelection(IEntityDescriptor entityDescriptor) 
55      {
56          mEntityDescriptor = entityDescriptor;
57      }
58      
59      /*** Creates new ProxySelection
60       */
61      public ProxySelection(ISelection selection) 
62      {
63          // Check entity, the proxy must have a descriptor.
64          if(selection==null)
65              throw new InvalidSelectionException("ProxySelection can not be instatiated with a null selecltion.");
66          
67          // Store state data.
68          mEntityDescriptor = selection.getEntityDescriptor();
69          this.setRemoteSelection(selection);
70      }
71         
72      // ISelection implementation -----------------------------------------------
73      
74      /*** Return true if any (one or more) of the collections's contained 
75       * entities has the DIRTY flag set to true that is have unsaved changes.
76       */
77      public boolean isDirty()
78      {
79          if(mRemoteSelection!=null)
80              return mRemoteSelection.isDirty();
81          else
82              return false;
83      }
84      
85      /*** Stores all contained entities that have the DIRTY flag set to true.
86       * The entities are stored in a single transaction meaning that if one
87       * store fails then all fails.
88       */
89      public void storeAll()
90      {
91          if(mRemoteSelection!=null)
92              mRemoteSelection.storeAll();
93      }
94      
95      /*** Deletes all contained entities. The entities are deleted in a single 
96       * transaction meaning that if one delete fails then all fails.
97       * USE THIS METHOD WITH CAUTION.
98       */
99      public void deleteAll()
100     {
101         if(mRemoteSelection!=null)
102             mRemoteSelection.deleteAll();
103     }
104     
105     /*** Performs a refresh on all contained entities. The refresh is batched
106      * to save performance.
107      */
108     public void refreshAll()
109     {
110         if(mRemoteSelection!=null)
111             mRemoteSelection.refreshAll();
112     }
113     
114     /*** Adds the provided IEntity object to the end of selection object. 
115      * If an entity with the same identity already exists in the selection the
116      * requeat is ignored and false is returned.
117      */
118     public boolean addEntity(IEntity entity)
119     {
120         if(mRemoteSelection!=null)
121             return mRemoteSelection.addEntity(entity);
122         else
123             return false;
124     }
125     
126     /*** Adds the provided IEntity object to at the specified index in the 
127      * selection object. If an entity with the same identity already exists 
128      * in the selection the requeat is ignored and false is returned.
129      */
130     public boolean addEntity(int index, IEntity entity)
131     {
132         if(mRemoteSelection!=null)
133             return mRemoteSelection.addEntity(index, entity);
134         else
135             return false;
136     }
137     
138     /*** Access method that returns the contained IEntity object with the 
139      * specified index.
140      */
141     public IEntity getEntity(int index)
142     {
143         if(mRemoteSelection!=null)
144             return mRemoteSelection.getEntity(index);
145         else
146             return null;
147     }
148     
149     /*** Mutation method that removes the provided entity from the selection.
150      * Returns true if the entity was found and removed otherwise false is 
151      * returned.
152      */
153     public boolean removeEntity(IEntity entity)
154     {
155         if(mRemoteSelection!=null)
156             return mRemoteSelection.removeEntity(entity);
157         else
158             return false;
159     }
160     
161     /*** Mutation method that removes the indexed entity from the selection.
162      * Returns the indexed entity if it was found and removed otherwise null  
163      * is returned. The entities are not effecte in any other way.
164      */
165     public IEntity removeEntity(int index)
166     {
167         if(mRemoteSelection!=null)
168             return mRemoteSelection.removeEntity(index);
169         else
170             return null;
171     }
172     
173     /*** Returns a java.util.Iterator object that iterates over all entities
174      * in the selection. The iterator should be read only and should not 
175      * support the remove method. The entities are not effected in any other way.
176      */
177     public Iterator iterator()
178     {
179         if(mRemoteSelection!=null)
180             return mRemoteSelection.iterator();
181         else
182             return null;
183     }
184     
185     /*** Mutation method the removes all entities currently stored in the 
186      * selection. The entities are not effected in any other way.
187      */
188     public void clear()
189     {
190         if(mRemoteSelection!=null)
191             mRemoteSelection.clear();
192     }
193     
194     /*** Access method that reurns the number entities currently contained in
195      * the selection object.
196      */
197     public int size()
198     {
199         if(mRemoteSelection!=null)
200             return mRemoteSelection.size();
201         else
202             return 0;
203     }
204     
205     /*** Boolean access method that return true if the selection is empty.
206      */
207     public boolean isEmpty()
208     {
209         if(mRemoteSelection!=null)
210             return mRemoteSelection.isEmpty();
211         else
212             return true;
213     }
214     
215     /*** Access method that views the selection objct as a grid where row is
216      * the entity index and column is the field index for the stored entities.
217      */
218     public Object getData(int row, int column)
219     {
220         if(mRemoteSelection!=null)
221             return mRemoteSelection.getData(row, column);
222         else
223             return mEntityDescriptor.getFieldDescriptor(column).getDefaultValue();
224     }
225     
226     /*** Mutation method that views the selection objct as a grid where row is
227      * the entity index and column is the field index for the stored entities.
228      */
229     public void setData(int row, int column, Object dataValue)
230     {
231         if(mRemoteSelection!=null)
232             mRemoteSelection.setData(row, column, dataValue);
233     }
234     
235     /*** Access method that returns the IEntityDescriptor for the selection.
236      * The selection object will only support entities of that type.
237      */ 
238     public IEntityDescriptor getEntityDescriptor()
239     {
240         return mEntityDescriptor;
241     }    
242     
243     /*** Adds an ISelectionListener to receive notifiactions of changes
244      * in the entity content of the collection object. 
245      */
246     public void addSelectionListener(ISelectionListener listener)
247     {
248         if(mSelectionListener==null && mRemoteSelection!=null)
249              mRemoteSelection.addSelectionListener(this.getRelayListener());
250         mSelectionListener = (ISelectionListener)CELEventMulticaster.add(mSelectionListener, listener);
251     }
252     
253     /*** Removes an ISelectionListener from the collection object.
254      */
255     public void removeSelectionListener(ISelectionListener listener)
256     {
257         mSelectionListener = (ISelectionListener)CELEventMulticaster.remove(mSelectionListener, listener);
258         if(mSelectionListener==null && mRemoteSelection!=null)
259              mRemoteSelection.removeSelectionListener(this.getRelayListener());
260     }
261     
262     /*** Adds IEntityListener to receive notifications of performed 
263      * data operations on all entities contained in the collection object.
264      */
265     public void addEntityListener(IEntityListener listener)
266     {
267         if(mEntityListener==null && mRemoteSelection!=null)
268              mRemoteSelection.addEntityListener(this.getRelayListener());
269         mEntityListener = (IEntityListener)CELEventMulticaster.add(mEntityListener, listener);
270     }
271     
272     /*** Removes the specified IEntityListener from the collection object.
273      */
274     public void removeEntityListener(IEntityListener listener)
275     {
276         mEntityListener = (IEntityListener)CELEventMulticaster.remove(mEntityListener, listener);
277         if(mEntityChangeListener==null && mRemoteSelection!=null)
278              mRemoteSelection.removeEntityListener(this.getRelayListener());
279     }
280     
281     /*** Adds IEntityChangeListener to receive notifications of performed 
282      * data operations on all entities contained in the collection object. 
283      * Note that changes can in specific situations like during end-user 
284      * editation be very frequent. 
285      */
286     public void addEntityChangeListener(IEntityChangeListener listener)
287     {
288         if(mEntityChangeListener==null && mRemoteSelection!=null)
289              mRemoteSelection.addEntityChangeListener(this.getRelayListener());
290         mEntityChangeListener = (IEntityChangeListener)CELEventMulticaster.add(mEntityChangeListener, listener);
291     }
292     
293     /*** Removes the specified IEntityListener from the collection object.
294      */
295     public void removeEntityChangeListener(IEntityChangeListener listener)    
296     {
297         mEntityChangeListener = (IEntityChangeListener)CELEventMulticaster.remove(mEntityChangeListener, listener);
298         if(mEntityChangeListener==null && mRemoteSelection!=null)
299              mRemoteSelection.removeEntityChangeListener(this.getRelayListener());
300     }
301     
302     /*** Creates a sub selection with the indexed entities in the called 
303      * selection. The created selection that should be independant of changes 
304      * in the source/called selection after the time of creation.
305      */
306     public ISelection createSubSelection(int[] indexArray)
307     {
308         if(mRemoteSelection!=null)
309             return mRemoteSelection.createSubSelection(indexArray);
310         else
311             throw new IndexOutOfBoundsException();
312     }
313     
314     /*** Creates a sub selection with all qualified entities in the called 
315      * selection. The created selection that should be independant of changes 
316      * in the source/called selection after the time of creation.
317      */
318     public ISelection createSubSelection(Qualifier qualifier)
319     {
320         if(mRemoteSelection!=null)
321             return mRemoteSelection.createSubSelection(qualifier);
322         else
323             return new Selection(this.getEntityDescriptor());
324     }
325 
326     /*** Help method that returns true if the provided IEntity object exists in
327      * the selection otherwise false is returned.
328      */
329     public boolean contains(IEntity entity)
330     {
331         if(mRemoteSelection!=null)
332             return mRemoteSelection.contains(entity);
333         else
334             return false;
335     }
336     
337     /*** Help method that returns the index of the provided IEntity object in 
338      * the selection if it exists othewise a negative value is returned.
339      */
340     public int indexOf(IEntity entity)
341     {
342         if(mRemoteSelection!=null)
343             return mRemoteSelection.indexOf(entity);
344         else
345             return -1;
346     }
347     
348     /*** Help method that returns the index of the the first entity object in 
349      * the selection with the specified field set to the specified value.
350      */
351     public int indexOf(IFieldDescriptor fieldDescriptor, Object fieldData)
352     {
353         if(mRemoteSelection!=null)
354             return mRemoteSelection.indexOf(fieldDescriptor, fieldData);
355         else
356             return -1;
357     }
358 
359     /*** Help method that returns true if the provided IEntity object will be
360      * accepted by the selection if added or inserted to it. Reasons for not 
361      * accepting an entity is wrong type (IEntityDescriptor), already included
362      * or if selection is qualified the entity may fail qualification.
363      */
364     public boolean doesAccept(IEntity entity)
365     {
366         if(mRemoteSelection!=null)
367             return mRemoteSelection.doesAccept(entity);
368         else
369             return false;
370     }
371     
372     /*** This method sorts the called selection using the provided comparator.
373      * One single content change event will be fired when this method is called.
374      * Note that if the provided Comparator does not support IEntity objects
375      * an exeption will be thrown.
376      * @see EntityCollator
377      */
378     public void sort(Comparator comparator)
379     {
380         if(mRemoteSelection!=null)
381             mRemoteSelection.sort(comparator);
382     }
383 
384     /*** Returns a Set that acts as a wrapper for the selection. The Set object 
385      * should reflect changes to and from the wrapped selection.
386      */
387     public Set asSet()
388     {
389         if(mRemoteSelection!=null)
390             return mRemoteSelection.asSet();
391         else
392             return null; // Temporary solution! TODO!!!
393     }
394     
395     /*** Returns a List that acts as a wrapper for the selection. The List  
396      * object should reflect changes to and from the wrapped selection.
397      */
398     public List asList()
399     {
400         if(mRemoteSelection!=null)
401             return mRemoteSelection.asList();
402         else
403             return null; // Temporary solution! TODO!!!
404     }
405     
406     // IProxySelection implementation ------------------------------------------
407     
408     /*** Boolean access method that returns true if the proxy has a remote
409      * selection if false then getRemoteSelection() will return null.
410      */
411     public boolean hasRemoteSelection()
412     {
413         return this.getRemoteSelection()!=null;
414     }
415     
416     /*** Access method that returns the remote selection of the proxy.
417      * May return null if the proxy does not currently have a remote.
418      */
419     public ISelection getRemoteSelection()
420     {
421         return mRemoteSelection;
422     }
423     
424     /*** Boolean access method that returns true if the proxy has a source
425      * selection if false then getSourceSelection() will return null.
426      */
427     public boolean hasSourceSelection()
428     {
429         return this.getSourceSelection()!=null;
430     }
431     
432     /*** Access method that returns the source selection of the proxy.
433      * May return null if the proxy does not currently have a source entity.
434      */
435     public ISelection getSourceSelection()
436     {
437         if(mRemoteSelection==null)
438             return null;
439         if(mRemoteSelection instanceof IProxySelection)
440             return ((IProxySelection)mRemoteSelection).getSourceSelection();
441         else
442             return this.getRemoteSelection();
443     }
444     
445     /*** Optional mutation method that throws an UnsupportedOperationException 
446      * if the implementing class does not support the method.
447      */
448     public void setRemoteSelection(ISelection selection)
449     {
450         // Ignore entity if equal to the current remote entity.
451         if(mRemoteSelection==selection)
452             return; 
453         
454         // Verify the new entities type.
455         if(selection!=null && selection.getEntityDescriptor()!=mEntityDescriptor)
456             throw new InvalidSelectionException("ProxySelection with type \""+mEntityDescriptor.getCodeName()+"\" was asigned entity of type \""+selection.getEntityDescriptor().getCodeName()+"\".");
457         
458         // Remove all listeners in the remote object.
459         if(mRemoteSelection!=null)
460         {
461             if(mSelectionListener!=null)
462                 mRemoteSelection.removeSelectionListener(this.getRelayListener());
463             if(mEntityListener!=null)
464                 mRemoteSelection.removeEntityListener(this.getRelayListener());
465             if(mEntityChangeListener!=null)
466                 mRemoteSelection.removeEntityChangeListener(this.getRelayListener());
467             if(mProxyListener!=null && mRemoteSelection instanceof IProxySelection)
468                  ((IProxySelection)mRemoteSelection).removeProxyListener(this.getRelayListener());
469         }
470         
471         // Update the stored remote entity.
472         mRemoteSelection = selection;
473         this.doAfterRemoteChange();
474         
475         // Register listeners on the new remote object.
476         if(mRemoteSelection!=null)
477         {
478             if(mSelectionListener!=null)
479                 mRemoteSelection.addSelectionListener(this.getRelayListener());
480             if(mEntityListener!=null)
481                 mRemoteSelection.addEntityListener(this.getRelayListener());
482             if(mEntityChangeListener!=null)
483                 mRemoteSelection.addEntityChangeListener(this.getRelayListener());
484             if(mProxyListener!=null && mRemoteSelection instanceof IProxySelection)
485                  ((IProxySelection)mRemoteSelection).addProxyListener(this.getRelayListener());
486         }
487         
488         // Fire relevant proxy event and contents changed event.
489         this.fireProxyEvent(ProxyEvent.CHANGED);
490         if(mSelectionListener!=null)
491             mSelectionListener.contentsChanged(new SelectionEvent(this));
492     }
493     
494     /*** Adds an IProxyListener to receive notifications of changes of 
495      * the remote object.
496      */
497     public void addProxyListener(IProxyListener listener)
498     {
499         if(mProxyListener==null && mRemoteSelection!=null && mRemoteSelection instanceof IProxyEntity)
500              ((IProxyEntity)mRemoteSelection).addProxyListener(this.getRelayListener());
501         mProxyListener = (IProxyListener)CELEventMulticaster.add(mProxyListener, listener);
502     }    
503     
504     /*** Removes the specified IProxyListener from the remote object.
505      */
506     public void removeProxyListener(IProxyListener listener)
507     {
508         mProxyListener = (IProxyListener)CELEventMulticaster.remove(mProxyListener, listener);
509         if(mProxyListener==null && mRemoteSelection!=null && mRemoteSelection instanceof IProxySelection)
510              ((IProxySelection)mRemoteSelection).removeProxyListener(this.getRelayListener());
511     }    
512     
513     // Help methods ------------------------------------------------------------
514         
515     /*** Help method to fire a SelectionEvent to all registered
516      * SelectionListeners for notification of generally changed contents.
517      */
518     protected void fireContentsChanged()
519     {
520         if(mSelectionListener!=null)
521             mSelectionListener.contentsChanged(new SelectionEvent(this));
522     }
523     
524     /*** Help method to fire a SelectionEvent to all registered
525      * SelectionListeners for notification of an added entity.
526      */
527     protected void fireEntityAdded(IEntity entity, int row)
528     {
529         if(mSelectionListener!=null)
530             mSelectionListener.entityAdded(new SelectionEvent(this, SelectionEvent.ENTITY_ADDED, entity, row));
531     }
532     
533     /*** Help method to fire a SelectionEvent to all registered
534      * SelectionListeners for notification of an removed entity.
535      */
536     protected void fireEntityRemoved(IEntity entity, int row)
537     {
538         if(mSelectionListener!=null)
539             mSelectionListener.entityRemoved(new SelectionEvent(this, SelectionEvent.ENTITY_REMOVED, entity, row));
540     }
541 
542     /*** Fires a ProxyEvent with the provided operation type to all registered
543      * IProxyListener objects.
544      */
545     protected void fireProxyEvent(int eventType)
546     {
547         if(mProxyListener==null)
548             return;
549         else if(eventType == ProxyEvent.CHANGED)
550             mProxyListener.remoteChanged(new ProxyEvent(this, eventType));
551         else if(eventType == ProxyEvent.EXPANDED)
552             mProxyListener.remoteExpanded(new ProxyEvent(this, eventType));
553     }
554     
555     /*** Fires an EntityChangeEvent with the provided operation type to all 
556      * registered IEntityChangeListener objects.
557      */
558     protected void fireStatusChangedEvent(IEntity entity, int statusType, boolean newState) 
559     {
560         if(mEntityChangeListener!=null)
561             mEntityChangeListener.statusChanged(new EntityChangeEvent(entity, statusType, newState));
562      }
563     
564     /*** Help method that returns the selections relay listener.
565      */
566     protected RelayListener getRelayListener()
567     {
568         if(mEntityRelayListener==null)
569             mEntityRelayListener = this.createRelayListener();
570         return mEntityRelayListener;
571     }
572     
573     /*** Help method that creates a new relay listener. Can be used by subclasses
574      * replace or extend the RelayListener.
575      */
576     protected RelayListener createRelayListener()
577     {
578         return new RelayListener();
579     }
580     
581     /*** Empty help method provided for sub class customization. The method is
582      * called directly after the remote object has been changed but before any
583      * listeners has been registered or events been fired. 
584      */
585     protected void doAfterRemoteChange()
586     {
587     }
588 
589     // Nested classes ----------------------------------------------------------
590     
591     protected class RelayListener implements ISelectionListener, IEntityListener, IEntityChangeListener, IProxyListener
592     {
593         // ISelectionListener implementation -----------------------------------
594         public void entityRemoved(SelectionEvent event)
595         {
596             if(mSelectionListener!=null)
597                 mSelectionListener.entityRemoved(event);
598         }
599         
600         public void entityAdded(SelectionEvent event)
601         {
602             if(mSelectionListener!=null)
603                 mSelectionListener.entityAdded(event);
604         }
605         
606         public void contentsChanged(SelectionEvent event)
607         {
608             if(mSelectionListener!=null)
609                 mSelectionListener.contentsChanged(event);
610         }        
611 
612         // IEntityListener implemntation ---------------------------------------
613         public void storePerformed(EntityEvent event)
614         {
615             if(mEntityListener!=null)
616                 mEntityListener.storePerformed(event);
617         }
618         
619         public void deletePerformed(EntityEvent event)
620         {
621             if(mEntityListener!=null)
622                 mEntityListener.deletePerformed(event);
623         }
624         
625         public void refreshPerformed(EntityEvent event)
626         {
627             if(mEntityListener!=null)
628                 mEntityListener.refreshPerformed(event);
629         }
630         
631         // IEntityChangeListener implemntation ---------------------------------
632         public void dataChanged(EntityChangeEvent event)
633         {
634             if(mEntityChangeListener!=null)
635                 mEntityChangeListener.dataChanged(event);
636         }
637         
638         public void statusChanged(EntityChangeEvent event)
639         {
640             if(mEntityChangeListener!=null)
641                 mEntityChangeListener.statusChanged(event);
642         }        
643         
644         // IProxyListener implementation ---------------------------------------
645         public void remoteChanged(ProxyEvent event)
646         {
647             if(mProxyListener!=null)
648                 mProxyListener.remoteChanged(event);
649         }
650 
651         public void remoteExpanded(ProxyEvent event)
652         {
653             if(mProxyListener!=null)
654                 mProxyListener.remoteExpanded(event);
655         }
656     }
657 }