ManyToManyAssociationConfiguration.java
package org.codefilarete.stalactite.engine.configurer.manytomany;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.function.Supplier;
import org.codefilarete.reflection.AccessorByMethod;
import org.codefilarete.reflection.AccessorByMethodReference;
import org.codefilarete.reflection.AccessorDefinition;
import org.codefilarete.reflection.Accessors;
import org.codefilarete.reflection.MethodReferenceCapturer;
import org.codefilarete.reflection.Mutator;
import org.codefilarete.reflection.MutatorByMethod;
import org.codefilarete.reflection.MutatorByMethodReference;
import org.codefilarete.reflection.PropertyAccessor;
import org.codefilarete.reflection.ReversibleAccessor;
import org.codefilarete.stalactite.dsl.naming.ColumnNamingStrategy;
import org.codefilarete.stalactite.dsl.naming.ForeignKeyNamingStrategy;
import org.codefilarete.stalactite.engine.configurer.manytomany.ManyToManyRelation.MappedByConfiguration;
import org.codefilarete.stalactite.engine.runtime.ConfiguredRelationalPersister;
import org.codefilarete.stalactite.sql.ddl.structure.PrimaryKey;
import org.codefilarete.stalactite.sql.ddl.structure.Table;
import org.codefilarete.tool.Nullable;
import org.codefilarete.tool.Reflections;
import org.danekja.java.util.function.serializable.SerializableBiConsumer;
import org.danekja.java.util.function.serializable.SerializableFunction;
import static org.codefilarete.tool.Nullable.nullable;
/**
* Class that stores elements necessary to many-to-many association configuration
* @author Guillaume Mary
*/
class ManyToManyAssociationConfiguration<SRC, TRGT, SRCID, TRGTID, C1 extends Collection<TRGT>, C2 extends Collection<SRC>,
LEFTTABLE extends Table<LEFTTABLE>, RIGHTTABLE extends Table<RIGHTTABLE>> {
private final ManyToManyRelation<SRC, TRGT, TRGTID, C1, C2> manyToManyRelation;
private final ConfiguredRelationalPersister<SRC, SRCID> srcPersister;
private final PrimaryKey<LEFTTABLE, SRCID> leftPrimaryKey;
private final ForeignKeyNamingStrategy foreignKeyNamingStrategy;
private final ColumnNamingStrategy indexColumnNamingStrategy;
private final ReversibleAccessor<SRC, C1> collectionGetter;
private final Mutator<SRC, C1> setter;
private final boolean orphanRemoval;
private final boolean writeAuthorized;
private final AccessorDefinition accessorDefinition;
private final Supplier<C1> collectionFactory;
ManyToManyAssociationConfiguration(ManyToManyRelation<SRC, TRGT, TRGTID, C1, C2> manyToManyRelation,
ConfiguredRelationalPersister<SRC, SRCID> srcPersister,
PrimaryKey<LEFTTABLE, SRCID> leftPrimaryKey,
ForeignKeyNamingStrategy foreignKeyNamingStrategy,
ColumnNamingStrategy indexColumnNamingStrategy,
boolean orphanRemoval,
boolean writeAuthorized) {
this.manyToManyRelation = manyToManyRelation;
this.srcPersister = srcPersister;
this.leftPrimaryKey = leftPrimaryKey;
this.foreignKeyNamingStrategy = foreignKeyNamingStrategy;
this.collectionGetter = manyToManyRelation.getCollectionAccessor();
this.indexColumnNamingStrategy = indexColumnNamingStrategy;
this.setter = collectionGetter.toMutator();
// we don't use AccessorDefinition.giveMemberDefinition(..) because it gives a cross-member definition, loosing get/set for example,
// whereas we need this information to build better association table name
this.orphanRemoval = orphanRemoval;
this.writeAuthorized = writeAuthorized;
this.accessorDefinition = AccessorDefinition.giveDefinition(this.manyToManyRelation.getCollectionAccessor()); //buildAccessorDefinition();
this.collectionFactory = buildCollectionFactory();
}
private Supplier<C1> buildCollectionFactory() {
Supplier<C1> result = manyToManyRelation.getCollectionFactory();
if (result == null) {
result = Reflections.giveCollectionFactory((Class<C1>) this.accessorDefinition.getMemberType());
}
return result;
}
public ManyToManyRelation<SRC, TRGT, TRGTID, C1, C2> getManyToManyRelation() {
return manyToManyRelation;
}
public ColumnNamingStrategy getIndexColumnNamingStrategy() {
return indexColumnNamingStrategy;
}
public ConfiguredRelationalPersister<SRC, SRCID> getSrcPersister() {
return srcPersister;
}
public PrimaryKey<LEFTTABLE, SRCID> getLeftPrimaryKey() {
return leftPrimaryKey;
}
public ForeignKeyNamingStrategy getForeignKeyNamingStrategy() {
return foreignKeyNamingStrategy;
}
public ReversibleAccessor<SRC, C1> getCollectionGetter() {
return collectionGetter;
}
public Mutator<SRC, C1> getSetter() {
return setter;
}
public boolean isOrphanRemoval() {
return orphanRemoval;
}
public boolean isWriteAuthorized() {
return writeAuthorized;
}
/**
* Equivalent to {@link ManyToManyRelation#getCollectionAccessor()} but used for table and colum naming.
*/
public AccessorDefinition getAccessorDefinition() {
return accessorDefinition;
}
/**
* Gives the collection factory used to instantiate relation field.
*
* @return the one given by {@link ManyToManyRelation#getCollectionFactory()} or one deduced from member signature
*/
protected Supplier<C1> getCollectionFactory() {
return collectionFactory;
}
/**
* Build the accessor for the reverse property, made of configured getter and setter. If one of them is unavailable, it's deduced from the
* present one. If both are absent, null is returned.
*
* @return null if no getter nor setter were defined
*/
@javax.annotation.Nullable
PropertyAccessor<TRGT, C2> buildReversePropertyAccessor() {
MappedByConfiguration<SRC, TRGT, C2> mappedByConfiguration = manyToManyRelation.getMappedByConfiguration();
Nullable<AccessorByMethodReference<TRGT, C2>> getterReference = nullable(mappedByConfiguration.getReverseCollectionAccessor()).map(Accessors::accessorByMethodReference);
Nullable<MutatorByMethodReference<TRGT, C2>> setterReference = nullable(mappedByConfiguration.getReverseCollectionMutator()).map(Accessors::mutatorByMethodReference);
if (getterReference.isAbsent() && setterReference.isAbsent()) {
return null;
} else if (getterReference.isPresent() && setterReference.isPresent()) {
// we keep close to user demand : we keep its method references
return new PropertyAccessor<>(getterReference.get(), setterReference.get());
} else if (getterReference.isPresent() && setterReference.isAbsent()) {
// we keep close to user demand : we keep its method reference ...
// ... but we can't do it for mutator, so we use the most equivalent manner : a mutator based on setter method (fallback to property if not present)
return new PropertyAccessor<>(getterReference.get(), new AccessorByMethod<TRGT, C2>(captureMethod(mappedByConfiguration.getReverseCollectionAccessor())).toMutator());
} else {
// we keep close to user demand : we keep its method reference ...
// ... but we can't do it for getter, so we use the most equivalent manner : a mutator based on setter method (fallback to property if not present)
return new PropertyAccessor<>(new MutatorByMethod<TRGT, C2>(captureMethod(mappedByConfiguration.getReverseCollectionMutator())).toAccessor(), setterReference.get());
}
}
private final MethodReferenceCapturer methodSpy = new MethodReferenceCapturer();
private Method captureMethod(SerializableFunction getter) {
return this.methodSpy.findMethod(getter);
}
private Method captureMethod(SerializableBiConsumer setter) {
return this.methodSpy.findMethod(setter);
}
}