package com.atlassian.stash.internal.crowd;

import com.atlassian.crowd.dao.directory.DirectoryDAOHibernate;
import com.atlassian.crowd.embedded.api.Directory;
import com.atlassian.crowd.embedded.spi.DirectoryDao;
import com.atlassian.crowd.exception.DirectoryNotFoundException;
import com.atlassian.crowd.exception.ObjectNotFoundException;
import com.atlassian.crowd.model.directory.DirectoryImpl;
import com.atlassian.crowd.search.query.entity.EntityQuery;
import org.hibernate.Hibernate;

import java.util.List;

/**
 * Extension of Crowd's {@link com.atlassian.crowd.dao.directory.DirectoryDAOHibernate} that walks all lazy loaded 
 * references on {@link Directory} instances before returning them. Since Stash does not use the open-session-in-view 
 * pattern, without this code {@link org.hibernate.LazyInitializationException} would occur when trying access one of 
 * the lazy loaded fields outside of a transaction.
 *
 * @since 1.3
 */
public class HibernateDirectoryDao extends DirectoryDAOHibernate implements DirectoryDao {

    @Override
    public List<Directory> findAll() {
        return initialize(super.findAll());
    }

    @Override
    public DirectoryImpl findById(long id) throws DirectoryNotFoundException {
        return initialize(super.findById(id));
    }

    @Override
    public DirectoryImpl findByName(String name) throws DirectoryNotFoundException {
        return initialize(super.findByName(name));
    }

    @Override
    public Object load(long ID) throws ObjectNotFoundException {
        return initialize(DirectoryImpl.class.cast(super.load(ID)));
    }

    @Override
    public Object loadReference(long id) {
        return initialize(DirectoryImpl.class.cast(super.loadReference(id)));
    }

    @Override
    public List<Directory> search(EntityQuery query) {
        return initialize(super.search(query));
    }

    /**
     * Initializes all lazy loaded fields on a DirectoryImpl instance
     * @param directory the Directory instance to initialize
     * @return the same instance returned for convenience
     */
    static DirectoryImpl initialize(DirectoryImpl directory) {
        if (directory != null) {
            Hibernate.initialize(directory.getAllowedOperations());
            Hibernate.initialize(directory.getAttributes());
        }
        
        return directory;
    }

    /**
     * Initializes all lazy loaded fields on a List of  DirectoryImpl instances
     * @param directories the Directory instances to initialize
     * @return the same List, returned for convenience
     */
    static List<Directory> initialize(List<Directory> directories) {
        for (Directory directory : directories) {
            initialize((DirectoryImpl) directory);
        }
        return directories;
    }
}
