001 /*
002 * Copyright 2002-2004 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016 package org.springframework.rules.constraint;
017
018 import java.lang.reflect.Method;
019
020 import org.springframework.rules.constraint.Constraint;
021 import org.springframework.rules.reporting.TypeResolvable;
022 import org.springframework.util.Assert;
023
024 /**
025 * A adapter that can adapt a method on an object that accepts a single argument
026 * and returns a boolean result a UnaryPredicate. For example, a DAO might have
027 * the method <code>isUnique(String objectName)<code> that
028 * tests whether not a name parameter is unique. To adapt that method as a
029 * UnaryPredicate, use this class.
030 *
031 * @author Keith Donald
032 */
033 public class MethodInvokingConstraint implements Constraint, TypeResolvable {
034
035 private Object targetObject;
036
037 private Method testMethod;
038
039 private String type;
040
041 /**
042 * Creates a MethodInvokingConstraint for the provided target object - the
043 * constraint logic is encapsulated within the specified method name.
044 *
045 * Note: this constructor will attempt to guess the parameter type for the
046 * method as it accept a single unary argument and return a boolean result.
047 *
048 * @param targetObject
049 * The target object
050 * @param methodName
051 * The method name
052 */
053 public MethodInvokingConstraint(Object targetObject, String methodName) {
054 this(targetObject, methodName, null, null);
055 }
056
057 public MethodInvokingConstraint(Object targetObject, String methodName,
058 String constraintType) {
059 this(targetObject, methodName, null, constraintType);
060 }
061
062 private Class guessParameterType(Object object, String methodName) {
063 Method[] methods = targetObject.getClass().getMethods();
064 for (int i = 0; i < methods.length; i++) {
065 Method m = methods[i];
066 if (m.getName().equals(methodName)) {
067 Class[] types = m.getParameterTypes();
068 if (types.length == 1) {
069 return types[0];
070 }
071 }
072 }
073 throw new IllegalArgumentException(
074 "No single argument, boolean method found with name '"
075 + methodName + "'");
076 }
077
078 public MethodInvokingConstraint(Object targetObject, String methodName,
079 Class parameterType) {
080 this(targetObject, methodName, parameterType, null);
081 }
082
083 public MethodInvokingConstraint(Object targetObject, String methodName,
084 Class parameterType, String constraintType) {
085 Assert.notNull(targetObject, "targetObject is required");
086 this.targetObject = targetObject;
087 if (parameterType == null) {
088 parameterType = guessParameterType(targetObject, methodName);
089 }
090 setType(constraintType);
091 try {
092 this.testMethod = targetObject.getClass().getMethod(methodName,
093 new Class[]{parameterType});
094 }
095 catch (NoSuchMethodException e) {
096 throw new RuntimeException(e);
097 }
098 Class returnType = testMethod.getReturnType();
099 Assert.isTrue(returnType == Boolean.class
100 || returnType == boolean.class, "Return type must be a boolean type");
101 }
102
103 public String getType() {
104 return type;
105 }
106
107 public void setType(String type) {
108 this.type = type;
109 }
110
111 public boolean test(Object argument) {
112 try {
113 return ((Boolean) testMethod.invoke(targetObject,
114 new Object[]{argument})).booleanValue();
115 }
116 catch (Exception e) {
117 throw new RuntimeException(e);
118 }
119 }
120 }