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 < 0 || index > 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 < 0 || index >= 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 * < 0 || index >= 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 * < 0 || index > 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 }