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