/*
* Copyright 2012 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.drools.planner.config.heuristic.selector.value;
import java.util.Collection;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import org.drools.planner.config.EnvironmentMode;
import org.drools.planner.config.heuristic.selector.SelectorConfig;
import org.drools.planner.config.heuristic.selector.common.SelectionOrder;
import org.drools.planner.config.util.ConfigUtils;
import org.drools.planner.core.domain.entity.PlanningEntityDescriptor;
import org.drools.planner.core.domain.solution.SolutionDescriptor;
import org.drools.planner.core.domain.variable.PlanningVariableDescriptor;
import org.drools.planner.core.heuristic.selector.common.SelectionCacheType;
import org.drools.planner.core.heuristic.selector.common.decorator.SelectionProbabilityWeightFactory;
import org.drools.planner.core.heuristic.selector.entity.decorator.CachingEntitySelector;
import org.drools.planner.core.heuristic.selector.entity.decorator.ShufflingEntitySelector;
import org.drools.planner.core.heuristic.selector.value.decorator.CachingValueSelector;
import org.drools.planner.core.heuristic.selector.value.decorator.ProbabilityValueSelector;
import org.drools.planner.core.heuristic.selector.value.FromSolutionPropertyValueSelector;
import org.drools.planner.core.heuristic.selector.value.ValueSelector;
import org.drools.planner.core.heuristic.selector.value.decorator.ShufflingValueSelector;
@XStreamAlias("valueSelector")
public class ValueSelectorConfig extends SelectorConfig {
protected String planningVariableName = null;
protected SelectionCacheType cacheType = null;
protected SelectionOrder selectionOrder = null;
// TODO filterClass
protected Class<? extends SelectionProbabilityWeightFactory> valueProbabilityWeightFactoryClass = null;
// TODO sorterClass, increasingStrength
public String getPlanningVariableName() {
return planningVariableName;
}
public void setPlanningVariableName(String planningVariableName) {
this.planningVariableName = planningVariableName;
}
public SelectionCacheType getCacheType() {
return cacheType;
}
public void setCacheType(SelectionCacheType cacheType) {
this.cacheType = cacheType;
}
public SelectionOrder getSelectionOrder() {
return selectionOrder;
}
public void setSelectionOrder(SelectionOrder selectionOrder) {
this.selectionOrder = selectionOrder;
}
public Class<? extends SelectionProbabilityWeightFactory> getValueProbabilityWeightFactoryClass() {
return valueProbabilityWeightFactoryClass;
}
public void setValueProbabilityWeightFactoryClass(Class<? extends SelectionProbabilityWeightFactory> valueProbabilityWeightFactoryClass) {
this.valueProbabilityWeightFactoryClass = valueProbabilityWeightFactoryClass;
}
// ************************************************************************
// Builder methods
// ************************************************************************
/**
*
* @param environmentMode never null
* @param solutionDescriptor never null
* @param entityDescriptor never null
* @param minimumCacheType never null, If caching is used (different from {@link SelectionCacheType#JUST_IN_TIME}),
* then it should be at least this {@link SelectionCacheType} because an ancestor already uses such caching
* and less would be pointless.
* @param inheritedSelectionOrder never null
* @return never null
*/
public ValueSelector buildValueSelector(EnvironmentMode environmentMode,
SolutionDescriptor solutionDescriptor, PlanningEntityDescriptor entityDescriptor,
SelectionCacheType minimumCacheType, SelectionOrder inheritedSelectionOrder) {
PlanningVariableDescriptor variableDescriptor = fetchVariableDescriptor(entityDescriptor);
SelectionCacheType resolvedCacheType = SelectionCacheType.resolve(cacheType, minimumCacheType);
minimumCacheType = SelectionCacheType.max(minimumCacheType, resolvedCacheType);
SelectionOrder resolvedSelectionOrder = SelectionOrder.resolve(selectionOrder,
inheritedSelectionOrder);
// baseValueSelector and lower should be SelectionOrder.ORIGINAL if they are going to get cached completely
ValueSelector valueSelector = buildBaseValueSelector(environmentMode, variableDescriptor,
minimumCacheType, resolvedCacheType.isCached() ? SelectionOrder.ORIGINAL : resolvedSelectionOrder);
boolean alreadyCached = false;
// TODO filterclass
if (valueProbabilityWeightFactoryClass != null) {
if (resolvedSelectionOrder != SelectionOrder.RANDOM) {
throw new IllegalArgumentException("The valueSelectorConfig (" + this
+ ") with valueProbabilityWeightFactoryClass ("
+ valueProbabilityWeightFactoryClass + ") has a non-random resolvedSelectionOrder ("
+ resolvedSelectionOrder + ").");
}
SelectionProbabilityWeightFactory valueProbabilityWeightFactory = ConfigUtils.newInstance(this,
"valueProbabilityWeightFactoryClass", valueProbabilityWeightFactoryClass);
valueSelector = new ProbabilityValueSelector(valueSelector,
resolvedCacheType, valueProbabilityWeightFactory);
alreadyCached = true;
}
if (resolvedSelectionOrder == SelectionOrder.SHUFFLED) {
valueSelector = new ShufflingValueSelector(valueSelector, resolvedCacheType);
alreadyCached = true;
}
if (resolvedCacheType.isCached() && !alreadyCached) {
// TODO this might be pretty pointless, because FromSolutionPropertyValueSelector caches
valueSelector = new CachingValueSelector(valueSelector, resolvedCacheType,
resolvedSelectionOrder == SelectionOrder.RANDOM);
}
return valueSelector;
}
private PlanningVariableDescriptor fetchVariableDescriptor(PlanningEntityDescriptor entityDescriptor) {
PlanningVariableDescriptor variableDescriptor;
if (planningVariableName != null) {
variableDescriptor = entityDescriptor.getPlanningVariableDescriptor(planningVariableName);
if (variableDescriptor == null) {
throw new IllegalArgumentException("The valueSelectorConfig (" + this
+ ") has a planningVariableName ("
+ planningVariableName + ") for planningEntityClass ("
+ entityDescriptor.getPlanningEntityClass()
+ ") that is not annotated as a planningVariable.\n" +
"Check your planningEntity implementation's annotated methods.");
}
} else {
Collection<PlanningVariableDescriptor> planningVariableDescriptors = entityDescriptor
.getPlanningVariableDescriptors();
if (planningVariableDescriptors.size() != 1) {
throw new IllegalArgumentException("The valueSelectorConfig (" + this
+ ") has no configured planningVariableName ("
+ planningVariableName + ") for planningEntityClass ("
+ entityDescriptor.getPlanningEntityClass()
+ ") and because there are multiple in the planningVariableNameSet ("
+ entityDescriptor.getPlanningVariableNameSet()
+ "), it can not be deducted automatically.");
}
variableDescriptor = planningVariableDescriptors.iterator().next();
}
return variableDescriptor;
}
private ValueSelector buildBaseValueSelector(
EnvironmentMode environmentMode, PlanningVariableDescriptor variableDescriptor,
SelectionCacheType minimumCacheType, SelectionOrder resolvedSelectionOrder) {
// FromSolutionPropertyValueSelector caches by design, so it uses the minimumCacheType
if (minimumCacheType.compareTo(SelectionCacheType.PHASE) < 0) {
// TODO we probably want to default this to SelectionCacheType.JUST_IN_TIME
minimumCacheType = SelectionCacheType.PHASE;
}
return new FromSolutionPropertyValueSelector(variableDescriptor,
minimumCacheType, resolvedSelectionOrder == SelectionOrder.RANDOM);
}
public void inherit(ValueSelectorConfig inheritedConfig) {
super.inherit(inheritedConfig);
if (planningVariableName == null) {
planningVariableName = inheritedConfig.getPlanningVariableName();
}
cacheType = ConfigUtils.inheritOverwritableProperty(cacheType, inheritedConfig.getCacheType());
selectionOrder = ConfigUtils.inheritOverwritableProperty(selectionOrder, inheritedConfig.getSelectionOrder());
valueProbabilityWeightFactoryClass = ConfigUtils.inheritOverwritableProperty(
valueProbabilityWeightFactoryClass, inheritedConfig.getValueProbabilityWeightFactoryClass());
}
@Override
public String toString() {
return getClass().getSimpleName() + "(" + planningVariableName + ")";
}
}