Skip to content

Commit

Permalink
PLANNER-352 Add MethodMemberAccessor + delete ReadMethodAccessor hier…
Browse files Browse the repository at this point in the history
…archy
  • Loading branch information
ge0ffrey committed Jun 16, 2015
1 parent a385008 commit 2c520ed
Show file tree
Hide file tree
Showing 15 changed files with 233 additions and 151 deletions.
Expand Up @@ -17,15 +17,19 @@

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

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider;

/**
* Avoids the usage of Introspector to work on Android too.
*/
Expand Down Expand Up @@ -138,6 +142,47 @@ public static Method getSetterMethod(Class containingClass, Class propertyType,
}
}

public static void assertGetterMethod(Method getterMethod, Class<? extends Annotation> annotationClass) {
if (getterMethod.getParameterTypes().length != 0) {
throw new IllegalStateException("The getterMethod (" + getterMethod + ") with a "
+ annotationClass.getSimpleName() + " annotation must not have any parameters ("
+ Arrays.toString(getterMethod.getParameterTypes()) + ").");
}
String methodName = getterMethod.getName();
if (methodName.startsWith(PROPERTY_ACCESSOR_PREFIX_GET)) {
if (getterMethod.getReturnType() != void.class) {
throw new IllegalStateException("The getterMethod (" + getterMethod + ") with a "
+ annotationClass.getSimpleName() + " annotation must have a non-void return type ("
+ getterMethod.getReturnType() + ").");
}
} else if (methodName.startsWith(PROPERTY_ACCESSOR_PREFIX_IS)) {
if (getterMethod.getReturnType() == boolean.class) {
throw new IllegalStateException("The getterMethod (" + getterMethod + ") with a "
+ annotationClass.getSimpleName() + " annotation must have a primitive boolean return type ("
+ getterMethod.getReturnType() + ") or use another prefix in its methodName ("
+ methodName + ").");
}
} else {
throw new IllegalStateException("The getterMethod (" + getterMethod + ") with a "
+ annotationClass.getSimpleName() + " annotation has a methodName ("
+ methodName + ") that does not start with a valid prefix ("
+ Arrays.toString(PROPERTY_ACCESSOR_PREFIXES) + ").");
}
}

public static void assertReadMethod(Method readMethod, Class<? extends Annotation> annotationClass) {
if (readMethod.getParameterTypes().length != 0) {
throw new IllegalStateException("The readMethod (" + readMethod + ") with a "
+ annotationClass.getSimpleName() + " annotation must not have any parameters ("
+ Arrays.toString(readMethod.getParameterTypes()) + ").");
}
if (readMethod.getReturnType() == void.class) {
throw new IllegalStateException("The readMethod (" + readMethod + ") with a "
+ annotationClass.getSimpleName() + " annotation must have a non-void return type ("
+ readMethod.getReturnType() + ").");
}
}

/**
* @param type never null
* @return true if it is a {@link Map}
Expand Down
Expand Up @@ -120,4 +120,9 @@ public Annotation[] getDeclaredAnnotations() {
return getterMethod.getDeclaredAnnotations();
}

@Override
public String toString() {
return "bean property " + propertyName + " on class " + getterMethod.getDeclaringClass();
}

}
Expand Up @@ -94,4 +94,9 @@ public Annotation[] getDeclaredAnnotations() {
return field.getDeclaredAnnotations();
}

@Override
public String toString() {
return "field " + field;
}

}
Expand Up @@ -27,6 +27,7 @@
* which is a property (with a getter and optional setter {@link Method}) or a {@link Field}.
* @see BeanPropertyMemberAccessor
* @see FieldMemberAccessor
* @see MethodMemberAccessor
*/
public interface MemberAccessor extends AnnotatedElement {

Expand Down
@@ -0,0 +1,118 @@
/*
* Copyright 2015 JBoss Inc
*
* 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.common.member;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;

import org.optaplanner.core.impl.domain.common.ReflectionHelper;

/**
* A {@link MemberAccessor} based on a single read {@link Method}.
* Do not confuse with {@link BeanPropertyMemberAccessor} which is richer.
*/
public final class MethodMemberAccessor implements MemberAccessor {

private final Class<?> returnType;
private final String methodName;
private final Method readMethod;

public MethodMemberAccessor(Method readMethod) {
this.readMethod = readMethod;
readMethod.setAccessible(true); // Performance hack by avoiding security checks
returnType = readMethod.getReturnType();
methodName = readMethod.getName();
if (readMethod.getParameterTypes().length != 0) {
throw new IllegalArgumentException("The readMethod (" + readMethod + ") must not have any parameters ("
+ Arrays.toString(readMethod.getParameterTypes()) + ").");
}
if (readMethod.getReturnType() == void.class) {
throw new IllegalArgumentException("The readMethod (" + readMethod + ") must have a return type ("
+ readMethod.getReturnType() + ").");
}
}

public String getName() {
return methodName;
}

@Override
public Class<?> getType() {
return returnType;
}

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

public Object executeGetter(Object bean) {
try {
return readMethod.invoke(bean);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Cannot call property (" + methodName
+ ") getterMethod (" + readMethod + ") on bean of class (" + bean.getClass() + ").", e);
} catch (InvocationTargetException e) {
throw new IllegalStateException("The property (" + methodName
+ ") getterMethod (" + readMethod + ") on bean of class (" + bean.getClass()
+ ") throws an exception.",
e.getCause());
}
}

@Override
public boolean supportSetter() {
return false;
}

public void executeSetter(Object bean, Object value) {
throw new UnsupportedOperationException();
}

// ************************************************************************
// 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();
}

@Override
public String toString() {
return "method " + methodName + " on class " + readMethod.getDeclaringClass();
}

}

This file was deleted.

This file was deleted.

Expand Up @@ -43,6 +43,7 @@
import org.optaplanner.core.impl.domain.common.member.MemberAccessor;
import org.optaplanner.core.impl.domain.common.member.BeanPropertyMemberAccessor;
import org.optaplanner.core.impl.domain.common.ReflectionHelper;
import org.optaplanner.core.impl.domain.common.member.MethodMemberAccessor;
import org.optaplanner.core.impl.domain.policy.DescriptorPolicy;
import org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor;
import org.optaplanner.core.impl.domain.variable.anchor.AnchorShadowVariableDescriptor;
Expand Down Expand Up @@ -150,7 +151,9 @@ private void processValueRangeProviderAnnotations(DescriptorPolicy descriptorPol
Collections.sort(methodList, new AlphabeticMemberComparator());
for (Method method : methodList) {
if (method.isAnnotationPresent(ValueRangeProvider.class)) {
descriptorPolicy.addFromEntityValueRangeProvider(method);
ReflectionHelper.assertReadMethod(method, ValueRangeProvider.class);
MemberAccessor memberAccessor = new MethodMemberAccessor(method);
descriptorPolicy.addFromEntityValueRangeProvider(memberAccessor);
}
}
}
Expand All @@ -175,12 +178,7 @@ private void processPlanningVariableAnnotations(DescriptorPolicy descriptorPolic
Class<? extends Annotation> variableAnnotationClass = extractVariableAnnotationClass(method);
if (variableAnnotationClass != null) {
noVariableAnnotation = false;
if (!ReflectionHelper.isGetterMethod(method)) {
throw new IllegalStateException("The entityClass (" + entityClass
+ ")'s method (" + method + ") with a "
+ variableAnnotationClass.getSimpleName() + " annotation must be a valid getter method.\n"
+ " That annotation can only be used on a JavaBeans getter method or on a field.");
}
ReflectionHelper.assertGetterMethod(method, variableAnnotationClass);
MemberAccessor memberAccessor = new BeanPropertyMemberAccessor(method);
if (!memberAccessor.supportSetter()) {
throw new IllegalStateException("The entityClass (" + entityClass
Expand Down

0 comments on commit 2c520ed

Please sign in to comment.