private Queryable determineAppropriateOwnerPersister(NonScalarReturn ownerDescriptor) {
    String entityName = null;
    if (ownerDescriptor instanceof RootReturn) {
      entityName = ((RootReturn) ownerDescriptor).getEntityName();
    } else if (ownerDescriptor instanceof CollectionReturn) {
      CollectionReturn collRtn = (CollectionReturn) ownerDescriptor;
      String role = collRtn.getOwnerEntityName() + "." + collRtn.getOwnerProperty();
      CollectionPersister persister = getFactory().getCollectionPersister(role);
      EntityType ownerType = (EntityType) persister.getElementType();
      entityName = ownerType.getAssociatedEntityName(getFactory());
    } else if (ownerDescriptor instanceof FetchReturn) {
      FetchReturn fetchRtn = (FetchReturn) ownerDescriptor;
      Queryable persister = determineAppropriateOwnerPersister(fetchRtn.getOwner());
      Type ownerType = persister.getPropertyType(fetchRtn.getOwnerProperty());
      if (ownerType.isEntityType()) {
        entityName = ((EntityType) ownerType).getAssociatedEntityName(getFactory());
      } else if (ownerType.isCollectionType()) {
        Type ownerCollectionElementType = ((CollectionType) ownerType).getElementType(getFactory());
        if (ownerCollectionElementType.isEntityType()) {
          entityName =
              ((EntityType) ownerCollectionElementType).getAssociatedEntityName(getFactory());
        }
      }
    }

    if (entityName == null) {
      throw new HibernateException("Could not determine fetch owner : " + ownerDescriptor);
    }

    return (Queryable) getFactory().getEntityPersister(entityName);
  }
  public CustomLoader(CustomQuery customQuery, SessionFactoryImplementor factory) {
    super(factory);

    this.sql = customQuery.getSQL();
    this.querySpaces.addAll(customQuery.getQuerySpaces());
    this.namedParameterBindPoints = customQuery.getNamedParameterBindPoints();

    List entityPersisters = new ArrayList();
    List entityOwners = new ArrayList();
    List entityAliases = new ArrayList();

    List collectionPersisters = new ArrayList();
    List collectionOwners = new ArrayList();
    List collectionAliases = new ArrayList();

    List lockModes = new ArrayList();
    List resultColumnProcessors = new ArrayList();
    List nonScalarReturnList = new ArrayList();
    List resultTypes = new ArrayList();
    List specifiedAliases = new ArrayList();
    int returnableCounter = 0;
    boolean hasScalars = false;

    List includeInResultRowList = new ArrayList();

    Iterator itr = customQuery.getCustomQueryReturns().iterator();
    while (itr.hasNext()) {
      final Return rtn = (Return) itr.next();
      if (rtn instanceof ScalarReturn) {
        ScalarReturn scalarRtn = (ScalarReturn) rtn;
        resultTypes.add(scalarRtn.getType());
        specifiedAliases.add(scalarRtn.getColumnAlias());
        resultColumnProcessors.add(
            new ScalarResultColumnProcessor(
                StringHelper.unquote(scalarRtn.getColumnAlias(), factory.getDialect()),
                scalarRtn.getType()));
        includeInResultRowList.add(true);
        hasScalars = true;
      } else if (ConstructorReturn.class.isInstance(rtn)) {
        final ConstructorReturn constructorReturn = (ConstructorReturn) rtn;
        resultTypes.add(null); // this bit makes me nervous
        includeInResultRowList.add(true);
        hasScalars = true;

        ScalarResultColumnProcessor[] scalarProcessors =
            new ScalarResultColumnProcessor[constructorReturn.getScalars().length];
        int i = 0;
        for (ScalarReturn scalarReturn : constructorReturn.getScalars()) {
          scalarProcessors[i++] =
              new ScalarResultColumnProcessor(
                  StringHelper.unquote(scalarReturn.getColumnAlias(), factory.getDialect()),
                  scalarReturn.getType());
        }

        resultColumnProcessors.add(
            new ConstructorResultColumnProcessor(
                constructorReturn.getTargetClass(), scalarProcessors));
      } else if (rtn instanceof RootReturn) {
        RootReturn rootRtn = (RootReturn) rtn;
        Queryable persister = (Queryable) factory.getEntityPersister(rootRtn.getEntityName());
        entityPersisters.add(persister);
        lockModes.add((rootRtn.getLockMode()));
        resultColumnProcessors.add(new NonScalarResultColumnProcessor(returnableCounter++));
        nonScalarReturnList.add(rtn);
        entityOwners.add(-1);
        resultTypes.add(persister.getType());
        specifiedAliases.add(rootRtn.getAlias());
        entityAliases.add(rootRtn.getEntityAliases());
        ArrayHelper.addAll(querySpaces, persister.getQuerySpaces());
        includeInResultRowList.add(true);
      } else if (rtn instanceof CollectionReturn) {
        CollectionReturn collRtn = (CollectionReturn) rtn;
        String role = collRtn.getOwnerEntityName() + "." + collRtn.getOwnerProperty();
        QueryableCollection persister = (QueryableCollection) factory.getCollectionPersister(role);
        collectionPersisters.add(persister);
        lockModes.add(collRtn.getLockMode());
        resultColumnProcessors.add(new NonScalarResultColumnProcessor(returnableCounter++));
        nonScalarReturnList.add(rtn);
        collectionOwners.add(-1);
        resultTypes.add(persister.getType());
        specifiedAliases.add(collRtn.getAlias());
        collectionAliases.add(collRtn.getCollectionAliases());
        // determine if the collection elements are entities...
        Type elementType = persister.getElementType();
        if (elementType.isEntityType()) {
          Queryable elementPersister =
              (Queryable) ((EntityType) elementType).getAssociatedJoinable(factory);
          entityPersisters.add(elementPersister);
          entityOwners.add(-1);
          entityAliases.add(collRtn.getElementEntityAliases());
          ArrayHelper.addAll(querySpaces, elementPersister.getQuerySpaces());
        }
        includeInResultRowList.add(true);
      } else if (rtn instanceof EntityFetchReturn) {
        EntityFetchReturn fetchRtn = (EntityFetchReturn) rtn;
        NonScalarReturn ownerDescriptor = fetchRtn.getOwner();
        int ownerIndex = nonScalarReturnList.indexOf(ownerDescriptor);
        entityOwners.add(ownerIndex);
        lockModes.add(fetchRtn.getLockMode());
        Queryable ownerPersister = determineAppropriateOwnerPersister(ownerDescriptor);
        EntityType fetchedType =
            (EntityType) ownerPersister.getPropertyType(fetchRtn.getOwnerProperty());
        String entityName = fetchedType.getAssociatedEntityName(getFactory());
        Queryable persister = (Queryable) factory.getEntityPersister(entityName);
        entityPersisters.add(persister);
        nonScalarReturnList.add(rtn);
        specifiedAliases.add(fetchRtn.getAlias());
        entityAliases.add(fetchRtn.getEntityAliases());
        ArrayHelper.addAll(querySpaces, persister.getQuerySpaces());
        includeInResultRowList.add(false);
      } else if (rtn instanceof CollectionFetchReturn) {
        CollectionFetchReturn fetchRtn = (CollectionFetchReturn) rtn;
        NonScalarReturn ownerDescriptor = fetchRtn.getOwner();
        int ownerIndex = nonScalarReturnList.indexOf(ownerDescriptor);
        collectionOwners.add(ownerIndex);
        lockModes.add(fetchRtn.getLockMode());
        Queryable ownerPersister = determineAppropriateOwnerPersister(ownerDescriptor);
        String role = ownerPersister.getEntityName() + '.' + fetchRtn.getOwnerProperty();
        QueryableCollection persister = (QueryableCollection) factory.getCollectionPersister(role);
        collectionPersisters.add(persister);
        nonScalarReturnList.add(rtn);
        specifiedAliases.add(fetchRtn.getAlias());
        collectionAliases.add(fetchRtn.getCollectionAliases());
        // determine if the collection elements are entities...
        Type elementType = persister.getElementType();
        if (elementType.isEntityType()) {
          Queryable elementPersister =
              (Queryable) ((EntityType) elementType).getAssociatedJoinable(factory);
          entityPersisters.add(elementPersister);
          entityOwners.add(ownerIndex);
          entityAliases.add(fetchRtn.getElementEntityAliases());
          ArrayHelper.addAll(querySpaces, elementPersister.getQuerySpaces());
        }
        includeInResultRowList.add(false);
      } else {
        throw new HibernateException(
            "unexpected custom query return type : " + rtn.getClass().getName());
      }
    }

    this.entityPersisters = new Queryable[entityPersisters.size()];
    for (int i = 0; i < entityPersisters.size(); i++) {
      this.entityPersisters[i] = (Queryable) entityPersisters.get(i);
    }
    this.entiytOwners = ArrayHelper.toIntArray(entityOwners);
    this.entityAliases = new EntityAliases[entityAliases.size()];
    for (int i = 0; i < entityAliases.size(); i++) {
      this.entityAliases[i] = (EntityAliases) entityAliases.get(i);
    }

    this.collectionPersisters = new QueryableCollection[collectionPersisters.size()];
    for (int i = 0; i < collectionPersisters.size(); i++) {
      this.collectionPersisters[i] = (QueryableCollection) collectionPersisters.get(i);
    }
    this.collectionOwners = ArrayHelper.toIntArray(collectionOwners);
    this.collectionAliases = new CollectionAliases[collectionAliases.size()];
    for (int i = 0; i < collectionAliases.size(); i++) {
      this.collectionAliases[i] = (CollectionAliases) collectionAliases.get(i);
    }

    this.lockModes = new LockMode[lockModes.size()];
    for (int i = 0; i < lockModes.size(); i++) {
      this.lockModes[i] = (LockMode) lockModes.get(i);
    }

    this.resultTypes = ArrayHelper.toTypeArray(resultTypes);
    this.transformerAliases = ArrayHelper.toStringArray(specifiedAliases);

    this.rowProcessor =
        new ResultRowProcessor(
            hasScalars,
            (ResultColumnProcessor[])
                resultColumnProcessors.toArray(
                    new ResultColumnProcessor[resultColumnProcessors.size()]));

    this.includeInResultRow = ArrayHelper.toBooleanArray(includeInResultRowList);
  }