View Javadoc

1   package org.apache.ojb.odmg.collections;
2   
3   /* Copyright 2002-2005 The Apache Software Foundation
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  import java.io.Serializable;
19  import java.util.AbstractSet;
20  import java.util.ArrayList;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Vector;
24  import java.util.Collection;
25  
26  import org.apache.ojb.broker.PBKey;
27  import org.apache.ojb.broker.PersistenceBroker;
28  import org.apache.ojb.broker.PersistenceBrokerAware;
29  import org.apache.ojb.broker.PersistenceBrokerException;
30  import org.apache.ojb.broker.ManageableCollection;
31  import org.apache.ojb.broker.core.ValueContainer;
32  import org.apache.ojb.broker.metadata.ClassDescriptor;
33  import org.apache.ojb.broker.metadata.FieldDescriptor;
34  import org.apache.ojb.broker.query.Criteria;
35  import org.apache.ojb.broker.query.Query;
36  import org.apache.ojb.broker.query.QueryByCriteria;
37  import org.apache.ojb.broker.util.logging.Logger;
38  import org.apache.ojb.broker.util.logging.LoggerFactory;
39  import org.apache.ojb.odmg.PBCapsule;
40  import org.apache.ojb.odmg.TransactionImpl;
41  import org.apache.ojb.odmg.TxManagerFactory;
42  import org.apache.ojb.odmg.RuntimeObject;
43  import org.apache.ojb.odmg.oql.OQLQueryImpl;
44  import org.odmg.DCollection;
45  import org.odmg.DList;
46  import org.odmg.DSet;
47  import org.odmg.ODMGRuntimeException;
48  import org.odmg.OQLQuery;
49  import org.odmg.Transaction;
50  
51  
52  /**
53   *
54   */
55  public class DSetImpl extends AbstractSet implements DSet, Serializable, PersistenceBrokerAware, ManageableCollection
56  {
57  	private static final long serialVersionUID = -4459673364598652639L;
58  
59      private transient Logger log;
60  
61      private Integer id;
62      private List elements;
63  
64      private PBKey pbKey;
65  
66      /**
67       * Used by PB-Kernel to instantiate ManageableCollections
68       * FOR INTERNAL USE ONLY
69       */
70      public DSetImpl()
71      {
72          super();
73          elements = new ArrayList();
74  //        if(getTransaction() == null)
75  //        {
76  //            throw new TransactionNotInProgressException("Materialization of DCollection instances must be done" +
77  //                    " within a odmg-tx");
78  //        }
79          getPBKey();
80      }
81  
82      /**
83       * DSetImpl constructor comment.
84       */
85      public DSetImpl(PBKey pbKey)
86      {
87          this();
88          this.pbKey = pbKey;
89      }
90  
91      protected Logger getLog()
92      {
93          if (log == null)
94          {
95              log = LoggerFactory.getLogger(DSetImpl.class);
96          }
97          return log;
98      }
99  
100     private DSetEntry prepareEntry(Object obj)
101     {
102         return new DSetEntry(this, obj);
103     }
104 
105     protected TransactionImpl getTransaction()
106     {
107         return TxManagerFactory.instance().getTransaction();
108     }
109 
110     protected boolean checkForOpenTransaction(TransactionImpl tx)
111     {
112         boolean result = false;
113         if(tx != null && tx.isOpen())
114         {
115             result = true;
116         }
117         return result;
118     }
119 
120     public PBKey getPBKey()
121     {
122         if(pbKey == null)
123         {
124             TransactionImpl tx = getTransaction();
125             if(tx != null && tx.isOpen())
126             {
127                 pbKey = tx.getBroker().getPBKey();
128             }
129         }
130         return pbKey;
131     }
132 
133     public void setPBKey(PBKey pbKey)
134     {
135         this.pbKey = pbKey;
136     }
137 
138     public boolean remove(Object o)
139     {
140         return super.remove(o);
141     }
142 
143     public boolean removeAll(Collection c)
144     {
145         return super.removeAll(c);
146     }
147 
148     public boolean add(Object o)
149     {
150         if (!this.contains(o))
151         {
152             DSetEntry entry = prepareEntry(o);
153             elements.add(entry);
154             // if we are in a transaction: get locks !
155             TransactionImpl tx = getTransaction();
156             if ((tx != null) && (tx.isOpen()))
157             {
158                 List regList = tx.getRegistrationList();
159                 RuntimeObject rt = new RuntimeObject(this, tx);
160                 tx.lockAndRegister(rt, Transaction.WRITE, false, regList);
161 
162                 rt = new RuntimeObject(o, tx);
163                 tx.lockAndRegister(rt, Transaction.READ, regList);
164 
165                 rt = new RuntimeObject(entry, tx, true);
166                 tx.lockAndRegister(rt, Transaction.WRITE, false, regList);
167             }
168             return true;
169         }
170         else
171         {
172             return false;
173         }
174     }
175 
176     /**
177      * Create a new <code>DSet</code> object that contains the elements of this
178      * collection minus the elements in <code>otherSet</code>.
179      * @param	otherSet	A set containing elements that should not be in the result set.
180      * @return	A newly created <code>DSet</code> instance that contains the elements
181      * of this set minus those elements in <code>otherSet</code>.
182      */
183     public DSet difference(DSet otherSet)
184     {
185         DSetImpl result = new DSetImpl(getPBKey());
186         Iterator iter = this.iterator();
187         while (iter.hasNext())
188         {
189             Object candidate = iter.next();
190             if (!otherSet.contains(candidate))
191             {
192                 result.add(candidate);
193             }
194         }
195         return result;
196     }
197 
198     /**
199      * Determines whether there is an element of the collection that evaluates to true
200      * for the predicate.
201      * @param	predicate	An OQL boolean query predicate.
202      * @return	True if there is an element of the collection that evaluates to true
203      * for the predicate, otherwise false.
204      * @exception	org.odmg.QueryInvalidException	The query predicate is invalid.
205      */
206     public boolean existsElement(String predicate) throws org.odmg.QueryInvalidException
207     {
208         DList results = (DList) this.query(predicate);
209         if (results == null || results.size() == 0)
210             return false;
211         else
212             return true;
213     }
214 
215     public List getElements()
216     {
217         return elements;
218     }
219 
220     public void setElements(List elements)
221     {
222         this.elements = elements;
223     }
224 
225     public Integer getId()
226     {
227         return id;
228     }
229 
230     /**
231      * Create a new <code>DSet</code> object that is the set intersection of this
232      * <code>DSet</code> object and the set referenced by <code>otherSet</code>.
233      * @param	otherSet	The other set to be used in the intersection operation.
234      * @return	A newly created <code>DSet</code> instance that contains the
235      * intersection of the two sets.
236      */
237     public DSet intersection(DSet otherSet)
238     {
239         DSet union = this.union(otherSet);
240         DSetImpl result = new DSetImpl(getPBKey());
241         Iterator iter = union.iterator();
242         while (iter.hasNext())
243         {
244             Object candidate = iter.next();
245             if (this.contains(candidate) && otherSet.contains(candidate))
246             {
247                 result.add(candidate);
248             }
249         }
250         return result;
251     }
252 
253     /**
254      * Returns an iterator over the elements in this collection.  There are no
255      * guarantees concerning the order in which the elements are returned
256      * (unless this collection is an instance of some class that provides a
257      * guarantee).
258      *
259      * @return an <tt>Iterator</tt> over the elements in this collection
260      */
261     public Iterator iterator()
262     {
263         return new DSetIterator(this);
264     }
265 
266     /**
267      * Determine whether this set is a proper subset of the set referenced by
268      * <code>otherSet</code>.
269      * @param	otherSet	Another set.
270      * @return True if this set is a proper subset of the set referenced by
271      * <code>otherSet</code>, otherwise false.
272      */
273     public boolean properSubsetOf(org.odmg.DSet otherSet)
274     {
275         return (this.size() > 0 && this.size() < otherSet.size() && this.subsetOf(otherSet));
276     }
277 
278     /**
279      * Determine whether this set is a proper superset of the set referenced by
280      * <code>otherSet</code>.
281      * @param	otherSet	Another set.
282      * @return True if this set is a proper superset of the set referenced by
283      * <code>otherSet</code>, otherwise false.
284      */
285     public boolean properSupersetOf(org.odmg.DSet otherSet)
286     {
287         return (otherSet.size() > 0 && otherSet.size() < this.size() && this.supersetOf(otherSet));
288     }
289 
290     /**
291      * Evaluate the boolean query predicate for each element of the collection and
292      * return a new collection that contains each element that evaluated to true.
293      * @param	predicate	An OQL boolean query predicate.
294      * @return	A new collection containing the elements that evaluated true for the predicate.
295      * @exception	org.odmg.QueryInvalidException	The query predicate is invalid.
296      */
297     public DCollection query(String predicate) throws org.odmg.QueryInvalidException
298     {
299         // 1.build complete OQL statement
300         String oql = "select all from java.lang.Object where " + predicate;
301         TransactionImpl tx = getTransaction();
302 
303         OQLQuery predicateQuery = tx.getImplementation().newOQLQuery();
304 
305         PBCapsule capsule = new PBCapsule(tx.getImplementation().getCurrentPBKey(), tx);
306         PersistenceBroker broker = capsule.getBroker();
307 
308         try
309         {
310             predicateQuery.create(oql);
311             Query pQ = ((OQLQueryImpl) predicateQuery).getQuery();
312             Criteria pCrit = pQ.getCriteria();
313 
314             Criteria allElementsCriteria = this.getPkCriteriaForAllElements(broker);
315             // join selection of elements with predicate criteria:
316             pCrit.addAndCriteria(allElementsCriteria);
317             Class clazz = this.getElementsExtentClass(broker);
318             Query q = new QueryByCriteria(clazz, pCrit);
319             if (log.isDebugEnabled()) log.debug(q.toString());
320             // 2. perfom query
321             return (DSetImpl) broker.getCollectionByQuery(DSetImpl.class, q);
322         }
323         catch (PersistenceBrokerException e)
324         {
325             throw new ODMGRuntimeException(e.getMessage());
326         }
327         finally
328         {
329             capsule.destroy();
330         }
331     }
332 
333     private Criteria getPkCriteriaForAllElements(PersistenceBroker broker)
334     {
335         try
336         {
337             Criteria crit = null;
338             for (int i = 0; i < elements.size(); i++)
339             {
340                 DListEntry entry = (DListEntry) elements.get(i);
341                 Object obj = entry.getRealSubject();
342                 ClassDescriptor cld = broker.getClassDescriptor(obj.getClass());
343 
344                 FieldDescriptor[] pkFields = cld.getPkFields();
345                 ValueContainer[] pkValues = broker.serviceBrokerHelper().getKeyValues(cld, obj);
346 
347                 Criteria criteria = new Criteria();
348                 for (int j = 0; j < pkFields.length; j++)
349                 {
350                     FieldDescriptor fld = pkFields[j];
351                     criteria.addEqualTo(fld.getPersistentField().getName(), pkValues[j].getValue());
352                 }
353 
354                 if (crit == null)
355                     crit = criteria;
356                 else
357                     crit.addOrCriteria(criteria);
358             }
359             return crit;
360         }
361         catch (PersistenceBrokerException e)
362         {
363             log.error(e);
364             return null;
365         }
366     }
367 
368     private Class getElementsExtentClass(PersistenceBroker broker) throws PersistenceBrokerException
369     {
370         // we ll have to compute the most general extent class here !!!
371         DListEntry entry = (DListEntry) elements.get(0);
372         Class elementsClass = entry.getRealSubject().getClass();
373         Class extentClass = broker.getTopLevelClass(elementsClass);
374         return extentClass;
375     }
376 
377 
378     /**
379      * Access all of the elements of the collection that evaluate to true for the
380      * provided query predicate.
381      * @param	predicate	An OQL boolean query predicate.
382      * @return	An iterator used to iterate over the elements that evaluated true for the predicate.
383      * @exception	org.odmg.QueryInvalidException	The query predicate is invalid.
384      */
385     public Iterator select(String predicate) throws org.odmg.QueryInvalidException
386     {
387         return this.query(predicate).iterator();
388     }
389 
390     /**
391      * Selects the single element of the collection for which the provided OQL query
392      * predicate is true.
393      * @param	predicate	An OQL boolean query predicate.
394      * @return The element that evaluates to true for the predicate. If no element
395      * evaluates to true, null is returned.
396      * @exception	org.odmg.QueryInvalidException	The query predicate is invalid.
397      */
398     public Object selectElement(String predicate) throws org.odmg.QueryInvalidException
399     {
400         return ((DList) this.query(predicate)).get(0);
401     }
402 
403     /**
404      * Sets the elements.
405      * @param elements The elements to set
406      */
407     public void setElements(Vector elements)
408     {
409         this.elements = elements;
410     }
411 
412     /**
413      * Sets the id.
414      * @param id The id to set
415      */
416     public void setId(Integer id)
417     {
418         this.id = id;
419     }
420 
421     /**
422      * Returns the number of elements in this collection.  If this collection
423      * contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
424      * <tt>Integer.MAX_VALUE</tt>.
425      *
426      * @return the number of elements in this collection
427      */
428     public int size()
429     {
430         return elements.size();
431     }
432 
433     /**
434      * Determine whether this set is a subset of the set referenced by <code>otherSet</code>.
435      * @param	otherSet	Another set.
436      * @return True if this set is a subset of the set referenced by <code>otherSet</code>,
437      * otherwise false.
438      */
439     public boolean subsetOf(DSet otherSet)
440     {
441         return otherSet.containsAll(this);
442     }
443 
444     /**
445      * Determine whether this set is a superset of the set referenced by <code>otherSet</code>.
446      * @param	otherSet	Another set.
447      * @return True if this set is a superset of the set referenced by <code>otherSet</code>,
448      * otherwise false.
449      */
450     public boolean supersetOf(DSet otherSet)
451     {
452         return this.containsAll(otherSet);
453     }
454 
455     /**
456      * Create a new <code>DSet</code> object that is the set union of this
457      * <code>DSet</code> object and the set referenced by <code>otherSet</code>.
458      * @param	otherSet	The other set to be used in the union operation.
459      * @return	A newly created <code>DSet</code> instance that contains the union of the two sets.
460      */
461     public DSet union(DSet otherSet)
462     {
463         DSetImpl result = new DSetImpl(getPBKey());
464         result.addAll(this);
465         result.addAll(otherSet);
466         return result;
467     }
468 
469 
470     //***************************************************************
471     // ManageableCollection interface
472     //***************************************************************
473 
474     /**
475      * add a single Object to the Collection. This method is used during reading Collection elements
476      * from the database. Thus it is is save to cast anObject to the underlying element type of the
477      * collection.
478      */
479     public void ojbAdd(Object anObject)
480     {
481         DSetEntry entry = prepareEntry(anObject);
482         entry.setPosition(elements.size());
483         elements.add(entry);
484     }
485 
486     /**
487      * adds a Collection to this collection. Used in reading Extents from the Database.
488      * Thus it is save to cast otherCollection to this.getClass().
489      */
490     public void ojbAddAll(ManageableCollection otherCollection)
491     {
492         // don't use this to avoid locking
493         // this.addAll((DListImpl) otherCollection);
494         Iterator it = otherCollection.ojbIterator();
495         while (it.hasNext())
496         {
497             ojbAdd(it.next());
498         }
499     }
500 
501     public void afterStore(PersistenceBroker broker) throws PersistenceBrokerException
502     {
503     }
504 
505     /**
506      * returns an Iterator over all elements in the collection. Used during store and delete Operations.
507      * If the implementor does not return an iterator over ALL elements, OJB cannot store and delete all elements properly.
508      */
509     public Iterator ojbIterator()
510     {
511         return this.iterator();
512     }
513 
514     //***************************************************************
515     // PersistenceBrokerAware interface
516     //***************************************************************
517 
518     /**
519      * prepare itself for persistence. Each DList entry generates an
520      * {@link org.apache.ojb.broker.Identity} for the wrapped persistent
521      * object.
522      */
523     public void beforeInsert(PersistenceBroker broker) throws PersistenceBrokerException
524     {
525 //        Iterator it = elements.iterator();
526 //        DSetEntry entry;
527 //        while (it.hasNext())
528 //        {
529 //            entry = (DSetEntry) it.next();
530 //            entry.prepareForPersistency(broker);
531 //        }
532     }
533 
534     /**
535      * noop
536      */
537     public void beforeUpdate(PersistenceBroker broker) throws PersistenceBrokerException
538     {
539     }
540 
541     /**
542      * noop
543      */
544     public void beforeDelete(PersistenceBroker broker) throws PersistenceBrokerException
545     {
546     }
547 
548     /**
549      * noop
550      */
551     public void afterUpdate(PersistenceBroker broker) throws PersistenceBrokerException
552     {
553     }
554 
555     /**
556      * noop
557      */
558     public void afterInsert(PersistenceBroker broker) throws PersistenceBrokerException
559     {
560     }
561 
562     /**
563      * noop
564      */
565     public void afterDelete(PersistenceBroker broker) throws PersistenceBrokerException
566     {
567     }
568 
569     /**
570      * noop
571      */
572     public void afterLookup(PersistenceBroker broker) throws PersistenceBrokerException
573     {
574     }
575 }