class ListChangeMoveSelectorFactory<Solution_>
        extends AbstractMoveSelectorFactory<Solution_, ListChangeMoveSelectorConfig> {

    public ListChangeMoveSelectorFactory(ListChangeMoveSelectorConfig moveSelectorConfig) {
        super(moveSelectorConfig);
    }

    @Override
    protected MoveSelector<Solution_> buildBaseMoveSelector(HeuristicConfigPolicy<Solution_> configPolicy,
            SelectionCacheType minimumCacheType, boolean randomSelection) {
        checkUnfolded("valueSelectorConfig", config.getValueSelectorConfig());
        checkUnfolded("destinationSelectorConfig", config.getDestinationSelectorConfig());
        checkUnfolded("destinationEntitySelectorConfig", config.getDestinationSelectorConfig().getEntitySelectorConfig());
        checkUnfolded("destinationValueSelectorConfig", config.getDestinationSelectorConfig().getValueSelectorConfig());

        SelectionOrder selectionOrder = SelectionOrder.fromRandomSelectionBoolean(randomSelection);

        EntityDescriptor<Solution_> entityDescriptor = EntitySelectorFactory
                .<Solution_> create(config.getDestinationSelectorConfig().getEntitySelectorConfig())
                .extractEntityDescriptor(configPolicy);

        ValueSelector<Solution_> sourceValueSelector = ValueSelectorFactory
                .<Solution_> create(config.getValueSelectorConfig())
                .buildValueSelector(configPolicy, entityDescriptor, minimumCacheType, selectionOrder);

        if (!(sourceValueSelector instanceof EntityIndependentValueSelector)) {
            throw new IllegalArgumentException("The listChangeMoveSelector (" + config
                    + ") for a list variable needs to be based on an "
                    + EntityIndependentValueSelector.class.getSimpleName() + " (" + sourceValueSelector + ")."
                    + " Check your valueSelectorConfig.");
        }

        DestinationSelector<Solution_> destinationSelector = DestinationSelectorFactory
                .<Solution_> create(config.getDestinationSelectorConfig())
                .buildDestinationSelector(configPolicy, minimumCacheType, randomSelection);

        return new ListChangeMoveSelector<>(
                (EntityIndependentValueSelector<Solution_>) sourceValueSelector,
                destinationSelector,
                randomSelection);
    }

    @Override
    protected MoveSelectorConfig<?> buildUnfoldedMoveSelectorConfig(HeuristicConfigPolicy<Solution_> configPolicy) {<FILL_FUNCTION_BODY>}

    public static ListChangeMoveSelectorConfig buildChildMoveSelectorConfig(
            ListVariableDescriptor<?> variableDescriptor,
            ValueSelectorConfig inheritedValueSelectorConfig,
            DestinationSelectorConfig inheritedDestinationSelectorConfig) {

        ValueSelectorConfig childValueSelectorConfig = new ValueSelectorConfig(inheritedValueSelectorConfig);
        if (childValueSelectorConfig.getMimicSelectorRef() == null) {
            childValueSelectorConfig.setVariableName(variableDescriptor.getVariableName());
        }

        return new ListChangeMoveSelectorConfig()
                .withValueSelectorConfig(childValueSelectorConfig)
                .withDestinationSelectorConfig(new DestinationSelectorConfig(inheritedDestinationSelectorConfig)
                        .withEntitySelectorConfig(
                                Optional.ofNullable(inheritedDestinationSelectorConfig)
                                        .map(DestinationSelectorConfig::getEntitySelectorConfig)
                                        .map(EntitySelectorConfig::new) // use copy constructor if inherited not null
                                        .orElseGet(EntitySelectorConfig::new) // otherwise create new instance
                                        // override entity class (destination entity selector is never replaying)
                                        .withEntityClass(variableDescriptor.getEntityDescriptor().getEntityClass()))
                        .withValueSelectorConfig(
                                Optional.ofNullable(inheritedDestinationSelectorConfig)
                                        .map(DestinationSelectorConfig::getValueSelectorConfig)
                                        .map(ValueSelectorConfig::new) // use copy constructor if inherited not null
                                        .orElseGet(ValueSelectorConfig::new) // otherwise create new instance
                                        // override variable name (destination value selector is never replaying)
                                        .withVariableName(variableDescriptor.getVariableName())));
    }
}

        Collection<EntityDescriptor<Solution_>> entityDescriptors;
        EntityDescriptor<Solution_> onlyEntityDescriptor = config.getDestinationSelectorConfig() == null ? null
                : config.getDestinationSelectorConfig().getEntitySelectorConfig() == null ? null
                        : EntitySelectorFactory
                                .<Solution_> create(config.getDestinationSelectorConfig().getEntitySelectorConfig())
                                .extractEntityDescriptor(configPolicy);
        if (onlyEntityDescriptor != null) {
            entityDescriptors = Collections.singletonList(onlyEntityDescriptor);
        } else {
            entityDescriptors = configPolicy.getSolutionDescriptor().getGenuineEntityDescriptors();
        }
        if (entityDescriptors.size() > 1) {
            throw new IllegalArgumentException("The listChangeMoveSelector (" + config
                    + ") cannot unfold when there are multiple entities (" + entityDescriptors + ")."
                    + " Please use one listChangeMoveSelector per each planning list variable.");
        }
        EntityDescriptor<Solution_> entityDescriptor = entityDescriptors.iterator().next();

        List<ListVariableDescriptor<Solution_>> variableDescriptorList = new ArrayList<>();
        GenuineVariableDescriptor<Solution_> onlyVariableDescriptor = config.getValueSelectorConfig() == null ? null
                : ValueSelectorFactory.<Solution_> create(config.getValueSelectorConfig())
                        .extractVariableDescriptor(configPolicy, entityDescriptor);
        GenuineVariableDescriptor<Solution_> onlyDestinationVariableDescriptor =
                config.getDestinationSelectorConfig() == null ? null
                        : config.getDestinationSelectorConfig().getValueSelectorConfig() == null ? null
                                : ValueSelectorFactory
                                        .<Solution_> create(config.getDestinationSelectorConfig().getValueSelectorConfig())
                                        .extractVariableDescriptor(configPolicy, entityDescriptor);
        if (onlyVariableDescriptor != null && onlyDestinationVariableDescriptor != null) {
            if (!onlyVariableDescriptor.isListVariable()) {
                throw new IllegalArgumentException("The listChangeMoveSelector (" + config
                        + ") is configured to use a planning variable (" + onlyVariableDescriptor
                        + "), which is not a planning list variable."
                        + " Either fix your annotations and use a @" + PlanningListVariable.class.getSimpleName()
                        + " on the variable to make it work with listChangeMoveSelector"
                        + " or use a changeMoveSelector instead.");
            }
            if (!onlyDestinationVariableDescriptor.isListVariable()) {
                throw new IllegalArgumentException("The destinationSelector (" + config.getDestinationSelectorConfig()
                        + ") is configured to use a planning variable (" + onlyDestinationVariableDescriptor
                        + "), which is not a planning list variable.");
            }
            if (onlyVariableDescriptor != onlyDestinationVariableDescriptor) {
                throw new IllegalArgumentException("The listChangeMoveSelector's valueSelector ("
                        + config.getValueSelectorConfig()
                        + ") and destinationSelector's valueSelector ("
                        + config.getDestinationSelectorConfig().getValueSelectorConfig()
                        + ") must be configured for the same planning variable.");
            }
            if (onlyEntityDescriptor != null) {
                // No need for unfolding or deducing
                return null;
            }
            variableDescriptorList.add((ListVariableDescriptor<Solution_>) onlyVariableDescriptor);
        } else {
            variableDescriptorList.addAll(
                    entityDescriptor.getGenuineVariableDescriptorList().stream()
                            .filter(GenuineVariableDescriptor::isListVariable)
                            .map(variableDescriptor -> ((ListVariableDescriptor<Solution_>) variableDescriptor))
                            .collect(Collectors.toList()));
        }
        if (variableDescriptorList.isEmpty()) {
            throw new IllegalArgumentException("The listChangeMoveSelector (" + config
                    + ") cannot unfold because there are no planning list variables.");
        }
        if (variableDescriptorList.size() > 1) {
            throw new IllegalArgumentException("The listChangeMoveSelector (" + config
                    + ") cannot unfold because there are multiple planning list variables.");
        }
        ListChangeMoveSelectorConfig listChangeMoveSelectorConfig = buildChildMoveSelectorConfig(
                variableDescriptorList.get(0),
                config.getValueSelectorConfig(),
                config.getDestinationSelectorConfig());
        listChangeMoveSelectorConfig.inheritFolded(config);
        return listChangeMoveSelectorConfig;
    
<methods>public void <init>(org.optaplanner.core.config.heuristic.selector.move.generic.list.ListChangeMoveSelectorConfig) ,public MoveSelector<Solution_> buildMoveSelector(HeuristicConfigPolicy<Solution_>, org.optaplanner.core.config.heuristic.selector.common.SelectionCacheType, org.optaplanner.core.config.heuristic.selector.common.SelectionOrder) <variables>