in core/optaplanner-core-impl/src/main/java/org/optaplanner/core/impl/domain/variable/inverserelation/InverseRelationShadowVariableDescriptor.java [75:148]
private void linkShadowSources(DescriptorPolicy descriptorPolicy) {
InverseRelationShadowVariable shadowVariableAnnotation = variableMemberAccessor
.getAnnotation(InverseRelationShadowVariable.class);
Class<?> variablePropertyType = getVariablePropertyType();
Class<?> sourceClass;
if (Collection.class.isAssignableFrom(variablePropertyType)) {
Type genericType = variableMemberAccessor.getGenericType();
sourceClass = ConfigUtils.extractCollectionGenericTypeParameterLeniently(
"entityClass", entityDescriptor.getEntityClass(),
variablePropertyType, genericType,
InverseRelationShadowVariable.class, variableMemberAccessor.getName()).orElse(Object.class);
singleton = false;
} else {
sourceClass = variablePropertyType;
singleton = true;
}
EntityDescriptor<Solution_> sourceEntityDescriptor = getEntityDescriptor().getSolutionDescriptor()
.findEntityDescriptor(sourceClass);
if (sourceEntityDescriptor == null) {
throw new IllegalArgumentException("The entityClass (" + entityDescriptor.getEntityClass()
+ ") has an @" + InverseRelationShadowVariable.class.getSimpleName()
+ " annotated property (" + variableMemberAccessor.getName()
+ ") with a sourceClass (" + sourceClass
+ ") which is not a valid planning entity."
+ "\nMaybe check the annotations of the class (" + sourceClass + ")."
+ "\nMaybe add the class (" + sourceClass
+ ") among planning entities in the solver configuration.");
}
String sourceVariableName = shadowVariableAnnotation.sourceVariableName();
// TODO can we getGenuineVariableDescriptor()?
sourceVariableDescriptor = sourceEntityDescriptor.getVariableDescriptor(sourceVariableName);
if (sourceVariableDescriptor == null) {
throw new IllegalArgumentException("The entityClass (" + entityDescriptor.getEntityClass()
+ ") has an @" + InverseRelationShadowVariable.class.getSimpleName()
+ " annotated property (" + variableMemberAccessor.getName()
+ ") with sourceVariableName (" + sourceVariableName
+ ") which is not a valid planning variable on entityClass ("
+ sourceEntityDescriptor.getEntityClass() + ").\n"
+ sourceEntityDescriptor.buildInvalidVariableNameExceptionMessage(sourceVariableName));
}
chained = (sourceVariableDescriptor instanceof GenuineVariableDescriptor) &&
((GenuineVariableDescriptor<Solution_>) sourceVariableDescriptor).isChained();
boolean list = (sourceVariableDescriptor instanceof GenuineVariableDescriptor) &&
((GenuineVariableDescriptor<Solution_>) sourceVariableDescriptor).isListVariable();
if (singleton) {
if (!chained && !list) {
throw new IllegalArgumentException("The entityClass (" + entityDescriptor.getEntityClass()
+ ") has an @" + InverseRelationShadowVariable.class.getSimpleName()
+ " annotated property (" + variableMemberAccessor.getName()
+ ") which does not return a " + Collection.class.getSimpleName()
+ " with sourceVariableName (" + sourceVariableName
+ ") which is neither a list variable @" + PlanningListVariable.class.getSimpleName()
+ " nor a chained variable @" + PlanningVariable.class.getSimpleName()
+ "(graphType=" + PlanningVariableGraphType.CHAINED + ")."
+ " Only list and chained variables support a singleton inverse.");
}
} else {
if (chained || list) {
throw new IllegalArgumentException("The entityClass (" + entityDescriptor.getEntityClass()
+ ") has an @" + InverseRelationShadowVariable.class.getSimpleName()
+ " annotated property (" + variableMemberAccessor.getName()
+ ") which returns a " + Collection.class.getSimpleName()
+ " (" + variablePropertyType
+ ") with sourceVariableName (" + sourceVariableName
+ ") which is a" + (chained
? " chained variable @" + PlanningVariable.class.getSimpleName()
+ "(graphType=" + PlanningVariableGraphType.CHAINED
+ "). A chained variable supports only a singleton inverse."
: " list variable @" + PlanningListVariable.class.getSimpleName()
+ ". A list variable supports only a singleton inverse."));
}
}
sourceVariableDescriptor.registerSinkVariableDescriptor(this);
}