# PersisterBuilderContext.java

    package org.codefilarete.stalactite.engine.configurer.builder;

    import java.util.ArrayDeque;
    import java.util.Collections;
    import java.util.Queue;

    import org.codefilarete.stalactite.dsl.entity.EntityMappingConfiguration;
    import org.codefilarete.stalactite.dsl.entity.EntityMappingConfigurationProvider;
    import org.codefilarete.stalactite.engine.EntityPersister;
    import org.codefilarete.stalactite.engine.PersisterRegistry;
    import org.codefilarete.tool.collection.KeepOrderSet;

    /**
     * Running context of {@link org.codefilarete.stalactite.engine.configurer.builder.DefaultPersisterBuilder}. Allows to share some information during configuration of the whole entity graph.
     * The currently available instance is accessible through {@link #CURRENT} variable which is created and destroyed by
     * {@link org.codefilarete.stalactite.engine.configurer.builder.DefaultPersisterBuilder#buildOrGiveExisting(EntityMappingConfiguration)}
     *
     * @author Guillaume Mary
     */
    public class PersisterBuilderContext {

    	/**
    	 * Give access to current {@link PersisterBuilderContext}, created and destroyed by
    	 * {@link org.codefilarete.stalactite.engine.configurer.builder.DefaultPersisterBuilder#build(EntityMappingConfigurationProvider)}
    	 *
    	 * Made static because several {@link org.codefilarete.stalactite.engine.configurer.builder.DefaultPersisterBuilder}s are instantiated along the build process.
    	 * Not the best design ever, but works !
    	 */
    	public static final ThreadLocal<PersisterBuilderContext> CURRENT = new ThreadLocal<>();

    	/**
    	 * List of post initializers to be invoked after persister instantiation and main configuration
    	 */
    	private final KeepOrderSet<BuildLifeCycleListener> buildLifeCycleListeners = new KeepOrderSet<>();

    	/**
    	 * Currently treated configurations. Made to detect cycle in graph.
    	 */
    	private final Queue<Class> treatedConfigurations = Collections.asLifoQueue(new ArrayDeque<>());

    	private final PersisterRegistry persisterRegistry;

    	public PersisterBuilderContext(PersisterRegistry persisterRegistry) {
    		this.persisterRegistry = persisterRegistry;
    	}

    	public PersisterRegistry getPersisterRegistry() {
    		return persisterRegistry;
    	}

    	public KeepOrderSet<BuildLifeCycleListener> getBuildLifeCycleListeners() {
    		return buildLifeCycleListeners;
    	}

    	public void addBuildLifeCycleListener(BuildLifeCycleListener listener) {
    		buildLifeCycleListeners.add(listener);
    	}

    	/**
    	 * Executes some code by remembering given configuration as treated so cycle (already configured entity) can be detected through {@link #isCycling(EntityMappingConfiguration)}
    	 *
    	 * @param entityMappingConfiguration a configuration to remember as being currently treated
    	 * @param callable some code to run
    	 */
    	public void runInContext(EntityMappingConfiguration<?, ?> entityMappingConfiguration, Runnable callable) {
    		runInContext(entityMappingConfiguration.getEntityType(), callable);
    	}

    	/**
    	 * Executes some code by remembering given configuration as treated so cycle (already configured entity) can be detected through {@link #isCycling(EntityMappingConfiguration)}
    	 *
    	 * @param entityMappingConfiguration a configuration to remember as being currently treated
    	 * @param callable some code to run
    	 */
    	public void runInContext(EntityPersister<?, ?> entityMappingConfiguration, Runnable callable) {
    		runInContext(entityMappingConfiguration.getClassToPersist(), callable);
    	}

    	private void runInContext(Class entityClass, Runnable callable) {
    		try {
    			treatedConfigurations.add(entityClass);
    			callable.run();
    		} finally {
    			treatedConfigurations.remove();
    		}
    	}

    	/**
    	 * Checks if given configuration was already treated by current build process. {@link #runInContext(EntityMappingConfiguration, Runnable)} should
    	 * have be invoked somehow before.
    	 *
    	 * @param entityMappingConfiguration configuration to be checked for cycle
    	 * @return true if given configuration was already processed
    	 */
    	public boolean isCycling(EntityMappingConfiguration<?, ?> entityMappingConfiguration) {
    		return treatedConfigurations.contains(entityMappingConfiguration.getEntityType());
    	}
    }