View Javadoc

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 }