# KeyValueRecord.java

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

    import org.codefilarete.reflection.DefaultReadWritePropertyAccessPoint;
    import org.codefilarete.reflection.ReadWritePropertyAccessPoint;
    import org.codefilarete.stalactite.engine.diff.CollectionDiffer;

    /**
     * Represents a line in table storage. Acts as a wrapper of map entry (key and value) with source bean identifier addition
     * (to store foreign key to source entity)
     *
     * @param <K> Map entry key type
     * @param <V> Map entry value type
     * @param <ID> source bean identifier type
     */
    // Made public due to protected methods in MapRelationConfigurer expecting arguments to be accessible from any overriding class
    public class KeyValueRecord<K, V, ID> {

    	static final ReadWritePropertyAccessPoint<KeyValueRecord<Object, Object, Object>, Object> KEY_ACCESSOR = DefaultReadWritePropertyAccessPoint.fromMethodReference(
    			KeyValueRecord::getKey,
    			KeyValueRecord::setKey);

    	public static final ReadWritePropertyAccessPoint<KeyValueRecord<Object, Object, Object>, Object> VALUE_ACCESSOR = DefaultReadWritePropertyAccessPoint.fromMethodReference(
    			KeyValueRecord::getValue,
    			KeyValueRecord::setValue);

    	private RecordId<K, ID> id;
    	private K key;
    	private V value;
    	private boolean persisted = false;

    	/**
    	 * Default constructor for select instantiation
    	 */
    	public KeyValueRecord() {
    	}

    	public KeyValueRecord(ID id, K key, V value) {
    		this.id = new RecordId<>(id, key);
    		this.key = key;
    		this.value = value;
    	}

    	public boolean isNew() {
    		return !persisted;
    	}

    	public boolean isPersisted() {
    		return persisted;
    	}

    	public void markAsPersisted() {
    		this.persisted = true;
    	}

    	public KeyValueRecord<K, V, ID> setPersisted(boolean persisted) {
    		this.persisted = persisted;
    		return this;
    	}

    	public RecordId<K, ID> getId() {
    		return id;
    	}

    	public void setId(RecordId<K, ID> id) {
    		this.id = id;
    		this.persisted = true;
    	}

    	public K getKey() {
    		return key;
    	}

    	public void setKey(K key) {
    		this.key = key;
    	}

    	public V getValue() {
    		return value;
    	}

    	public void setValue(V value) {
    		this.value = value;
    	}

    	/**
    	 * Identifier for {@link CollectionDiffer} support (update use case), because it compares beans
    	 * through their "footprint" which is their id in default/entity case, but since we are value type, we must provide a dedicated footprint.
    	 * Could be hashCode() if it was implemented on identifier + element, but implementing it would require implementing equals() (to comply
    	 * with best practices) which is not our case nor required by {@link CollectionDiffer}.
    	 * Note : name of this method is not important
    	 */
    	public int footprint() {
    		// Footprint is composed of id + key only, not value, because its le logic of what's expected ! if you're not
    		// convinced, remember that adding value to it makes the whole record being different when the value of an entry
    		// changes ( put(1, "a") then put(1, "b") ), though it makes differ mechanism do a delete + insert instead of
    		// a simple update, which is less optimal.
    		int result = id.hashCode();
    		result = 31 * result + key.hashCode();
    		return result;
    	}
    }