package org.apache.ojb.odmg.collections;
/* Copyright 2002-2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import org.apache.ojb.broker.ManageableCollection;
import org.apache.ojb.broker.OJBRuntimeException;
import org.apache.ojb.broker.PBKey;
import org.apache.ojb.broker.PersistenceBroker;
import org.apache.ojb.broker.PersistenceBrokerException;
import org.apache.ojb.broker.core.ValueContainer;
import org.apache.ojb.broker.metadata.ClassDescriptor;
import org.apache.ojb.broker.metadata.FieldDescriptor;
import org.apache.ojb.broker.metadata.fieldaccess.PersistentFieldFactory;
import org.apache.ojb.broker.query.Criteria;
import org.apache.ojb.broker.query.Query;
import org.apache.ojb.broker.query.QueryByCriteria;
import org.apache.ojb.broker.util.logging.Logger;
import org.apache.ojb.broker.util.logging.LoggerFactory;
import org.apache.ojb.odmg.PBCapsule;
import org.apache.ojb.odmg.TransactionImpl;
import org.apache.ojb.odmg.TxManagerFactory;
import org.apache.ojb.odmg.oql.OQLQueryImpl;
import org.odmg.DArray;
import org.odmg.DCollection;
import org.odmg.DList;
import org.odmg.ODMGRuntimeException;
import org.odmg.OQLQuery;
import org.odmg.Transaction;
import java.util.Vector;
import java.util.Iterator;
import java.util.ListIterator;
/**
* Insert the type's description here.
* Creation date: (10.02.2001 20:50:26)
* @author Thomas Mahler
*/
public class DListImpl extends java.util.AbstractList implements DList, DArray,
ManageableCollection, java.io.Serializable
{
private static final long serialVersionUID = -835415723401679339L;
private Logger log = LoggerFactory.getLogger(DListImpl.class);
/**
* decide whether we can defer ID generation based on the type of persistentField we are using
* performance hit to generate these ID's is to much if you never use them.
*/
private int id = -1;
private Vector elements;
private int size;
/**
* PBKey this DList belongs to.
*/
protected PBKey pbKey;
/**
* DListImpl constructor comment.
*/
public DListImpl()
{
super();
elements = new Vector();
this.size = 0;
if (!PersistentFieldFactory.usesAccessorsAndMutators())
id = generateNewId();
}
/**
* DListImpl constructor comment.
*/
public DListImpl(PBKey pbKey)
{
super();
elements = new Vector();
this.size = 0;
this.pbKey = pbKey;
if (!PersistentFieldFactory.usesAccessorsAndMutators())
id = generateNewId();
}
/**
* DListImpl constructor comment.
*/
public DListImpl(int theId, int theSize)
{
super();
elements = new Vector();
id = theId;
this.size = theSize;
}
public PBKey getPBKey()
{
return pbKey;
}
public void setPBKey(PBKey pbKey)
{
this.pbKey = pbKey;
}
/**
* Inserts the specified element at the specified position in this list
* (optional operation). Shifts the element currently at that position
* (if any) and any subsequent elements to the right (adds one to their
* indices).
*
* @param index index at which the specified element is to be inserted.
* @param element element to be inserted.
*
* @throws UnsupportedOperationException if the <tt>add</tt> method is not
* supported by this list.
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this list.
* @throws IllegalArgumentException if some aspect of the specified
* element prevents it from being added to this list.
* @throws IndexOutOfBoundsException if the index is out of range
* (index < 0 || index > size()).
*/
public void add(int index, Object element)
{
this.size++;
DListEntry entry = new DListEntry(this, element);
elements.add(index, entry);
// if we are in a transaction: acquire locks !
Transaction tx = TxManagerFactory.instance().getTransaction();
if ((tx != null) && (tx.isOpen()))
{
tx.lock(this, Transaction.WRITE);
tx.lock(entry, Transaction.WRITE);
tx.lock(element, Transaction.READ);
}
// changing the position markers of entries:
int offset = 0;
try
{
offset = ((DListEntry) elements.get(index - 1)).getPosition();
}
catch (Exception ignored)
{
}
for (int i = offset; i < elements.size(); i++)
{
entry = (DListEntry) elements.get(i);
entry.setPosition(i);
}
}
/**
* Removes the element at the specified position in this list (optional
* operation). Shifts any subsequent elements to the left (subtracts one
* from their indices). Returns the element that was removed from the
* list.<p>
*
* This implementation always throws an
* <tt>UnsupportedOperationException</tt>.
*
* @param index the index of the element to remove.
* @return the element previously at the specified position.
*
* @throws UnsupportedOperationException if the <tt>remove</tt> method is
* not supported by this list.
* @throws IndexOutOfBoundsException if the specified index is out of
* range (<tt>index < 0 || index >= size()</tt>).
*/
public Object remove(int index)
{
DListEntry entry = (DListEntry) elements.get(index);
// if we are in a transaction: acquire locks !
TransactionImpl tx = TxManagerFactory.instance().getTransaction();
if ((tx != null) && (tx.isOpen()))
{
tx.markDelete(entry);
}
this.size--;
elements.remove(index);
// changing the position markers of entries:
int offset = 0;
try
{
offset = ((DListEntry) elements.get(index)).getPosition();
}
catch (Exception ignored)
{
}
for (int i = offset; i < elements.size(); i++)
{
entry = (DListEntry) elements.get(i);
entry.setPosition(i);
}
return entry.getRealSubject();
}
/**
* Creates a new <code>DList</code> object that contains the contents of this
* <code>DList</code> object concatenated
* with the contents of the <code>otherList</code> object.
* @param otherList The list whose elements are placed at the end of the list
* returned by this method.
* @return A new <code>DList</code> that is the concatenation of this list and
* the list referenced by <code>otherList</code>.
*/
public DList concat(org.odmg.DList otherList)
{
DListImpl result = new DListImpl(pbKey);
result.addAll(this);
result.addAll(otherList);
return result;
}
/**
* Determines whether there is an element of the collection that evaluates to true
* for the predicate.
* @param predicate An OQL boolean query predicate.
* @return True if there is an element of the collection that evaluates to true
* for the predicate, otherwise false.
* @exception org.odmg.QueryInvalidException The query predicate is invalid.
*/
public boolean existsElement(String predicate) throws org.odmg.QueryInvalidException
{
DList results = (DList) this.query(predicate);
if (results == null || results.size() == 0)
return false;
else
return true;
}
/**
* return a unique id for PK
*/
protected int generateNewId()
{
PBCapsule capsule = new PBCapsule(null, TxManagerFactory.instance().getTransaction());
try
{
PersistenceBroker broker = capsule.getBroker();
FieldDescriptor fld = broker.getClassDescriptor(this.getClass()).getAutoIncrementFields()[0];
Integer val = (Integer) broker.serviceSequenceManager().getUniqueValue(fld);
int result = val.intValue();
return result;
}
catch (Exception e)
{
log.error("Generation of new id failed", e);
throw new PersistenceBrokerException(e);
}
finally
{
capsule.destroy();
}
}
/**
* Returns the element at the specified position in this list.
*
* @param index index of element to return.
* @return the element at the specified position in this list.
*
* @throws IndexOutOfBoundsException if the index is out of range (index
* < 0 || index >= size()).
*/
public Object get(int index)
{
DListEntry entry = (DListEntry) elements.get(index);
return entry.getRealSubject();
}
/**
* Insert the method's description here.
* Creation date: (10.02.2001 20:53:01)
* @return java.util.Vector
*/
public Vector getElements()
{
return elements;
}
/**
* Lazily return the Id, no point in precomputing it.
* @return int
*/
public int getId()
{
if (id == -1)
{
id = generateNewId();
}
return id;
}
/**
* Returns an iterator over the elements in this collection. There are no
* guarantees concerning the order in which the elements are returned
* (unless this collection is an instance of some class that provides a
* guarantee).
*
* @return an <tt>Iterator</tt> over the elements in this collection
*/
public Iterator iterator()
{
return new DListIterator(this);
}
/**
* Returns a list iterator of the elements in this list (in proper
* sequence).
*
* @return a list iterator of the elements in this list (in proper
* sequence).
*/
public ListIterator listIterator()
{
return new DListIterator(this);
}
/**
* Returns a list iterator of the elements in this list (in proper
* sequence), starting at the specified position in this list. The
* specified index indicates the first element that would be returned by
* an initial call to the <tt>next</tt> method. An initial call to
* the <tt>previous</tt> method would return the element with the
* specified index minus one.
*
* @param index index of first element to be returned from the
* list iterator (by a call to the <tt>next</tt> method).
* @return a list iterator of the elements in this list (in proper
* sequence), starting at the specified position in this list.
* @throws IndexOutOfBoundsException if the index is out of range (index
* < 0 || index > size()).
*/
public ListIterator listIterator(int index)
{
return new DListIterator(this, index);
}
private Criteria getPkCriteriaForAllElements(PersistenceBroker brokerForClass)
{
try
{
Criteria crit = null;
for (int i = 0; i < elements.size(); i++)
{
DListEntry entry = (DListEntry) elements.get(i);
Object obj = entry.getRealSubject();
ClassDescriptor cld = brokerForClass.getClassDescriptor(obj.getClass());
FieldDescriptor[] pkFields = cld.getPkFields();
ValueContainer[] pkValues = brokerForClass.serviceBrokerHelper().getKeyValues(cld, obj);
Criteria criteria = new Criteria();
for (int j = 0; j < pkFields.length; j++)
{
FieldDescriptor fld = pkFields[j];
criteria.addEqualTo(fld.getPersistentField().getName(), pkValues[j].getValue());
}
if (crit == null)
crit = criteria;
else
crit.addOrCriteria(criteria);
}
return crit;
}
catch (PersistenceBrokerException e)
{
return null;
}
}
private Class getElementsExtentClass(PersistenceBroker brokerForClass) throws PersistenceBrokerException
{
// we ll have to compute the most general extent class here !!!
DListEntry entry = (DListEntry) elements.get(0);
Class elementsClass = entry.getRealSubject().getClass();
Class extentClass = brokerForClass.getTopLevelClass(elementsClass);
return extentClass;
}
/**
* Evaluate the boolean query predicate for each element of the collection and
* return a new collection that contains each element that evaluated to true.
* @param predicate An OQL boolean query predicate.
* @return A new collection containing the elements that evaluated true for the predicate.
* @exception org.odmg.QueryInvalidException The query predicate is invalid.
*/
public DCollection query(String predicate) throws org.odmg.QueryInvalidException
{
// 1.build complete OQL statement
String oql = "select all from java.lang.Object where " + predicate;
/* @todo Use a ObjectFactory to instantiate OQLQuery? */
OQLQuery predicateQuery = new OQLQueryImpl(pbKey);
predicateQuery.create(oql);
Query pQ = ((OQLQueryImpl) predicateQuery).getQuery();
Criteria pCrit = pQ.getCriteria();
PBCapsule handle = new PBCapsule(pbKey, TxManagerFactory.instance().getTransaction());
DList result;
try
{
PersistenceBroker broker = handle.getBroker();
Criteria allElementsCriteria = this.getPkCriteriaForAllElements(broker);
// join selection of elements with predicate criteria:
allElementsCriteria.addAndCriteria(pCrit);
Class clazz = null;
try
{
clazz = this.getElementsExtentClass(broker);
}
catch (PersistenceBrokerException e)
{
throw new ODMGRuntimeException(e.getMessage());
}
Query q = new QueryByCriteria(clazz, allElementsCriteria);
if (log.isDebugEnabled()) log.debug(q.toString());
result = null;
try
{
result = (DListImpl) broker.getCollectionByQuery(DListImpl.class, q);
}
catch (PersistenceBrokerException e)
{
log.error("Query failed", e);
throw new OJBRuntimeException(e);
}
}
finally
{
// cleanup stuff
if (handle != null) handle.destroy();
}
// 3. return resulting collection
return result;
}
/**
* Access all of the elements of the collection that evaluate to true for the
* provided query predicate.
* @param predicate An OQL boolean query predicate.
* @return An iterator used to iterate over the elements that evaluated true for the predicate.
* @exception org.odmg.QueryInvalidException The query predicate is invalid.
*/
public Iterator select(String predicate) throws org.odmg.QueryInvalidException
{
return this.query(predicate).iterator();
}
/**
* Selects the single element of the collection for which the provided OQL query
* predicate is true.
* @param predicate An OQL boolean query predicate.
* @return The element that evaluates to true for the predicate. If no element
* evaluates to true, null is returned.
* @exception org.odmg.QueryInvalidException The query predicate is invalid.
*/
public Object selectElement(String predicate) throws org.odmg.QueryInvalidException
{
return ((DList) this.query(predicate)).get(0);
}
/**
* Returns the number of elements in this collection. If this collection
* contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
* <tt>Integer.MAX_VALUE</tt>.
*
* @return the number of elements in this collection
*/
public int size()
{
return elements.size();
}
/**
* add a single Object to the Collection. This method is used during reading Collection elements
* from the database. Thus it is is save to cast anObject to the underlying element type of the
* collection.
*/
public void ojbAdd(java.lang.Object anObject)
{
this.size++;
DListEntry entry = new DListEntry(this, anObject);
entry.setPosition(elements.size());
elements.add(entry);
}
/**
* adds a Collection to this collection. Used in reading Extents from the Database.
* Thus it is save to cast otherCollection to this.getClass().
*/
public void ojbAddAll(org.apache.ojb.broker.ManageableCollection otherCollection)
{
// don't use this to avoid locking
// this.addAll((DListImpl) otherCollection);
Iterator it = otherCollection.ojbIterator();
while (it.hasNext())
{
ojbAdd(it.next());
}
}
public void afterStore(PersistenceBroker broker) throws PersistenceBrokerException
{
}
/**
* returns an Iterator over all elements in the collection. Used during store and delete Operations.
* If the implementor does not return an iterator over ALL elements, OJB cannot store and delete all elements properly.
*/
public java.util.Iterator ojbIterator()
{
return this.iterator();
}
/**
* Resize the array to have <code>newSize</code> elements.
* @param newSize The new size of the array.
*/
public void resize(int newSize)
{
}
/**
* Sets the elements.
* @param elements The elements to set
*/
public void setElements(Vector elements)
{
this.elements = elements;
}
/**
* Sets the id.
* @param id The id to set
*/
public void setId(int id)
{
this.id = id;
}
/**
* Gets the size.
* @return Returns a int
*/
public int getSize()
{
return size;
}
/**
* Sets the size.
* @param size The size to set
*/
public void setSize(int size)
{
this.size = size;
}
}