# AbstractIdentification.java

    package org.codefilarete.stalactite.engine.configurer;

    import java.util.Map;
    import java.util.function.Consumer;
    import java.util.function.Function;

    import org.codefilarete.reflection.ReadWritePropertyAccessPoint;
    import org.codefilarete.stalactite.dsl.entity.EntityMappingConfiguration;
    import org.codefilarete.stalactite.dsl.entity.EntityMappingConfiguration.CompositeKeyMapping;
    import org.codefilarete.stalactite.dsl.idpolicy.IdentifierPolicy;
    import org.codefilarete.stalactite.mapping.DefaultEntityMapping;
    import org.codefilarete.stalactite.mapping.id.manager.AlreadyAssignedIdentifierManager;
    import org.codefilarete.stalactite.mapping.id.manager.IdentifierInsertionManager;
    import org.codefilarete.stalactite.sql.ddl.structure.Column;
    import org.codefilarete.stalactite.sql.ddl.structure.Table;

    /**
     * Stores information about entity identification during configuration process.
     * Two cases are expected :
     * - identification for single-column primary key, stored in {@link SingleColumnIdentification}
     * - identification for multiple-columns primary key, stored in {@link CompositeKeyIdentification}
     *
     * All this is not expected to be exposed out of configuration process.
     *
     * @param <C> entity type
     * @param <I> identifier type
     * @author Guillaume Mary
     * @see #forSingleKey(EntityMappingConfiguration)
     * @see #forCompositeKey(EntityMappingConfiguration, CompositeKeyMapping)
     */
    public abstract class AbstractIdentification<C, I> {

    	/**
    	 * Build a single-key identification definition.
    	 * Argument identificationDefiner is expected to have a {@link EntityMappingConfiguration.SingleKeyMapping}
    	 * as key mapping, else a {@link ClassCastException} will be thrown.
    	 *
    	 * @param identificationDefiner a configuration that defines a single-key identifier.
    	 * @param <C> identified entity
    	 * @param <I> identifier type
    	 * @return a single-key identification
    	 */
    	public static <C, I> SingleColumnIdentification<C, I> forSingleKey(EntityMappingConfiguration<C, I> identificationDefiner) {
    		return new SingleColumnIdentification<>(identificationDefiner, ((EntityMappingConfiguration.SingleKeyMapping<C, I>) identificationDefiner.getKeyMapping()).getIdentifierPolicy());
    	}

    	/**
    	 * Build a composite-key identification definition.
    	 * Composite-key identification is always an already-defined identifier policy.
    	 *
    	 * @param identificationDefiner a configuration that defines a composite-key identifier.
    	 * @param <C> identified entity
    	 * @param <I> identifier type
    	 * @return a composite-key identification
    	 */
    	public static <C, I> CompositeKeyIdentification<C, I> forCompositeKey(EntityMappingConfiguration<C, I> identificationDefiner, CompositeKeyMapping<C, I> foundKeyMapping) {
    		return new CompositeKeyIdentification<>(identificationDefiner, foundKeyMapping.getMarkAsPersistedFunction(), foundKeyMapping.getIsPersistedFunction());
    	}

    	private final ReadWritePropertyAccessPoint<C, I> idAccessor;
    	private final EntityMappingConfiguration<C, I> identificationDefiner;
    	private final EntityMappingConfiguration.KeyMapping<C, I> keyLinkage;

    	/**
    	 * Insertion manager for {@link DefaultEntityMapping} that owns identifier policy
    	 */
    	private IdentifierInsertionManager<C, I> insertionManager;

    	/**
    	 * Insertion manager for {@link DefaultEntityMapping} that doesn't own identifier policy : they get an already-assigned one.
    	 * Set for entity that inherits another one which defines identifier policy.
    	 */
    	private AlreadyAssignedIdentifierManager<C, I> fallbackInsertionManager;

    	private AbstractIdentification(EntityMappingConfiguration<C, I> identificationDefiner) {
    		this.keyLinkage = identificationDefiner.getKeyMapping();
    		this.idAccessor = identificationDefiner.getKeyMapping().getAccessor();
    		this.identificationDefiner = identificationDefiner;
    	}

    	public EntityMappingConfiguration.KeyMapping<C, I> getKeyLinkage() {
    		return keyLinkage;
    	}

    	public ReadWritePropertyAccessPoint<C, I> getIdAccessor() {
    		return idAccessor;
    	}

    	public EntityMappingConfiguration<C, I> getIdentificationDefiner() {
    		return identificationDefiner;
    	}

    	public IdentifierInsertionManager<C, I> getInsertionManager() {
    		return insertionManager;
    	}

    	public AbstractIdentification<C, I> setInsertionManager(IdentifierInsertionManager<C, I> insertionManager) {
    		this.insertionManager = insertionManager;
    		return this;
    	}

    	public AlreadyAssignedIdentifierManager<C, I> getFallbackInsertionManager() {
    		return fallbackInsertionManager;
    	}

    	public AbstractIdentification<C, I> setFallbackInsertionManager(AlreadyAssignedIdentifierManager<C, I> fallbackInsertionManager) {
    		this.fallbackInsertionManager = fallbackInsertionManager;
    		return this;
    	}

    	/**
    	 * Identification for single-column case
    	 *
    	 * @param <C> persisted entity type
    	 * @param <I> identifier type
    	 */
    	public static class SingleColumnIdentification<C, I> extends AbstractIdentification<C, I> {

    		private final IdentifierPolicy<I> identifierPolicy;

    		private SingleColumnIdentification(EntityMappingConfiguration<C, I> identificationDefiner, IdentifierPolicy<I> identifierPolicy) {
    			super(identificationDefiner);
    			this.identifierPolicy = identifierPolicy;
    		}

    		public IdentifierPolicy<I> getIdentifierPolicy() {
    			return identifierPolicy;
    		}
    	}

    	/**
    	 * Identification for multiple-column case
    	 *
    	 * @param <C> persisted entity type
    	 * @param <I> identifier type
    	 */
    	public static class CompositeKeyIdentification<C, I> extends AbstractIdentification<C, I> {

    		private final Consumer<C> markAsPersistedFunction;

    		private final Function<C, Boolean> isPersistedFunction;

    		private Map<ReadWritePropertyAccessPoint<I, Object>, Column<Table, Object>> compositeKeyMapping;

    		private CompositeKeyIdentification(EntityMappingConfiguration<C, I> identificationDefiner,
    										   Consumer<C> markAsPersistedFunction,
    										   Function<C, Boolean> isPersistedFunction) {
    			super(identificationDefiner);
    			this.markAsPersistedFunction = markAsPersistedFunction;
    			this.isPersistedFunction = isPersistedFunction;
    		}

    		public Consumer<C> getMarkAsPersistedFunction() {
    			return markAsPersistedFunction;
    		}

    		public Function<C, Boolean> getIsPersistedFunction() {
    			return isPersistedFunction;
    		}

    		public Map<ReadWritePropertyAccessPoint<I, Object>, Column<Table, Object>> getCompositeKeyMapping() {
    			return compositeKeyMapping;
    		}

    		public void setCompositeKeyMapping(Map<? extends ReadWritePropertyAccessPoint<I, Object>, ? extends Column<?, Object>> compositeKeyMapping) {
    			this.compositeKeyMapping = (Map<ReadWritePropertyAccessPoint<I, Object>, Column<Table, Object>>) compositeKeyMapping;
    		}
    	}
    }