KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > genimen > djeneric > repository > DjAssociation


1 /*
2  * Copyright (c) 2001-2005 by Genimen BV (www.genimen.com) All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, is permitted provided that the following conditions are met: -
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer. - Redistributions in binary
8  * form must reproduce the above copyright notice, this list of conditions and
9  * the following disclaimer in the documentation and/or other materials
10  * provided with the distribution. - All advertising materials mentioning
11  * features or use of this software must display the following acknowledgment:
12  * "This product includes Djeneric." - Products derived from this software may
13  * not be called "Djeneric" nor may "Djeneric" appear in their names without
14  * prior written permission of Genimen BV. - Redistributions of any form
15  * whatsoever must retain the following acknowledgment: "This product includes
16  * Djeneric."
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL GENIMEN BV, DJENERIC.ORG, OR CONTRIBUTORS
22  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */

30 package com.genimen.djeneric.repository;
31
32 import java.util.ArrayList JavaDoc;
33
34 import com.genimen.djeneric.language.Messages;
35 import com.genimen.djeneric.repository.exceptions.CanNotDeleteException;
36 import com.genimen.djeneric.repository.exceptions.DjenericException;
37 import com.genimen.djeneric.repository.exceptions.ObjectNotDefinedException;
38 import com.genimen.djeneric.repository.exceptions.PropertyFormatException;
39
40 /**
41  * A DjAssociation defines an association between two objects: a master (the 1
42  * side of the association) and zero or more details. The association is based
43  * on the definition of a {@link DjRelation}in the model, and handles the
44  * retrieval of the 'detail instances' (You can regard an association as a
45  * specific instance of a relation)
46  *
47  * @author Wido Riezebos @created 22 mei 2002
48  */

49 public class DjAssociation
50 {
51   DjObject _master;
52   DjExtent _detailExtent;
53   DjRelation _rel;
54   ArrayList JavaDoc _synchronizationListeners = null;
55
56   // null == not loaded yet
57
DjList _loadedDetails = null;
58   boolean _specializationOfActualDetailExtent = false;
59
60   DjSession _session;
61
62   /**
63    * Constructor for the DjAssociation object
64    *
65    * @param session
66    * The session this association belongs to
67    * @param master
68    * The object serving as the master for this association
69    * @param rel
70    * The relation this association is based on
71    */

72   protected DjAssociation(DjSession session, DjObject master, DjRelation rel, DjExtent specificDetailExtent)
73   {
74     _session = session;
75     _master = master;
76     _detailExtent = specificDetailExtent;
77     _rel = rel;
78   }
79
80   public boolean isSpecialized()
81   {
82     return _rel.getDetailExtent() != _detailExtent;
83   }
84
85   /**
86    * Creates a new DjQueryByExample object for use in the detail extent
87    *
88    * @return The DjQueryByExample that can be used to query the detail extent
89    * extent
90    * @exception DjenericException
91    */

92   protected DjQueryByExample createNewQbe() throws DjenericException
93   {
94     return _session.createQueryByExample(_detailExtent);
95   }
96
97   /**
98    * Returns the restriction object (if defined in the model)
99    *
100    * @return The DjRestriction
101    * @exception DjenericException
102    */

103   public DjRestriction getRestriction() throws DjenericException
104   {
105     return getRelation().getRestriction();
106   }
107
108   /**
109    * Gets the name of the association (same as the name of relation it is based
110    * on)
111    *
112    * @return The name value
113    */

114   public String JavaDoc getName()
115   {
116     return _rel.getName();
117   }
118
119   /**
120    * Gets the loadedDetailArrayList attribute of the DjAssociation object (null
121    * if not loaded yet)
122    *
123    * @return The loadedDetailArrayList value
124    */

125   protected DjList getLoadedDetailArrayList()
126   {
127     return _loadedDetails;
128   }
129
130   /**
131    * Gets the session that owns the DjAssociation object
132    *
133    * @return The session value
134    */

135   protected DjSession getSession()
136   {
137     return _session;
138   }
139
140   /**
141    * Gets the object serving as master for the association
142    *
143    * @return The master value
144    */

145   public DjObject getMaster()
146   {
147     return _master;
148   }
149
150   /**
151    * Gets the extent of the master object
152    *
153    * @return The masterExtent value
154    */

155   public DjExtent getMasterExtent()
156   {
157     return _master.getExtent();
158   }
159
160   /**
161    * Gets the extent of the details
162    *
163    * @return The detail extent
164    */

165   public DjExtent getDetailExtent()
166   {
167     return _detailExtent;
168   }
169
170   /**
171    * Gets the relation this association is based on
172    *
173    * @return The relation
174    */

175   public DjRelation getRelation()
176   {
177     return _rel;
178   }
179
180   /**
181    * Returns a descriptive value for this association
182    *
183    * @return Description of the Return Value
184    */

185   public String JavaDoc toString()
186   {
187     String JavaDoc result;
188     if (_rel.isDetailsContained()) result = Messages.getString("DjAssociation.ByValue", getName());
189     else result = Messages.getString("DjAssociation.ByReference", getName());
190
191     return result;
192   }
193
194   /**
195    * Gets the description of the relation from the model
196    *
197    * @return The description value
198    */

199   public String JavaDoc getDescription()
200   {
201     return _rel.getDescription();
202   }
203
204   /**
205    * Creates a new detail object
206    *
207    * @return The new detail object
208    * @exception DjenericException
209    */

210   public DjObject createNew() throws DjenericException
211   {
212     DjObject po = _session.createObject(_detailExtent);
213
214     // Set the property that points to the master:
215
po.set(getRelation().getDetailProperty().getName(), getMaster());
216
217     // if (_loadedDetails != null)
218
// {
219
// _loadedDetails.add(po);
220
// }
221

222     return po;
223   }
224
225   /**
226    * Returns true if the details are loaded (requested before)
227    *
228    * @return Returns true if the details are loaded (requested before)
229    */

230   public boolean isDetailsLoaded()
231   {
232     return (_loadedDetails != null);
233   }
234
235   /**
236    * Returns true if there is at least one detail present.
237    *
238    * @return Returns true if there is at least one detail present
239    * @exception DjenericException
240    */

241   public boolean objectsExist() throws DjenericException
242   {
243     if (isDetailsLoaded() && getObjects().size() > 0) return true;
244     return detailsExist();
245   }
246
247   /**
248    * Returns a list of detail objects that are not persisted yet (new in
249    * session)
250    *
251    * @return List of new details
252    * @exception DjenericException
253    */

254   public DjList getNewObjectsFromSession() throws DjenericException
255   {
256     DjList lst = new DjList();
257     lst.setStoredTypeName(_detailExtent);
258     getSession().addNewDetailsFromSession(getMaster(), getRelation(), _detailExtent, lst);
259     return lst;
260   }
261
262   /**
263    * Returns all objects that are detail of the master
264    *
265    * @return All detail objects
266    * @exception DjenericException
267    */

268   public DjList getObjects() throws DjenericException
269   {
270     return getObjects(false);
271   }
272
273   public void load() throws DjenericException
274   {
275     getObjects(false);
276   }
277
278   /**
279    * Returns all objects that are detail of the master. If reloadFromDb is set
280    * to true the objects that were previously loaded will be refreshed to
281    * reflect the latest status in the persistent store.
282    *
283    * @param reloadFromDb
284    * If true cached objects are refreshed
285    * @return Returns all objects that are detail of the master
286    * @exception DjenericException
287    */

288   public DjList getObjects(boolean reloadFromDb) throws DjenericException
289   {
290     if (_loadedDetails == null || reloadFromDb)
291     {
292       loadDetails(null, reloadFromDb);
293     }
294     else _loadedDetails.purgeDeleted();
295
296     return _loadedDetails;
297   }
298
299   /**
300    * Returns all objects that are detail of the master and meet the QBE query
301    * criteria. If reloadFromDb is set to true the objects that were previously
302    * loaded will be refreshed to reflect the latest status in the persistent
303    * store.
304    *
305    * @param qbe
306    * Description of the Parameter
307    * @param reloadFromDb
308    * Description of the Parameter
309    * @return The objects value
310    * @exception DjenericException
311    * Djeneric exception
312    */

313   public DjList getObjects(DjQueryByExample qbe, boolean reloadFromDb) throws DjenericException
314   {
315     loadDetails(qbe, reloadFromDb);
316     return _loadedDetails;
317   }
318
319   /**
320    * Returns true if the details are contained and not referenced. This is used
321    * to determine if details should be deleted if a master is deleted. (Based
322    * on the definition of the relaion in the model)
323    *
324    * @return true is details are contained
325    */

326   protected boolean isDetailsContained()
327   {
328     return _rel.isDetailsContained();
329   }
330
331   /**
332    * Refreshes the detail list using the objects loaded by the session (NOT
333    * using the database) Use this method to reflect updates of the detail
334    * property pointing to the master (the FK column in the database)
335    *
336    */

337   public void synchronizeWithSession() throws DjenericException
338   {
339     // Is there something to synch at all?
340
boolean wasLoaded = isDetailsLoaded();
341
342     if (!wasLoaded) _loadedDetails = new DjList();
343     _loadedDetails.setStoredTypeName(getDetailExtent());
344
345     boolean needsSynch = false;
346     DjList added = null;
347     DjList removed = null;
348
349     // first add any new details to the list
350
DjList lst = new DjList();
351     lst.setStoredTypeName(_detailExtent);
352     getSession().addDetailsFromSession(getMaster(), getRelation(), _detailExtent, lst);
353
354     for (int i = 0; i < lst.size(); i++)
355     {
356       DjObject obj = (DjObject) lst.get(i);
357
358       if (!_loadedDetails.contains(obj))
359       {
360         _loadedDetails.add(lst.get(i));
361         needsSynch = true;
362         if (added == null)
363         {
364           added = new DjList();
365           added.setStoredTypeName(_detailExtent);
366         }
367
368         added.add(lst.get(i));
369       }
370     }
371
372     // now remove objects from the list that are not a detail of
373
// this master anymore because of an update of the property
374
// that pointed to the master
375
DjQueryByExample qbe = createNewQbe();
376     DjExtent detailExtent = getDetailExtent();
377     int detailPropIdx = detailExtent.getPropertyIndex(getRelation().getDetailProperty().getName());
378     long masterObjectId = getMaster().getObjectId();
379     qbe.setLong(detailPropIdx, masterObjectId);
380     int i = 0;
381     while (i < _loadedDetails.size())
382     {
383       DjObject obj = _loadedDetails.getDjenericObjectAt(i);
384
385       if (!qbe.match(obj) || obj.isMarkedForDelete())
386       {
387         if (removed == null)
388         {
389           removed = new DjList();
390           removed.setStoredTypeName(_detailExtent);
391         }
392         removed.add(_loadedDetails.getDjenericObjectAt(i));
393         _loadedDetails.remove(i);
394         needsSynch = true;
395       }
396       else i++;
397     }
398
399     // Set the loaded list back to 'not loaded' if there was no change
400
// and it was not loaded previously:
401
if (!wasLoaded && !needsSynch) _loadedDetails = null;
402     else if (needsSynch) fireNeedsSynch(added, removed);
403   }
404
405   protected void fireNeedsSynch(DjList added, DjList removed)
406   {
407     if (_synchronizationListeners == null) return;
408
409     if (added == null)
410     {
411       added = new DjList();
412       added.setStoredTypeName(_detailExtent);
413     }
414
415     if (removed == null)
416     {
417       removed = new DjList();
418       removed.setStoredTypeName(_detailExtent);
419     }
420
421     for (int i = 0; i < _synchronizationListeners.size(); i++)
422     {
423       DjSynchronizationListener lsnr = (DjSynchronizationListener) _synchronizationListeners.get(i);
424       lsnr.synchNeeded(this, added, removed);
425     }
426   }
427
428   public void addSynchronizationListener(DjSynchronizationListener lsnr)
429   {
430     if (_synchronizationListeners == null) _synchronizationListeners = new ArrayList JavaDoc();
431
432     if (!_synchronizationListeners.contains(lsnr)) _synchronizationListeners.add(lsnr);
433   }
434
435   public void removeSynchronizationListener(DjSynchronizationListener lsnr)
436   {
437     if (_synchronizationListeners == null) return;
438     _synchronizationListeners.remove(lsnr);
439   }
440
441   /**
442    * Loads all objects that are detail of the master and meet the QBE query
443    * criteria. If reloadFromDb is set to true the objects that were previously
444    * loaded will be refreshed to reflect the latest status in the persistent
445    * store.
446    *
447    * @param qbe
448    * Description of the Parameter
449    * @param reloadFromDb
450    * Description of the Parameter
451    * @exception DjenericException
452    * Djeneric exception
453    */

454   protected void loadDetails(DjQueryByExample qbe, boolean reloadFromDb) throws DjenericException
455   {
456     if (qbe == null) qbe = createNewQbe();
457     qbe.setLong(getDetailPropertyName(), getMaster().getObjectId());
458     _loadedDetails = getSession().getObjects(qbe, reloadFromDb);
459     _loadedDetails.setStoredTypeName(getDetailExtent());
460   }
461
462   /**
463    * Returns a cursor that retrieves all details. If reloadFromDb is set to
464    * true the objects that were previously loaded will be refreshed to reflect
465    * the latest status in the persistent store.
466    *
467    * @param forceReloadFromDB
468    * Description of the Parameter
469    * @return The objectsCursor value
470    * @exception DjenericException
471    * Djeneric exception
472    */

473   public DjCursor getObjectsCursor(boolean forceReloadFromDB) throws DjenericException
474   {
475     return getObjectsCursor(createNewQbe(), forceReloadFromDB);
476   }
477
478   /**
479    * Returns a cursor that retrieves all objects that are detail of the master
480    * and meet the QBE query criteria. If reloadFromDb is set to true the
481    * objects that were previously loaded will be refreshed to reflect the
482    * latest status in the persistent store.
483    *
484    * @param qbe
485    * Description of the Parameter
486    * @param forceReloadFromDB
487    * Description of the Parameter
488    * @return The objectsCursor value
489    * @exception DjenericException
490    * Djeneric exception
491    */

492   public DjCursor getObjectsCursor(DjQueryByExample qbe, boolean forceReloadFromDB) throws DjenericException
493   {
494     qbe.setLong(getDetailPropertyName(), getMaster().getObjectId());
495     return getSession().getObjectsCursor(qbe, forceReloadFromDB);
496   }
497
498   public String JavaDoc getDetailPropertyName()
499   {
500     return getRelation().getDetailProperty().getName();
501   }
502
503   /**
504    * Returns a cursor that retrieves all objects that are detail of the master
505    * and meet the QBE query criteria. If reloadFromDb is set to true the
506    * objects that were previously loaded will be refreshed to reflect the
507    * latest status in the persistent store.
508    *
509    * @param qbe
510    * Description of the Parameter
511    * @param forceReloadFromDB
512    * Description of the Parameter
513    * @return The objectsCursor value
514    * @exception DjenericException
515    * Djeneric exception
516    */

517   public DjCursor getObjectsCursor(DjOql oql, boolean forceReloadFromDB) throws DjenericException
518   {
519     String JavaDoc masterPropertyName = "_master" + getDetailPropertyName();
520
521     oql.addBaseRestriction(getDetailPropertyName() + " == :" + masterPropertyName);
522     oql.setParameter(masterPropertyName, getMaster().getObjectId());
523     return getSession().getObjectsCursor(oql, forceReloadFromDB);
524   }
525
526   /**
527    * Returns true if details exist in the persistent store (or there are new
528    * objects in the session)
529    *
530    * @return Description of the Return Value
531    * @exception DjenericException
532    * Djeneric exception
533    */

534   protected boolean detailsExist() throws DjenericException
535   {
536     DjQueryByExample qbe = createNewQbe();
537     qbe.setLong(getRelation().getDetailProperty().getName(), getMaster().getObjectId());
538     DjCursor cursor = null;
539     try
540     {
541       cursor = getSession().getObjectsCursor(qbe);
542       DjObject obj = null;
543       while ((obj = cursor.getNext()) != null)
544       {
545         if (!obj.isMarkedForDelete()) return true;
546       }
547       return false;
548     }
549     finally
550     {
551       if (cursor != null) cursor.close();
552     }
553   }
554
555   public boolean isOneToOne()
556   {
557     return getRelation().isOneToOne();
558   }
559
560   public boolean isOneToMany()
561   {
562     return getRelation().isOneToMany();
563   }
564
565   public void addAll(DjList list) throws DjenericException
566   {
567     for (int i = 0; i < list.size(); i++)
568     {
569       add(list.getDjenericObjectAt(i));
570     }
571   }
572
573   public void add(DjObject detail) throws DjenericException
574   {
575     if (!isDetailsLoaded()) load();
576
577     if (!getObjects().contains(detail))
578     {
579       if (isOneToOne())
580       {
581         removeCurrentDetails();
582       }
583       getObjects().add(detail);
584     }
585     detail.set(getDetailPropertyName(), getMaster());
586   }
587
588   public void remove(DjObject detail) throws DjenericException
589   {
590     if (!isDetailsLoaded()) return;
591     getObjects().remove(detail);
592   }
593
594   private void removeCurrentDetails() throws CanNotDeleteException, DjenericException
595   {
596     for (int i = 0; i < getObjects().size(); i++)
597     {
598       unlinkDetail(i);
599     }
600     getObjects().clear();
601   }
602
603   private void unlinkDetail(int i) throws DjenericException, CanNotDeleteException, ObjectNotDefinedException,
604       PropertyFormatException
605   {
606     if (isDetailsContained()) getObjects().getDjenericObjectAt(i).markForDelete();
607     else getObjects().getDjenericObjectAt(i).set(getDetailPropertyName(), null);
608   }
609
610   public void replaceDetail(int indexValue, DjObject detail) throws DjenericException
611   {
612     if (!getObjects().contains(detail))
613     {
614       unlinkDetail(indexValue);
615       getObjects().set(indexValue, detail);
616     }
617   }
618
619   public boolean isSpecializationOfActualDetailExtent()
620   {
621     return _specializationOfActualDetailExtent;
622   }
623
624   public void setSpecializationOfActualDetailExtent(boolean specializationOfActualDetailExtent)
625   {
626     _specializationOfActualDetailExtent = specializationOfActualDetailExtent;
627   }
628
629   public void markAllDeleted() throws CanNotDeleteException, DjenericException
630   {
631     getObjects().markAllDeleted();
632   }
633
634   public void markAllDeletedExcluding(ArrayList JavaDoc lst) throws CanNotDeleteException, DjenericException
635   {
636     getObjects().markAllDeletedExcluding(lst);
637   }
638
639 }
Popular Tags