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 }