ElementCollectionRelation.java
package org.codefilarete.stalactite.engine.configurer.elementcollection;
import java.util.Collection;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import org.codefilarete.reflection.Accessor;
import org.codefilarete.reflection.AccessorByMethodReference;
import org.codefilarete.reflection.AccessorChain;
import org.codefilarete.reflection.AccessorChain.ValueInitializerOnNullValue;
import org.codefilarete.reflection.Accessors;
import org.codefilarete.reflection.MutatorByMethodReference;
import org.codefilarete.reflection.ReadWriteAccessorChain;
import org.codefilarete.reflection.ReadWritePropertyAccessPoint;
import org.codefilarete.reflection.SerializableAccessor;
import org.codefilarete.reflection.SerializableMutator;
import org.codefilarete.reflection.SerializablePropertyAccessor;
import org.codefilarete.reflection.SerializablePropertyMutator;
import org.codefilarete.reflection.ValueAccessPoint;
import org.codefilarete.reflection.ValueAccessPointMap;
import org.codefilarete.stalactite.dsl.embeddable.EmbeddableMappingConfigurationProvider;
import org.codefilarete.stalactite.sql.ddl.Size;
import org.codefilarete.stalactite.sql.ddl.structure.Column;
import org.codefilarete.stalactite.sql.ddl.structure.Table;
import org.codefilarete.tool.Reflections;
/**
* Support for element-collection configuration
*
* @author Guillaume Mary
*/
public class ElementCollectionRelation<SRC, TRGT, S extends Collection<TRGT>> {
/** The method that gives the entities from the "root" entity */
private final ReadWritePropertyAccessPoint<SRC, S> collectionAccessor;
private final Class<TRGT> componentType;
/** Indicator to store element indices in the database, retrieve them, and sort the collection while loading the entities */
private boolean ordered = false;
/** Optional provider of collection instance to be used if collection value is null */
private Supplier<S> collectionFactory;
private Table targetTable;
private String targetTableName;
private Column<Table, ?> reverseColumn;
private String reverseColumnName;
/** Element column name override, used in simple case : {@link EmbeddableMappingConfigurationProvider} null, aka not when element is a complex type */
private String elementColumnName;
/** Index column name override */
private String indexingColumnName;
/** Complex type mapping, optional */
@Nullable
private final EmbeddableMappingConfigurationProvider<TRGT> embeddableConfigurationProvider;
/** Complex type mapping override, to be used when {@link EmbeddableMappingConfigurationProvider} is not null */
private final ValueAccessPointMap<TRGT, String, ValueAccessPoint<TRGT>> overriddenColumnNames = new ValueAccessPointMap<>();
/** Complex type mapping override, to be used when {@link EmbeddableMappingConfigurationProvider} is not null */
private final ValueAccessPointMap<TRGT, Size, ValueAccessPoint<TRGT>> overriddenColumnSizes = new ValueAccessPointMap<>();
private Size elementColumnSize;
/**
* @param setter collection accessor
* @param componentType element type in collection
* @param embeddableConfigurationProvider complex type mapping, null when element is a simple type (String, Integer, ...)
*/
public ElementCollectionRelation(SerializablePropertyMutator<SRC, S> setter,
Class<TRGT> componentType,
@Nullable EmbeddableMappingConfigurationProvider<TRGT> embeddableConfigurationProvider) {
// we keep close to user demand : we keep its method reference
this.collectionAccessor = Accessors.readWriteAccessPoint(setter);
this.componentType = componentType;
this.embeddableConfigurationProvider = embeddableConfigurationProvider;
}
/**
* @param getter collection accessor
* @param componentType element type in collection
* @param embeddableConfigurationProvider complex type mapping, null when element is a simple type (String, Integer, ...)
*/
public ElementCollectionRelation(SerializablePropertyAccessor<SRC, S> getter,
Class<TRGT> componentType,
@Nullable EmbeddableMappingConfigurationProvider<TRGT> embeddableConfigurationProvider) {
// we keep close to user demand : we keep its method reference
this.collectionAccessor = Accessors.readWriteAccessPoint(getter);
this.componentType = componentType;
this.embeddableConfigurationProvider = embeddableConfigurationProvider;
}
public ElementCollectionRelation(ReadWritePropertyAccessPoint<SRC, S> collectionAccessor ,
Class<TRGT> componentType,
@Nullable EmbeddableMappingConfigurationProvider<TRGT> embeddableConfigurationProvider) {
this.collectionAccessor = collectionAccessor;
this.componentType = componentType;
this.embeddableConfigurationProvider = embeddableConfigurationProvider;
}
public ReadWritePropertyAccessPoint<SRC, S> getCollectionAccessor() {
return collectionAccessor;
}
public boolean isOrdered() {
return this.ordered;
}
public void setOrdered(boolean ordered) {
this.ordered = ordered;
}
public void ordered() {
this.ordered = true;
}
public void setIndexingColumnName(String columnName) {
ordered();
this.indexingColumnName = columnName;
}
public String getIndexingColumnName() {
return indexingColumnName;
}
public Supplier<S> getCollectionFactory() {
return collectionFactory;
}
public ElementCollectionRelation<SRC, TRGT, S> setCollectionFactory(Supplier<? extends S> collectionFactory) {
this.collectionFactory = (Supplier<S>) collectionFactory;
return this;
}
public ValueAccessPointMap<TRGT, String, ValueAccessPoint<TRGT>> getOverriddenColumnNames() {
return this.overriddenColumnNames;
}
public void overrideName(SerializableAccessor<TRGT, ?> methodRef, String columnName) {
this.overriddenColumnNames.put(new AccessorByMethodReference<>(methodRef), columnName);
}
public void overrideName(SerializableMutator<TRGT, ?> methodRef, String columnName) {
this.overriddenColumnNames.put(new MutatorByMethodReference<>(methodRef), columnName);
}
public void overrideSize(SerializableAccessor<TRGT, ?> methodRef, Size columnSize) {
this.overriddenColumnSizes.put(new AccessorByMethodReference<>(methodRef), columnSize);
}
public void overrideSize(SerializableMutator<TRGT, ?> methodRef, Size columnSize) {
this.overriddenColumnSizes.put(new MutatorByMethodReference<>(methodRef), columnSize);
}
public ValueAccessPointMap<TRGT, Size, ValueAccessPoint<TRGT>> getOverriddenColumnSizes() {
return this.overriddenColumnSizes;
}
public Table getTargetTable() {
return targetTable;
}
public void setTargetTable(Table targetTable) {
this.targetTable = targetTable;
}
public String getTargetTableName() {
return targetTableName;
}
public void setTargetTableName(String targetTableName) {
this.targetTableName = targetTableName;
}
public <I> Column<Table, I> getReverseColumn() {
return (Column<Table, I>) reverseColumn;
}
public void setReverseColumn(Column<Table, ?> reverseColumn) {
this.reverseColumn = reverseColumn;
}
public String getReverseColumnName() {
return reverseColumnName;
}
public ElementCollectionRelation<SRC, TRGT, S> setReverseColumnName(String reverseColumnName) {
this.reverseColumnName = reverseColumnName;
return this;
}
public Class<TRGT> getComponentType() {
return componentType;
}
public EmbeddableMappingConfigurationProvider<TRGT> getEmbeddableConfigurationProvider() {
return embeddableConfigurationProvider;
}
public void setElementColumnName(String columnName) {
this.elementColumnName = columnName;
}
public String getElementColumnName() {
return elementColumnName;
}
public void setElementColumnSize(Size elementColumnSize) {
this.elementColumnSize = elementColumnSize;
}
public Size getElementColumnSize() {
return elementColumnSize;
}
/**
* Clones this object to make one with the given accessor as prefix of current one.
* Made to "slide" current instance with an accessor prefix. Used for embeddable objects with relation to make the relation being accessible
* from the "root" entity.
*
* @param accessor the prefix of the clone to be created
* @param embeddedType the concrete type of the embeddable bean, because accessor may provide an abstraction
* @return a clones of this instance prefixed with the given accessor
* @param <C> the root entity type that owns the embeddable which has this relation
*/
public <C> ElementCollectionRelation<C, TRGT, S> embedInto(Accessor<C, SRC> accessor, Class<SRC> embeddedType) {
AccessorChain<C, S> shiftedTargetProvider = new AccessorChain<>(accessor, collectionAccessor);
shiftedTargetProvider.setNullValueHandler(new ValueInitializerOnNullValue() {
@Override
protected <T> T newInstance(Accessor<?, T> segmentAccessor, Class<T> valueType) {
if (segmentAccessor == accessor) {
return (T) Reflections.newInstance(embeddedType);
} else if (segmentAccessor == collectionAccessor){
if (collectionFactory != null) {
return (T) collectionFactory.get();
} else {
return super.newInstance(segmentAccessor, valueType);
}
} else {
return super.newInstance(segmentAccessor, valueType);
}
}
});
ElementCollectionRelation<C, TRGT, S> result = new ElementCollectionRelation<>(new ReadWriteAccessorChain<>(shiftedTargetProvider), componentType, embeddableConfigurationProvider);
result.setElementColumnName(this.getElementColumnName());
result.setElementColumnSize(this.getElementColumnSize());
result.setTargetTable(this.getTargetTable());
result.setTargetTableName(this.getTargetTableName());
result.setReverseColumn(this.getReverseColumn());
result.setReverseColumnName(this.getReverseColumnName());
result.getOverriddenColumnNames().putAll(this.getOverriddenColumnNames());
result.getOverriddenColumnSizes().putAll(this.getOverriddenColumnSizes());
result.setOrdered(this.isOrdered());
result.setIndexingColumnName(this.getIndexingColumnName());
result.setCollectionFactory(this.getCollectionFactory());
return result;
}
}