gt: 				rowCollection.readFrom(rs, persister, descriptor, owner);
context: /*
   * Hibernate, Relational Persistence for Idiomatic Java
   *
   * JBoss, Home of Professional Open Source
   * Copyright 2010-2011 Red Hat Inc. and/or its affiliates and other contributors
   * as indicated by the @authors tag. All rights reserved.
   * See the copyright.txt in the distribution for a
   * full listing of individual contributors.
   *
   * This copyrighted material is made available to anyone wishing to use,
   * modify, copy, or redistribute it subject to the terms and conditions
   * of the GNU Lesser General Public License, v. 2.1.
   * This program is distributed in the hope that it will be useful, but WITHOUT A
   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
   * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
   * You should have received a copy of the GNU Lesser General Public License,
   * v.2.1 along with this distribution; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   * MA  02110-1301, USA.
   */
  package org.hibernate.ogm.loader;
  
  import org.apache.log4j.Logger;
  import org.hibernate.HibernateException;
  import org.hibernate.LockMode;
  import org.hibernate.LockOptions;
  import org.hibernate.WrongClassException;
  import org.hibernate.annotations.common.AssertionFailure;
  import org.hibernate.cfg.NotYetImplementedException;
  import org.hibernate.collection.spi.PersistentCollection;
  import org.hibernate.engine.internal.TwoPhaseLoad;
  import org.hibernate.engine.spi.*;
  import org.hibernate.event.spi.EventSource;
  import org.hibernate.event.spi.PostLoadEvent;
  import org.hibernate.event.spi.PreLoadEvent;
  import org.hibernate.internal.util.collections.ArrayHelper;
  import org.hibernate.loader.CollectionAliases;
  import org.hibernate.loader.entity.UniqueEntityLoader;
  import org.hibernate.ogm.datastore.impl.DatastoreServices;
  import org.hibernate.ogm.datastore.spi.Association;
  import org.hibernate.ogm.datastore.spi.Tuple;
  import org.hibernate.ogm.dialect.GridDialect;
  import org.hibernate.ogm.grid.EntityKey;
  import org.hibernate.ogm.grid.RowKey;
  import org.hibernate.ogm.jdbc.TupleAsMapResultSet;
  import org.hibernate.ogm.persister.CollectionPhysicalModel;
  import org.hibernate.ogm.persister.EntityKeyBuilder;
  import org.hibernate.ogm.persister.OgmCollectionPersister;
  import org.hibernate.ogm.persister.OgmEntityPersister;
  import org.hibernate.ogm.util.impl.Log;
  import org.hibernate.ogm.util.impl.LoggerFactory;
  import org.hibernate.ogm.util.impl.PropertyMetadataProvider;
  import org.hibernate.ogm.util.impl.StringHelper;
  import org.hibernate.persister.collection.CollectionPersister;
  import org.hibernate.persister.entity.Loadable;
  import org.hibernate.persister.entity.UniqueKeyLoadable;
  import org.hibernate.pretty.MessageHelper;
  import org.hibernate.proxy.HibernateProxy;
  import org.hibernate.type.AssociationType;
  import org.hibernate.type.EntityType;
  import org.hibernate.type.Type;
  
  import java.io.Serializable;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import java.util.ArrayList;
  import java.util.List;
  
  /**
   * Load an entity from the Grid
   * 
   * @author Emmanuel Bernard
   */
  public class OgmLoader implements UniqueEntityLoader {
  
  	// private static final Log log = LoggerFactory.make();
  	private static final Logger log = Logger.getLogger(OgmLoader.class);
  
  	private final OgmEntityPersister[] entityPersisters;
  	private final OgmCollectionPersister[] collectionPersisters;
  	private final SessionFactoryImplementor factory;
  	private LockMode[] defaultLockModes;
  	private final CollectionAliases[] collectionAliases;
  	private final GridDialect gridDialect;
  
  	/**
  	 * Load a collection
  	 */
  	public OgmLoader(OgmCollectionPersister[] collectionPersisters) {
  		if (collectionPersisters == null || collectionPersisters.length == 0) {
  			throw new AssertionFailure(
  					"CollectionPersister[] must not be null or empty");
  		}
  		this.entityPersisters = new OgmEntityPersister[] {};
  		this.collectionPersisters = collectionPersisters;
  		this.factory = collectionPersisters[0].getFactory();
  		this.gridDialect = this.factory.getServiceRegistry()
  				.getService(DatastoreServices.class).getGridDialect();
  
  		// NONE, because its the requested lock mode, not the actual!
  		final int fromSize = 1;
  		this.defaultLockModes = ArrayHelper.fillArray(LockMode.NONE, fromSize);
  		this.collectionAliases = new CollectionAliases[collectionPersisters.length];
  		for (int i = 0; i < collectionPersisters.length; i++) {
  			collectionAliases[i] = new OgmColumnCollectionAliases(
  					collectionPersisters[i]);
  		}
  	}
  
  	/**
  	 * Load an entity
  	 */
  	public OgmLoader(OgmEntityPersister[] entityPersisters) {
  		if (entityPersisters == null || entityPersisters.length == 0) {
  			throw new AssertionFailure(
  					"EntityPersister[] must not be null or empty");
  		}
  		this.entityPersisters = entityPersisters;
  		this.collectionPersisters = new OgmCollectionPersister[] {};
  		this.factory = entityPersisters[0].getFactory();
  		this.gridDialect = this.factory.getServiceRegistry()
  				.getService(DatastoreServices.class).getGridDialect();
  
  		// NONE, because its the requested lock mode, not the actual!
  		final int fromSize = 1;
  		this.defaultLockModes = ArrayHelper.fillArray(LockMode.NONE, fromSize);
  		this.collectionAliases = new CollectionAliases[0];
  	}
  
  	/** returns the collection column names */
  	public CollectionAliases[] getCollectionAliases() {
  		return collectionAliases;
  	}
  
  	private SessionFactoryImplementor getFactory() {
  		return factory;
  	}
  
  	/**
  	 * {@inheritDoc}
  	 */
  	@Override
  	public Object load(Serializable id, Object optionalObject,
  			SessionImplementor session) throws HibernateException {
  		return load(id, optionalObject, session, LockOptions.NONE);
  	}
  
  	/**
  	 * {@inheritDoc}
  	 */
  	@Override
  	public Object load(Serializable id, Object optionalObject,
  			SessionImplementor session, LockOptions lockOptions) {
  		final OgmEntityPersister currentPersister = entityPersisters[0];
  		if (log.isDebugEnabled()) {
  			log.debug("loading entity: "
  					+ MessageHelper.infoString(currentPersister, id,
  							currentPersister.getIdentifierType(),
  							session.getFactory()));
  		}
  		QueryParameters qp = new QueryParameters();
  		qp.setPositionalParameterTypes(new Type[] { currentPersister
  				.getIdentifierType() });
  		qp.setPositionalParameterValues(new Object[] { id });
  		qp.setOptionalObject(optionalObject);
  		qp.setOptionalEntityName(currentPersister.getEntityName());
  		qp.setOptionalId(id);
  		qp.setLockOptions(lockOptions);
  
  		Object result = doQueryAndInitializeNonLazyCollections(session, qp,
  				false);
  		return result;
  	}
  
  	/**
  	 * Called by subclasses that initialize collections
  	 */
  	public final void loadCollection(final SessionImplementor session,
  			final Serializable id, final Type type) throws HibernateException {
  
  		if (log.isDebugEnabled()) {
  			log.debug("loading collection: "
  					+ MessageHelper.collectionInfoString(
  							getCollectionPersisters()[0], id, getFactory()));
  		}
  
  		Serializable[] ids = new Serializable[] { id };
  		QueryParameters qp = new QueryParameters(new Type[] { type }, ids, ids);
  		doQueryAndInitializeNonLazyCollections(session, qp, true);
  
  		log.debug("done loading collection");
  
  	}
  
  	OgmEntityPersister[] getEntityPersisters() {
  		return entityPersisters;
  	}
  
  	/**
  	 * Load the entity activating the persistence context execution boundaries
  	 */
  	private Object doQueryAndInitializeNonLazyCollections(
  			SessionImplementor session, QueryParameters qp,
  			boolean returnProxies) {
  
  		// TODO handles the read only
  		final PersistenceContext persistenceContext = session
  				.getPersistenceContext();
  		boolean defaultReadOnlyOrig = persistenceContext.isDefaultReadOnly();
  		persistenceContext.beforeLoad();
  		Object result;
  		try {
  			try {
  				result = doQuery(session, qp, returnProxies);
  			} finally {
  				persistenceContext.afterLoad();
  			}
  			persistenceContext.initializeNonLazyCollections();
  		} finally {
  			// Restore the original default
  			persistenceContext.setDefaultReadOnly(defaultReadOnlyOrig);
  		}
  
  		log.debug("done entity load");
  		return result;
  	}
  
  	/**
  	 * Execute the physical query and initialize the various entities and
  	 * collections
  	 */
  	private Object doQuery(SessionImplementor session, QueryParameters qp,
  			boolean returnProxies) {
  		// TODO support lock timeout
  
  		int entitySpan = entityPersisters.length;
  		final List<Object> hydratedObjects = entitySpan == 0 ? null
  				: new ArrayList<Object>(entitySpan * 10);
  		// TODO yuk! Is there a cleaner way to access the id?
  		final Serializable id = qp.getOptionalId() != null ? qp.getOptionalId()
  				: (Serializable) qp.getCollectionKeys()[0];
  		TupleAsMapResultSet resultset = getResultSet(id, session);
  
  		// Todo implement lockmode
  		// final LockMode[] lockModesArray = getLockModes(
  		// queryParameters.getLockOptions() );
  		// FIXME should we use subselects as it's closer to this process??
  
  		// TODO is resultset a good marker, or should it be an ad-hoc marker??
  		// It likely all depends on what resultset ends up being
  		handleEmptyCollections(qp.getCollectionKeys(), resultset, session);
  
  		final org.hibernate.engine.spi.EntityKey[] keys = new org.hibernate.engine.spi.EntityKey[entitySpan];
  
  		// for each element in resultset
  		// TODO should we collect List<Object> as result? Not necessary today
  		Object result = null;
  		try {
  			while (resultset.next()) {
  				result = getRowFromResultSet(resultset, session, qp,
  						// lockmodeArray,
  						qp.getOptionalId(), hydratedObjects, keys,
  						returnProxies);
  			}
  
  			// TODO collect subselect result key
  		} catch (SQLException e) {
  			// never happens this is not a regular ResultSet
  		}
  
  		// end of for each element in resultset
  
  		initializeEntitiesAndCollections(hydratedObjects, resultset, session,
  				qp.isReadOnly(session));
  		// TODO create subselects
  		return result;
  	}
  
  	/**
  	 * If this is a collection initializer, we need to tell the session that a
  	 * collection is being initialized, to account for the possibility of the
  	 * collection having no elements (hence no rows in the result set).
  	 */
  	private void handleEmptyCollections(final Serializable[] keys,
  			final ResultSet resultSetId, final SessionImplementor session) {
  
  		if (keys != null) {
  			// this is a collection initializer, so we must create a collection
  			// for each of the passed-in keys, to account for the possibility
  			// that the collection is empty and has no rows in the result set
  
  			CollectionPersister[] collectionPersisters = getCollectionPersisters();
  			for (int j = 0; j < collectionPersisters.length; j++) {
  				for (int i = 0; i < keys.length; i++) {
  					// handle empty collections
  
  					if (log.isDebugEnabled()) {
  						log.debug("result set contains (possibly empty) collection: "
  								+ MessageHelper.collectionInfoString(
  										collectionPersisters[j], keys[i],
  										getFactory()));
  					}
  
  					session.getPersistenceContext()
  							.getLoadContexts()
  							.getCollectionLoadContext(resultSetId)
  							.getLoadingCollection(collectionPersisters[j],
  									keys[i]);
  				}
  			}
  		}
  
  		// else this is not a collection initializer (and empty collections will
  		// be detected by looking for the owner's identifier in the result set)
  	}
  
  	private Object getRowFromResultSet(ResultSet resultset,
  			SessionImplementor session, QueryParameters qp,
  			Serializable optionalId, List<Object> hydratedObjects,
  			org.hibernate.engine.spi.EntityKey[] keys, boolean returnProxies)
  			throws SQLException {
  		final OgmEntityPersister[] persisters = getEntityPersisters();
  		final int entitySpan = persisters.length;
  		extractKeysFromResultSet(session, optionalId, keys);
  
  		registerNonExists(keys, persisters, session);
  
  		// it's a non existing object: cut short
  		if (resultset == null) {
  			return null;
  		}
  
  		final Object[] row = getRow(resultset.unwrap(TupleAsMapResultSet.class)
  				.getTuple(), persisters, keys, qp.getOptionalObject(),
  				getOptionalObjectKey(qp, session),
  				getLockModes(qp.getLockOptions()), hydratedObjects, session);
  
  		readCollectionElements(row, resultset, session);
  
  		if (returnProxies) {
  			// now get an existing proxy for each row element (if there is one)
  			for (int i = 0; i < entitySpan; i++) {
  				Object entity = row[i];
  				Object proxy = session.getPersistenceContext().proxyFor(
  						persisters[i], keys[i], entity);
  				if (entity != proxy) {
  					// force the proxy to resolve itself
  					((HibernateProxy) proxy).getHibernateLazyInitializer()
  							.setImplementation(entity);
  					row[i] = proxy;
  				}
  			}
  		}
  
  		// applyPostLoadLocks
  		// nothing to do atm it seems, the code in Hibernate Core does not do
  		// anything either
  
  		return getResultColumnOrRow(row);
  	}
  
  	private void extractKeysFromResultSet(SessionImplementor session,
  			Serializable optionalId, org.hibernate.engine.spi.EntityKey[] keys) {
  		// TODO Implement all Loader#extractKeysFromResultSet (ie resolution in
  		// case of composite ids with associations)
  		// in the mean time the next two lines are the simplified version
  		if (keys.length == 0) {
  			// do nothing, this is a collection
  		} else {
  			final org.hibernate.engine.spi.EntityKey key = session
  					.generateEntityKey(optionalId, entityPersisters[0]);
  			keys[0] = key;
  		}
  	}
  
  	private TupleAsMapResultSet getResultSet(Serializable id,
  			SessionImplementor session) {
  		// TODO this if won't work when we will support collections inside the
  		// entity tuple but that will do for now
  		final TupleAsMapResultSet resultset = new TupleAsMapResultSet();
  		if (getEntityPersisters().length > 0) {
  			final EntityKey key = EntityKeyBuilder.fromPersisterId(
  					getEntityPersisters()[0], id);
  			Tuple entry = gridDialect.getTuple(key);
  			if (entry != null) {
  				resultset.addTuple(entry);
  			}
  		} else {
  			// collection persister
  			if (getCollectionPersisters().length != 1) {
  				throw new AssertionFailure(
  						"Found an unexpected number of collection persisters: "
  								+ getCollectionPersisters().length);
  			}
  			final CollectionPhysicalModel persister = (CollectionPhysicalModel) getCollectionPersisters()[0];
  			PropertyMetadataProvider metadataProvider = new PropertyMetadataProvider()
  					.gridDialect(gridDialect)
  					.tableName(persister.getTableName()).key(id)
  					.keyColumnNames(persister.getKeyColumnNames())
  					.keyGridType(persister.getKeyGridType()).session(session);
  			Association assoc = metadataProvider.getCollectionMetadataOrNull();
  			if (assoc != null) {
  				for (RowKey rowKey : assoc.getKeys()) {
  					resultset.addTuple(assoc.get(rowKey));
  				}
  			}
  		}
  		return resultset;
  	}
  
  	private Object getResultColumnOrRow(Object[] row) {
  		// getResultColumnOrRow
  		// today we don't use this to apply the result transformer and we don't
  		// have operations to do like other loaders
  		// TODO investigate further the result transformer application
  		return row.length == 1 ? row[0] : row;
  
  	}
  
  	private void readCollectionElements(Object[] row, ResultSet resultSet,
  			SessionImplementor session) throws HibernateException, SQLException {
  		// TODO: make this handle multiple collection roles!
  
  		final CollectionPersister[] collectionPersisters = getCollectionPersisters();
  		if (collectionPersisters != null) {
  
  			// we don't load more than one instance per row, shortcircuiting it
  			// for the moment
  			final int[] collectionOwners = null;
  
  			for (int i = 0; i < collectionPersisters.length; i++) {
  				final CollectionAliases[] descriptors = getCollectionAliases();
  				final boolean hasCollectionOwners = collectionOwners != null
  						&& collectionOwners[i] > -1;
  				// true if this is a query and we are loading multiple instances
  				// of the same collection role
  				// otherwise this is a CollectionInitializer and we are loading
  				// up a single collection or batch
  
  				final Object owner = hasCollectionOwners ? row[collectionOwners[i]]
  						: null; // if null, owner will be retrieved from session
  
  				final CollectionPersister collectionPersister = collectionPersisters[i];
  				final Serializable key;
  				if (owner == null) {
  					key = null;
  				} else {
  					key = collectionPersister.getCollectionType()
  							.getKeyOfOwner(owner, session);
  					// TODO: old version did not require hashmap lookup:
  					// keys[collectionOwner].getIdentifier()
  				}
  
  				readCollectionElement(owner, key, collectionPersister,
  						descriptors[i], resultSet, // TODO CURRENT must use the
  													// same instance across all
  													// calls
  						session);
  
  			}
  
  		}
  
  	}
  
  	/**
  	 * Read one collection element from the current row of the JDBC result set
  	 */
  	private void readCollectionElement(final Object optionalOwner,
  			final Serializable optionalKey,
  			final CollectionPersister persister,
  			final CollectionAliases descriptor, final ResultSet rs,
  			final SessionImplementor session) throws HibernateException,
  			SQLException {
  
  		final PersistenceContext persistenceContext = session
  				.getPersistenceContext();
  
  		// implement persister.readKey using the grid type (later)
  		final Serializable collectionRowKey = (Serializable) persister.readKey(
  				rs, descriptor.getSuffixedKeyAliases(), session);
  
  		if (collectionRowKey != null) {
  			// we found a collection element in the result set
  
  			if (log.isDebugEnabled()) {
  				log.debug("found row of collection: "
  						+ MessageHelper.collectionInfoString(persister,
  								collectionRowKey, getFactory()));
  			}
  
  			Object owner = optionalOwner;
  			if (owner == null) {
  				owner = persistenceContext.getCollectionOwner(collectionRowKey,
  						persister);
  				if (owner == null) {
  					// TODO: This is assertion is disabled because there is a
  					// bug that means the
  					// original owner of a transient, uninitialized collection
  					// is not known
  					// if the collection is re-referenced by a different object
  					// associated
  					// with the current Session
  					// throw new
  					// AssertionFailure("bug loading unowned collection");
  				}
  			}
  
  			PersistentCollection rowCollection = persistenceContext
  					.getLoadContexts().getCollectionLoadContext(rs)
  					.getLoadingCollection(persister, collectionRowKey);
  
  			if (rowCollection != null) {
  