private void linkShadowSources()

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);
    }