package org.hibernate.type;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import org.hibernate.EntityMode;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.CollectionEntry;
import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.MarkerObject;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.relational.Size;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import org.jboss.logging.Logger;
import org.dom4j.Element;
import org.dom4j.Node;
public abstract class CollectionType extends AbstractType implements AssociationType {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, CollectionType.class.getName());
private static final Object NOT_NULL_COLLECTION = new MarkerObject( "NOT NULL COLLECTION" );
public static final Object UNFETCHED_COLLECTION = new MarkerObject( "UNFETCHED COLLECTION" );
private final TypeFactory.TypeScope typeScope;
private final String role;
private final String foreignKeyPropertyName;
private final boolean isEmbeddedInXML;
@Deprecated
public CollectionType(TypeFactory.TypeScope typeScope, String role, String foreignKeyPropertyName, boolean isEmbeddedInXML) {
this.typeScope = typeScope;
this.role = role;
this.foreignKeyPropertyName = foreignKeyPropertyName;
this.isEmbeddedInXML = isEmbeddedInXML;
}
public CollectionType(TypeFactory.TypeScope typeScope, String role, String foreignKeyPropertyName) {
this.typeScope = typeScope;
this.role = role;
this.foreignKeyPropertyName = foreignKeyPropertyName;
this.isEmbeddedInXML = true;
}
@Override
public boolean isEmbeddedInXML() {
return isEmbeddedInXML;
}
public String getRole() {
return role;
}
public Object indexOf(Object collection, Object element) {
throw new UnsupportedOperationException( "generic collections don't have indexes" );
}
public boolean contains(Object collection, Object childObject, SessionImplementor session) {
Iterator elems = getElementsIterator( collection, session );
while ( elems.hasNext() ) {
Object element = elems.next();
if ( element instanceof HibernateProxy ) {
LazyInitializer li = ( (HibernateProxy) element ).getHibernateLazyInitializer();
if ( !li.isUninitialized() ) element = li.getImplementation();
}
if ( element == childObject ) return true;
}
return false;
}
@Override
public boolean isCollectionType() {
return true;
}
@Override
public final boolean isEqual(Object x, Object y) {
return x == y
|| ( x instanceof PersistentCollection && ( (PersistentCollection) x ).wasInitialized() && ( (PersistentCollection) x ).isWrapper( y ) )
|| ( y instanceof PersistentCollection && ( (PersistentCollection) y ).wasInitialized() && ( (PersistentCollection) y ).isWrapper( x ) );
}
@Override
public int compare(Object x, Object y) {
return 0;
}
@Override
public int getHashCode(Object x) {
throw new UnsupportedOperationException( "cannot doAfterTransactionCompletion lookups on collections" );
}
public abstract PersistentCollection instantiate(SessionImplementor session, CollectionPersister persister, Serializable key);
@Override
public Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner) throws SQLException {
return nullSafeGet( rs, new String[] { name }, session, owner );
}
@Override
public Object nullSafeGet(ResultSet rs, String[] name, SessionImplementor session, Object owner)
throws HibernateException, SQLException {
return resolve( null, session, owner );
}
@Override
public final void nullSafeSet(PreparedStatement st, Object value, int index, boolean[] settable,
SessionImplementor session) throws HibernateException, SQLException {
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index,
SessionImplementor session) throws HibernateException, SQLException {
}
@Override
public int[] sqlTypes(Mapping session) throws MappingException {
return ArrayHelper.EMPTY_INT_ARRAY;
}
@Override
public Size[] dictatedSizes(Mapping mapping) throws MappingException {
return new Size[] { LEGACY_DICTATED_SIZE };
}
@Override
public Size[] defaultSizes(Mapping mapping) throws MappingException {
return new Size[] { LEGACY_DEFAULT_SIZE };
}
@Override
public int getColumnSpan(Mapping session) throws MappingException {
return 0;
}
@Override
public String toLoggableString(Object value, SessionFactoryImplementor factory)
throws HibernateException {
if ( value == null ) {
return "null";
}
else if ( !Hibernate.isInitialized( value ) ) {
return "<uninitialized>";
}
else {
return renderLoggableString( value, factory );
}
}
protected String renderLoggableString(Object value, SessionFactoryImplementor factory) throws HibernateException {
final List<String> list = new ArrayList<String>();
Type elemType = getElementType( factory );
Iterator itr = getElementsIterator( value );
while ( itr.hasNext() ) {
list.add( elemType.toLoggableString( itr.next(), factory ) );
}
return list.toString();
}
@Override
public Object deepCopy(Object value, SessionFactoryImplementor factory)
throws HibernateException {
return value;
}
@Override
public String getName() {
return getReturnedClass().getName() + '(' + getRole() + ')';
}
public Iterator getElementsIterator(Object collection, SessionImplementor session) {
return getElementsIterator(collection);
}
protected Iterator getElementsIterator(Object collection) {
return ( (Collection) collection ).iterator();
}
@Override
public boolean isMutable() {
return false;
}
@Override
public Serializable disassemble(Object value, SessionImplementor session, Object owner)
throws HibernateException {
final Serializable key = getKeyOfOwner(owner, session);
if (key==null) {
return null;
}
else {
return getPersister(session)
.getKeyType()
.disassemble( key, session, owner );
}
}
@Override
public Object assemble(Serializable cached, SessionImplementor session, Object owner)
throws HibernateException {
if (cached==null) {
return null;
}
else {
final Serializable key = (Serializable) getPersister(session)
.getKeyType()
.assemble( cached, session, owner);
return resolveKey( key, session, owner );
}
}
private boolean isOwnerVersioned(SessionImplementor session) throws MappingException {
return getPersister( session ).getOwnerEntityPersister().isVersioned();
}
private CollectionPersister getPersister(SessionImplementor session) {
return session.getFactory().getCollectionPersister( role );
}
@Override
public boolean isDirty(Object old, Object current, SessionImplementor session)
throws HibernateException {
return super.isDirty( old, current, session );
}
@Override
public boolean isDirty(Object old, Object current, boolean[] checkable, SessionImplementor session)
throws HibernateException {
return isDirty(old, current, session);
}
public abstract PersistentCollection wrap(SessionImplementor session, Object collection);
@Override
public boolean isAssociationType() {
return true;
}
@Override
public ForeignKeyDirection getForeignKeyDirection() {
return ForeignKeyDirection.FOREIGN_KEY_TO_PARENT;
}
public Serializable getKeyOfOwner(Object owner, SessionImplementor session) {
EntityEntry entityEntry = session.getPersistenceContext().getEntry( owner );
if ( entityEntry == null ) return null;
if ( foreignKeyPropertyName == null ) {
return entityEntry.getId();
}
else {
Object id;
if ( entityEntry.getLoadedState() != null ) {
id = entityEntry.getLoadedValue( foreignKeyPropertyName );
}
else {
id = entityEntry.getPersister().getPropertyValue( owner, foreignKeyPropertyName );
}
Type keyType = getPersister( session ).getKeyType();
if ( !keyType.getReturnedClass().isInstance( id ) ) {
id = keyType.semiResolve(
entityEntry.getLoadedValue( foreignKeyPropertyName ),
session,
owner
);
}
return (Serializable) id;
}
}
public Serializable getIdOfOwnerOrNull(Serializable key, SessionImplementor session) {
Serializable ownerId = null;
if ( foreignKeyPropertyName == null ) {
ownerId = key;
}
else {
Type keyType = getPersister( session ).getKeyType();
EntityPersister ownerPersister = getPersister( session ).getOwnerEntityPersister();
Class ownerMappedClass = ownerPersister.getMappedClass();
if ( ownerMappedClass.isAssignableFrom( keyType.getReturnedClass() ) &&
keyType.getReturnedClass().isInstance( key ) ) {
ownerId = ownerPersister.getIdentifier( key, session );
}
else {
}
}
return ownerId;
}
@Override
public Object hydrate(ResultSet rs, String[] name, SessionImplementor session, Object owner) {
return NOT_NULL_COLLECTION;
}
@Override
public Object resolve(Object value, SessionImplementor session, Object owner)
throws HibernateException {
return resolveKey( getKeyOfOwner( owner, session ), session, owner );
}
private Object resolveKey(Serializable key, SessionImplementor session, Object owner) {
return key == null ? null :
getCollection( key, session, owner );
}
@Override
public Object semiResolve(Object value, SessionImplementor session, Object owner)
throws HibernateException {
throw new UnsupportedOperationException(
"collection mappings may not form part of a property-ref" );
}
public boolean isArrayType() {
return false;
}
@Override
public boolean useLHSPrimaryKey() {
return foreignKeyPropertyName == null;
}
@Override
public String getRHSUniqueKeyPropertyName() {
return null;
}
@Override
public Joinable getAssociatedJoinable(SessionFactoryImplementor factory)
throws MappingException {
return (Joinable) factory.getCollectionPersister( role );
}
@Override
public boolean isModified(Object old, Object current, boolean[] checkable, SessionImplementor session) throws HibernateException {
return false;
}
@Override
public String getAssociatedEntityName(SessionFactoryImplementor factory)
throws MappingException {
try {
QueryableCollection collectionPersister = (QueryableCollection) factory
.getCollectionPersister( role );
if ( !collectionPersister.getElementType().isEntityType() ) {
throw new MappingException(
"collection was not an association: " +
collectionPersister.getRole()
);
}
return collectionPersister.getElementPersister().getEntityName();
}
catch (ClassCastException cce) {
throw new MappingException( "collection role is not queryable " + role );
}
}
public Object replaceElements(
Object original,
Object target,
Object owner,
Map copyCache,
SessionImplementor session) {
java.util.Collection result = ( java.util.Collection ) target;
result.clear();
Type elemType = getElementType( session.getFactory() );
Iterator iter = ( (java.util.Collection) original ).iterator();
while ( iter.hasNext() ) {
result.add( elemType.replace( iter.next(), null, session, owner, copyCache ) );
}
if ( original instanceof PersistentCollection ) {
if ( result instanceof PersistentCollection ) {
final PersistentCollection originalPersistentCollection = (PersistentCollection) original;
final PersistentCollection resultPersistentCollection = (PersistentCollection) result;
preserveSnapshot( originalPersistentCollection, resultPersistentCollection, elemType, owner, copyCache, session );
if ( ! originalPersistentCollection.isDirty() ) {
resultPersistentCollection.clearDirty();
}
}
}
return result;
}
private void preserveSnapshot(
PersistentCollection original,
PersistentCollection result,
Type elemType,
Object owner,
Map copyCache,
SessionImplementor session) {
Serializable originalSnapshot = original.getStoredSnapshot();
Serializable resultSnapshot = result.getStoredSnapshot();
Serializable targetSnapshot;
if ( originalSnapshot instanceof List ) {
targetSnapshot = new ArrayList(
( (List) originalSnapshot ).size() );
for ( Object obj : (List) originalSnapshot ) {
( (List) targetSnapshot ).add( elemType.replace( obj, null, session, owner, copyCache ) );
}
}
else if ( originalSnapshot instanceof Map ) {
if ( originalSnapshot instanceof SortedMap ) {
targetSnapshot = new TreeMap( ( (SortedMap) originalSnapshot ).comparator() );
}
else {
targetSnapshot = new HashMap(
CollectionHelper.determineProperSizing( ( (Map) originalSnapshot ).size() ),
CollectionHelper.LOAD_FACTOR
);
}
for ( Map.Entry<Object, Object> entry : ( (Map<Object, Object>) originalSnapshot ).entrySet() ) {
Object key = entry.getKey();
Object value = entry.getValue();
Object resultSnapshotValue = ( resultSnapshot == null )
? null
: ( (Map<Object, Object>) resultSnapshot ).get( key );
Object newValue = elemType.replace( value, resultSnapshotValue, session, owner, copyCache );
if ( key == value ) {
( (Map) targetSnapshot ).put( newValue, newValue );
}
else {
( (Map) targetSnapshot ).put( key, newValue );
}
}
}
else if ( originalSnapshot instanceof Object[] ) {
Object[] arr = (Object[]) originalSnapshot;
for ( int i = 0; i < arr.length; i++ ) {
arr[i] = elemType.replace( arr[i], null, session, owner, copyCache );
}
targetSnapshot = originalSnapshot;
}
else {
targetSnapshot = resultSnapshot;
}
CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( result );
if ( ce != null ) {
ce.resetStoredSnapshot( result, targetSnapshot );
}
}
protected Object instantiateResult(Object original) {
return instantiate( -1 );
}
public abstract Object instantiate(int anticipatedSize);
@Override
public Object replace(
final Object original,
final Object target,
final SessionImplementor session,
final Object owner,
final Map copyCache) throws HibernateException {
if ( original == null ) {
return null;
}
if ( !Hibernate.isInitialized( original ) ) {
return target;
}
Object result = target == null || target == original ? instantiateResult( original ) : target;
result = replaceElements( original, result, owner, copyCache, session );
if ( original == target ) {
boolean wasClean = PersistentCollection.class.isInstance( target ) && !( ( PersistentCollection ) target ).isDirty();
replaceElements( result, target, owner, copyCache, session );
if ( wasClean ) {
( ( PersistentCollection ) target ).clearDirty();
}
result = target;
}
return result;
}
public final Type getElementType(SessionFactoryImplementor factory) throws MappingException {
return factory.getCollectionPersister( getRole() ).getElementType();
}
@Override
public String toString() {
return getClass().getName() + '(' + getRole() + ')';
}
@Override
public String getOnCondition(String alias, SessionFactoryImplementor factory, Map enabledFilters)
throws MappingException {
return getAssociatedJoinable( factory ).filterFragment( alias, enabledFilters );
}
@Override
public String getOnCondition(
String alias,
SessionFactoryImplementor factory,
Map enabledFilters,
Set<String> treatAsDeclarations) {
return getAssociatedJoinable( factory ).filterFragment( alias, enabledFilters, treatAsDeclarations );
}
public Object getCollection(Serializable key, SessionImplementor session, Object owner) {
CollectionPersister persister = getPersister( session );
final PersistenceContext persistenceContext = session.getPersistenceContext();
final EntityMode entityMode = persister.getOwnerEntityPersister().getEntityMode();
PersistentCollection collection = persistenceContext.getLoadContexts().locateLoadingCollection( persister, key );
if ( collection == null ) {
collection = persistenceContext.useUnownedCollection( new CollectionKey(persister, key, entityMode) );
if ( collection == null ) {
collection = instantiate( session, persister, key );
collection.setOwner(owner);
persistenceContext.addUninitializedCollection( persister, collection, key );
if ( initializeImmediately() ) {
session.initializeCollection( collection, false );
}
else if ( !persister.isLazy() ) {
persistenceContext.addNonLazyCollection( collection );
}
if ( hasHolder() ) {
session.getPersistenceContext().addCollectionHolder( collection );
}
}
if ( LOG.isTraceEnabled() ) {
LOG.tracef( "Created collection wrapper: %s",
MessageHelper.collectionInfoString( persister, collection,
key, session ) );
}
}
collection.setOwner(owner);
return collection.getValue();
}
public boolean hasHolder() {
return false;
}
protected boolean initializeImmediately() {
return false;
}
@Override
public String getLHSPropertyName() {
return foreignKeyPropertyName;
}
@Override
public boolean isXMLElement() {
return true;
}
@Override
public Object fromXMLNode(Node xml, Mapping factory) throws HibernateException {
return xml;
}
@Override
public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory)
throws HibernateException {
if ( !isEmbeddedInXML ) {
node.detach();
}
else {
replaceNode( node, (Element) value );
}
}
@Override
public boolean isAlwaysDirtyChecked() {
return true;
}
@Override
public boolean[] toColumnNullness(Object value, Mapping mapping) {
return ArrayHelper.EMPTY_BOOLEAN_ARRAY;
}
}