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
28 /*** <Description for ControledProxySelection>
29 *
30 * @author Dennis Zikovic
31 * @version 1.00
32 *
33 *//*
34 *
35 * WHEN WHO WHY & WHAT
36 * -----------------------------------------------------------------------------
37 * 2002-04-22 Dennis Zikovic Creation
38 */
39 public class ControlledProxySelection extends ProxySelection implements IControlledProxy
40 {
41
42
43
44 private IProxyController mControlEntityProxy;
45 private IEntityRelationPath mRelationPath;
46 private ActionQueue mEditActionQueue;
47
48
49
50 /*** Creates new ControledSelectionProxy. Note protected scope the class
51 * should normally be instantiated by a call to object impementing the
52 * IProxyController interface and the getControlledProxy method.
53 */
54 protected ControlledProxySelection(IProxyController controller, IEntityDescriptor selectionDescriptor)
55 {
56 this(controller, selectionDescriptor, null);
57 }
58
59 /*** Creates new ControledSelectionProxy. Note protected scope the class
60 * should normally be instantiated by a call to object impementing the
61 * IProxyController interface and the getControlledProxy method.
62 * Provided path should be directed from controller to slave.
63 */
64 protected ControlledProxySelection(IProxyController controller, IEntityDescriptor selectionDescriptor, IEntityRelationPath relationPath)
65 {
66 super(selectionDescriptor);
67 mEditActionQueue = new ActionQueue();
68
69
70 if(relationPath!=null && !relationPath.isRelatedByPath(controller.getEntityDescriptor(), selectionDescriptor))
71 throw new InvalidRelationException("The relation path is not valid for the descriptors "+controller.getEntityDescriptor()+" and "+selectionDescriptor);
72
73
74 mControlEntityProxy = controller;
75 if(relationPath!=null)
76 mRelationPath = relationPath;
77 else
78 mRelationPath = this.createRelationPath();
79
80
81 ControllListener listener = new ControllListener();
82 mControlEntityProxy.addProxyListener(listener);
83 mControlEntityProxy.addEntityChangeListener(listener);
84
85
86 this.addEntityChangeListener(new IEntityChangeListener()
87 {
88 public void dataChanged(EntityChangeEvent event)
89 {
90 }
91
92 public void statusChanged(EntityChangeEvent event)
93 {
94 if(event.getStatusType()==IEntity.DIRTY)
95 {
96 if(event.getSourceEntity().isDirty())
97 registerChange(event.getSourceEntity());
98 else if(event.getSourceEntity().isPersistent())
99 mEditActionQueue.clear((IUnaryEntityAction)event.getSourceEntity().getEntityDescriptor().getAction(IEntityDescriptor.STORE_ACTION), event.getSourceEntity());
100 }
101 }
102 });
103
104
105 this.loadRemoteSelection();
106 }
107
108
109
110 /*** Return true if any (one or more) of the collections's contained
111 * entities has the DIRTY flag set to true that is have unsaved changes.
112 */
113 public boolean isDirty()
114 {
115 return super.isDirty() || !mEditActionQueue.isEmpty();
116 }
117
118 /*** Stores all contained entities that have the DIRTY flag set to true.
119 * The entities are stored in a single transaction meaning that if one
120 * store fails then all fails.
121 */
122 public void storeAll()
123 {
124
125 ITransactionEntityAction queued = null;
126 ITransactionEntityAction store = (ITransactionEntityAction)this.getEntityDescriptor().getAction(IEntityDescriptor.STORE_ACTION);
127 Iterator it = mEditActionQueue.getActions();
128 while(it.hasNext())
129 {
130 queued = (ITransactionEntityAction)it.next();
131 if(queued.getEntityDescriptor()==store)
132 it.remove();
133 }
134
135
136 for(int j=0; j<this.size(); j++)
137 if(this.getEntity(j).isPersistent())
138 this.registerChange(this.getEntity(j));
139 }
140
141 /*** Deletes all contained entities. The entities are deleted in a single
142 * transaction meaning that if one delete fails then all fails.
143 * USE THIS METHOD WITH CAUTION.
144 */
145 public void deleteAll()
146 {
147
148 mEditActionQueue.clear((ITransactionEntityAction)this.getEntityDescriptor().getAction(IEntityDescriptor.STORE_ACTION));
149
150
151 for(int j=0; j<this.size(); j++)
152 if(this.getEntity(j).isPersistent())
153 this.registerRemove(this.getEntity(j));
154 }
155
156 /*** Performs a refresh based on the current controller. All queued edit
157 * operations are cleared.
158 */
159 public void refreshAll()
160 {
161 mEditActionQueue.clear();
162 this.loadRemoteSelection();
163 }
164
165 /*** Adds the provided IEntity object to the end of selection object.
166 * If an entity with the same identity already exists in the selection the
167 * requeat is ignored and false is returned.
168 */
169 public boolean addEntity(IEntity entity)
170 {
171 boolean ok = super.addEntity(entity);
172 if(ok)
173 this.registerAdd(entity);
174 return ok;
175 }
176
177 /*** Adds the provided IEntity object to at the specified index in the
178 * selection object. If an entity with the same identity already exists
179 * in the selection the requeat is ignored and false is returned.
180 */
181 public boolean addEntity(int index, IEntity entity)
182 {
183 boolean ok = super.addEntity(index, entity);
184 if(ok)
185 this.registerAdd(entity);
186 return ok;
187 }
188
189 /*** Mutation method that removes the provided entity from the selection.
190 * Returns true if the entity was found and removed otherwise false is
191 * returned.
192 */
193 public boolean removeEntity(IEntity entity)
194 {
195 boolean ok = super.removeEntity(entity);
196 if(ok)
197 this.registerRemove(entity);
198 return ok;
199 }
200
201 /*** Mutation method that removes the indexed entity from the selection.
202 * Returns the indexed entity if it was found and removed otherwise null
203 * is returned. The entities are not effecte in any other way.
204 */
205 public IEntity removeEntity(int index)
206 {
207 IEntity entity = super.removeEntity(index);
208 if(entity!=null)
209 this.registerRemove(entity);
210 return entity;
211 }
212
213
214
215 public IProxyController getController()
216 {
217 return mControlEntityProxy;
218 }
219
220 public IEntityRelationPath getRelationPath()
221 {
222 return mRelationPath;
223 }
224
225 public boolean isEditable()
226 {
227 return mRelationPath.getRelationCount()==1 || mRelationPath.getRelationCount()==2 &&
228 mRelationPath.getFirstRelation().getRelatedEntityDescriptor(mRelationPath.getFirstEntityDescriptor()).getEntityType()==IEntityDescriptor.LINK_ENTITY;
229 }
230
231 public void prepareLoad(IDataTransaction transaction)
232 {
233 if(mControlEntityProxy==null || mControlEntityProxy.isEmpty())
234 this.clear();
235 else
236 {
237
238 ISelection selection = new Selection(this.getEntityDescriptor());
239
240 if(mRelationPath.getRelationCount()==1)
241 {
242 Qualifier directQualifier = Relations.makeRelationQualifier(mControlEntityProxy, mRelationPath.getRelation(0));
243
244
245 transaction.addLoadSelection(this.getEntityDescriptor(), directQualifier, selection);
246 }
247 else
248 {
249 IFieldDescriptor[] targetFieldDescriptors = new IFieldDescriptor[mRelationPath.getRelation(0).getFieldCount()];
250 Iterator iter = mRelationPath.getRelation(0).getTargetFieldDescriptors();
251 int c = 0;
252 while (iter.hasNext())
253 targetFieldDescriptors[c++] = (IFieldDescriptor) iter.next();
254 Qualifier qualifier = Relations.makeFieldQualifier(targetFieldDescriptors, mControlEntityProxy);
255
256 transaction.addLoadSelection(this.getEntityDescriptor(), qualifier, mRelationPath, selection);
257 }
258
259 this.setRemoteSelection(selection);
260 }
261 }
262
263 public void prepareStore(IDataTransaction transaction)
264 {
265
266 if(mRelationPath.getRelationCount()==1 && this.isDirty() && !mControlEntityProxy.isPersistent())
267 {
268 for(int j=0; j<this.size(); j++)
269 if(!this.getEntity(j).isPersistent())
270 Relations.setReferenceData(this.getEntity(j), mControlEntityProxy, mRelationPath.getRelation(0));
271 }
272
273 mEditActionQueue.prepareTransaction(transaction);
274 }
275
276 /*** The finalizeTransaction method is called after a transaction have been
277 * fully completed. This method will always be called once after any call
278 * to prepareLoad or prepareStore.
279 */
280 public void finalizeTransaction(boolean successful)
281 {
282 if(successful)
283 mEditActionQueue.clear();
284
285
286
287
288
289 if(this.isDirty())
290 {
291 for(int j=0; j<this.size(); j++)
292 if(!this.getEntity(j).isPersistent())
293 this.registerAdd(this.getEntity(j));
294 }
295 }
296
297
298
299 protected boolean isMasterRelationLinked()
300 {
301 if(mRelationPath.getRelationCount()==2)
302 return mRelationPath.getLastRelation().getRelatedEntityDescriptor(mRelationPath.getLastEntityDescriptor()).getEntityType()==IEntityDescriptor.LINK_ENTITY;
303 else
304 return false;
305 }
306
307 protected IEntity getLinkEntity(IEntity entity)
308 {
309 IEntity linkEntity = null;
310 if(mRelationPath.getRelationCount()==2)
311 {
312 IEntityDescriptor linkDescriptor = mRelationPath.getLastRelation().getRelatedEntityDescriptor(mRelationPath.getLastEntityDescriptor());
313 }
314 return linkEntity;
315 }
316
317 protected IEntity makeLinkEntity(IEntity entity)
318 {
319 IEntity linkEntity = null;
320 if(mRelationPath.getRelationCount()==2)
321 {
322 IEntityDescriptor linkDescriptor = mRelationPath.getLastRelation().getRelatedEntityDescriptor(mRelationPath.getLastEntityDescriptor());
323 if(linkDescriptor.getEntityType()==IEntityDescriptor.LINK_ENTITY)
324 {
325 linkEntity = linkDescriptor.createEntity();
326
327 Relations.setReferenceData(linkEntity, mControlEntityProxy, mRelationPath.getRelation(0));
328 Relations.setReferenceData(linkEntity, entity, mRelationPath.getRelation(1));
329 }
330 }
331 return linkEntity;
332 }
333
334 protected void registerAdd(IEntity entity)
335 {
336 if(!this.isEditable())
337 throw new UnsupportedOperationException("RegisterAdd called on non editable proxy!");
338
339 boolean wasDirty = this.isDirty();
340
341
342
343
344 if(mRelationPath.getRelationCount()==1)
345 Relations.setReferenceData(entity, mControlEntityProxy, mRelationPath.getRelation(0));
346 if(!entity.isPersistent() || entity.isDirty())
347 {
348 ITransactionEntityAction action = (ITransactionEntityAction)this.getEntityDescriptor().getAction(IEntityDescriptor.STORE_ACTION);
349 mEditActionQueue.addAction(action, entity);
350 Log.print(this, "Store action queued on "+entity);
351 }
352
353
354 IEntity linkEntity = this.makeLinkEntity(entity);
355 if(linkEntity!=null)
356 {
357 ITransactionEntityAction action = (ITransactionEntityAction)linkEntity.getEntityDescriptor().getAction(IEntityDescriptor.STORE_ACTION);
358 mEditActionQueue.addAction(action, linkEntity);
359 Log.print(this, "Store action queued on link "+linkEntity);
360 }
361
362
363 if(wasDirty!=this.isDirty())
364 this.fireStatusChangedEvent(entity, IEntity.DIRTY, this.isDirty());
365 }
366
367 protected void registerChange(IEntity entity)
368 {
369 if(!this.isEditable())
370 throw new UnsupportedOperationException("RegisterChange called on non editable proxy!");
371
372 boolean wasDirty = this.isDirty();
373
374 if(entity.isDirty() && entity.isPersistent())
375 {
376 ITransactionEntityAction action = (ITransactionEntityAction)this.getEntityDescriptor().getAction(IEntityDescriptor.STORE_ACTION);
377 mEditActionQueue.addAction(action, entity);
378 Log.print(this, "Store action queued on "+entity);
379 }
380
381
382 if(wasDirty!=this.isDirty())
383 this.fireStatusChangedEvent(entity, IEntity.DIRTY, this.isDirty());
384 }
385
386 protected void registerRemove(IEntity entity)
387 {
388 if(!this.isEditable())
389 throw new UnsupportedOperationException("RegisterRemove called on non editable proxy!");
390
391 boolean wasDirty = this.isDirty();
392
393 IEntity referencedEntity = entity;
394 if(this.isMasterRelationLinked())
395 {
396 entity = this.makeLinkEntity(entity);
397 entity.refresh();
398 }
399 if(entity.isPersistent())
400 {
401
402 ITransactionEntityAction action = (ITransactionEntityAction)entity.getEntityDescriptor().getAction(IEntityDescriptor.DELETE_ACTION);
403 mEditActionQueue.addAction(action, entity);
404 Log.print(this, "Delete action queued on "+entity);
405 }
406 else
407 {
408 mEditActionQueue.clear(null, entity);
409 mEditActionQueue.clear(null, referencedEntity);
410 }
411
412
413 if(wasDirty!=this.isDirty())
414 this.fireStatusChangedEvent(entity, IEntity.DIRTY, this.isDirty());
415 }
416
417 protected void loadRemoteSelection()
418 {
419 IDataTransaction transaction = this.getEntityDescriptor().getDataSourceDescriptor().getDefaultDataSource().getDataService().newTransaction();
420 this.prepareLoad(transaction);
421 if(!transaction.isEmpty());
422 transaction.commit();
423 }
424
425 /*** This method is called each time the view entity object are replaced.
426 * It is called prior to doOnDataChange and does does by default clears
427 * the selection if the master is set to "empty".
428 */
429 protected void doOnControllerChange()
430 {
431 if((mControlEntityProxy instanceof IProxyEntity) && ((IProxyEntity)mControlEntityProxy).getSourceEntity()==null)
432 this.clear();
433 }
434
435 /*** This method is called each time any data in the viewed entity data are
436 * changed. It is called emidiately after doOnEntityChange and does nothing
437 * by default.
438 */
439 protected void doOnControllerDataChange(IFieldDescriptor fieldDescriptor)
440 {
441 }
442
443 /*** Protected help method responsible for creating the relation path
444 * between the master and slave descriptor. The method is only called
445 * if no relation path was provided in the object construction. By default
446 * i creates a new EntityRelationPath with no provided waypoint by it can
447 * be overrriden for more customized bahaviour.
448 */
449 protected IEntityRelationPath createRelationPath()
450 {
451 return EntityRelationPath.create(mControlEntityProxy.getEntityDescriptor(), this.getEntityDescriptor());
452 }
453
454
455
456 protected class ControllListener implements IEntityChangeListener, IProxyListener
457 {
458
459 public void dataChanged(EntityChangeEvent event)
460 {
461 doOnControllerDataChange(event.getFieldDescriptor());
462 }
463
464 public void statusChanged(EntityChangeEvent event)
465 {
466 }
467
468
469 public void remoteChanged(ProxyEvent event)
470 {
471 doOnControllerChange();
472 doOnControllerDataChange(null);
473 }
474
475 public void remoteExpanded(ProxyEvent event)
476 {
477 doOnControllerDataChange(null);
478 }
479 }
480 }