# MergeJoinNode.java

    package org.codefilarete.stalactite.engine.runtime.load;

    import javax.annotation.Nullable;

    import java.util.IdentityHashMap;
    import java.util.Set;

    import org.codefilarete.stalactite.engine.runtime.load.EntityJoinTree.JoinType;
    import org.codefilarete.stalactite.engine.runtime.load.PassiveJoinNode.PassiveJoinRowConsumer;
    import org.codefilarete.stalactite.mapping.RowTransformer;
    import org.codefilarete.stalactite.query.api.Fromable;
    import org.codefilarete.stalactite.query.api.JoinLink;
    import org.codefilarete.stalactite.query.api.Selectable;
    import org.codefilarete.stalactite.sql.ddl.structure.Key;
    import org.codefilarete.stalactite.sql.result.ColumnedRow;
    import org.codefilarete.tool.Reflections;

    /**
     * Join node that fulfill some bean properties from a database row.
     *
     * @author Guillaume Mary
     */
    public class MergeJoinNode<C, T1 extends Fromable, T2 extends Fromable, I> extends AbstractJoinNode<C, T1, T2, I> {

    	private final EntityMerger<C> merger;

    	public MergeJoinNode(JoinNode<?, T1> parent,
    						 Key<T1, I> leftJoinColumn,
    						 Key<T2, I> rightJoinColumn,
    						 JoinType joinType,
    						 @Nullable String tableAlias,
    						 EntityMerger<C> merger) {
    		this(parent, leftJoinColumn, rightJoinColumn, joinType, tableAlias, merger, merger.getSelectableColumns());
    	}

    	public MergeJoinNode(JoinNode<?, T1> parent,
    						 Key<T1, I> leftJoinColumn,
    						 Key<T2, I> rightJoinColumn,
    						 JoinType joinType,
    						 @Nullable String tableAlias,
    						 EntityMerger<C> merger,
    						 Set<Selectable<?>> selectableColumns) {
    		super(parent, leftJoinColumn, rightJoinColumn, joinType, selectableColumns, tableAlias);
    		this.merger = merger;
    	}

    	public MergeJoinNode(JoinNode<?, T1> parent,
    						 Key<T1, I> leftJoinColumn,
    						 Key<T2, I> rightJoinColumn,
    						 JoinType joinType,
    						 @Nullable String tableAlias,
    						 EntityMerger<C> merger,
    						 Set<Selectable<?>> selectableColumns,
    						 IdentityHashMap<JoinLink<?, ?>, JoinLink<?, ?>> columnClones) {
    		super(parent, leftJoinColumn, rightJoinColumn, joinType, selectableColumns, tableAlias, columnClones);
    		this.merger = merger;
    	}

    	public EntityMerger<C> getMerger() {
    		return merger;
    	}

    	@Override
    	public MergeJoinRowConsumer<C> toConsumer(JoinNode<C, T2> joinNode) {
    		return new MergeJoinRowConsumer<>((MergeJoinNode<C, ?, ?, ?>) joinNode, merger.getRowTransformer());
    	}

    	/**
    	 * Design tip : Technically same as {@link PassiveJoinRowConsumer} but goal is different, that's why it has its own class
    	 *
    	 * @param <C>
    	 */
    	public static class MergeJoinRowConsumer<C> implements JoinRowConsumer {

    		private final MergeJoinNode<C, ?, ?, ?> node;
    		protected final RowTransformer<C> merger;

    		public MergeJoinRowConsumer(MergeJoinNode<C, ?, ?, ?> node, RowTransformer<C> merger) {
    			this.node = node;
    			this.merger = merger;
    		}

    		@Override
    		public MergeJoinNode<C, ?, ?, ?> getNode() {
    			return node;
    		}

    		void mergeProperties(C parentJoinEntity, ColumnedRow row) {
    			this.merger.applyRowToBean(row, parentJoinEntity);
    		}

    		/**
    		 * Implemented for debug. DO NOT RELY ON IT for anything else.
    		 */
    		@Override
    		public String toString() {
    			return Reflections.toString(this.getClass());
    		}
    	}
    }