Skip to content

Commit

Permalink
SolutionDescriptor: extract duplicate code with EntityDescriptor (to …
Browse files Browse the repository at this point in the history
…assure same behaviour) + merge ClassBrowser into ConfigUtils + allow non-getter read methods for problem facts too
  • Loading branch information
ge0ffrey committed Mar 30, 2016
1 parent 04562dc commit bc7307a
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 229 deletions.
Expand Up @@ -17,8 +17,21 @@
package org.optaplanner.core.config.util; package org.optaplanner.core.config.util;


import org.optaplanner.core.config.AbstractConfig; import org.optaplanner.core.config.AbstractConfig;
import org.optaplanner.core.impl.domain.common.AlphabeticMemberComparator;
import org.optaplanner.core.impl.domain.common.ReflectionHelper;
import org.optaplanner.core.impl.domain.common.accessor.BeanPropertyMemberAccessor;
import org.optaplanner.core.impl.domain.common.accessor.FieldMemberAccessor;
import org.optaplanner.core.impl.domain.common.accessor.MemberAccessor;
import org.optaplanner.core.impl.domain.common.accessor.MethodMemberAccessor;


import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;


public class ConfigUtils { public class ConfigUtils {


Expand Down Expand Up @@ -182,6 +195,86 @@ public static long floorDivide(long dividend, long divisor) { // TODO: Java8: re
return (dividend / divisor) + correction; return (dividend / divisor) + correction;
} }


// ************************************************************************
// Member and annotation methods
// ************************************************************************

/**
* @param clazz never null
* @return never null, sorted by type (fields before methods), then by {@link AlphabeticMemberComparator}.
*/
public static List<Member> getDeclaredMembers(Class<?> clazz) {
Stream<Field> fieldStream = Stream.of(clazz.getDeclaredFields())
.sorted(new AlphabeticMemberComparator());
Stream<Method> methodStream = Stream.of(clazz.getDeclaredMethods())
.sorted(new AlphabeticMemberComparator());
return Stream.<Member>concat(fieldStream, methodStream)
.collect(Collectors.toList());
}

public static Class<? extends Annotation> extractAnnotationClass(Member member,
Class<? extends Annotation>... annotations) {
Class<? extends Annotation> annotationClass = null;
for (Class<? extends Annotation> detectedAnnotationClass : annotations) {
if (((AnnotatedElement) member).isAnnotationPresent(detectedAnnotationClass)) {
if (annotationClass != null) {
throw new IllegalStateException("The class (" + member.getDeclaringClass()
+ ") has a member (" + member + ") that has both a "
+ annotationClass.getSimpleName() + " annotation and a "
+ detectedAnnotationClass.getSimpleName() + " annotation.");
}
annotationClass = detectedAnnotationClass;
// Do not break early: check other annotations too
}
}
return annotationClass;
}

public static MemberAccessor buildMemberAccessor(Member member, MemberAccessorType memberAccessorType, Class<? extends Annotation> annotationClass) {
if (member instanceof Field) {
Field field = (Field) member;
return new FieldMemberAccessor(field);
} else if (member instanceof Method) {
Method method = (Method) member;
MemberAccessor memberAccessor;
switch (memberAccessorType) {
case FIELD_OR_READ_METHOD:
ReflectionHelper.assertReadMethod(method, annotationClass);
memberAccessor = new MethodMemberAccessor(method);
break;
case FIELD_OR_GETTER_METHOD:
case FIELD_OR_GETTER_METHOD_WITH_SETTER:
ReflectionHelper.assertGetterMethod(method, annotationClass);
memberAccessor = new BeanPropertyMemberAccessor(method);
break;
default:
throw new IllegalStateException("The memberAccessorType (" + memberAccessorType
+ ") is not implemented.");
}
if (memberAccessorType == MemberAccessorType.FIELD_OR_GETTER_METHOD_WITH_SETTER
&& !memberAccessor.supportSetter()) {
throw new IllegalStateException("The class (" + method.getDeclaringClass()
+ ") has a " + annotationClass.getSimpleName()
+ " annotated getter method (" + method
+ "), but lacks a setter for that property (" + memberAccessor.getName() + ").");
}
return memberAccessor;
} else {
throw new IllegalStateException("Impossible state: the member (" + member + ")'s type is not a "
+ Field.class.getSimpleName() + " or a " + Method.class.getSimpleName() + ".");
}
}

public enum MemberAccessorType {
FIELD_OR_READ_METHOD,
FIELD_OR_GETTER_METHOD,
FIELD_OR_GETTER_METHOD_WITH_SETTER
}

// ************************************************************************
// Private constructor
// ************************************************************************

private ConfigUtils() { private ConfigUtils() {
} }


Expand Down
Expand Up @@ -30,7 +30,6 @@
import org.optaplanner.core.impl.domain.common.accessor.BeanPropertyMemberAccessor; import org.optaplanner.core.impl.domain.common.accessor.BeanPropertyMemberAccessor;
import org.optaplanner.core.impl.domain.common.accessor.FieldMemberAccessor; import org.optaplanner.core.impl.domain.common.accessor.FieldMemberAccessor;
import org.optaplanner.core.impl.domain.common.accessor.MemberAccessor; import org.optaplanner.core.impl.domain.common.accessor.MemberAccessor;
import org.optaplanner.core.impl.domain.common.accessor.MethodMemberAccessor;
import org.optaplanner.core.impl.domain.policy.DescriptorPolicy; import org.optaplanner.core.impl.domain.policy.DescriptorPolicy;
import org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor; import org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor;
import org.optaplanner.core.impl.domain.variable.anchor.AnchorShadowVariableDescriptor; import org.optaplanner.core.impl.domain.variable.anchor.AnchorShadowVariableDescriptor;
Expand All @@ -45,18 +44,21 @@
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement; import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.*;


import static org.optaplanner.core.config.util.ConfigUtils.MemberAccessorType.*;

/** /**
* @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation * @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation
*/ */
public class EntityDescriptor<Solution_> { public class EntityDescriptor<Solution_> {


public static final List<Class<? extends Annotation>> VARIABLE_ANNOTATION_CLASSES = Arrays.asList( public static final Class[] VARIABLE_ANNOTATION_CLASSES = {
PlanningVariable.class, PlanningVariable.class,
InverseRelationShadowVariable.class, AnchorShadowVariable.class, InverseRelationShadowVariable.class, AnchorShadowVariable.class,
CustomShadowVariable.class); CustomShadowVariable.class};


private final SolutionDescriptor<Solution_> solutionDescriptor; private final SolutionDescriptor<Solution_> solutionDescriptor;


Expand Down Expand Up @@ -140,21 +142,13 @@ private void processDifficulty(DescriptorPolicy descriptorPolicy, PlanningEntity


private void processValueRangeProviderAnnotations(DescriptorPolicy descriptorPolicy) { private void processValueRangeProviderAnnotations(DescriptorPolicy descriptorPolicy) {
// Only iterate declared fields and methods, not inherited members, to avoid registering the same ValueRangeProvider twice // Only iterate declared fields and methods, not inherited members, to avoid registering the same ValueRangeProvider twice
List<Field> fieldList = Arrays.asList(entityClass.getDeclaredFields()); List<Member> memberList = ConfigUtils.getDeclaredMembers(entityClass);
Collections.sort(fieldList, new AlphabeticMemberComparator()); for (Member member : memberList) {
for (Field field : fieldList) { if (((AnnotatedElement) member).isAnnotationPresent(ValueRangeProvider.class)) {
if (field.isAnnotationPresent(ValueRangeProvider.class)) { MemberAccessor memberAccessor = ConfigUtils.buildMemberAccessor(
MemberAccessor memberAccessor = new FieldMemberAccessor(field); member, FIELD_OR_READ_METHOD, ValueRangeProvider.class);
descriptorPolicy.addFromEntityValueRangeProvider(memberAccessor); descriptorPolicy.addFromEntityValueRangeProvider(
} memberAccessor);
}
List<Method> methodList = Arrays.asList(entityClass.getDeclaredMethods());
Collections.sort(methodList, new AlphabeticMemberComparator());
for (Method method : methodList) {
if (method.isAnnotationPresent(ValueRangeProvider.class)) {
ReflectionHelper.assertReadMethod(method, ValueRangeProvider.class);
MemberAccessor memberAccessor = new MethodMemberAccessor(method);
descriptorPolicy.addFromEntityValueRangeProvider(memberAccessor);
} }
} }
} }
Expand All @@ -163,30 +157,14 @@ private void processPlanningVariableAnnotations(DescriptorPolicy descriptorPolic
declaredGenuineVariableDescriptorMap = new LinkedHashMap<>(); declaredGenuineVariableDescriptorMap = new LinkedHashMap<>();
declaredShadowVariableDescriptorMap = new LinkedHashMap<>(); declaredShadowVariableDescriptorMap = new LinkedHashMap<>();
boolean noVariableAnnotation = true; boolean noVariableAnnotation = true;
List<Field> fieldList = Arrays.asList(entityClass.getDeclaredFields()); List<Member> memberList = ConfigUtils.getDeclaredMembers(entityClass);
Collections.sort(fieldList, new AlphabeticMemberComparator()); for (Member member : memberList) {
for (Field field : fieldList) { Class<? extends Annotation> variableAnnotationClass = ConfigUtils.extractAnnotationClass(
Class<? extends Annotation> variableAnnotationClass = extractVariableAnnotationClass(field); member, VARIABLE_ANNOTATION_CLASSES);
if (variableAnnotationClass != null) {
noVariableAnnotation = false;
MemberAccessor memberAccessor = new FieldMemberAccessor(field);
registerVariableAccessor(descriptorPolicy, variableAnnotationClass, memberAccessor);
}
}
List<Method> methodList = Arrays.asList(entityClass.getDeclaredMethods());
Collections.sort(methodList, new AlphabeticMemberComparator());
for (Method method : methodList) {
Class<? extends Annotation> variableAnnotationClass = extractVariableAnnotationClass(method);
if (variableAnnotationClass != null) { if (variableAnnotationClass != null) {
noVariableAnnotation = false; noVariableAnnotation = false;
ReflectionHelper.assertGetterMethod(method, variableAnnotationClass); MemberAccessor memberAccessor = ConfigUtils.buildMemberAccessor(
MemberAccessor memberAccessor = new BeanPropertyMemberAccessor(method); member, FIELD_OR_GETTER_METHOD_WITH_SETTER, variableAnnotationClass);
if (!memberAccessor.supportSetter()) {
throw new IllegalStateException("The entityClass (" + entityClass
+ ") has a " + variableAnnotationClass.getSimpleName()
+ " annotated getter method (" + method
+ "), but lacks a setter for that property (" + memberAccessor.getName() + ").");
}
registerVariableAccessor(descriptorPolicy, variableAnnotationClass, memberAccessor); registerVariableAccessor(descriptorPolicy, variableAnnotationClass, memberAccessor);
} }
} }
Expand All @@ -197,23 +175,6 @@ private void processPlanningVariableAnnotations(DescriptorPolicy descriptorPolic
} }
} }


private Class<? extends Annotation> extractVariableAnnotationClass(AnnotatedElement member) {
Class<? extends Annotation> annotationClass = null;
for (Class<? extends Annotation> detectedAnnotationClass : VARIABLE_ANNOTATION_CLASSES) {
if (member.isAnnotationPresent(detectedAnnotationClass)) {
if (annotationClass != null) {
throw new IllegalStateException("The entityClass (" + entityClass
+ ") has a member (" + member + ") that has both a "
+ annotationClass.getSimpleName() + " annotation and a "
+ detectedAnnotationClass.getSimpleName() + " annotation.");
}
annotationClass = detectedAnnotationClass;
// Do not break early: check other annotations too
}
}
return annotationClass;
}

private void registerVariableAccessor(DescriptorPolicy descriptorPolicy, private void registerVariableAccessor(DescriptorPolicy descriptorPolicy,
Class<? extends Annotation> variableAnnotationClass, MemberAccessor memberAccessor) { Class<? extends Annotation> variableAnnotationClass, MemberAccessor memberAccessor) {
String memberName = memberAccessor.getName(); String memberName = memberAccessor.getName();
Expand Down

This file was deleted.

0 comments on commit bc7307a

Please sign in to comment.