# PrimaryKeyStep.java

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

    import java.util.Map;

    import org.codefilarete.reflection.AccessorDefinition;
    import org.codefilarete.reflection.ReadWritePropertyAccessPoint;
    import org.codefilarete.stalactite.dsl.entity.EntityMappingConfiguration;
    import org.codefilarete.stalactite.dsl.entity.EntityMappingConfiguration.KeyMapping;
    import org.codefilarete.stalactite.dsl.entity.EntityMappingConfiguration.SingleKeyMapping;
    import org.codefilarete.stalactite.dsl.idpolicy.GeneratedKeysPolicy;
    import org.codefilarete.stalactite.dsl.key.CompositeKeyMappingConfiguration;
    import org.codefilarete.stalactite.dsl.naming.ColumnNamingStrategy;
    import org.codefilarete.stalactite.dsl.naming.UniqueConstraintNamingStrategy;
    import org.codefilarete.stalactite.engine.configurer.AbstractIdentification;
    import org.codefilarete.stalactite.engine.configurer.AbstractIdentification.CompositeKeyIdentification;
    import org.codefilarete.stalactite.engine.configurer.AbstractIdentification.SingleColumnIdentification;
    import org.codefilarete.stalactite.engine.configurer.builder.embeddable.EmbeddableMapping;
    import org.codefilarete.stalactite.engine.configurer.builder.embeddable.EmbeddableMappingBuilder;
    import org.codefilarete.stalactite.engine.configurer.entity.CompositeKeyLinkageSupport;
    import org.codefilarete.stalactite.sql.ddl.structure.Column;
    import org.codefilarete.stalactite.sql.ddl.structure.PrimaryKey;
    import org.codefilarete.stalactite.sql.ddl.structure.Table;
    import org.codefilarete.stalactite.sql.statement.binder.ColumnBinderRegistry;
    import org.codefilarete.tool.VisibleForTesting;

    import static org.codefilarete.tool.Nullable.nullable;

    public class PrimaryKeyStep<C, I> {

    	/**
    	 * Creates primary key on table owning identification
    	 *
    	 * @param identification information that allow to create primary key
    	 * @return the created {@link PrimaryKey}
    	 */
    	@VisibleForTesting
    	<T extends Table<T>> PrimaryKey<T, I> addIdentifyingPrimarykey(AbstractIdentification<C, I> identification,
    																   Map<EntityMappingConfiguration, Table> tableMap,
    																   ColumnBinderRegistry columnBinderRegistry,
    																   ColumnNamingStrategy columnNamingStrategy,
    																   UniqueConstraintNamingStrategy uniqueConstraintNamingStrategy) {
    		T pkTable = (T) tableMap.get(identification.getIdentificationDefiner());
    		KeyMapping<C, I> keyLinkage = identification.getKeyLinkage();
    		AccessorDefinition identifierDefinition = AccessorDefinition.giveDefinition(identification.getIdAccessor());
    		if (identification instanceof AbstractIdentification.CompositeKeyIdentification) {
    			CompositeKeyMappingConfiguration<I> configuration = ((CompositeKeyLinkageSupport<C, I>) keyLinkage).getCompositeKeyMappingBuilder().getConfiguration();
    			// Note that we won't care about readonly column returned by build(..) since we're on a primary key case, then
    			// some readonly columns would be nonsense
    			EmbeddableMappingBuilder<I, T> compositeKeyBuilder = new EmbeddableMappingBuilder<>(configuration, pkTable, columnBinderRegistry, columnNamingStrategy, uniqueConstraintNamingStrategy);
    			EmbeddableMapping<I, T> build = compositeKeyBuilder.build();
    			Map<ReadWritePropertyAccessPoint<I, Object>, Column<T, Object>> compositeKeyMapping = build.getMapping();
    			compositeKeyMapping.values().forEach(Column::primaryKey);
    			((CompositeKeyIdentification<C, I>) identification).setCompositeKeyMapping(compositeKeyMapping);
    		} else {
    			String columnName = determineColumnName((SingleKeyMapping) keyLinkage, columnNamingStrategy);
    			// nullability = false may not be necessary because of primary key, let for principle
    			Column<?, I> primaryKey = pkTable.addColumn(columnName, identifierDefinition.getMemberType(), ((SingleKeyMapping<C, I>) keyLinkage).getColumnOptions().getColumnSize(), false);
    			primaryKey.primaryKey();
    			if (((SingleColumnIdentification<C, I>) identification).getIdentifierPolicy() instanceof GeneratedKeysPolicy) {
    				primaryKey.autoGenerated();
    			}
    		}
    		return pkTable.getPrimaryKey();
    	}

    	private <O> String determineColumnName(SingleKeyMapping<C, O> keyLinkage, ColumnNamingStrategy columnNamingStrategy) {
    		return nullable(keyLinkage.getColumnOptions().getColumnName())
    				.elseSet(keyLinkage::getFieldName)
    				.elseSet(() -> columnNamingStrategy.giveName(AccessorDefinition.giveDefinition(keyLinkage.getAccessor())))
    				.get();
    	}
    }