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  
26  /*** <Description for OrderedProxySelection>
27   *
28   * @author  Dennis Zikovic
29   * @version 1.00
30   *
31   *//* 
32   *
33   * WHEN        WHO               WHY & WHAT
34   * -----------------------------------------------------------------------------
35   * 2001-12-07  Dennis Zikovic    Creation
36   */
37  public class OrderedProxySelection extends ProxySelection
38  {
39      // Constants ---------------------------------------------------------------
40      
41      // Data members ------------------------------------------------------------
42      private int[] mIndexArray;
43      private Comparator mComparator;
44      
45      // Constructors ------------------------------------------------------------
46      
47      /*** Creates new OrderedProxySelection
48       */
49      public OrderedProxySelection(IEntityDescriptor entityDescriptor) 
50      {
51          super(entityDescriptor);
52      }
53      
54      /*** Creates new OrderedProxySelection
55       */
56      public OrderedProxySelection(IEntityDescriptor entityDescriptor, Comparator comparator) 
57      {
58          this(entityDescriptor);
59          mComparator = comparator;
60      }
61      
62      /*** Creates new OrderedProxySelection
63       */
64      public OrderedProxySelection(ISelection selection) 
65      {
66          super(selection);
67      }
68      
69      /*** Creates new OrderedProxySelection
70       */
71      public OrderedProxySelection(ISelection selection, Comparator comparator) 
72      {
73          this(selection);
74          mComparator = comparator;
75          this.sort();
76      }
77      
78      // Superclass overrides ----------------------------------------------------
79      
80      /*** Adds the provided IEntity object to at the specified index in the 
81       * selection object. If an entity with the same identity already exists 
82       * in the selection the requeat is ignored and false is returned.
83       */
84      public boolean addEntity(int index, IEntity entity)
85      {
86          if(this.getRemoteSelection()!=null)
87              return this.getRemoteSelection().addEntity(this.getRemoteIndex(index), entity);
88          else
89              return false;
90      }
91      
92      /*** Access method that returns the contained IEntity object with the 
93       * specified index.
94       */
95      public IEntity getEntity(int index)
96      {
97          if(this.getRemoteSelection()!=null)
98              return this.getRemoteSelection().getEntity(this.getRemoteIndex(index));
99          else
100             return null;
101     }
102     
103     /*** Mutation method that removes the indexed entity from the selection.
104      * Returns the indexed entity if it was found and removed otherwise null  
105      * is returned. The entities are not effecte in any other way.
106      */
107     public IEntity removeEntity(int index)
108     {
109         if(this.getRemoteSelection()!=null)
110             return this.getRemoteSelection().removeEntity(this.getRemoteIndex(index));
111         else
112             return null;
113     }
114     
115     /*** Access method that views the selection objct as a grid where row is
116      * the entity index and column is the field index for the stored entities.
117      */
118     public Object getData(int row, int column)
119     {
120         if(this.getRemoteSelection()!=null)
121             return this.getRemoteSelection().getData(this.getRemoteIndex(row), column);
122         else
123             return this.getEntityDescriptor().getFieldDescriptor(column).getDefaultValue();
124     }
125     
126     /*** Mutation method that views the selection objct as a grid where row is
127      * the entity index and column is the field index for the stored entities.
128      */
129     public void setData(int row, int column, Object dataValue)
130     {
131         if(this.getRemoteSelection()!=null)
132             this.getRemoteSelection().setData(this.getRemoteIndex(row), column, dataValue);
133     }
134     
135     /*** Creates a sub selection with the indexed entities in the called 
136      * selection. The created selection that should be independant of changes 
137      * in the source/called selection after the time of creation.
138      */
139     public ISelection createSubSelection(int[] indexArray)
140     {
141         ISelection selection = new Selection(this.getEntityDescriptor());
142         for(int j=0; j<indexArray.length; j++)
143             selection.addEntity(this.getEntity(indexArray[j]));
144         return selection;
145     }
146 
147     /*** Help method that returns the index of the provided IEntity object in 
148      * the selection if it exists othewise a negative value is returned.
149      */
150     public int indexOf(IEntity entity)
151     {
152         if(this.getRemoteSelection()!=null)
153             return this.getOrderedIndex(this.getRemoteSelection().indexOf(entity));
154         else
155             return -1;
156     }
157 
158     /*** Access method that returns the selections relay listener.
159      */
160     protected RelayListener createRelayListener()
161     {
162         return new OrderedRelayListener();
163     }
164 
165     /*** Overriden to sort the index map for the new remote selection. 
166      */
167     protected void doAfterRemoteChange()
168     {
169         this.sort();
170     }
171 
172     // Access methods ----------------------------------------------------------
173     
174     public Comparator getComparator()
175     {
176         return mComparator;
177     }
178     
179     public void setComparator(Comparator comp)
180     {
181         mComparator = comp;
182         this.sort();
183     }
184     
185     public void setCollationField(IFieldDescriptor fieldDescriptor)
186     {
187         this.setCollationField(fieldDescriptor, true);
188     }
189     
190     public void setCollationField(IFieldDescriptor fieldDescriptor, boolean ascending)
191     {
192         mComparator = new EntityCollator(this.getEntityDescriptor());
193         ((EntityCollator)mComparator).addCollationField(fieldDescriptor, ascending);
194         this.sort();
195     }
196     
197     public void addCollationField(IFieldDescriptor fieldDescriptor)
198     {
199         this.addCollationField(fieldDescriptor, true);
200     }
201 
202     public void addCollationField(IFieldDescriptor fieldDescriptor, boolean ascending)
203     {
204         if(!(mComparator instanceof EntityCollator))
205             mComparator = new EntityCollator(this.getEntityDescriptor());
206         ((EntityCollator)mComparator).addCollationField(fieldDescriptor, ascending);
207         this.sort();
208     }
209 
210     public void clearCollationFields()
211     {
212         mComparator = null;
213     }
214     
215     /*** Converts an ordered entity index to a remote unordered index.
216      */
217     public int getRemoteIndex(int orderedIndex)
218     {
219         if(mComparator!=null && mIndexArray!=null && mIndexArray.length>0)
220             return mIndexArray[orderedIndex];
221         else
222             return orderedIndex;
223     }
224     
225     /*** Converts a remote unordered entity index to an ordered index.
226      */
227     public int getOrderedIndex(int remoteIndex)
228     {
229         if(mComparator==null || mIndexArray==null)
230             return remoteIndex;
231 
232         int orderedIndex = mIndexArray.length-1;
233         while(orderedIndex>=0 && mIndexArray[orderedIndex]!=remoteIndex)
234             orderedIndex--;
235         return orderedIndex;
236     }
237     
238     // Help methods ------------------------------------------------------------
239 
240     /*** Sorts the selection using the current entity collator. Note that the
241      * sort is automatically called when a new collator is set using any of
242      * the order changing methods in the object. The method should however be 
243      * used if the registered Comperator is changed externally.
244      */ 
245     public void sort()
246     {
247         if(!this.hasRemoteSelection())
248             mIndexArray = null;
249         
250         // Break if no Comparator is defined.
251         if(mComparator==null || !this.hasRemoteSelection())
252             return;
253         
254         // Check for size changes.
255         ISelection remoteSelection = this.getRemoteSelection();
256         if(mIndexArray==null || mIndexArray.length!=remoteSelection.size())
257         {            
258             if(mIndexArray!=null && mIndexArray.length<remoteSelection.size())
259             {
260                 // If the size has increased, use the old array to save order.
261                 int[] oldArray = mIndexArray;
262                 mIndexArray = new int[remoteSelection.size()];
263                 System.arraycopy(oldArray, 0, mIndexArray, 0, oldArray.length);
264                 for(int j=oldArray.length; j<mIndexArray.length; j++)
265                     mIndexArray[j] = j;
266             }
267             else
268             {
269                 // Else, do it the hard way.
270                 mIndexArray = new int[remoteSelection.size()];
271                 for(int j=0; j<mIndexArray.length; j++)
272                     mIndexArray[j] = j;
273             }
274         }
275         
276         // Sort the index array.   
277         boolean orderChanged = false;
278         int j, k, tmp;
279         for(j=1; j<mIndexArray.length; j++) 
280         {
281             k=j;
282             while(k>0 &&  mComparator.compare(remoteSelection.getEntity(mIndexArray[j]), remoteSelection.getEntity(mIndexArray[k-1])) < 0)
283                 k--;
284             
285             if(k<j) 
286             {
287                 tmp = mIndexArray[j];
288                 System.arraycopy(mIndexArray, k, mIndexArray, k+1, j-k);
289                 mIndexArray[k] = tmp;
290                 orderChanged = true;
291             }
292         }
293         
294         // Fire contents change event if order was changed.
295         if(orderChanged)
296             this.fireContentsChanged();
297     }
298     
299     // Nested classes ----------------------------------------------------------
300     
301     protected class OrderedRelayListener extends RelayListener
302     {
303         // ISelectionListener implementation -----------------------------------
304         public void entityRemoved(SelectionEvent event)
305         {
306             // Could be improved, resort is not really necessary!
307             sort(); 
308             super.entityRemoved(event);
309         }
310         
311         public void entityAdded(SelectionEvent event)
312         {
313             // If sorted insert new entity sp that sorting order is maintained
314             if(mComparator!=null && hasRemoteSelection())
315             {
316                 ISelection remoteSelection = getRemoteSelection();
317                 int[] newIndexArray = new int[mIndexArray.length + 1];
318                 int insertIndex = -1;
319                 
320                 // Find index in mIndexArray for the added entity
321                 for (int j=0; j<mIndexArray.length; j++)
322                     if (mComparator.compare(remoteSelection.getEntity(mIndexArray[j]), event.getEntity()) > 0)
323                     {
324                         insertIndex = j;
325                         break;
326                     }
327                     
328                 // New entity is greater than or equal to the last entity, insert last
329                 if (insertIndex == -1)
330                     insertIndex = newIndexArray.length - 1;
331                     
332                 // Recreate mIndexArray
333                 newIndexArray[insertIndex] = remoteSelection.indexOf(event.getEntity());
334                 System.arraycopy(mIndexArray, 0, newIndexArray, 0, insertIndex);
335                 System.arraycopy(mIndexArray, insertIndex, newIndexArray, insertIndex + 1, mIndexArray.length - insertIndex);
336                     
337                 mIndexArray = newIndexArray;
338             }
339 
340             super.entityAdded(event);
341         }
342         
343         public void contentsChanged(SelectionEvent event)
344         {
345             sort();
346             super.contentsChanged(event);
347         }        
348         
349         // IEntityChangeListener implemntation ---------------------------------
350         public void dataChanged(EntityChangeEvent event)
351         {
352             // Could be improved, resort is not allways necessary!       
353             // Can we afford to resort for every data change?!!
354             sort();
355             super.dataChanged(event);
356         }
357         
358         // IProxyListener implementation ---------------------------------------
359         public void remoteChanged(ProxyEvent event)
360         {
361             sort();
362             super.remoteChanged(event);
363         }
364     }
365 }