Skip to content

Commit

Permalink
PLANNER-352 Prepare for field annotations: refactor PropertyAccessor …
Browse files Browse the repository at this point in the history
…to extend AnnotatedElement
  • Loading branch information
ge0ffrey committed Jun 10, 2015
1 parent b9defb9 commit 5f43b16
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 47 deletions.
Expand Up @@ -16,17 +16,23 @@


package org.optaplanner.core.impl.domain.common; package org.optaplanner.core.impl.domain.common;


import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Type;


public interface PropertyAccessor { public interface PropertyAccessor extends AnnotatedElement {


String getName(); String getName();


Class<?> getPropertyType(); Class<?> getType();


Method getReadMethod(); /**

* As defined by {@link Method#getGenericReturnType()} and {@link Field#getGenericType()}.
Method getWriteMethod(); * @return never null
*/
Type getGenericType();


Object executeGetter(Object bean); Object executeGetter(Object bean);


Expand Down
Expand Up @@ -17,8 +17,10 @@
package org.optaplanner.core.impl.domain.common; package org.optaplanner.core.impl.domain.common;


import java.beans.PropertyDescriptor; import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Type;


/** /**
* Wraps {@link PropertyDescriptor} for faster and easier access. * Wraps {@link PropertyDescriptor} for faster and easier access.
Expand All @@ -32,31 +34,31 @@ public final class ReflectionPropertyAccessor implements PropertyAccessor {
public ReflectionPropertyAccessor(PropertyDescriptor propertyDescriptor) { public ReflectionPropertyAccessor(PropertyDescriptor propertyDescriptor) {
this.propertyDescriptor = propertyDescriptor; this.propertyDescriptor = propertyDescriptor;
readMethod = propertyDescriptor.getReadMethod(); readMethod = propertyDescriptor.getReadMethod();
if (readMethod != null) { if (readMethod == null) {
readMethod.setAccessible(true); // Performance hack by avoiding security checks throw new IllegalStateException("The propertyDescriptor (" + propertyDescriptor
+ ") lacks a readMethod (" + readMethod + ").");
} }
readMethod.setAccessible(true); // Performance hack by avoiding security checks
writeMethod = propertyDescriptor.getWriteMethod(); writeMethod = propertyDescriptor.getWriteMethod();
if (writeMethod != null) { if (writeMethod != null) {
writeMethod.setAccessible(true); // Performance hack by avoiding security checks writeMethod.setAccessible(true); // Performance hack by avoiding security checks
} }
} }


public Method getReadMethod() {
return readMethod;
}

public Method getWriteMethod() {
return writeMethod;
}

public String getName() { public String getName() {
return propertyDescriptor.getName(); return propertyDescriptor.getName();
} }


public Class<?> getPropertyType() { @Override
public Class<?> getType() {
return propertyDescriptor.getPropertyType(); return propertyDescriptor.getPropertyType();
} }


@Override
public Type getGenericType() {
return readMethod.getGenericReturnType();
}

public Object executeGetter(Object bean) { public Object executeGetter(Object bean) {
try { try {
return readMethod.invoke(bean); return readMethod.invoke(bean);
Expand All @@ -83,4 +85,28 @@ public void executeSetter(Object bean, Object value) {
} }
} }


// ************************************************************************
// AnnotatedElement methods
// ************************************************************************

@Override
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return readMethod.isAnnotationPresent(annotationClass);
}

@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
return readMethod.getAnnotation(annotationClass);
}

@Override
public Annotation[] getAnnotations() {
return readMethod.getAnnotations();
}

@Override
public Annotation[] getDeclaredAnnotations() {
return readMethod.getDeclaredAnnotations();
}

} }
Expand Up @@ -61,7 +61,6 @@ public class SolutionDescriptor {
private final BeanInfo solutionBeanInfo; private final BeanInfo solutionBeanInfo;
private SolutionCloner solutionCloner; private SolutionCloner solutionCloner;


private final Map<String, PropertyAccessor> propertyAccessorMap;
private final Map<String, PropertyAccessor> entityPropertyAccessorMap; private final Map<String, PropertyAccessor> entityPropertyAccessorMap;
private final Map<String, PropertyAccessor> entityCollectionPropertyAccessorMap; private final Map<String, PropertyAccessor> entityCollectionPropertyAccessorMap;


Expand All @@ -77,7 +76,6 @@ public SolutionDescriptor(Class<? extends Solution> solutionClass) {
throw new IllegalStateException("The solutionClass (" + solutionClass + ") is not a valid java bean.", e); throw new IllegalStateException("The solutionClass (" + solutionClass + ") is not a valid java bean.", e);
} }
int mapSize = solutionBeanInfo.getPropertyDescriptors().length; int mapSize = solutionBeanInfo.getPropertyDescriptors().length;
propertyAccessorMap = new LinkedHashMap<String, PropertyAccessor>(mapSize);
entityPropertyAccessorMap = new LinkedHashMap<String, PropertyAccessor>(mapSize); entityPropertyAccessorMap = new LinkedHashMap<String, PropertyAccessor>(mapSize);
entityCollectionPropertyAccessorMap = new LinkedHashMap<String, PropertyAccessor>(mapSize); entityCollectionPropertyAccessorMap = new LinkedHashMap<String, PropertyAccessor>(mapSize);
entityDescriptorMap = new LinkedHashMap<Class<?>, EntityDescriptor>(mapSize); entityDescriptorMap = new LinkedHashMap<Class<?>, EntityDescriptor>(mapSize);
Expand Down Expand Up @@ -143,16 +141,14 @@ private void processMethodAnnotations(DescriptorPolicy descriptorPolicy) {
private void processPropertyAnnotations(DescriptorPolicy descriptorPolicy) { private void processPropertyAnnotations(DescriptorPolicy descriptorPolicy) {
boolean noPlanningEntityPropertyAnnotation = true; boolean noPlanningEntityPropertyAnnotation = true;
for (PropertyDescriptor propertyDescriptor : solutionBeanInfo.getPropertyDescriptors()) { for (PropertyDescriptor propertyDescriptor : solutionBeanInfo.getPropertyDescriptors()) {
PropertyAccessor propertyAccessor = new ReflectionPropertyAccessor(propertyDescriptor); if (propertyDescriptor.getReadMethod() != null) {
propertyAccessorMap.put(propertyAccessor.getName(), propertyAccessor); PropertyAccessor propertyAccessor = new ReflectionPropertyAccessor(propertyDescriptor);
Method propertyGetter = propertyAccessor.getReadMethod(); if (propertyAccessor.isAnnotationPresent(PlanningEntityProperty.class)) {
if (propertyGetter != null) {
if (propertyGetter.isAnnotationPresent(PlanningEntityProperty.class)) {
noPlanningEntityPropertyAnnotation = false; noPlanningEntityPropertyAnnotation = false;
entityPropertyAccessorMap.put(propertyAccessor.getName(), propertyAccessor); entityPropertyAccessorMap.put(propertyAccessor.getName(), propertyAccessor);
} else if (propertyGetter.isAnnotationPresent(PlanningEntityCollectionProperty.class)) { } else if (propertyAccessor.isAnnotationPresent(PlanningEntityCollectionProperty.class)) {
noPlanningEntityPropertyAnnotation = false; noPlanningEntityPropertyAnnotation = false;
if (!Collection.class.isAssignableFrom(propertyAccessor.getPropertyType())) { if (!Collection.class.isAssignableFrom(propertyAccessor.getType())) {
throw new IllegalStateException("The solutionClass (" + solutionClass throw new IllegalStateException("The solutionClass (" + solutionClass
+ ") has a PlanningEntityCollection annotated property (" + ") has a PlanningEntityCollection annotated property ("
+ propertyAccessor.getName() + ") that does not return a Collection."); + propertyAccessor.getName() + ") that does not return a Collection.");
Expand Down Expand Up @@ -220,10 +216,6 @@ public Map<String, PropertyAccessor> getEntityCollectionPropertyAccessorMap() {
// Model methods // Model methods
// ************************************************************************ // ************************************************************************


public PropertyAccessor getPropertyAccessor(String propertyName) {
return propertyAccessorMap.get(propertyName);
}

public Set<Class<?>> getEntityClassSet() { public Set<Class<?>> getEntityClassSet() {
return entityDescriptorMap.keySet(); return entityDescriptorMap.keySet();
} }
Expand Down Expand Up @@ -387,7 +379,7 @@ public List<Object> getEntityList(Solution solution) {
public List<Object> getEntityListByEntityClass(Solution solution, Class<?> entityClass) { public List<Object> getEntityListByEntityClass(Solution solution, Class<?> entityClass) {
List<Object> entityList = new ArrayList<Object>(); List<Object> entityList = new ArrayList<Object>();
for (PropertyAccessor entityPropertyAccessor : entityPropertyAccessorMap.values()) { for (PropertyAccessor entityPropertyAccessor : entityPropertyAccessorMap.values()) {
if (entityPropertyAccessor.getPropertyType().isAssignableFrom(entityClass)) { if (entityPropertyAccessor.getType().isAssignableFrom(entityClass)) {
Object entity = extractEntity(entityPropertyAccessor, solution); Object entity = extractEntity(entityPropertyAccessor, solution);
if (entity != null && entityClass.isInstance(entity)) { if (entity != null && entityClass.isInstance(entity)) {
entityList.add(entity); entityList.add(entity);
Expand Down
Expand Up @@ -48,8 +48,7 @@ private void processPropertyAnnotations(DescriptorPolicy descriptorPolicy) {
} }


public void linkShadowSources(DescriptorPolicy descriptorPolicy) { public void linkShadowSources(DescriptorPolicy descriptorPolicy) {
AnchorShadowVariable shadowVariableAnnotation = variablePropertyAccessor.getReadMethod() AnchorShadowVariable shadowVariableAnnotation = variablePropertyAccessor.getAnnotation(AnchorShadowVariable.class);
.getAnnotation(AnchorShadowVariable.class);
String sourceVariableName = shadowVariableAnnotation.sourceVariableName(); String sourceVariableName = shadowVariableAnnotation.sourceVariableName();
sourceVariableDescriptor = entityDescriptor.getVariableDescriptor(sourceVariableName); sourceVariableDescriptor = entityDescriptor.getVariableDescriptor(sourceVariableName);
if (sourceVariableDescriptor == null) { if (sourceVariableDescriptor == null) {
Expand Down
Expand Up @@ -48,7 +48,7 @@ public void processAnnotations(DescriptorPolicy descriptorPolicy) {
} }


private void processPropertyAnnotations(DescriptorPolicy descriptorPolicy) { private void processPropertyAnnotations(DescriptorPolicy descriptorPolicy) {
CustomShadowVariable shadowVariableAnnotation = variablePropertyAccessor.getReadMethod() CustomShadowVariable shadowVariableAnnotation = variablePropertyAccessor
.getAnnotation(CustomShadowVariable.class); .getAnnotation(CustomShadowVariable.class);
variableListenerClass = shadowVariableAnnotation.variableListenerClass(); variableListenerClass = shadowVariableAnnotation.variableListenerClass();
CustomShadowVariable.Source[] sources = shadowVariableAnnotation.sources(); CustomShadowVariable.Source[] sources = shadowVariableAnnotation.sources();
Expand All @@ -62,7 +62,7 @@ private void processPropertyAnnotations(DescriptorPolicy descriptorPolicy) {
} }


public void linkShadowSources(DescriptorPolicy descriptorPolicy) { public void linkShadowSources(DescriptorPolicy descriptorPolicy) {
CustomShadowVariable shadowVariableAnnotation = variablePropertyAccessor.getReadMethod() CustomShadowVariable shadowVariableAnnotation = variablePropertyAccessor
.getAnnotation(CustomShadowVariable.class); .getAnnotation(CustomShadowVariable.class);
SolutionDescriptor solutionDescriptor = entityDescriptor.getSolutionDescriptor(); SolutionDescriptor solutionDescriptor = entityDescriptor.getSolutionDescriptor();
CustomShadowVariable.Source[] sources = shadowVariableAnnotation.sources(); CustomShadowVariable.Source[] sources = shadowVariableAnnotation.sources();
Expand Down
Expand Up @@ -24,8 +24,6 @@
import java.util.List; import java.util.List;


import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.optaplanner.core.api.domain.entity.PlanningEntity;
import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.api.domain.solution.Solution; import org.optaplanner.core.api.domain.solution.Solution;
import org.optaplanner.core.api.domain.valuerange.CountableValueRange; import org.optaplanner.core.api.domain.valuerange.CountableValueRange;
import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider; import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider;
Expand All @@ -34,7 +32,6 @@
import org.optaplanner.core.config.heuristic.selector.common.decorator.SelectionSorterOrder; import org.optaplanner.core.config.heuristic.selector.common.decorator.SelectionSorterOrder;
import org.optaplanner.core.config.util.ConfigUtils; import org.optaplanner.core.config.util.ConfigUtils;
import org.optaplanner.core.impl.domain.common.PropertyAccessor; import org.optaplanner.core.impl.domain.common.PropertyAccessor;
import org.optaplanner.core.impl.domain.common.ReflectionPropertyAccessor;
import org.optaplanner.core.impl.domain.entity.descriptor.EntityDescriptor; import org.optaplanner.core.impl.domain.entity.descriptor.EntityDescriptor;
import org.optaplanner.core.impl.domain.policy.DescriptorPolicy; import org.optaplanner.core.impl.domain.policy.DescriptorPolicy;
import org.optaplanner.core.impl.domain.valuerange.descriptor.CompositeValueRangeDescriptor; import org.optaplanner.core.impl.domain.valuerange.descriptor.CompositeValueRangeDescriptor;
Expand Down Expand Up @@ -69,8 +66,7 @@ public void processAnnotations(DescriptorPolicy descriptorPolicy) {
} }


private void processPropertyAnnotations(DescriptorPolicy descriptorPolicy) { private void processPropertyAnnotations(DescriptorPolicy descriptorPolicy) {
PlanningVariable planningVariableAnnotation = variablePropertyAccessor.getReadMethod() PlanningVariable planningVariableAnnotation = variablePropertyAccessor.getAnnotation(PlanningVariable.class);
.getAnnotation(PlanningVariable.class);
processNullable(descriptorPolicy, planningVariableAnnotation); processNullable(descriptorPolicy, planningVariableAnnotation);
processChained(descriptorPolicy, planningVariableAnnotation); processChained(descriptorPolicy, planningVariableAnnotation);
processValueRangeRefs(descriptorPolicy, planningVariableAnnotation); processValueRangeRefs(descriptorPolicy, planningVariableAnnotation);
Expand All @@ -79,11 +75,11 @@ private void processPropertyAnnotations(DescriptorPolicy descriptorPolicy) {


private void processNullable(DescriptorPolicy descriptorPolicy, PlanningVariable planningVariableAnnotation) { private void processNullable(DescriptorPolicy descriptorPolicy, PlanningVariable planningVariableAnnotation) {
nullable = planningVariableAnnotation.nullable(); nullable = planningVariableAnnotation.nullable();
if (nullable && variablePropertyAccessor.getPropertyType().isPrimitive()) { if (nullable && variablePropertyAccessor.getType().isPrimitive()) {
throw new IllegalArgumentException("The entityClass (" + entityDescriptor.getEntityClass() throw new IllegalArgumentException("The entityClass (" + entityDescriptor.getEntityClass()
+ ") has a PlanningVariable annotated property (" + variablePropertyAccessor.getName() + ") has a PlanningVariable annotated property (" + variablePropertyAccessor.getName()
+ ") with nullable (" + nullable + "), which is not compatible with the primitive propertyType (" + ") with nullable (" + nullable + "), which is not compatible with the primitive propertyType ("
+ variablePropertyAccessor.getPropertyType() + ")."); + variablePropertyAccessor.getType() + ").");
} }
Class<? extends SelectionFilter> reinitializeVariableEntityFilterClass Class<? extends SelectionFilter> reinitializeVariableEntityFilterClass
= planningVariableAnnotation.reinitializeVariableEntityFilter(); = planningVariableAnnotation.reinitializeVariableEntityFilter();
Expand All @@ -100,11 +96,11 @@ private void processNullable(DescriptorPolicy descriptorPolicy, PlanningVariable


private void processChained(DescriptorPolicy descriptorPolicy, PlanningVariable planningVariableAnnotation) { private void processChained(DescriptorPolicy descriptorPolicy, PlanningVariable planningVariableAnnotation) {
chained = planningVariableAnnotation.graphType() == PlanningVariableGraphType.CHAINED; chained = planningVariableAnnotation.graphType() == PlanningVariableGraphType.CHAINED;
if (chained && !variablePropertyAccessor.getPropertyType().isAssignableFrom( if (chained && !variablePropertyAccessor.getType().isAssignableFrom(
entityDescriptor.getEntityClass())) { entityDescriptor.getEntityClass())) {
throw new IllegalArgumentException("The entityClass (" + entityDescriptor.getEntityClass() throw new IllegalArgumentException("The entityClass (" + entityDescriptor.getEntityClass()
+ ") has a PlanningVariable annotated property (" + variablePropertyAccessor.getName() + ") has a PlanningVariable annotated property (" + variablePropertyAccessor.getName()
+ ") with chained (" + chained + ") and propertyType (" + variablePropertyAccessor.getPropertyType() + ") with chained (" + chained + ") and propertyType (" + variablePropertyAccessor.getType()
+ ") which is not a superclass/interface of or the same as the entityClass (" + ") which is not a superclass/interface of or the same as the entityClass ("
+ entityDescriptor.getEntityClass() + ")."); + entityDescriptor.getEntityClass() + ").");
} }
Expand Down
Expand Up @@ -20,7 +20,6 @@
import java.util.List; import java.util.List;


import org.optaplanner.core.impl.domain.common.PropertyAccessor; import org.optaplanner.core.impl.domain.common.PropertyAccessor;
import org.optaplanner.core.impl.domain.common.ReflectionPropertyAccessor;
import org.optaplanner.core.impl.domain.entity.descriptor.EntityDescriptor; import org.optaplanner.core.impl.domain.entity.descriptor.EntityDescriptor;


public abstract class VariableDescriptor { public abstract class VariableDescriptor {
Expand Down Expand Up @@ -55,7 +54,7 @@ public String getSimpleEntityAndVariableName() {
} }


public Class<?> getVariablePropertyType() { public Class<?> getVariablePropertyType() {
return variablePropertyAccessor.getPropertyType(); return variablePropertyAccessor.getType();
} }


// ************************************************************************ // ************************************************************************
Expand Down
Expand Up @@ -51,12 +51,12 @@ private void processPropertyAnnotations(DescriptorPolicy descriptorPolicy) {
} }


public void linkShadowSources(DescriptorPolicy descriptorPolicy) { public void linkShadowSources(DescriptorPolicy descriptorPolicy) {
InverseRelationShadowVariable shadowVariableAnnotation = variablePropertyAccessor.getReadMethod() InverseRelationShadowVariable shadowVariableAnnotation = variablePropertyAccessor
.getAnnotation(InverseRelationShadowVariable.class); .getAnnotation(InverseRelationShadowVariable.class);
Class<?> variablePropertyType = getVariablePropertyType(); Class<?> variablePropertyType = getVariablePropertyType();
Class<?> masterClass; Class<?> masterClass;
if (Collection.class.isAssignableFrom(variablePropertyType)) { if (Collection.class.isAssignableFrom(variablePropertyType)) {
Type genericType = variablePropertyAccessor.getReadMethod().getGenericReturnType(); Type genericType = variablePropertyAccessor.getGenericType();
if (!(genericType instanceof ParameterizedType)) { if (!(genericType instanceof ParameterizedType)) {
throw new IllegalArgumentException("The entityClass (" + entityDescriptor.getEntityClass() throw new IllegalArgumentException("The entityClass (" + entityDescriptor.getEntityClass()
+ ") has a " + InverseRelationShadowVariable.class.getSimpleName() + ") has a " + InverseRelationShadowVariable.class.getSimpleName()
Expand Down

0 comments on commit 5f43b16

Please sign in to comment.