1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.caleigo.core;
20
21 import java.util.*;
22
23 import org.caleigo.core.event.*;
24 import org.caleigo.core.exception.*;
25 import org.caleigo.toolkit.log.*;
26
27 /*** Selection is an entity collection class that can store zero or more
28 * IEntity objects. The entities stored by this interface are type specified
29 * and must be defined by a single IEntityDescriptor.
30 *
31 * The Selection class is ordered and contained entities can be accesed
32 * by index. The class does not accept duplicate entities if contained
33 * entity has identity fields.
34 *
35 * @author Dennis Zikovic
36 * @version 1.00
37 *
38 *//*
39 *
40 * WHEN WHO WHY & WHAT
41 * ------------------------------------------------------------------------------
42 * 2001-10-10 Dennis Zikovic Creation
43 */
44 public class Selection implements ISelection
45 {
46
47 private List mEntityList;
48 private IEntityDescriptor mEntityDescriptor;
49
50 private transient EntityRelayListener mEntityRelayListener;
51 private transient ISelectionListener mSelectionListener;
52 private transient IEntityListener mEntityListener;
53 private transient IEntityChangeListener mEntityChangeListener;
54
55
56
57 public Selection(IEntityDescriptor entityDescriptor)
58 {
59 mEntityDescriptor = entityDescriptor;
60 mEntityList = new ArrayList();
61 }
62
63 /*** This is a copy constructor that makes a shallow copy of the contained
64 * entities in the provided collection.
65 */
66 public Selection(ISelection selection)
67 {
68 mEntityDescriptor = selection.getEntityDescriptor();
69 mEntityList = new ArrayList(selection.asList());
70 }
71
72
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 boolean dirty = false;
80 for(int j=0; !dirty && j<this.size(); j++)
81 dirty = this.getEntity(j).isDirty();
82 return dirty;
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
92 if(!this.isDirty())
93 return;
94
95
96 if(this.getEntityDescriptor().getDataSourceDescriptor().getDefaultDataSource()==null)
97 throw new DataServiceNotFoundException("No default data service has been set for data source: "+this.getEntityDescriptor().getDataSourceDescriptor().getCodeName());
98 IDataService service = this.getEntityDescriptor().getDataSourceDescriptor().getDefaultDataSource().getDataService();
99
100
101 IDataTransaction trans = service.newTransaction();
102 for(int j=0; j<this.size(); j++)
103 if(this.getEntity(j).isDirty())
104 trans.addStore(this.getEntity(j));
105 trans.commit();
106 }
107
108 /*** Deletes all contained entities. The entities are deleted in a single
109 * transaction meaning that if one delete fails then all fails.
110 * USE THIS METHOD WITH CAUTION!
111 */
112 public void deleteAll()
113 {
114
115 if(this.getEntityDescriptor().getDataSourceDescriptor().getDefaultDataSource()==null)
116 throw new DataServiceNotFoundException("No default data service has been set for data source: "+this.getEntityDescriptor().getDataSourceDescriptor().getCodeName());
117 IDataService service = this.getEntityDescriptor().getDataSourceDescriptor().getDefaultDataSource().getDataService();
118
119
120 IDataTransaction trans = service.newTransaction();
121 for(int j=0; j<this.size(); j++)
122 trans.addDelete(this.getEntity(j));
123 trans.commit();
124 }
125
126 /*** Performs a refresh on all contained entities. The refresh is batched
127 * to save performance.
128 */
129 public void refreshAll()
130 {
131
132 if(this.getEntityDescriptor().getDataSourceDescriptor().getDefaultDataSource()==null)
133 throw new DataServiceNotFoundException("No default data service has been set for data source: "+this.getEntityDescriptor().getDataSourceDescriptor().getCodeName());
134 IDataService service = this.getEntityDescriptor().getDataSourceDescriptor().getDefaultDataSource().getDataService();
135
136
137 IDataTransaction trans = service.newTransaction();
138 for(int j=0; j<this.size(); j++)
139 trans.addRefresh(this.getEntity(j));
140 trans.commit();
141 }
142
143 /*** Adds the provided IEntity object to the end of selection object.
144 * If an entity with the same identity already exists in the selection the
145 * requeat is ignored and false is returned.
146 */
147 public boolean addEntity(IEntity entity)
148 {
149 if(this.doesAccept(entity))
150 {
151
152 boolean res = mEntityList.add(entity);
153
154
155 this.registerListeners(entity);
156
157
158 if(res)
159 this.fireEntityAdded(entity, mEntityList.size());
160
161 return res;
162 }
163 else
164 return false;
165 }
166
167 /*** Adds the provided IEntity object to at the specified index in the
168 * selection object. If an entity with the same identity already exists
169 * in the selection the requeat is ignored and false is returned.
170 */
171 public boolean addEntity(int index, IEntity entity)
172 {
173 if(this.doesAccept(entity))
174 {
175
176 mEntityList.add(index, entity);
177
178
179 this.registerListeners(entity);
180
181
182 this.fireEntityAdded(entity, index);
183
184 return true;
185 }
186 else
187 return false;
188 }
189
190 /*** Access method that returns the contained IEntity object with the
191 * specified index.
192 */
193 public IEntity getEntity(int index)
194 {
195 return (IEntity)mEntityList.get(index);
196 }
197
198 /*** Mutation method that removes the provided entity from the selection.
199 * Returns true if the entity was found and removed otherwise false is
200 * returned.
201 */
202 public boolean removeEntity(IEntity entity)
203 {
204 int index = mEntityList.indexOf(entity);
205 if(index>=0)
206 this.removeEntity(index);
207 return index>=0;
208 }
209
210 /*** Mutation method that removes the indexed entity from the selection.
211 * Returns the indexed entity if it was found and removed otherwise null
212 * is returned. The entities are not effecte in any other way.
213 */
214 public IEntity removeEntity(int index)
215 {
216 IEntity oldEntity = (IEntity)mEntityList.remove(index);
217
218
219 this.unregisterListeners(oldEntity);
220
221
222 if(oldEntity!=null)
223 this.fireEntityRemoved(oldEntity, index);
224
225 return oldEntity;
226 }
227
228 /*** Returns a java.util.Iterator object that iterates over all entities
229 * in the selection. The iterator should be read only and should not
230 * support the remove method. The entities are not effected in any other way.
231 */
232 public Iterator iterator()
233 {
234 return mEntityList.listIterator();
235 }
236
237 /*** Mutation method the removes all entities currently stored in the
238 * selection. The entities are not effected in any other way.
239 */
240 public void clear()
241 {
242
243 this.unregisterEntityListener();
244 this.unregisterEntityChangeListener();
245
246
247 mEntityList.clear();
248
249
250 this.fireContentsChanged();
251 }
252
253 /*** Access method that reurns the number entities currently contained in
254 * the selection object.
255 */
256 public int size()
257 {
258 return mEntityList.size();
259 }
260
261 /*** Boolean access method that return true if the selection is empty.
262 */
263 public boolean isEmpty()
264 {
265 return mEntityList.isEmpty();
266 }
267
268 /*** Access method that views the selection objct as a grid where row is
269 * the entity index and column is the field index for the stored entities.
270 */
271 public Object getData(int row, int column)
272 {
273 if(row<this.size() && column<this.getEntityDescriptor().getFieldCount())
274 return ((IEntity)mEntityList.get(row)).getData(this.getEntityDescriptor().getFieldDescriptor(column));
275 else
276 return null;
277 }
278
279 /*** Mutation method that views the selection objct as a grid where row is
280 * the entity index and column is the field index for the stored entities.
281 */
282 public void setData(int row, int column, Object dataValue)
283 {
284 if(row<this.size() && column<this.getEntityDescriptor().getFieldCount())
285 ((IEntity)mEntityList.get(row)).setData(this.getEntityDescriptor().getFieldDescriptor(column), dataValue);
286 }
287
288 /*** Access method that returns the IEntityDescriptor for the selection.
289 * The selection object will only support entities of that type.
290 */
291 public IEntityDescriptor getEntityDescriptor()
292 {
293 return mEntityDescriptor;
294 }
295
296 /*** Creates a sub selection with the indexed entities in the called
297 * selection. The created selection that should be independant of changes
298 * in the source/called selection after the time of creation. If any index
299 * in the array is out of bounds it will be ignored and no exception will
300 * be thrown.
301 */
302 public ISelection createSubSelection(int[] indexArray)
303 {
304 ISelection selection = new Selection(this.getEntityDescriptor());
305 for(int j=0; j<indexArray.length; j++)
306 if(indexArray[j]>=0 && indexArray[j]<selection.size())
307 selection.addEntity(this.getEntity(indexArray[j]));
308 return selection;
309 }
310
311 /*** Creates a sub selection with all qualified entities in the called
312 * selection. The created selection that should be independant of changes
313 * in the source/called selection after the time of creation.
314 */
315 public ISelection createSubSelection(Qualifier qualifier)
316 {
317 ISelection selection = new Selection(this.getEntityDescriptor());
318 for(int j=0; j<this.size(); j++)
319 if(qualifier.doesQualify(this.getEntity(j)))
320 selection.addEntity(this.getEntity(j));
321 return selection;
322 }
323
324 /*** Help method that returns true if the provided IEntity object exists in
325 * the selection otherwise false is returned.
326 */
327 public boolean contains(IEntity entity)
328 {
329 return mEntityList.contains(entity);
330 }
331
332 /*** Help method that returns the index of the provided IEntity object in
333 * the selection if it exists othewise a negative value is returned.
334 */
335 public int indexOf(IEntity entity)
336 {
337 return mEntityList.indexOf(entity);
338 }
339
340 /*** Help method that returns the index of the the first entity object in
341 * the selection with the specified field set to the specified value.
342 */
343 public int indexOf(IFieldDescriptor fieldDescriptor, Object fieldData)
344 {
345 if(fieldDescriptor==null || !this.getEntityDescriptor().contains(fieldDescriptor))
346 throw new InvalidFieldException("The field "+fieldDescriptor+" is not a part of "+this.getEntityDescriptor()+"!");
347 int fieldIndex = this.getEntityDescriptor().getFieldIndex(fieldDescriptor);
348
349 int index = -1;
350 for(int j=0; index<0 && j<this.size(); j++)
351 if(DataType.isDataEqual(this.getData(j, fieldIndex), fieldData))
352 index=j;
353 return index;
354 }
355
356 /*** Help method that returns true if the provided IEntity object will be
357 * accepted by the selection if added or inserted to it. Reasons for not
358 * accepting an entity is wrong type (IEntityDescriptor), already included
359 * (valid only if the entity descripror has identity fields) or if the
360 * selection is qualified the entity may fail qualification.
361 */
362 public boolean doesAccept(IEntity entity)
363 {
364 boolean hasIdentityFields = false;
365 for(int j=0; !hasIdentityFields && j<mEntityDescriptor.getFieldCount(); j++)
366 hasIdentityFields = mEntityDescriptor.getFieldDescriptor(j).isIdentityField();
367 return mEntityDescriptor.equals(entity.getEntityDescriptor()) && !(hasIdentityFields && this.contains(entity));
368 }
369
370 /*** This method sorts the called selection using the provided comparator.
371 * One single content change event will be fired when this method is called.
372 * Note that if the provided Comparator does not support IEntity objects
373 * an exeption will be thrown.
374 * @see EntityCollator
375 */
376 public void sort(Comparator comparator)
377 {
378 Collections.sort(mEntityList, comparator);
379 this.fireContentsChanged();
380 }
381
382 /*** Returns a Set that acts as a wrapper for the selection. The Set object
383 * should reflect changes to and from the wrapped selection.
384 */
385 public Set asSet()
386 {
387 return new EntityPoolSet();
388 }
389
390 /*** Returns a List that acts as a wrapper for the selection. The List
391 * object should reflect changes to and from the wrapped selection.
392 */
393 public List asList()
394 {
395 return new SelectionList();
396 }
397
398 /*** Adds an ISelectionListener to receive notifiactions of changes
399 * in the entity content of the collection object.
400 */
401 public void addSelectionListener(ISelectionListener listener)
402 {
403 mSelectionListener = (ISelectionListener)CELEventMulticaster.add(mSelectionListener, listener);
404 }
405
406 /*** Removes an ISelectionListener from the collection object.
407 */
408 public void removeSelectionListener(ISelectionListener listener)
409 {
410 mSelectionListener = (ISelectionListener)CELEventMulticaster.remove(mSelectionListener, listener);
411 }
412
413 /*** Adds IEntityListener to receive notifications of performed
414 * data operations on all entities contained in the collection object.
415 */
416 public void addEntityListener(IEntityListener listener)
417 {
418 if(mEntityListener==null)
419 this.registerEntityListener();
420 mEntityListener = (IEntityListener)CELEventMulticaster.add(mEntityListener, listener);
421 }
422
423 /*** Removes the specified IEntityListener from the collection object.
424 */
425 public void removeEntityListener(IEntityListener listener)
426 {
427 mEntityListener = (IEntityListener)CELEventMulticaster.remove(mEntityListener, listener);
428 if(mEntityChangeListener==null)
429 this.unregisterEntityListener();
430 }
431
432 /*** Adds IEntityChangeListener to receive notifications of performed
433 * data operations on all entities contained in the collection object.
434 * Note that changes can in specific situations like during end-user
435 * editation be very frequent.
436 */
437 public void addEntityChangeListener(IEntityChangeListener listener)
438 {
439 if(mEntityChangeListener==null)
440 this.registerEntityChangeListener();
441 mEntityChangeListener = (IEntityChangeListener)CELEventMulticaster.add(mEntityChangeListener, listener);
442 }
443
444 /*** Removes the specified IEntityListener from the collection object.
445 */
446 public void removeEntityChangeListener(IEntityChangeListener listener)
447 {
448 mEntityChangeListener = (IEntityChangeListener)CELEventMulticaster.remove(mEntityChangeListener, listener);
449 if(mEntityChangeListener==null)
450 this.unregisterEntityChangeListener();
451 }
452
453
454
455 /*** Help method to fire a SelectionEvent to all registered
456 * SelectionListeners for notification of generally changed contents.
457 */
458 protected void fireContentsChanged()
459 {
460 if(mSelectionListener!=null)
461 mSelectionListener.contentsChanged(new SelectionEvent(this));
462 }
463
464 /*** Help method to fire a SelectionEvent to all registered
465 * SelectionListeners for notification of an added entity.
466 */
467 protected void fireEntityAdded(IEntity entity, int row)
468 {
469 if(mSelectionListener!=null)
470 mSelectionListener.entityAdded(new SelectionEvent(this, SelectionEvent.ENTITY_ADDED, entity, row));
471 }
472
473 /*** Help method to fire a SelectionEvent to all registered
474 * SelectionListeners for notification of an removed entity.
475 */
476 protected void fireEntityRemoved(IEntity entity, int row)
477 {
478 if(mSelectionListener!=null)
479 mSelectionListener.entityRemoved(new SelectionEvent(this, SelectionEvent.ENTITY_REMOVED, entity, row));
480 }
481
482 /*** Adds listeners relaying events from the specified entity to relevant
483 * listeners registered in the collection object. Both IEntityListener and
484 * IEntityChangeListener are handled by the method.
485 */
486 protected void registerListeners(IEntity entity)
487 {
488 if(mEntityListener!=null)
489 entity.addEntityListener(mEntityRelayListener);
490 if(mEntityChangeListener!=null)
491 entity.addEntityChangeListener(mEntityRelayListener);
492 }
493
494 /*** Removes listeners relaying events from the specified entity to relevant
495 * listeners registered in the collection object. Both IEntityListener and
496 * IEntityChangeListener are handled by the method.
497 */
498 protected void unregisterListeners(IEntity entity)
499 {
500 if(mEntityListener!=null)
501 entity.removeEntityListener(mEntityRelayListener);
502 if(mEntityChangeListener!=null)
503 entity.removeEntityChangeListener(mEntityRelayListener);
504 }
505
506 /*** Adds a IEntityListener to all contained entities that relays events
507 * to IEntityListener registered in the collection object.
508 */
509 protected void registerEntityListener()
510 {
511 if(mEntityRelayListener==null)
512 mEntityRelayListener = new EntityRelayListener();
513 for(int j=0; j<this.size(); j++)
514 this.getEntity(j).addEntityListener(mEntityRelayListener);
515 }
516
517 /*** Removes the IEntityListener from all contained entities that relays
518 * events to IEntityListener registered in the collection object.
519 */
520 protected void unregisterEntityListener()
521 {
522 for(int j=0; j<this.size(); j++)
523 this.getEntity(j).removeEntityListener(mEntityRelayListener);
524 }
525
526 /*** Adds a IEntityChangeListener to all contained entities that relays
527 * events to IEntityChangeListener registered in the collection object.
528 */
529 protected void registerEntityChangeListener()
530 {
531 if(mEntityRelayListener==null)
532 mEntityRelayListener = new EntityRelayListener();
533 for(int j=0; j<this.size(); j++)
534 this.getEntity(j).addEntityChangeListener(mEntityRelayListener);
535 }
536
537 /*** Removes the IEntityChangeListener from all contained entities that relays
538 * events to IEntityChangeListener registered in the collection object.
539 */
540 protected void unregisterEntityChangeListener()
541 {
542 for(int j=0; j<this.size(); j++)
543 this.getEntity(j).removeEntityChangeListener(mEntityRelayListener);
544 }
545
546 /*** Help method that logs text information from the collection object using
547 * the log package. This method only logs identity field data.
548 */
549 public void log(Object source)
550 {
551 this.log(source, null);
552 }
553
554 /*** Help method that logs text information from the collection object using
555 * the log package. This method logs data from all specified fields.
556 */
557 public void log(Object source, IFieldDescriptor[] fields)
558 {
559 for(int j=0; j<this.size(); j++)
560 Log.print(source, AbstractEntity.makeLogString(this.getEntity(j), fields));
561 }
562
563
564 protected class EntityPoolSet extends AbstractSet
565 {
566 public boolean add(Object obj)
567 {
568 if(obj instanceof IEntity)
569 return Selection.this.addEntity((IEntity)obj);
570 else
571 return false;
572 }
573
574 public int size()
575 {
576 return Selection.this.size();
577 }
578
579 public Iterator iterator()
580 {
581 return Selection.this.iterator();
582 }
583 }
584
585 protected class SelectionList extends AbstractList
586 {
587 public int size()
588 {
589 return Selection.this.size();
590 }
591
592 public Object get(int index)
593 {
594 return Selection.this.getEntity(index);
595 }
596
597 public Object set(int index, Object obj)
598 {
599 if(obj instanceof IEntity)
600 {
601 IEntity entity = Selection.this.removeEntity(index);
602 Selection.this.addEntity(index, (IEntity)obj);
603 return entity;
604 }
605 else
606 return null;
607 }
608
609 public boolean add(Object obj)
610 {
611 if(obj instanceof IEntity)
612 return Selection.this.addEntity((IEntity)obj);
613 else
614 return false;
615 }
616
617 public void add(int index, Object obj)
618 {
619 if(obj instanceof IEntity)
620 Selection.this.addEntity(index, (IEntity)obj);
621 }
622
623 public boolean remove(Object obj)
624 {
625 if(obj instanceof IEntity)
626 return Selection.this.removeEntity((IEntity)obj);
627 else
628 return false;
629 }
630
631 public Object remove(int index)
632 {
633 return Selection.this.removeEntity(index);
634 }
635 }
636
637 protected class EntityRelayListener implements IEntityListener, IEntityChangeListener
638 {
639
640 public void storePerformed(EntityEvent event)
641 {
642 mEntityListener.storePerformed(event);
643 }
644
645 public void deletePerformed(EntityEvent event)
646 {
647 mEntityListener.deletePerformed(event);
648 }
649
650 public void refreshPerformed(EntityEvent event)
651 {
652 mEntityListener.refreshPerformed(event);
653 }
654
655
656 public void dataChanged(EntityChangeEvent event)
657 {
658 mEntityChangeListener.dataChanged(event);
659 }
660
661 public void statusChanged(EntityChangeEvent event)
662 {
663 mEntityChangeListener.statusChanged(event);
664 }
665 }
666 }