001    package org.apache.ojb.odmg.collections;
002    
003    /* Copyright 2002-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    import java.io.Serializable;
019    import java.util.AbstractSet;
020    import java.util.ArrayList;
021    import java.util.Iterator;
022    import java.util.List;
023    import java.util.Vector;
024    import java.util.Collection;
025    
026    import org.apache.ojb.broker.PBKey;
027    import org.apache.ojb.broker.PersistenceBroker;
028    import org.apache.ojb.broker.PersistenceBrokerAware;
029    import org.apache.ojb.broker.PersistenceBrokerException;
030    import org.apache.ojb.broker.ManageableCollection;
031    import org.apache.ojb.broker.core.ValueContainer;
032    import org.apache.ojb.broker.metadata.ClassDescriptor;
033    import org.apache.ojb.broker.metadata.FieldDescriptor;
034    import org.apache.ojb.broker.query.Criteria;
035    import org.apache.ojb.broker.query.Query;
036    import org.apache.ojb.broker.query.QueryByCriteria;
037    import org.apache.ojb.broker.util.logging.Logger;
038    import org.apache.ojb.broker.util.logging.LoggerFactory;
039    import org.apache.ojb.odmg.PBCapsule;
040    import org.apache.ojb.odmg.TransactionImpl;
041    import org.apache.ojb.odmg.TxManagerFactory;
042    import org.apache.ojb.odmg.RuntimeObject;
043    import org.apache.ojb.odmg.oql.OQLQueryImpl;
044    import org.odmg.DCollection;
045    import org.odmg.DList;
046    import org.odmg.DSet;
047    import org.odmg.ODMGRuntimeException;
048    import org.odmg.OQLQuery;
049    import org.odmg.Transaction;
050    
051    
052    /**
053     *
054     */
055    public class DSetImpl extends AbstractSet implements DSet, Serializable, PersistenceBrokerAware, ManageableCollection
056    {
057            private static final long serialVersionUID = -4459673364598652639L;
058    
059        private transient Logger log;
060    
061        private Integer id;
062        private List elements;
063    
064        private PBKey pbKey;
065    
066        /**
067         * Used by PB-Kernel to instantiate ManageableCollections
068         * FOR INTERNAL USE ONLY
069         */
070        public DSetImpl()
071        {
072            super();
073            elements = new ArrayList();
074    //        if(getTransaction() == null)
075    //        {
076    //            throw new TransactionNotInProgressException("Materialization of DCollection instances must be done" +
077    //                    " within a odmg-tx");
078    //        }
079            getPBKey();
080        }
081    
082        /**
083         * DSetImpl constructor comment.
084         */
085        public DSetImpl(PBKey pbKey)
086        {
087            this();
088            this.pbKey = pbKey;
089        }
090    
091        protected Logger getLog()
092        {
093            if (log == null)
094            {
095                log = LoggerFactory.getLogger(DSetImpl.class);
096            }
097            return log;
098        }
099    
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    }