1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.functor.core.algorithm; 18 19 import java.io.Serializable; 20 21 import org.apache.commons.functor.Function; 22 23 /** 24 * Tail recursion for {@link Function functions}. If the {@link Function} 25 * returns another function of type <code>functionType</code>, that function 26 * is executed. Functions are executed until a non function value or a 27 * function of a type other than that expected is returned. 28 */ 29 public class RecursiveEvaluation implements Function<Object>, Serializable { 30 /** 31 * serialVersionUID declaration. 32 */ 33 private static final long serialVersionUID = -7992078213921938619L; 34 private final Class<?> functionType; 35 private Function<?> function; 36 37 /** 38 * Create a new RecursiveEvaluation. Recursion will continue while the 39 * returned value is of the same runtime class as <code>function</code>. 40 * @param function initial, potentially recursive Function 41 */ 42 public RecursiveEvaluation(Function<?> function) { 43 this(function, getClass(function)); 44 } 45 46 /** 47 * Create a new RecursiveEvaluation. 48 * @param function initial, potentially recursive Function 49 * @param functionType as long as result is an instance, keep processing. 50 */ 51 public RecursiveEvaluation(Function<?> function, Class<?> functionType) { 52 if (function == null) { 53 throw new IllegalArgumentException("Function argument was null"); 54 } 55 if (functionType == null || !Function.class.isAssignableFrom(functionType)) { 56 throw new IllegalArgumentException(Function.class + " is not assignable from " + functionType); 57 } 58 this.function = function; 59 this.functionType = functionType; 60 } 61 62 /** 63 * {@inheritDoc} 64 */ 65 public final Object evaluate() { 66 Object result = null; 67 // if the function returns another function, execute it. stop executing 68 // when the result is not of the expected type. 69 while (true) { 70 result = function.evaluate(); 71 if (functionType.isInstance(result)) { 72 function = (Function<?>) result; 73 continue; 74 } else { 75 break; 76 } 77 } 78 return result; 79 } 80 81 /** 82 * {@inheritDoc} 83 */ 84 public final boolean equals(Object obj) { 85 if (obj == this) { 86 return true; 87 } 88 if (!(obj instanceof RecursiveEvaluation)) { 89 return false; 90 } 91 return ((RecursiveEvaluation) obj).function.equals(function); 92 } 93 94 /** 95 * {@inheritDoc} 96 */ 97 public int hashCode() { 98 return "RecursiveEvaluation".hashCode() << 2 ^ function.hashCode(); 99 } 100 101 /** 102 * Get the class of the specified object, or <code>null</code> if <code>o</code> is <code>null</code>. 103 * @param o Object to check 104 * @return Class found 105 */ 106 private static <T> Class<?> getClass(Function<?> f) { 107 return f == null ? null : f.getClass(); 108 } 109 }