# ManyToOneCycleConfigurer.java

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

    import java.util.LinkedHashSet;
    import java.util.Set;

    import org.codefilarete.stalactite.engine.configurer.CascadeConfigurationResult;
    import org.codefilarete.stalactite.engine.configurer.builder.PostInitializer;
    import org.codefilarete.stalactite.engine.configurer.onetoone.FirstPhaseCycleLoadListener;
    import org.codefilarete.stalactite.engine.runtime.ConfiguredRelationalPersister;
    import org.codefilarete.stalactite.engine.runtime.cycle.ManyToOneCycleLoader;

    /**
     * Container of {@link ManyToOneRelationConfigurer}s of same entity type and their relation name (through {@link RelationConfigurer}).
     * Expected to exist as a one-per-entity-type.
     *
     * As a {@link PostInitializer}, will invoke every registered {@link ManyToOneRelationConfigurer}
     * {@link ManyToOneOwnedBySourceConfigurer#configureWithSelectIn2Phases(String, ConfiguredRelationalPersister, FirstPhaseCycleLoadListener, String) configureWithSelectIn2Phases method}
     * with a {@link ManyToOneCycleLoader}.
     *
     * @param <TRGT> type of all registered {@link ManyToOneRelationConfigurer}
     */
    public class ManyToOneCycleConfigurer<TRGT> extends PostInitializer<TRGT> {

    	// instantiated as a LinkedHashSet only for steady debugging purpose, could be replaced by a HashSet
    	private final Set<RelationConfigurer<?, ?, ?>> relations = new LinkedHashSet<>();
    	private final ManyToOneRelation<?, TRGT, ?, ?> manyToOneRelation;

    	public ManyToOneCycleConfigurer(Class<TRGT> targetEntityType, ManyToOneRelation<?, TRGT, ?, ?> manyToOneRelation) {
    		super(targetEntityType);
    		this.manyToOneRelation = manyToOneRelation;
    	}

    	public <SRC> void addCycleSolver(String relationIdentifier,
    									 ManyToOneOwnedBySourceConfigurer<SRC, TRGT, ?, ?, ?, ?, ?> manyToOneRelationConfigurer) {
    		this.relations.add(new RelationConfigurer<>(relationIdentifier, manyToOneRelationConfigurer));
    	}

    	@Override
    	public void consume(ConfiguredRelationalPersister<TRGT, ?> targetPersister) {
    		registerRelationLoader(targetPersister);
    	}

    	private <SRC, TRGTID> void registerRelationLoader(ConfiguredRelationalPersister<TRGT, TRGTID> targetPersister) {
    		ManyToOneCycleLoader<SRC, TRGT, TRGTID> manyToOneCycleLoader = new ManyToOneCycleLoader<>(targetPersister);
    		targetPersister.addSelectListener(manyToOneCycleLoader);
    		relations.forEach((RelationConfigurer c) -> {
    			String tableAlias = c.relationName.replaceAll("\\W", "_");
    			CascadeConfigurationResult<SRC, TRGT> configurationResult = c.manyToOneRelationConfigurer.configureWithSelectIn2Phases(
    					tableAlias, targetPersister, manyToOneCycleLoader, manyToOneRelation.getColumnName());
    			manyToOneCycleLoader.addRelation(c.relationName, configurationResult);
    		});
    	}

    	private class RelationConfigurer<SRC, SRCID, TRGTID> {

    		private final String relationName;
    		private final ManyToOneOwnedBySourceConfigurer<SRC, TRGT, SRCID, TRGTID, ?, ?, ?> manyToOneRelationConfigurer;

    		public RelationConfigurer(String relationName,
    								  ManyToOneOwnedBySourceConfigurer<SRC, TRGT, SRCID, TRGTID, ?, ?, ?> manyToOneRelationConfigurer) {
    			this.relationName = relationName;
    			this.manyToOneRelationConfigurer = manyToOneRelationConfigurer;
    		}
    	}
    }