/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* 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.optaplanner.core.config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.impl.domain.entity.descriptor.EntityDescriptor;
import org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor;
import org.optaplanner.core.impl.domain.variable.descriptor.GenuineVariableDescriptor;
/**
* A config class is a user friendly, validating configuration class that maps XML input.
* It builds the runtime impl classes (which are optimized for scalability and performance instead).
* <p>
* A config class should adhere to "configuration by exception" in its XML/JSON input/output,
* so all non-static fields should be null by default.
* Using the config class to build a runtime class, must not alter the config class's XML/JSON output.
* @param <C> the same class as the implementing subclass
*/
public abstract class AbstractConfig<C extends AbstractConfig> {
// ************************************************************************
// Builder methods
// ************************************************************************
/**
* @return never null
*/
public C newInstance() {
Class<C> configClass = (Class<C>) getClass();
try {
return configClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new IllegalStateException("The configClass (" + configClass
+ ") does not have a public no-arg constructor.\n"
+ "This is a bug, please report an issue with this stacktrace.", e);
}
}
// ************************************************************************
// Helper methods
// ************************************************************************
protected EntityDescriptor deduceEntityDescriptor(SolutionDescriptor solutionDescriptor,
Class<?> entityClass) {
EntityDescriptor entityDescriptor;
if (entityClass != null) {
entityDescriptor = solutionDescriptor.getEntityDescriptorStrict(entityClass);
if (entityDescriptor == null) {
throw new IllegalArgumentException("The config (" + this
+ ") has an entityClass (" + entityClass + ") that is not a known planning entity.\n"
+ "Check your solver configuration. If that class (" + entityClass.getSimpleName()
+ ") is not in the entityClassSet (" + solutionDescriptor.getEntityClassSet()
+ "), check your " + PlanningSolution.class.getSimpleName()
+ " implementation's annotated methods too.");
}
} else {
Collection<EntityDescriptor> entityDescriptors = solutionDescriptor.getGenuineEntityDescriptors();
if (entityDescriptors.size() != 1) {
throw new IllegalArgumentException("The config (" + this
+ ") has no entityClass (" + entityClass
+ ") configured and because there are multiple in the entityClassSet ("
+ solutionDescriptor.getEntityClassSet()
+ "), it can not be deducted automatically.");
}
entityDescriptor = entityDescriptors.iterator().next();
}
return entityDescriptor;
}
protected GenuineVariableDescriptor deduceVariableDescriptor(
EntityDescriptor entityDescriptor, String variableName) {
GenuineVariableDescriptor variableDescriptor;
if (variableName != null) {
variableDescriptor = entityDescriptor.getGenuineVariableDescriptor(variableName);
if (variableDescriptor == null) {
throw new IllegalArgumentException("The config (" + this
+ ") has a variableName (" + variableName
+ ") which is not a valid planning variable on entityClass ("
+ entityDescriptor.getEntityClass() + ").\n"
+ entityDescriptor.buildInvalidVariableNameExceptionMessage(variableName));
}
} else {
Collection<GenuineVariableDescriptor> variableDescriptors = entityDescriptor
.getGenuineVariableDescriptors();
if (variableDescriptors.size() != 1) {
throw new IllegalArgumentException("The config (" + this
+ ") has no configured variableName (" + variableName
+ ") for entityClass (" + entityDescriptor.getEntityClass()
+ ") and because there are multiple variableNames ("
+ entityDescriptor.getGenuineVariableNameSet()
+ "), it can not be deducted automatically.");
}
variableDescriptor = variableDescriptors.iterator().next();
}
return variableDescriptor;
}
protected List<GenuineVariableDescriptor> deduceVariableDescriptorList(
EntityDescriptor entityDescriptor, List<String> variableNameIncludeList) {
List<GenuineVariableDescriptor> variableDescriptorList = entityDescriptor.getGenuineVariableDescriptorList();
if (variableNameIncludeList == null) {
return variableDescriptorList;
}
List<GenuineVariableDescriptor> resolvedVariableDescriptorList = new ArrayList<>(variableDescriptorList.size());
for (String variableNameInclude : variableNameIncludeList) {
boolean found = false;
for (GenuineVariableDescriptor variableDescriptor : variableDescriptorList) {
if (variableDescriptor.getVariableName().equals(variableNameInclude)) {
resolvedVariableDescriptorList.add(variableDescriptor);
found = true;
break;
}
}
if (!found) {
throw new IllegalArgumentException("The config (" + this
+ ") has a variableNameInclude (" + variableNameInclude
+ ") which does not exist in the entity (" + entityDescriptor.getEntityClass()
+ ")'s variableDescriptorList (" + variableDescriptorList + ").");
}
}
return resolvedVariableDescriptorList;
}
// ************************************************************************
// Other methods
// ************************************************************************
/**
* Inherits each property of the {@code inheritedConfig} unless that property (or a semantic alternative)
* is defined by this instance (which overwrites the inherited behaviour).
* <p>
* After the inheritance, if a property on this {@link AbstractConfig} composition is replaced,
* it should not affect the inherited composition instance.
* @param inheritedConfig never null
*/
public abstract void inherit(C inheritedConfig);
@Override
public String toString() {
return getClass().getSimpleName() + "()";
}
}