# EntityIsManagedByPersisterAsserter.java

    package org.codefilarete.stalactite.engine.runtime;

    import java.util.List;
    import java.util.Objects;
    import java.util.Set;
    import java.util.function.Consumer;
    import java.util.stream.Collectors;

    import org.codefilarete.tool.Duo;
    import org.codefilarete.tool.Reflections;
    import org.codefilarete.tool.VisibleForTesting;
    import org.codefilarete.tool.collection.Iterables;

    /**
     * Persister which checks that given instances can be persisted by itself, throws exception if that's not the case.
     * Done in particular for inheritance cases where subtypes may have not been mapped but their entities are accepted (due to usual inheritance), this
     * would have resulted in partial persistence : only common (upper) part was took into account without warning user.
     *
     * Designed as a wrapper of an underlying delegate which really persists instances. Made as such for single responsibility consideration.
     *
     * @author Guillaume Mary
     */
    public class EntityIsManagedByPersisterAsserter<C, I> extends PersisterWrapper<C, I> {

    	private final Consumer<C> asserter;

    	public EntityIsManagedByPersisterAsserter(ConfiguredRelationalPersister<C, I> delegate) {
    		super(delegate);
    		if (getDeepestDelegate() instanceof PolymorphicPersister) {
    			Set<Class<? extends C>> supportedEntityTypes = ((PolymorphicPersister<C>) getDeepestDelegate()).getSupportedEntityTypes();
    			asserter = entity -> {
    				if (!supportedEntityTypes.contains(entity.getClass())) {
    					throw newAssertException(entity);
    				}
    			};
    		} else {
    			asserter = entity -> {
    				if (!getClassToPersist().equals(entity.getClass()))
    					throw newAssertException(entity);
    			};
    		}
    	}

    	private UnsupportedOperationException newAssertException(C entity) {
    		return new UnsupportedOperationException("Persister of " + Reflections.toString(getClassToPersist())
    						+ " is not configured to persist " + Reflections.toString(entity.getClass()));
    	}

    	@VisibleForTesting
    	void assertPersisterManagesEntities(Iterable<? extends C> entity) {
    		entity.forEach(this::assertPersisterManagesEntity);
    	}

    	@VisibleForTesting
    	void assertPersisterManagesEntity(C entity) {
    		asserter.accept(entity);
    	}

    	/* Please note that some methods were not overridden for calling assertion because the super implementation invokes some methods that do it,
    	 * hence in such a way we avoid multiple calls to assertion. Meanwhile, a Unit Test checks all of them.
    	 * Here they are:
    	 * - persist(entity)
    	 * - insert(C entity)
    	 * - update(C modified, C unmodified, boolean allColumnsStatement)
    	 * - update(C entity)
    	 * - update(Iterable<C> entities)
    	 * - updateById(C entity)
    	 * - delete(C entity)
    	 * - deleteById(C entity)
    	 */

    	@Override
    	public void persist(Iterable<? extends C> entities) {
    		assertPersisterManagesEntities(entities);
    		super.persist(entities);
    	}

    	@Override
    	public void insert(Iterable<? extends C> entities) {
    		assertPersisterManagesEntities(entities);
    		super.insert(entities);
    	}

    	@Override
    	public void update(Iterable<? extends Duo<C, C>> differencesIterable, boolean allColumnsStatement) {
    		// we clear asserter input from null because it doesn't support it, and it may happen in update cases of nullified one-to-one relation
    		List<C> nonNullEntities = Iterables.stream(differencesIterable).map(Duo::getLeft).filter(Objects::nonNull).collect(Collectors.toList());
    		assertPersisterManagesEntities(nonNullEntities);
    		super.update(differencesIterable, allColumnsStatement);
    	}

    	@Override
    	public void updateById(Iterable<? extends C> entities) {
    		assertPersisterManagesEntities(entities);
    		super.updateById(entities);
    	}

    	@Override
    	public void delete(Iterable<? extends C> entities) {
    		// we clear asserter input from null because it doesn't support it, and it may happen in update cases of nullified one-to-one relation
    		List<C> nonNullEntities = Iterables.stream(entities).filter(Objects::nonNull).collect(Collectors.toList());
    		assertPersisterManagesEntities(nonNullEntities);
    		super.delete(nonNullEntities);
    	}

    	@Override
    	public void deleteById(Iterable<? extends C> entities) {
    		assertPersisterManagesEntities(entities);
    		super.deleteById(entities);
    	}
    }