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 }