Open Source Repository

Home /hibernate/hibernate-3.2.7.ga | Repository Home



org/hibernate/collection/PersistentBag.java
//$Id: PersistentBag.java 10739 2006-11-06 22:00:41Z [email protected] $
package org.hibernate.collection;

import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import org.hibernate.EntityMode;
import org.hibernate.HibernateException;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.loader.CollectionAliases;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.type.Type;

/**
 * An unordered, unkeyed collection that can contain the same element
 * multiple times. The Java collections API, curiously, has no <tt>Bag</tt>.
 * Most developers seem to use <tt>List</tt>s to represent bag semantics,
 * so Hibernate follows this practice.
 *
 @author Gavin King
 */
public class PersistentBag extends AbstractPersistentCollection implements List {

  protected List bag;

  public PersistentBag(SessionImplementor session) {
    super(session);
  }

  public PersistentBag(SessionImplementor session, Collection coll) {
    super(session);
    if (coll instanceof List) {
      bag = (Listcoll;
    }
    else {
      bag = new ArrayList();
      Iterator iter = coll.iterator();
      while iter.hasNext() ) {
        bag.additer.next() );
      }
    }
    setInitialized();
    setDirectlyAccessible(true);
  }

  public PersistentBag() {} //needed for SOAP libraries, etc

  public boolean isWrapper(Object collection) {
    return bag==collection;
  }
  public boolean empty() {
    return bag.isEmpty();
  }
  
  public Iterator entries(CollectionPersister persister) {
    return bag.iterator();
  }

  public Object readFrom(ResultSet rs, CollectionPersister persister, CollectionAliases descriptor, Object owner)
  throws HibernateException, SQLException {
    // note that if we load this collection from a cartesian product
    // the multiplicity would be broken ... so use an idbag instead
    Object element = persister.readElementrs, owner, descriptor.getSuffixedElementAliases(), getSession() ) ;
    if (element!=nullbag.add(element);
    return element;
  }

  public void beforeInitialize(CollectionPersister persister, int anticipatedSize) {
    this.bag = List persister.getCollectionType().instantiateanticipatedSize );
  }

  public boolean equalsSnapshot(CollectionPersister persisterthrows HibernateException {
    Type elementType = persister.getElementType();
    EntityMode entityMode = getSession().getEntityMode();
    List sn = (ListgetSnapshot();
    if sn.size()!=bag.size() ) return false;
    Iterator iter = bag.iterator();
    while iter.hasNext() ) {
      Object elt = iter.next();
      final boolean unequal = countOccurrences(elt, bag, elementType, entityMode!=
        countOccurrences(elt, sn, elementType, entityMode);
      if unequal return false;
    }
    return true;
  }

  public boolean isSnapshotEmpty(Serializable snapshot) {
    return ( (Collectionsnapshot ).isEmpty();
  }
  
  private int countOccurrences(Object element, List list, Type elementType, EntityMode entityMode
  throws HibernateException {
    Iterator iter = list.iterator();
    int result=0;
    while iter.hasNext() ) {
      if elementType.isSameelement, iter.next(), entityMode ) ) result++;
    }
    return result;
  }

  public Serializable getSnapshot(CollectionPersister persister)
  throws HibernateException {
    EntityMode entityMode = getSession().getEntityMode();
    ArrayList clonedList = new ArrayListbag.size() );
    Iterator iter = bag.iterator();
    while iter.hasNext() ) {
      clonedList.addpersister.getElementType().deepCopyiter.next(), entityMode, persister.getFactory() ) );
    }
    return clonedList;
  }

  public Collection getOrphans(Serializable snapshot, String entityNamethrows HibernateException {
      List sn = (Listsnapshot;
      return getOrphanssn, bag, entityName, getSession() );
  }


  public Serializable disassemble(CollectionPersister persister)
  throws HibernateException {

    int length = bag.size();
    Serializable[] result = new Serializable[length];
    for int i=0; i<length; i++ ) {
      result[i= persister.getElementType().disassemblebag.get(i), getSession()null );
    }
    return result;
  }

  public void initializeFromCache(CollectionPersister persister, Serializable disassembled, Object owner)
  throws HibernateException {
    Serializable[] array = (Serializable[]) disassembled;
    int size = array.length;
    beforeInitializepersister, size );
    for int i = 0; i < size; i++ ) {
      Object element = persister.getElementType().assemblearray[i], getSession(), owner );
      if element!=null ) {
        bag.addelement );
      }
    }
  }

  public boolean needsRecreate(CollectionPersister persister) {
    return !persister.isOneToMany();
  }


  // For a one-to-many, a <bag> is not really a bag;
  // it is *really* a set, since it can't contain the
  // same element twice. It could be considered a bug
  // in the mapping dtd that <bag> allows <one-to-many>.

  // Anyway, here we implement <set> semantics for a
  // <one-to-many> <bag>!

  public Iterator getDeletes(CollectionPersister persister, boolean indexIsFormulathrows HibernateException {
    //if ( !persister.isOneToMany() ) throw new AssertionFailure("Not implemented for Bags");
    Type elementType = persister.getElementType();
    EntityMode entityMode = getSession().getEntityMode();
    ArrayList deletes = new ArrayList();
    List sn = (ListgetSnapshot();
    Iterator olditer = sn.iterator();
    int i=0;
    while olditer.hasNext() ) {
      Object old = olditer.next();
      Iterator newiter = bag.iterator();
      boolean found = false;
      if bag.size()>i && elementType.isSameold, bag.get(i++), entityMode ) ) {
      //a shortcut if its location didn't change!
        found = true;
      }
      else {
        //search for it
        //note that this code is incorrect for other than one-to-many
        while newiter.hasNext() ) {
          if elementType.isSameold, newiter.next(), entityMode ) ) {
            found = true;
            break;
          }
        }
      }
      if (!founddeletes.add(old);
    }
    return deletes.iterator();
  }

  public boolean needsInserting(Object entry, int i, Type elemTypethrows HibernateException {
    //if ( !persister.isOneToMany() ) throw new AssertionFailure("Not implemented for Bags");
    List sn = (ListgetSnapshot();
    final EntityMode entityMode = getSession().getEntityMode();
    if sn.size()>i && elemType.isSamesn.get(i), entry, entityMode ) ) {
    //a shortcut if its location didn't change!
      return false;
    }
    else {
      //search for it
      //note that this code is incorrect for other than one-to-many
      Iterator olditer = sn.iterator();
      while olditer.hasNext() ) {
        Object old = olditer.next();
        if elemType.isSameold, entry, entityMode ) ) return false;
      }
      return true;
    }
  }
  
  public boolean isRowUpdatePossible() {
    return false;
  }

  public boolean needsUpdating(Object entry, int i, Type elemType) {
    //if ( !persister.isOneToMany() ) throw new AssertionFailure("Not implemented for Bags");
    return false;
  }

  /**
   @see java.util.Collection#size()
   */
  public int size() {
    return readSize() ? getCachedSize() : bag.size();
  }

  /**
   @see java.util.Collection#isEmpty()
   */
  public boolean isEmpty() {
    return readSize() ? getCachedSize()==: bag.isEmpty();
  }

  /**
   @see java.util.Collection#contains(Object)
   */
  public boolean contains(Object object) {
    Boolean exists = readElementExistence(object);
    return exists==null 
        bag.contains(object
        exists.booleanValue();
  }

  /**
   @see java.util.Collection#iterator()
   */
  public Iterator iterator() {
    read();
    return new IteratorProxybag.iterator() );
  }

  /**
   @see java.util.Collection#toArray()
   */
  public Object[] toArray() {
    read();
    return bag.toArray();
  }

  /**
   @see java.util.Collection#toArray(Object[])
   */
  public Object[] toArray(Object[] a) {
    read();
    return bag.toArray(a);
  }

  /**
   @see java.util.Collection#add(Object)
   */
  public boolean add(Object object) {
    if !isOperationQueueEnabled() ) {
      write();
      return bag.add(object);
    }
    else {
      queueOperationnew SimpleAdd(object) );
      return true;
    }
  }

  /**
   @see java.util.Collection#remove(Object)
   */
  public boolean remove(Object o) {
    initializetrue );
    if bag.remove) ) {
      dirty();
      return true;
    }
    else {
      return false;
    }
  }

  /**
   @see java.util.Collection#containsAll(Collection)
   */
  public boolean containsAll(Collection c) {
    read();
    return bag.containsAll(c);
  }

  /**
   @see java.util.Collection#addAll(Collection)
   */
  public boolean addAll(Collection values) {
    if values.size()==return false;
    if !isOperationQueueEnabled() ) {
      write();
      return bag.addAll(values);
    }
    else {
      Iterator iter = values.iterator();
      while iter.hasNext() ) {
        queueOperationnew SimpleAdditer.next() ) );
      }
      return values.size()>0;
    }
  }

  /**
   @see java.util.Collection#removeAll(Collection)
   */
  public boolean removeAll(Collection c) {
    if c.size()>) {
      initializetrue );
      if bag.removeAll) ) {
        dirty();
        return true;
      }
      else {
        return false;
      }
    }
    else {
      return false;
    }
  }

  /**
   @see java.util.Collection#retainAll(Collection)
   */
  public boolean retainAll(Collection c) {
    initializetrue );
    if bag.retainAll) ) {
      dirty();
      return true;
    }
    else {
      return false;
    }
  }

  /**
   @see java.util.Collection#clear()
   */
  public void clear() {
    if isClearQueueEnabled() ) {
      queueOperationnew Clear() );
    }
    else {
      initializetrue );
      if ! bag.isEmpty() ) {
        bag.clear();
        dirty();
      }
    }
  }

  public Object getIndex(Object entry, int i, CollectionPersister persister) {
    throw new UnsupportedOperationException("Bags don't have indexes");
  }

  public Object getElement(Object entry) {
    return entry;
  }

  public Object getSnapshotElement(Object entry, int i) {
    List sn = (ListgetSnapshot();
    return sn.get(i);
  }

  public int occurrences(Object o) {
    read();
    Iterator iter = bag.iterator();
    int result=0;
    while iter.hasNext() ) {
      if o.equalsiter.next() ) ) result++;
    }
    return result;
  }

  // List OPERATIONS:

  /**
   @see java.util.List#add(int, Object)
   */
  public void add(int i, Object o) {
    write();
    bag.add(i, o);
  }

  /**
   @see java.util.List#addAll(int, Collection)
   */
  public boolean addAll(int i, Collection c) {
    if c.size()>) {
      write();
      return bag.addAll(i, c);
    }
    else {
      return false;
    }
  }

  /**
   @see java.util.List#get(int)
   */
  public Object get(int i) {
    read();
    return bag.get(i);
  }

  /**
   @see java.util.List#indexOf(Object)
   */
  public int indexOf(Object o) {
    read();
    return bag.indexOf(o);
  }

  /**
   @see java.util.List#lastIndexOf(Object)
   */
  public int lastIndexOf(Object o) {
    read();
    return bag.lastIndexOf(o);
  }

  /**
   @see java.util.List#listIterator()
   */
  public ListIterator listIterator() {
    read();
    return new ListIteratorProxybag.listIterator() );
  }

  /**
   @see java.util.List#listIterator(int)
   */
  public ListIterator listIterator(int i) {
    read();
    return new ListIteratorProxybag.listIterator(i) );
  }

  /**
   @see java.util.List#remove(int)
   */
  public Object remove(int i) {
    write();
    return bag.remove(i);
  }

  /**
   @see java.util.List#set(int, Object)
   */
  public Object set(int i, Object o) {
    write();
    return bag.set(i, o);
  }

  /**
   @see java.util.List#subList(int, int)
   */
  public List subList(int start, int end) {
    read();
    return new ListProxybag.subList(start, end) );
  }

  public String toString() {
    read();
    return bag.toString();
  }

  /*public boolean equals(Object other) {
    read();
    return bag.equals(other);
  }

  public int hashCode(Object other) {
    read();
    return bag.hashCode();
  }*/

  public boolean entryExists(Object entry, int i) {
    return entry!=null;
  }

  /**
   * Bag does not respect the collection API and do an
   * JVM instance comparison to do the equals.
   * The semantic is broken not to have to initialize a
   * collection for a simple equals() operation.
   @see java.lang.Object#equals(java.lang.Object)
   */
  public boolean equals(Object obj) {
    return super.equals(obj);
  }

  /**
   @see java.lang.Object#hashCode()
   */
  public int hashCode() {
    return super.hashCode();
  }

  final class Clear implements DelayedOperation {
    public void operate() {
      bag.clear();
    }
    public Object getAddedInstance() {
      return null;
    }
    public Object getOrphan() {
      throw new UnsupportedOperationException("queued clear cannot be used with orphan delete");
    }
  }

  final class SimpleAdd implements DelayedOperation {
    private Object value;
    
    public SimpleAdd(Object value) {
      this.value = value;
    }
    public void operate() {
      bag.add(value);
    }
    public Object getAddedInstance() {
      return value;
    }
    public Object getOrphan() {
      return null;
    }
  }

}