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.example.kata.two;
18  
19  import org.apache.commons.functor.Function;
20  import org.apache.commons.functor.Predicate;
21  import org.apache.commons.functor.Procedure;
22  import org.apache.commons.functor.core.Constant;
23  import org.apache.commons.functor.core.NoOp;
24  
25  /**
26   * Supports an Eiffel style loop construct.
27   * <pre>
28   * new EiffelStyleLoop()
29   *   .from(new Procedure() { public void run() {} }) // init code
30   *   .invariant(new Predicate() { public boolean test() {} }) // invariants
31   *   .variant(new Procedure() { public Object evaluate() {} }) // diminishing comparable value
32   *   // or
33   *   // .variant(new Predicate() { public boolean test() {} }) // more invariants
34   *   .until(new Predicate() { public boolean test() {} }) // terminating condition
35   *   .loop(new Procedure() { public void run() {} }) // the acutal loop
36   *   .run();
37   * </pre>
38   *
39   * Note that <tt>new EiffelStyleLoop().run()</tt> executes just fine.
40   * You only need to set the parts of the loop you want to use.
41   *
42   * @version $Revision: 666191 $ $Date: 2008-06-10 18:43:53 +0200 (Tue, 10 Jun 2008) $
43   * @author Rodney Waldhoff
44   */
45  public class EiffelStyleLoop implements Procedure {
46      public EiffelStyleLoop from(Procedure procedure) {
47          from = procedure;
48          return this;
49      }
50  
51      public EiffelStyleLoop invariant(Predicate predicate) {
52          invariant = predicate;
53          return this;
54      }
55  
56      public EiffelStyleLoop variant(Predicate predicate) {
57          variant = predicate;
58          return this;
59      }
60  
61      @SuppressWarnings("unchecked")
62      public EiffelStyleLoop variant(final Function function) {
63          return variant(new Predicate() {
64              public boolean test() {
65                  boolean result = true;
66                  Comparable next = (Comparable)(function.evaluate());
67                  if (null != last) {
68                      result = last.compareTo(next) > 0;
69                  }
70                  last = next;
71                  return result;
72              }
73              private Comparable last = null;
74          });
75      }
76  
77      public EiffelStyleLoop until(Predicate predicate) {
78          until = predicate;
79          return this;
80      }
81  
82      public EiffelStyleLoop loop(Procedure procedure) {
83          loop = procedure;
84          return this;
85      }
86  
87      public void run() {
88          from.run();
89          assertTrue(invariant.test());
90          while(! until.test() ) {
91              loop.run();
92              assertTrue(variant.test());
93              assertTrue(invariant.test());
94          }
95  
96          // Note that:
97          //   assertTrue(until.test());
98          // holds here, but isn't necessary since that's
99          // the only way we could get out of the loop
100 
101         // Also note that:
102         //   assertTrue(invariant.test());
103         // holds here, but was the last thing called
104         // before until.test()
105     }
106 
107     private void assertTrue(boolean value) {
108         if (!value) {
109             throw new IllegalStateException("Assertion failed");
110         }
111     }
112 
113     private Procedure from = NoOp.instance();
114     private Predicate invariant = Constant.truePredicate();
115     private Predicate variant = Constant.truePredicate();
116     private Predicate until = Constant.falsePredicate();
117     private Procedure loop = NoOp.instance();
118 
119 }