/*
* 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.lookup;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.optaplanner.core.api.domain.lookup.LookUpStrategyType;
import org.optaplanner.core.api.domain.lookup.PlanningId;
import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.config.util.ConfigUtils;
import org.optaplanner.core.impl.domain.common.accessor.MemberAccessor;
/**
* This class is thread-safe.
*/
public class LookUpStrategyResolver {
private final LookUpStrategyType lookUpStrategyType;
private final ConcurrentMap<Class<?>, LookUpStrategy> decisionCache = new ConcurrentHashMap<>();
public LookUpStrategyResolver(LookUpStrategyType lookUpStrategyType) {
this.lookUpStrategyType = lookUpStrategyType;
decisionCache.put(Boolean.class, new ImmutableLookUpStrategy());
decisionCache.put(Byte.class, new ImmutableLookUpStrategy());
decisionCache.put(Short.class, new ImmutableLookUpStrategy());
decisionCache.put(Integer.class, new ImmutableLookUpStrategy());
decisionCache.put(Long.class, new ImmutableLookUpStrategy());
decisionCache.put(Float.class, new ImmutableLookUpStrategy());
decisionCache.put(Double.class, new ImmutableLookUpStrategy());
decisionCache.put(BigInteger.class, new ImmutableLookUpStrategy());
decisionCache.put(BigDecimal.class, new ImmutableLookUpStrategy());
decisionCache.put(Character.class, new ImmutableLookUpStrategy());
decisionCache.put(String.class, new ImmutableLookUpStrategy());
decisionCache.put(LocalDate.class, new ImmutableLookUpStrategy());
decisionCache.put(LocalTime.class, new ImmutableLookUpStrategy());
decisionCache.put(LocalDateTime.class, new ImmutableLookUpStrategy());
}
/**
* This method is thread-safe.
* @param object never null
* @return never null
*/
public LookUpStrategy determineLookUpStrategy(Object object) {
Class<?> objectClass = object.getClass();
return decisionCache.computeIfAbsent(objectClass, key -> {
switch (lookUpStrategyType) {
case PLANNING_ID_OR_NONE:
MemberAccessor memberAccessor1 = ConfigUtils.findPlanningIdMemberAccessor(objectClass);
if (memberAccessor1 == null) {
return new NoneLookUpStrategy();
}
return new PlanningIdLookUpStrategy(memberAccessor1);
case PLANNING_ID_OR_FAIL_FAST:
MemberAccessor memberAccessor2 = ConfigUtils.findPlanningIdMemberAccessor(objectClass);
if (memberAccessor2 == null) {
throw new IllegalArgumentException("The class (" + objectClass
+ ") does not have a " + PlanningId.class.getSimpleName() + " annotation,"
+ " but the lookUpStrategyType (" + lookUpStrategyType + ") requires it.\n"
+ "Maybe add the " + PlanningId.class.getSimpleName() + " annotation"
+ " or change the " + PlanningSolution.class.getSimpleName() + " annotation's "
+ LookUpStrategyType.class.getSimpleName() + ".");
}
return new PlanningIdLookUpStrategy(memberAccessor2);
case EQUALITY:
Method equalsMethod;
Method hashCodeMethod;
try {
equalsMethod = object.getClass().getMethod("equals", Object.class);
hashCodeMethod = object.getClass().getMethod("hashCode");
} catch (NoSuchMethodException e) {
throw new IllegalStateException(
"Impossible state because equals() and hashCode() always exist.", e);
}
if (equalsMethod.getDeclaringClass().equals(Object.class)) {
throw new IllegalArgumentException("The class (" + object.getClass().getSimpleName()
+ ") doesn't override the equals() method, neither does any superclass.");
}
if (hashCodeMethod.getDeclaringClass().equals(Object.class)) {
throw new IllegalArgumentException("The class (" + object.getClass().getSimpleName()
+ ") overrides equals() but neither it nor any superclass"
+ " overrides the hashCode() method.");
}
return new EqualsLookUpStrategy();
case NONE:
return new NoneLookUpStrategy();
default:
throw new IllegalStateException("The lookUpStrategyType (" + lookUpStrategyType
+ ") is not implemented.");
}
});
}
}