001    package org.apache.ojb.odmg.collections;
002    
003    /* Copyright 2003-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.util.AbstractList;
019    import java.util.ArrayList;
020    import java.util.Iterator;
021    import java.util.List;
022    import java.util.ListIterator;
023    
024    import org.apache.commons.lang.builder.ToStringBuilder;
025    import org.apache.ojb.broker.ManageableCollection;
026    import org.apache.ojb.broker.OJBRuntimeException;
027    import org.apache.ojb.broker.PBKey;
028    import org.apache.ojb.broker.PersistenceBroker;
029    import org.apache.ojb.broker.PersistenceBrokerAware;
030    import org.apache.ojb.broker.PersistenceBrokerException;
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.DArray;
045    import org.odmg.DCollection;
046    import org.odmg.DList;
047    import org.odmg.ODMGRuntimeException;
048    import org.odmg.OQLQuery;
049    import org.odmg.QueryInvalidException;
050    import org.odmg.Transaction;
051    
052    
053    /**
054     *
055     * @author Thomas Mahler
056     * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
057     * @version $Id: DListImpl.java,v 1.1 2007-08-24 22:17:37 ewestfal Exp $
058     */
059    public class DListImpl extends AbstractList implements DList, DArray,
060            ManageableCollection, PersistenceBrokerAware
061    {
062        private static final long serialVersionUID = -9219943066614026526L;
063    
064        private transient Logger log;
065    
066        private Integer id;
067        private List elements;
068    
069        private PBKey pbKey;
070    
071        /**
072         * Used by PB-Kernel to instantiate ManageableCollections
073         * FOR INTERNAL USE ONLY
074         */
075        public DListImpl()
076        {
077            super();
078            elements = new ArrayList();
079    //        if(getTransaction() == null)
080    //        {
081    //            throw new TransactionNotInProgressException("Materialization of DCollection instances must be done" +
082    //                    " within a odmg-tx");
083    //        }
084            getPBKey();
085        }
086    
087        /**
088         * Used on odmg-level
089         */
090        public DListImpl(PBKey pbKey)
091        {
092            this();
093            this.pbKey = pbKey;
094        }
095    
096        protected Logger getLog()
097        {
098            if (log == null)
099            {
100                log = LoggerFactory.getLogger(DListImpl.class);
101            }
102            return log;
103        }
104    
105        private DListEntry prepareEntry(Object obj)
106        {
107            return new DListEntry(this, obj);
108        }
109    
110        protected TransactionImpl getTransaction()
111        {
112            return TxManagerFactory.instance().getTransaction();
113        }
114    
115        protected boolean checkForOpenTransaction(TransactionImpl tx)
116        {
117            boolean result = false;
118            if(tx != null && tx.isOpen())
119            {
120                result = true;
121            }
122            return result;
123        }
124    
125        public PBKey getPBKey()
126        {
127            if(pbKey == null)
128            {
129                TransactionImpl tx = getTransaction();
130                if(tx != null && tx.isOpen())
131                {
132                    pbKey = tx.getBroker().getPBKey();
133                }
134            }
135            return pbKey;
136        }
137    
138        public void setPBKey(PBKey pbKey)
139        {
140            this.pbKey = pbKey;
141        }
142    
143        /**
144         * Inserts the specified element at the specified position in this list
145         * (optional operation).  Shifts the element currently at that position
146         * (if any) and any subsequent elements to the right (adds one to their
147         * indices).
148         *
149         * @param index index at which the specified element is to be inserted.
150         * @param element element to be inserted.
151         *
152         * @throws UnsupportedOperationException if the <tt>add</tt> method is not
153         * supported by this list.
154         * @throws    ClassCastException if the class of the specified element
155         * prevents it from being added to this list.
156         * @throws    IllegalArgumentException if some aspect of the specified
157         * element prevents it from being added to this list.
158         * @throws    IndexOutOfBoundsException if the index is out of range
159         * (index &lt; 0 || index &gt; size()).
160         */
161        public void add(int index, Object element)
162        {
163            DListEntry entry = prepareEntry(element);
164            elements.add(index, entry);
165            // if we are in a transaction: acquire locks !
166            TransactionImpl tx = getTransaction();
167            if (checkForOpenTransaction(tx))
168            {
169                RuntimeObject rt = new RuntimeObject(this, tx);
170                List regList = tx.getRegistrationList();
171                tx.lockAndRegister(rt, Transaction.WRITE, false, regList);
172    
173                rt = new RuntimeObject(element, tx);
174                tx.lockAndRegister(rt, Transaction.READ, regList);
175    
176                rt = new RuntimeObject(entry, tx, true);
177                tx.lockAndRegister(rt, Transaction.WRITE, false, regList);
178            }
179    
180            // changing the position markers of entries:
181            int offset = 0;
182            try
183            {
184                offset = ((DListEntry) elements.get(index - 1)).getPosition();
185            }
186            catch (Exception ignored)
187            {
188            }
189            for (int i = offset; i < elements.size(); i++)
190            {
191                entry = (DListEntry) elements.get(i);
192                entry.setPosition(i);
193            }
194        }
195    
196        /**
197         * Removes the element at the specified position in this list (optional
198         * operation).  Shifts any subsequent elements to the left (subtracts one
199         * from their indices).  Returns the element that was removed from the
200         * list.<p>
201         *
202         * This implementation always throws an
203         * <tt>UnsupportedOperationException</tt>.
204         *
205         * @param index the index of the element to remove.
206         * @return the element previously at the specified position.
207         *
208         * @throws UnsupportedOperationException if the <tt>remove</tt> method is
209         *            not supported by this list.
210         * @throws IndexOutOfBoundsException if the specified index is out of
211         *            range (<tt>index &lt; 0 || index &gt;= size()</tt>).
212         */
213        public Object remove(int index)
214        {
215            DListEntry entry = (DListEntry) elements.get(index);
216            // if we are in a transaction: acquire locks !
217            TransactionImpl tx = getTransaction();
218            if (checkForOpenTransaction(tx))
219            {
220                tx.deletePersistent(new RuntimeObject(entry, tx));
221            }
222            elements.remove(index);
223            // changing the position markers of entries:
224            int offset = 0;
225            try
226            {
227                offset = ((DListEntry) elements.get(index)).getPosition();
228            }
229            catch (Exception ignored)
230            {
231            }
232            for (int i = offset; i < elements.size(); i++)
233            {
234                entry = (DListEntry) elements.get(i);
235                entry.setPosition(i);
236            }
237    
238            return entry.getRealSubject();
239        }
240    
241        /**
242         * Creates a new <code>DList</code> object that contains the contents of this
243         * <code>DList</code> object concatenated
244         * with the contents of the <code>otherList</code> object.
245         * @param   otherList       The list whose elements are placed at the end of the list
246         * returned by this method.
247         * @return  A new <code>DList</code> that is the concatenation of this list and
248         * the list referenced by <code>otherList</code>.
249         */
250        public DList concat(DList otherList)
251        {
252            DListImpl result = new DListImpl(pbKey);
253            result.addAll(this);
254            result.addAll(otherList);
255            return result;
256        }
257    
258        /**
259         * Determines whether there is an element of the collection that evaluates to true
260         * for the predicate.
261         * @param   predicate       An OQL boolean query predicate.
262         * @return  True if there is an element of the collection that evaluates to true
263         * for the predicate, otherwise false.
264         * @exception       org.odmg.QueryInvalidException  The query predicate is invalid.
265         */
266        public boolean existsElement(String predicate) throws org.odmg.QueryInvalidException
267        {
268            DList results = (DList) this.query(predicate);
269            if (results == null || results.size() == 0)
270                return false;
271            else
272                return true;
273        }
274    
275        /**
276         * Returns the element at the specified position in this list.
277         *
278         * @param index index of element to return.
279         * @return the element at the specified position in this list.
280         *
281         * @throws IndexOutOfBoundsException if the index is out of range (index
282         * &lt; 0 || index &gt;= size()).
283         */
284        public Object get(int index)
285        {
286            DListEntry entry = (DListEntry) elements.get(index);
287            return entry.getRealSubject();
288        }
289    
290        /**
291         * Insert the method's description here.
292         * Creation date: (10.02.2001 20:53:01)
293         * @return java.util.Vector
294         */
295        public List getElements()
296        {
297            return elements;
298        }
299    
300        /**
301         * Lazily return the Id, no point in precomputing it.
302         * @return int
303         */
304        public Integer getId()
305        {
306            return id;
307        }
308    
309        /**
310         * Returns an iterator over the elements in this collection.  There are no
311         * guarantees concerning the order in which the elements are returned
312         * (unless this collection is an instance of some class that provides a
313         * guarantee).
314         *
315         * @return an <tt>Iterator</tt> over the elements in this collection
316         */
317        public Iterator iterator()
318        {
319            return new DListIterator(this);
320        }
321    
322        /**
323         * Returns a list iterator of the elements in this list (in proper
324         * sequence).
325         *
326         * @return a list iterator of the elements in this list (in proper
327         * sequence).
328         */
329        public ListIterator listIterator()
330        {
331            return new DListIterator(this);
332        }
333    
334        /**
335         * Returns a list iterator of the elements in this list (in proper
336         * sequence), starting at the specified position in this list.  The
337         * specified index indicates the first element that would be returned by
338         * an initial call to the <tt>next</tt> method.  An initial call to
339         * the <tt>previous</tt> method would return the element with the
340         * specified index minus one.
341         *
342         * @param index index of first element to be returned from the
343         * list iterator (by a call to the <tt>next</tt> method).
344         * @return a list iterator of the elements in this list (in proper
345         * sequence), starting at the specified position in this list.
346         * @throws IndexOutOfBoundsException if the index is out of range (index
347         * &lt; 0 || index &gt; size()).
348         */
349        public ListIterator listIterator(int index)
350        {
351            return new DListIterator(this, index);
352        }
353    
354        private Criteria getPkCriteriaForAllElements(PersistenceBroker brokerForClass)
355        {
356            try
357            {
358                Criteria crit = null;
359                for (int i = 0; i < elements.size(); i++)
360                {
361                    DListEntry entry = (DListEntry) elements.get(i);
362                    Object obj = entry.getRealSubject();
363                    ClassDescriptor cld = brokerForClass.getClassDescriptor(obj.getClass());
364    
365                    FieldDescriptor[] pkFields = cld.getPkFields();
366                    ValueContainer[] pkValues = brokerForClass.serviceBrokerHelper().getKeyValues(cld, obj);
367    
368                    Criteria criteria = new Criteria();
369                    for (int j = 0; j < pkFields.length; j++)
370                    {
371                        FieldDescriptor fld = pkFields[j];
372                        criteria.addEqualTo(fld.getPersistentField().getName(), pkValues[j].getValue());
373                    }
374    
375                    if (crit == null)
376                        crit = criteria;
377                    else
378                        crit.addOrCriteria(criteria);
379                }
380                return crit;
381            }
382            catch (PersistenceBrokerException e)
383            {
384                return null;
385            }
386        }
387    
388        private Class getElementsExtentClass(PersistenceBroker brokerForClass) throws PersistenceBrokerException
389        {
390            // we ll have to compute the most general extent class here !!!
391            DListEntry entry = (DListEntry) elements.get(0);
392            Class elementsClass = entry.getRealSubject().getClass();
393            Class extentClass = brokerForClass.getTopLevelClass(elementsClass);
394            return extentClass;
395        }
396    
397        /**
398         * Evaluate the boolean query predicate for each element of the collection and
399         * return a new collection that contains each element that evaluated to true.
400         * @param   predicate       An OQL boolean query predicate.
401         * @return  A new collection containing the elements that evaluated true for the predicate.
402         * @exception       org.odmg.QueryInvalidException  The query predicate is invalid.
403         */
404        public DCollection query(String predicate) throws QueryInvalidException
405        {
406            // 1.build complete OQL statement
407            String oql = "select all from java.lang.Object where " + predicate;
408            TransactionImpl tx = getTransaction();
409            if (tx == null) throw new QueryInvalidException("Need running transaction to do query");
410    
411            OQLQuery predicateQuery = tx.getImplementation().newOQLQuery();
412            predicateQuery.create(oql);
413            Query pQ = ((OQLQueryImpl) predicateQuery).getQuery();
414            Criteria pCrit = pQ.getCriteria();
415    
416            PBCapsule handle = new PBCapsule(pbKey, tx);
417            DList result;
418            try
419            {
420                PersistenceBroker broker = handle.getBroker();
421                Criteria allElementsCriteria = this.getPkCriteriaForAllElements(broker);
422                // join selection of elements with predicate criteria:
423                allElementsCriteria.addAndCriteria(pCrit);
424    
425                Class clazz = null;
426                try
427                {
428                    clazz = this.getElementsExtentClass(broker);
429                }
430                catch (PersistenceBrokerException e)
431                {
432                    getLog().error(e);
433                    throw new ODMGRuntimeException(e.getMessage());
434                }
435                Query q = new QueryByCriteria(clazz, allElementsCriteria);
436                if (getLog().isDebugEnabled()) getLog().debug(q.toString());
437    
438                result = null;
439                try
440                {
441                    result = (DList) broker.getCollectionByQuery(DListImpl.class, q);
442                }
443                catch (PersistenceBrokerException e)
444                {
445                    getLog().error("Query failed", e);
446                    throw new OJBRuntimeException(e);
447                }
448            }
449            finally
450            {
451                // cleanup stuff
452                if (handle != null) handle.destroy();
453            }
454    
455            // 3. return resulting collection
456            return result;
457    
458        }
459    
460        public int hashCode()
461        {
462            int hashCode = 1;
463            Iterator it = elements.iterator();
464            while (it.hasNext())
465            {
466                Object obj = it.next();
467                hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode());
468            }
469            return hashCode;
470        }
471    
472        public String toString()
473        {
474            ToStringBuilder buf = new ToStringBuilder(this);
475            buf.append("id", id);
476            buf.append("pbKey", pbKey);
477            buf.append("[containing elements: ");
478            Iterator it = elements.iterator();
479            while (it.hasNext())
480            {
481                Object obj = it.next();
482                buf.append(obj != null ? obj.toString() : null);
483            }
484            buf.append("]");
485            return buf.toString();
486        }
487    
488        /**
489         * Access all of the elements of the collection that evaluate to true for the
490         * provided query predicate.
491         * @param   predicate       An OQL boolean query predicate.
492         * @return  An iterator used to iterate over the elements that evaluated true for the predicate.
493         * @exception       org.odmg.QueryInvalidException  The query predicate is invalid.
494         */
495        public Iterator select(String predicate) throws org.odmg.QueryInvalidException
496        {
497            return this.query(predicate).iterator();
498        }
499    
500        /**
501         * Selects the single element of the collection for which the provided OQL query
502         * predicate is true.
503         * @param   predicate       An OQL boolean query predicate.
504         * @return The element that evaluates to true for the predicate. If no element
505         * evaluates to true, null is returned.
506         * @exception       org.odmg.QueryInvalidException  The query predicate is invalid.
507         */
508        public Object selectElement(String predicate) throws org.odmg.QueryInvalidException
509        {
510            return ((DList) this.query(predicate)).get(0);
511        }
512    
513        /**
514         * Returns the number of elements in this collection.  If this collection
515         * contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
516         * <tt>Integer.MAX_VALUE</tt>.
517         *
518         * @return the number of elements in this collection
519         */
520        public int size()
521        {
522            return elements.size();
523        }
524    
525        /**
526         * add a single Object to the Collection. This method is used during reading Collection elements
527         * from the database. Thus it is is save to cast anObject to the underlying element type of the
528         * collection.
529         */
530        public void ojbAdd(Object anObject)
531        {
532            DListEntry entry = prepareEntry(anObject);
533            entry.setPosition(elements.size());
534            elements.add(entry);
535        }
536    
537        /**
538         * adds a Collection to this collection. Used in reading Extents from the Database.
539         * Thus it is save to cast otherCollection to this.getClass().
540         */
541        public void ojbAddAll(ManageableCollection otherCollection)
542        {
543            // don't use this to avoid locking
544            // this.addAll((DListImpl) otherCollection);
545            Iterator it = otherCollection.ojbIterator();
546            while (it.hasNext())
547            {
548                ojbAdd(it.next());
549            }
550        }
551    
552        public void afterStore(PersistenceBroker broker) throws PersistenceBrokerException
553        {
554        }
555    
556        /**
557         * returns an Iterator over all elements in the collection. Used during store and delete Operations.
558         * If the implementor does not return an iterator over ALL elements, OJB cannot store and delete all elements properly.
559         */
560        public Iterator ojbIterator()
561        {
562            return this.iterator();
563        }
564    
565        /**
566         * Resize the array to have <code>newSize</code> elements.
567         * @param   newSize The new size of the array.
568         */
569        public void resize(int newSize)
570        {
571        }
572    
573        /**
574         * Sets the elements.
575         * @param elements The elements to set
576         */
577        public void setElements(List elements)
578        {
579            this.elements = elements;
580        }
581    
582        /**
583         * Sets the id.
584         * @param id The id to set
585         */
586        public void setId(Integer id)
587        {
588            this.id = id;
589        }
590    
591    
592        //***************************************************************
593        // PersistenceBrokerAware interface
594        //***************************************************************
595    
596        /**
597         * prepare itself for persistence. Each DList entry generates an
598         * {@link org.apache.ojb.broker.Identity} for the wrapped persistent
599         * object.
600         */
601        public void beforeInsert(PersistenceBroker broker) throws PersistenceBrokerException
602        {
603    //        Iterator it = elements.iterator();
604    //        DListEntry entry;
605    //        while (it.hasNext())
606    //        {
607    //            entry = (DListEntry) it.next();
608    //            entry.prepareForPersistency(broker);
609    //        }
610        }
611    
612        /**
613         * noop
614         */
615        public void beforeUpdate(PersistenceBroker broker) throws PersistenceBrokerException
616        {
617        }
618    
619        /**
620         * noop
621         */
622        public void beforeDelete(PersistenceBroker broker) throws PersistenceBrokerException
623        {
624        }
625    
626        /**
627         * noop
628         */
629        public void afterUpdate(PersistenceBroker broker) throws PersistenceBrokerException
630        {
631        }
632    
633        /**
634         * noop
635         */
636        public void afterInsert(PersistenceBroker broker) throws PersistenceBrokerException
637        {
638        }
639    
640        /**
641         * noop
642         */
643        public void afterDelete(PersistenceBroker broker) throws PersistenceBrokerException
644        {
645        }
646    
647        /**
648         * noop
649         */
650        public void afterLookup(PersistenceBroker broker) throws PersistenceBrokerException
651        {
652        }
653    }