001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.functor.core.algorithm; 018 019 import java.io.Serializable; 020 021 import org.apache.commons.functor.Function; 022 023 /** 024 * Tail recursion for {@link Function functions}. If the {@link Function} 025 * returns another function of type <code>functionType</code>, that function 026 * is executed. Functions are executed until a non function value or a 027 * function of a type other than that expected is returned. 028 */ 029 public class RecursiveEvaluation implements Function<Object>, Serializable { 030 /** 031 * serialVersionUID declaration. 032 */ 033 private static final long serialVersionUID = -7992078213921938619L; 034 private final Class<?> functionType; 035 private Function<?> function; 036 037 /** 038 * Create a new RecursiveEvaluation. Recursion will continue while the 039 * returned value is of the same runtime class as <code>function</code>. 040 * @param function initial, potentially recursive Function 041 */ 042 public RecursiveEvaluation(Function<?> function) { 043 this(function, getClass(function)); 044 } 045 046 /** 047 * Create a new RecursiveEvaluation. 048 * @param function initial, potentially recursive Function 049 * @param functionType as long as result is an instance, keep processing. 050 */ 051 public RecursiveEvaluation(Function<?> function, Class<?> functionType) { 052 if (function == null) { 053 throw new IllegalArgumentException("Function argument was null"); 054 } 055 if (functionType == null || !Function.class.isAssignableFrom(functionType)) { 056 throw new IllegalArgumentException(Function.class + " is not assignable from " + functionType); 057 } 058 this.function = function; 059 this.functionType = functionType; 060 } 061 062 /** 063 * {@inheritDoc} 064 */ 065 public final Object evaluate() { 066 Object result = null; 067 // if the function returns another function, execute it. stop executing 068 // when the result is not of the expected type. 069 while (true) { 070 result = function.evaluate(); 071 if (functionType.isInstance(result)) { 072 function = (Function<?>) result; 073 continue; 074 } else { 075 break; 076 } 077 } 078 return result; 079 } 080 081 /** 082 * {@inheritDoc} 083 */ 084 public final boolean equals(Object obj) { 085 if (obj == this) { 086 return true; 087 } 088 if (!(obj instanceof RecursiveEvaluation)) { 089 return false; 090 } 091 return ((RecursiveEvaluation) obj).function.equals(function); 092 } 093 094 /** 095 * {@inheritDoc} 096 */ 097 public int hashCode() { 098 return "RecursiveEvaluation".hashCode() << 2 ^ function.hashCode(); 099 } 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 }