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
22 import java.util.*;
23
24 import org.caleigo.core.event.*;
25 import org.caleigo.core.exception.*;
26 import org.caleigo.toolkit.log.*;
27 import org.caleigo.toolkit.util.*;
28
29 /*** A ProxyEntity is a basic implementation of the IProxyEntity iterface.
30 * The class acts as a proxy to another IEntity object that may set by simple
31 * access a methods. All events are forwarded transparently.
32 *
33 * Note that controlled proxies are transient and will not be serialized.
34 *
35 * @author Dennis Zikovic
36 * @version 1.00
37 *
38 *//*
39 *
40 * WHEN WHO WHY & WHAT
41 * -----------------------------------------------------------------------------
42 * 2001-11-21 Dennis Zikovic Creation
43 */
44 public class ProxyEntity implements IProxyEntity, IProxyController
45 {
46
47 private IEntity mRemoteEntity;
48 private IEntityDescriptor mEntityDescriptor;
49
50 private RelayListener mRelayListener;
51 private transient IEntityListener mEntityListener;
52 private transient IEntityChangeListener mEntityChangeListener;
53 private transient IProxyListener mProxyListener;
54
55 private transient Map mControlledProxyMap;
56 private transient boolean mHasDirtyControlledProxies = false;
57 private transient ControlledProxyListener mControlledProxyListener;
58
59
60
61 /*** Creates new ProxyEntity
62 */
63 public ProxyEntity(IEntityDescriptor descriptor)
64 {
65 mEntityDescriptor = descriptor;
66 }
67
68 /*** Creates new ProxyEntity
69 */
70 public ProxyEntity(IEntity entity)
71 {
72
73 if(entity==null)
74 throw new InvalidEntityException("ProxyEntity can not be instatiated with a null entity.");
75
76
77 mEntityDescriptor = entity.getEntityDescriptor();
78 this.setRemoteEntity(entity);
79 }
80
81
82
83 /*** Overriden to display the entity description type, identifying data and
84 * state of the status flags (D)IRTY, (E)MPTY and (P)ERSISTANT.
85 */
86 public String toString()
87 {
88 if(mRemoteEntity!=null)
89 return "Proxy to "+mRemoteEntity.toString();
90 else
91 return "Proxy to "+mEntityDescriptor.getCodeName()+"(EMPTY)";
92 }
93
94
95
96 /*** Boolean access method that returns true if the proxy has a remote
97 * entity if false then getRemoteEntity() will return null.
98 */
99 public boolean hasRemoteEntity()
100 {
101 return this.getRemoteEntity()!=null;
102 }
103
104 /*** Access method that returns the remote entity of the proxy.
105 * May return null if the proxy does not currently have a remote.
106 */
107 public IEntity getRemoteEntity()
108 {
109 return mRemoteEntity;
110 }
111
112 /*** Boolean access method that returns true if the proxy has a source
113 * entity if false then getSourceEntity() will return null.
114 */
115 public boolean hasSourceEntity()
116 {
117 return this.getSourceEntity()!=null;
118 }
119
120 /*** Access method that returns the source entity of the proxy.
121 * May return null if the proxy does not currently have a source entity.
122 */
123 public IEntity getSourceEntity()
124 {
125 if(mRemoteEntity==null)
126 return null;
127 if(mRemoteEntity instanceof IProxyEntity)
128 return ((IProxyEntity)mRemoteEntity).getSourceEntity();
129 else
130 return this.getRemoteEntity();
131 }
132
133 /*** Optional mutation method that throws an UnsupportedOperationException
134 * if the implementing class does not support the method.
135 */
136 public void setRemoteEntity(IEntity entity)
137 {
138
139 if(mRemoteEntity==entity)
140 return;
141
142
143 if(entity!=null && entity.getEntityDescriptor()!=mEntityDescriptor)
144 throw new InvalidEntityException("ProxyEntity with type \""+mEntityDescriptor.getCodeName()+"\" was asigned entity of type \""+entity.getEntityDescriptor().getCodeName()+"\".");
145
146
147 if(mRemoteEntity!=null)
148 {
149 if(mEntityListener!=null)
150 mRemoteEntity.removeEntityListener(this.getRelayListener());
151 if(mEntityChangeListener!=null)
152 mRemoteEntity.removeEntityChangeListener(this.getRelayListener());
153 if(mRemoteEntity instanceof IProxyEntity)
154 ((IProxyEntity)mRemoteEntity).removeProxyListener(this.getRelayListener());
155 }
156
157
158 mRemoteEntity = entity;
159
160
161 if(mRemoteEntity!=null)
162 {
163 if(mEntityListener!=null)
164 mRemoteEntity.addEntityListener(this.getRelayListener());
165 if(mEntityChangeListener!=null)
166 mRemoteEntity.addEntityChangeListener(this.getRelayListener());
167 if(mRemoteEntity instanceof IProxyEntity)
168 ((IProxyEntity)mRemoteEntity).addProxyListener(this.getRelayListener());
169 }
170
171
172 this.loadControlledProxies(false);
173
174
175 this.fireProxyEvent(ProxyEvent.CHANGED);
176 }
177
178 /*** Adds an IProxyListener to receive notifications of changes of
179 * the remote object.
180 */
181 public void addProxyListener(IProxyListener listener)
182 {
183
184
185 mProxyListener = (IProxyListener)CELEventMulticaster.add(mProxyListener, listener);
186 }
187
188 /*** Removes the specified IProxyListener from the remote object.
189 */
190 public void removeProxyListener(IProxyListener listener)
191 {
192 mProxyListener = (IProxyListener)CELEventMulticaster.remove(mProxyListener, listener);
193
194
195 }
196
197
198
199 /*** This method will store any unsaved changes in the entity to it's
200 * realated persistent storage. This will reset the DIRTY flag and set
201 * the PERSISTENT flag.
202 */
203 public void store()
204 {
205 if(mRemoteEntity==null)
206 return;
207 if(this.hasDirtyControlledProxies())
208 {
209 this.storeControlledProxies();
210 }
211 else
212 mRemoteEntity.store();
213 }
214
215 /*** This method will delete the entity from it's related persistent storage.
216 * This will set the DIRTY flag and reset the PERSISTENT flag. Note that
217 * that the actual java object will not be deleted or changed in any other
218 * way besides its status changes makeing possible to recreate the object
219 * by simply calling the store() method.
220 */
221 public void delete()
222 {
223 if(mRemoteEntity!=null)
224 {
225 mRemoteEntity.delete();
226 this.loadControlledProxies(true);
227 }
228 }
229
230 /*** This method will refresh the entity with current data from the related
231 * persistent storage. All contained data with any existing changes will
232 * be replaced from the storage. This will reset the DIRTY flag and can
233 * possibly reset the PERSISTENT flag if the entity can no longer be found
234 * in the related database/storage.
235 */
236 public void refresh()
237 {
238 if(mRemoteEntity!=null)
239 {
240 mRemoteEntity.refresh();
241 this.loadControlledProxies(true);
242 }
243 }
244
245 /*** Copies and replaces the, in the entity, contained data by reading each
246 * individual data field as a property from the provided property source.
247 * Note that since IEntity extends the IDataProvider interface it is
248 * thereby possibli to copy other entities with this method. Even if the
249 * etities are not of the same type all fields with the same name will be
250 * copied into the entity. Identical data will not be copied and if any
251 * changes were made the DIRTY flag will be set.
252 */
253 public void copyData(IDataProvider provider)
254 {
255 if(mRemoteEntity!=null)
256 {
257 mRemoteEntity.copyData(provider);
258 this.loadControlledProxies(false);
259 }
260 }
261
262 /*** Returns true if the data in all the entities IDENTIY fields are
263 * considered equal according to their DataType class.
264 */
265 public boolean equals(Object entity)
266 {
267 if(mRemoteEntity!=null)
268 return mRemoteEntity.equals(entity);
269 else
270 return false;
271 }
272
273 /*** Compares all data values between the objects if they are of the
274 * same type. True is returned only if all contained data exactly matches
275 * the compareded entity's data according to their DataType class.
276 */
277 public boolean equalsExactly(Object entity)
278 {
279 if(mRemoteEntity!=null)
280 return mRemoteEntity.equalsExactly(entity);
281 else
282 return false;
283 }
284
285 /*** Returns true if the addressed entity field is contains a NULL value.
286 */
287 public boolean isDataNull(IFieldDescriptor fieldDescriptor)
288 {
289 if(mRemoteEntity!=null)
290 return mRemoteEntity.isDataNull(fieldDescriptor);
291 else
292 return fieldDescriptor.getDefaultValue()==null;
293 }
294
295 /*** Returns the data value of the addressed data field. Can return NULL if
296 * the field excepts and contains a NULL value.
297 * @exception org.caleigo.core.exception.InvalidFieldException
298 */
299 public Object getData(IFieldDescriptor fieldDescriptor)
300 {
301 if(mRemoteEntity!=null)
302 return mRemoteEntity.getData(fieldDescriptor);
303 else
304 return fieldDescriptor.getDefaultValue();
305 }
306
307 /*** Sets the value of the addressed data field. Sets the DIRTY flag and
308 * clears the EMPTY flag but only if the new value differs from the old.
309 * If the value is actually changed then one or more EntityChangeExceptions
310 * will be fired.
311 * @exception org.caleigo.core.exception.InvalidFieldException
312 * @exception org.caleigo.core.exception.ReadOnlyViolationException
313 */
314 public void setData(IFieldDescriptor fieldDescriptor, Object data)
315 {
316 if(mRemoteEntity!=null)
317 mRemoteEntity.setData(fieldDescriptor, data);
318 }
319
320 /*** Clear resets all data in the entity to their defalt values and sets
321 * the flags to reflect an empty unchanged data entity.
322 */
323 public void clear()
324 {
325 if(mRemoteEntity!=null)
326 mRemoteEntity.clear();
327 }
328
329 /*** Help method that validates the data contained in the called data
330 * object and returns a ValidationResult object. Call isValid on the
331 * returned object to verify data validity. Should never return null.
332 */
333 public ValidationResult validateData()
334 {
335
336 ValidationResult result = mRemoteEntity.validateData();
337 if(!result.isValid())
338 return result;
339
340
341
342
343
344
345 Iterator slaves = this.getControlledProxies();
346 while(result.isValid() && slaves.hasNext())
347 {
348 IControlledProxy slave = (IControlledProxy)slaves.next();
349 if((slave instanceof IEntity) && ((IEntity)slave).isDirty())
350 {
351 for(int j=0; result.isValid() && j<slave.getEntityDescriptor().getFieldCount(); j++)
352 if(mRemoteEntity.isPersistent() || !slave.getRelationPath().getLastRelation().isRelationField(slave.getEntityDescriptor().getFieldDescriptor(j)))
353 result = slave.getEntityDescriptor().getFieldDescriptor(j).validateData(((IEntity)slave).getData(slave.getEntityDescriptor().getFieldDescriptor(j)), (IEntity)slave);
354 }
355 else if(slave instanceof ISelection)
356 {
357
358 for(int k=0; k<((ISelection)slave).size() && result.isValid(); k++)
359 if(((ISelection)slave).getEntity(k).isDirty())
360 {
361 for(int j=0; result.isValid() && j<slave.getEntityDescriptor().getFieldCount(); j++)
362 if(mRemoteEntity.isPersistent() || !slave.getRelationPath().getLastRelation().isRelationField(slave.getEntityDescriptor().getFieldDescriptor(j)))
363 result = slave.getEntityDescriptor().getFieldDescriptor(j).validateData(((ISelection)slave).getEntity(k).getData(slave.getEntityDescriptor().getFieldDescriptor(j)), ((ISelection)slave).getEntity(k));
364 }
365 }
366 }
367
368 return result;
369 }
370
371 /*** Return the entity objects IEntityDescriptor that defines it's type and
372 * structure. Enables extended means of reflection for the entity.
373 */
374 public IEntityDescriptor getEntityDescriptor()
375 {
376 return mEntityDescriptor;
377 }
378
379 /*** Returns true if the addressed entity field has been changed since
380 * creation or the last syncronization with the persistent storage.
381 */
382 public boolean isFieldDirty(IFieldDescriptor fieldDescriptor)
383 {
384 if(mRemoteEntity!=null)
385 return mRemoteEntity.isFieldDirty(fieldDescriptor);
386 else
387 return false;
388 }
389
390 /*** Returns true if any entity field in the entity has been changed since
391 * creation or the last syncronization with the persistent storage.
392 */
393 public boolean isDirty()
394 {
395
396 if(mRemoteEntity!=null)
397 return mRemoteEntity.isDirty() || this.hasDirtyControlledProxies();
398 else
399 return false;
400 }
401
402 /*** Returns true for newly creted object that that has had no
403 * changes from the default data set at the moment of creation.
404 */
405 public boolean isEmpty()
406 {
407 if(mRemoteEntity!=null)
408 return mRemoteEntity.isEmpty();
409 else
410 return true;
411 }
412
413 /*** Returns true if the the entity reflects data that exists in a related
414 * persistent storage. This means that newly created and deleted entities
415 * will have this flag set to false.
416 */
417 public boolean isPersistent()
418 {
419 if(mRemoteEntity!=null)
420 return mRemoteEntity.isPersistent();
421 else
422 return false;
423 }
424
425 /*** Returns the data source that the entity object belongs to.
426 * Newly created will normally return the default data source defined
427 * by the IDataSourceDescriptor that the entity is linked to trough it's
428 * IEntityDescriptor. Entities loaded from a persistent storage will return
429 * the data source that identifies that storage/database.
430 */
431 public IDataSource getDataSource()
432 {
433 if(mRemoteEntity!=null)
434 return mRemoteEntity.getDataSource();
435 else if(mEntityDescriptor.getDataSourceDescriptor()!=null)
436 return mEntityDescriptor.getDataSourceDescriptor().getDefaultDataSource();
437 else
438 return null;
439 }
440
441 /*** Returns a identity qualifier that uniquely qualifies the entity in a
442 * persistent storage. If the entity is PERSISTENt and any of the data in
443 * the identity fields have changed since the storage syncronization the
444 * returned Qualifier will identify the stored persistent version of the
445 * entity and NOT& the updated local one.
446 */
447 public Qualifier getOriginQualifier()
448 {
449 if(mRemoteEntity!=null)
450 return mRemoteEntity.getOriginQualifier();
451 else
452 return null;
453 }
454
455 /*** Should not normally be used by standard API users. Would have been
456 * protected if the Java language spec allowed it.
457 */
458 public void setStatusFlag(int flags)
459 {
460 if(mRemoteEntity!=null)
461 mRemoteEntity.setStatusFlag(flags);
462 }
463
464 /*** Should not normally be used by standard API users. Would have been
465 * protected if the Java language spec allowed it.
466 */
467 public void clearStatusFlag(int flags)
468 {
469 if(mRemoteEntity!=null)
470 mRemoteEntity.clearStatusFlag(flags);
471 }
472
473 /*** Adds IEntityListener to receive notifications of performed
474 * data operations on the entity object.
475 */
476 public void addEntityListener(IEntityListener listener)
477 {
478 if(mEntityListener==null && mRemoteEntity!=null)
479 mRemoteEntity.addEntityListener(this.getRelayListener());
480 mEntityListener = (IEntityListener)CELEventMulticaster.add(mEntityListener, listener);
481 }
482
483 /*** Removes the specified IEntityListener from the entity object.
484 */
485 public void removeEntityListener(IEntityListener listener)
486 {
487 mEntityListener = (IEntityListener)CELEventMulticaster.remove(mEntityListener, listener);
488 if(mEntityChangeListener==null && mRemoteEntity!=null)
489 mRemoteEntity.removeEntityListener(this.getRelayListener());
490 }
491
492 /*** Adds IEntityChangeListener to receive notifications of changes in the
493 * entity's status and data content. Note that changes can in specific
494 * situations like during end-user editation be very frequent.
495 */
496 public void addEntityChangeListener(IEntityChangeListener listener)
497 {
498 if(mEntityChangeListener==null && mRemoteEntity!=null)
499 mRemoteEntity.addEntityChangeListener(this.getRelayListener());
500 mEntityChangeListener = (IEntityChangeListener)CELEventMulticaster.add(mEntityChangeListener, listener);
501 }
502
503 /*** Removes the specified IEntityListener from the entity object.
504 */
505 public void removeEntityChangeListener(IEntityChangeListener listener)
506 {
507 mEntityChangeListener = (IEntityChangeListener)CELEventMulticaster.remove(mEntityChangeListener, listener);
508 if(mEntityChangeListener==null && mRemoteEntity!=null)
509 mRemoteEntity.removeEntityChangeListener(this.getRelayListener());
510 }
511
512
513
514 /*** Compares all identity data values between the objects if they are of the
515 * same type that is are defined by the same entity descriptor. <BR><BR>
516 *
517 * If the compared object is not an IEntity object a ClassCastException
518 * will be thrown. If the object is an IEntity but of another type, that is
519 * described by another entity descriptor, then the entities will first be
520 * ordered according to type using the order MASTER_ENTITY, SLAVE_ENTITY,
521 * LINK_ENTITY, STATIC_ENTITY, CUSTOM_ENTITY and secondly according to the
522 * entity descriptors code name. <BR><BR>
523 *
524 * If the proxy does not have a remote it is compared with the provided
525 * entity using the default data values according to its type.
526 */
527 public int compareTo(Object entity)
528 {
529
530 if(entity==null || !(entity instanceof IEntityDescriptor))
531 throw new ClassCastException();
532 else if(mRemoteEntity!=null)
533 return mRemoteEntity.compareTo(entity);
534 else
535 {
536
537 if(this.getEntityDescriptor()!=((IEntity)entity).getEntityDescriptor())
538 {
539 if(this.getEntityDescriptor().getEntityType()!=((IEntity)entity).getEntityDescriptor().getEntityType())
540 return this.getEntityDescriptor().getEntityType()-((IEntity)entity).getEntityDescriptor().getEntityType();
541 else
542 return this.getEntityDescriptor().getCodeName().compareTo(((IEntity)entity).getEntityDescriptor().getCodeName());
543 }
544
545
546
547 int compValue = 0;
548 IFieldDescriptor field;
549 for(int j=0; compValue==0 && j<this.getEntityDescriptor().getFieldCount(); j++)
550 {
551 field = this.getEntityDescriptor().getFieldDescriptor(j);
552 if(field.isIdentityField())
553 compValue = field.getDataType().compare(this.getData(field), ((IEntity)entity).getData(field));
554 }
555 return compValue;
556 }
557 }
558
559
560
561 /*** Returns an IControlledProxy object controlled by the called controller.
562 * Implementing classes may cache the controlled proxies and return the
563 * same physical proxy object as long as the relationPath matches.
564 * The controller may refuse to controll a proxy and should then cast
565 * an IllegalArgumentException. If the used relation path does not link
566 * up the controller and the slave an InvalidRelationException is thrown.
567 */
568 public IControlledProxy getControlledProxy(IEntityDescriptor entityDescriptor, IEntityRelationPath relationPath)
569 {
570 IControlledProxy proxy = null;
571
572
573 if(mControlledProxyMap==null)
574 mControlledProxyMap = new HashMap();
575
576
577 SlaveProxyItem item = (SlaveProxyItem)mControlledProxyMap.get(relationPath);
578 if(item!=null)
579 proxy = item.getControlledProxy();
580
581
582 if(proxy==null)
583 {
584
585 if(relationPath==null || !relationPath.isRelatedByPath(this.getEntityDescriptor(), entityDescriptor))
586 throw new InvalidRelationException("Invalid relation path "+this.getEntityDescriptor()+" and "+entityDescriptor+" are not related by the path "+relationPath);
587
588
589 if(relationPath.getForwardCardinality()==IEntityRelationPath.ONE && relationPath.getRelation(0).canBeFullReference(this.getEntityDescriptor()))
590 {
591 proxy = new ControlledProxyEntity(this, entityDescriptor, relationPath);
592 ((IEntity)proxy).addEntityChangeListener(this.getControlledProxyListener());
593 }
594 else
595 {
596 proxy = new ControlledProxySelection(this, entityDescriptor, relationPath);
597 ((ISelection)proxy).addEntityChangeListener(this.getControlledProxyListener());
598 ((ISelection)proxy).addSelectionListener(this.getControlledProxyListener());
599 }
600
601
602 mControlledProxyMap.put(relationPath, new SlaveProxyItem(proxy));
603
604 Log.print(this, "Creating new controlled proxy: "+relationPath);
605 }
606 else
607 item.addProxyUsers();
608
609 return proxy;
610 }
611
612 /*** Used to inform the controller that a controlled proxy instance will be
613 * discarded and gives the controller a chance to deallocate resources
614 * connected to it.
615 */
616 public void releaseControlledProxy(IControlledProxy proxy)
617 {
618 if(mControlledProxyMap==null)
619 return;
620
621 SlaveProxyItem item = ((SlaveProxyItem)mControlledProxyMap.get(proxy.getRelationPath()));
622 if(item!=null)
623 {
624
625 item.decProxyUsers();
626
627
628 if(!item.hasProxyUsers())
629 {
630 mControlledProxyMap.remove(proxy.getRelationPath());
631 if(proxy instanceof IEntity)
632 ((IEntity)proxy).removeEntityChangeListener(this.getControlledProxyListener());
633 else if(proxy instanceof ISelection)
634 {
635 ((ISelection)proxy).removeEntityChangeListener(this.getControlledProxyListener());
636 ((ISelection)proxy).removeSelectionListener(this.getControlledProxyListener());
637 }
638 this.updateDirtyState();
639
640 Log.print(this, "Removing controlled proxy: "+proxy.getRelationPath());
641 }
642 }
643 }
644
645 /*** Returns an Iterator with all proxies currently controlled by the
646 * called controller.
647 */
648 public java.util.Iterator getControlledProxies()
649 {
650 if(mControlledProxyMap==null)
651 return Iterators.EMPTY_ITERATOR;
652 else
653 return new Iterators.UnmodifiableIterator(mControlledProxyMap.values().iterator())
654 {
655 public Object next()
656 {
657 return ((SlaveProxyItem)mIterator.next()).getControlledProxy();
658 }
659 };
660 }
661
662 /*** Prepares an transaction to load the proxy-chain structure that is
663 * controlled by the called IProxyController.
664 */
665 public void prepareControlledLoad(IDataTransaction transaction)
666 {
667 Iterator it = this.getControlledProxies();
668 while(it.hasNext())
669 ((IControlledProxy)it.next()).prepareLoad(transaction);
670 }
671
672 /*** Prepares an transaction to store any changes made to the proxy-chain
673 * structure that is controlled by the called IProxyController.
674 */
675 public void prepareControlledStore(IDataTransaction transaction)
676 {
677 Iterator it = this.getControlledProxies();
678 while(it.hasNext())
679 ((IControlledProxy)it.next()).prepareStore(transaction);
680 }
681
682 /*** The finalizeTransaction method is called after a transaction have been
683 * fully completed. This method will always be called once after any call
684 * to prepareLoad or prepareStore.
685 */
686 public void finalizeTransaction(boolean successful)
687 {
688 Iterator it = this.getControlledProxies();
689 while(it.hasNext())
690 ((IControlledProxy)it.next()).finalizeTransaction(successful);
691 }
692
693 /*** Help method that initializes the data in an entity object based on its
694 * relation to the controller.
695 */
696 public void initializeEntity(IEntity entity, IEntityRelationPath relationPath)
697 {
698 if(mControlledProxyMap==null)
699 return;
700
701
702 }
703
704
705 public Object getData(String codeName)
706 {
707 if(mRemoteEntity!=null)
708 return mRemoteEntity.getData(codeName);
709 else
710 return null;
711 }
712
713
714 public void setData(String codeName, Object dataValue)
715 {
716 if(mRemoteEntity!=null)
717 mRemoteEntity.setData(codeName, dataValue);
718 }
719
720 public void setData(IDataProvider dataProvider)
721 {
722 if(mRemoteEntity!=null)
723 mRemoteEntity.setData(dataProvider);
724 }
725
726
727
728 /*** This method is called each time the remote entity object are replaced.
729 * The method does by default load all controlled proxies.
730 */
731 public void doOnRemoteChange()
732 {
733
734 this.loadControlledProxies(false);
735 }
736
737 protected boolean hasDirtyControlledProxies()
738 {
739 Iterator it = this.getControlledProxies();
740 while(it.hasNext())
741 if(((IControlledProxy)it.next()).isDirty())
742 return true;
743 return false;
744 }
745
746 protected void loadControlledProxies(boolean includeLocalRemote) throws DataServiceException
747 {
748 IDataTransaction transaction = this.getDataSource().getDataService().newTransaction();
749 if(includeLocalRemote)
750 transaction.addRefresh(this.getSourceEntity());
751 else if(this.getSourceEntity()==null)
752 return;
753
754
755 try
756 {
757 this.prepareControlledLoad(transaction);
758 AsyncTransactionHandler transactionHandler = AsyncTransactionHandler.create();
759 transactionHandler.setCallback(this, "loadControlledProxiesCallback");
760 if(!transaction.isEmpty())
761 transactionHandler.commit(transaction);
762 else
763 {
764 this.finalizeTransaction(true);
765 this.updateDirtyState();
766 }
767
768
769
770
771 }
772 catch(DataServiceException e)
773 {
774 this.finalizeTransaction(false);
775 Log.printError(this, "Store transaction failed in proxy controller "+this);
776 throw e;
777 }
778
779
780 }
781
782 public void loadControlledProxiesCallback(int status)
783 {
784 if (status == AsyncTransactionHandler.COMMIT_SUCCEEDED)
785 this.finalizeTransaction(true);
786 else
787 this.finalizeTransaction(false);
788
789 this.updateDirtyState();
790 }
791
792 protected void storeControlledProxies() throws DataServiceException
793 {
794 if(this.getSourceEntity()==null)
795 return;
796
797 IDataTransaction transaction = this.getDataSource().getDataService().newTransaction();
798 if(this.getSourceEntity().isDirty() || !this.getSourceEntity().isPersistent())
799 transaction.addStore(this.getSourceEntity());
800
801
802 try
803 {
804 this.prepareControlledStore(transaction);
805 transaction.commit();
806 this.finalizeTransaction(true);
807 }
808 catch(DataServiceException e)
809 {
810 this.finalizeTransaction(false);
811 Log.printError(this, "Store transaction failed in proxy controller "+this);
812 throw e;
813 }
814
815 this.updateDirtyState();
816 }
817
818 /*** Fires an EntityEvent with the provided operation type to all registered
819 * IEntityListener objects.
820 */
821 protected void fireOpPerformedEvent(int opType)
822 {
823 if(mEntityListener == null)
824 return;
825 else if(opType == EntityEvent.STORED)
826 mEntityListener.storePerformed(new EntityEvent(this, opType));
827 else if(opType == EntityEvent.DELETED)
828 mEntityListener.deletePerformed(new EntityEvent(this, opType));
829 else if(opType == EntityEvent.REFRESHED)
830 mEntityListener.refreshPerformed(new EntityEvent(this, opType));
831 }
832
833 /*** Fires an EntityChangeEvent specifying a data field change to all
834 * registered IEntityChangeListener objects.
835 */
836 protected void fireDataChangedEvent(IFieldDescriptor fieldDescriptor, Object oldValue, Object newValue)
837 {
838 if(mEntityChangeListener!=null)
839 mEntityChangeListener.dataChanged(new EntityChangeEvent(this, fieldDescriptor, oldValue, newValue));
840 }
841
842 /*** Fires an EntityChangeEvent specifying a status change to all registered
843 * IEntityChangeListener objects.
844 */
845 protected void fireStatusChangedEvent(int statusType, boolean newStatus)
846 {
847 if(mEntityChangeListener!=null)
848 mEntityChangeListener.statusChanged(new EntityChangeEvent(this, statusType, newStatus));
849 }
850
851 /*** Fires a ProxyEvent with the provided operation type to all registered
852 * IProxyListener objects.
853 */
854 protected void fireProxyEvent(int eventType)
855 {
856 if(mProxyListener==null)
857 return;
858 else if(eventType == ProxyEvent.CHANGED)
859 mProxyListener.remoteChanged(new ProxyEvent(this, eventType));
860 else if(eventType == ProxyEvent.EXPANDED)
861 mProxyListener.remoteExpanded(new ProxyEvent(this, eventType));
862 }
863
864 /*** Help method that returns the selections relay listener.
865 * Can be overriden to customize the listener. Note that the method is
866 * contracted to allways return the same listener instance for a given
867 * ProxyEntity instance.
868 */
869 protected RelayListener getRelayListener()
870 {
871 if(mRelayListener==null)
872 mRelayListener = new RelayListener();
873 return mRelayListener;
874 }
875
876 /*** Help method that returns the selections slave change listener.
877 * Can be overriden to customize the listener. Note that the method is
878 * contracted to allways return the same listener instance for a given
879 * ProxyEntity instance.
880 */
881 protected ControlledProxyListener getControlledProxyListener()
882 {
883 if(mControlledProxyListener==null)
884 mControlledProxyListener = new ControlledProxyListener();
885 return mControlledProxyListener;
886 }
887
888 protected void updateDirtyState()
889 {
890 boolean hasDirtySlaves = this.hasDirtyControlledProxies();
891 boolean hasDirtyRemote = mRemoteEntity!=null ? mRemoteEntity.isDirty() : false;
892 if(mHasDirtyControlledProxies!=hasDirtySlaves && !hasDirtyRemote)
893 this.fireStatusChangedEvent(IEntity.DIRTY, hasDirtySlaves || hasDirtyRemote);
894 mHasDirtyControlledProxies = hasDirtySlaves;
895 }
896
897
898
899 private class SlaveProxyItem
900 {
901
902 private IControlledProxy mControlledProxy;
903 private int mProxyUserCount;
904
905
906
907 public SlaveProxyItem(IControlledProxy proxy)
908 {
909 mControlledProxy = proxy;
910 mProxyUserCount = 1;
911 }
912
913
914
915 public IControlledProxy getControlledProxy()
916 {
917 return mControlledProxy;
918 }
919
920 public void addProxyUsers()
921 {
922 mProxyUserCount++;
923 }
924
925 public void decProxyUsers()
926 {
927 mProxyUserCount--;
928 }
929
930 public boolean hasProxyUsers()
931 {
932 return mProxyUserCount>0;
933 }
934 }
935
936 private class RelayListener implements IEntityListener, IEntityChangeListener, IProxyListener, java.io.Serializable
937 {
938
939 public void storePerformed(EntityEvent event)
940 {
941 if(mEntityListener!=null)
942 mEntityListener.storePerformed(event);
943 }
944
945 public void deletePerformed(EntityEvent event)
946 {
947 if(mEntityListener!=null)
948 mEntityListener.deletePerformed(event);
949 }
950
951 public void refreshPerformed(EntityEvent event)
952 {
953 if(mEntityListener!=null)
954 mEntityListener.refreshPerformed(event);
955 }
956
957
958 public void dataChanged(EntityChangeEvent event)
959 {
960 if(mEntityChangeListener!=null)
961 mEntityChangeListener.dataChanged(event);
962 }
963
964 public void statusChanged(EntityChangeEvent event)
965 {
966 if(mEntityChangeListener!=null)
967 mEntityChangeListener.statusChanged(event);
968 }
969
970
971 public void remoteChanged(ProxyEvent event)
972 {
973 doOnRemoteChange();
974
975 if(mProxyListener!=null)
976 mProxyListener.remoteChanged(event);
977 }
978
979 public void remoteExpanded(ProxyEvent event)
980 {
981 if(mProxyListener!=null)
982 mProxyListener.remoteExpanded(event);
983 }
984 }
985
986 protected class ControlledProxyListener implements IEntityChangeListener, ISelectionListener
987 {
988
989 public void dataChanged(EntityChangeEvent event)
990 {
991 }
992
993 public void statusChanged(EntityChangeEvent event)
994 {
995 updateDirtyState();
996 }
997
998
999 public void contentsChanged(SelectionEvent event)
1000 {
1001 }
1002
1003 public void entityAdded(SelectionEvent event)
1004 {
1005 updateDirtyState();
1006 }
1007
1008 public void entityRemoved(SelectionEvent event)
1009 {
1010 updateDirtyState();
1011 }
1012 }
1013 }