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 }