/*
* Copyright 2016 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.impl.domain.solution;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty;
import org.optaplanner.core.api.domain.solution.PlanningEntityProperty;
import org.optaplanner.core.api.domain.solution.PlanningScore;
import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.api.domain.solution.drools.ProblemFactCollectionProperty;
import org.optaplanner.core.api.score.Score;
import org.optaplanner.core.impl.domain.common.ReflectionHelper;
/**
* Only used by OptaPlanner Workbench 6 (but not 7).
* This class will be removed in 8.0.
* @param <S> the {@link Score} type used by this use case
* @deprecated Use {@link PlanningSolution#autoDiscoverMemberType()} instead.
*/
@PlanningSolution
@Deprecated
public abstract class AbstractSolution<S extends Score> implements Serializable {
protected S score;
@PlanningScore
public S getScore() {
return score;
}
public void setScore(S score) {
this.score = score;
}
/**
* @return a list with every problem fact that is in a field of this class
* (directly or indirectly as an element of a {@link Collection} or {@link Map} field)
*/
@ProblemFactCollectionProperty
protected List<Object> getProblemFactList() {
List<Object> problemFactList = new ArrayList<>();
addProblemFactsFromClass(problemFactList, getClass());
return problemFactList;
}
/**
* Adds to an existing to {@link List} to avoid copying the entire list with {@link List#addAll(Collection)}.
* @param factList never null
* @param instanceClass never null
*/
private void addProblemFactsFromClass(List<Object> factList, Class<?> instanceClass) {
if (instanceClass.equals(AbstractSolution.class)) {
// The field score should not be included
return;
}
for (Field field : instanceClass.getDeclaredFields()) {
field.setAccessible(true);
if (isFieldAPlanningEntityPropertyOrPlanningEntityCollectionProperty(field, instanceClass)) {
continue;
}
Object value;
try {
value = field.get(this);
} catch (IllegalAccessException e) {
throw new IllegalStateException("The class (" + instanceClass + ") has a field (" + field
+ ") which can not be read to create the problem facts.", e);
}
if (value != null) {
if (value instanceof Collection) {
factList.addAll((Collection) value);
} else if (value instanceof Map) {
throw new IllegalStateException("The class (" + instanceClass + ") has a field (" + field
+ ") which is a " + Map.class.getSimpleName() + " and that's not yet supported.");
} else {
factList.add(value);
}
}
}
Class<?> superclass = instanceClass.getSuperclass();
if (superclass != null) {
addProblemFactsFromClass(factList, superclass);
}
}
private boolean isFieldAPlanningEntityPropertyOrPlanningEntityCollectionProperty(Field field,
Class<?> fieldInstanceClass) {
if (field.isAnnotationPresent(PlanningEntityProperty.class)
|| field.isAnnotationPresent(PlanningEntityCollectionProperty.class)) {
return true;
}
Method getterMethod = ReflectionHelper.getGetterMethod(fieldInstanceClass, field.getName());
if (getterMethod != null &&
(getterMethod.isAnnotationPresent(PlanningEntityProperty.class)
|| getterMethod.isAnnotationPresent(PlanningEntityCollectionProperty.class))) {
return true;
}
return false;
}
}