View Javadoc

1   /*
2    *  Copyright 2001-2004 The Apache Software Foundation
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  package net.sf.collections15.functors.closure;
17  
18  import net.sf.collections15.Closure;
19  import net.sf.collections15.Predicate;
20  import net.sf.collections15.functors.FunctorException;
21  
22  import java.io.Serializable;
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.List;
26  import java.util.Map;
27  
28  
29  /***
30   * <code>Closure</code> implementation that calls the <code>Closure</code> whose
31   * <code>Predicate</code> returns <code>true</code>, like a switch statement.
32   *
33   * @author Stephen Colebourne
34   * @author Chris Lambrou (port to Java 5.0)
35   * @since Collections15 1.0
36   */
37  public class SwitchClosure <E> implements Closure<E>, Serializable
38  {
39  
40      static final long serialVersionUID = 7882246541467547697L;
41  
42      /***
43       * The cases to consider.
44       */
45      private final List<Predicate<? super E>> predicates;
46  
47      /***
48       * The matching <code>Closure</code>s to execute.
49       */
50      private final List<Closure<? super E>> closures;
51  
52      /***
53       * The default <code>Closure</code> to execute if no tests match.
54       */
55      private final Closure<? super E> defaultClosure;
56  
57      /***
58       * Create a new <code>Closure</code> that calls one of a number of
59       * <code>Closures</code> in a map depending on whether or not the input
60       * object satisfies any of the <code>Predicates</code> in the map.
61       * <p/>
62       * The map consists of <code>Predicate</code> keys and <code>Closure</code>
63       * values. A <code>Closure</code> is executed on the input object if the
64       * input matches its corresponding <code>Predicate</code>. A default
65       * <code>Closure</code> can be specified in the map with a <code>null</code>
66       * key, and is executed on an input object if it doesn't satisfy any of the
67       * other non-null <code>Predicate</code> keys.
68       *
69       * @param predicatesAndClosures A map of <code>Predicate</code>s to
70       *                              <code>Closure</code>s. An input object is
71       *                              evaluated against the non-null
72       *                              <code>Predicate</code> keys, in the order
73       *                              defined by the entry set's iterator. For the
74       *                              first <code>Predicate</code> to evaluate to
75       *                              <code>true</code> against the key, the
76       *                              corresponding <code>Closure</code> is
77       *                              executed.
78       *                              <p/>
79       *                              If all of the (non-null) <code>Predicate</code>s
80       *                              evaluate to <code>false</code>, and a
81       *                              default <code>Closure</code> is defined (with a
82       *                              <code>null Predicate</code>), the default
83       *                              <code>Closure</code> is executed against the key, othersize an
84       *                              <code>IllegalArgumentException</code> will
85       *                              be thrown.
86       * @return The <code>switch</code> closure based on the contents of the
87       *         specified map.
88       * @throws IllegalArgumentException If the map is <code>null</code>.
89       * @throws IllegalArgumentException If any <code>Closure</code> in the map
90       *                                  is <code>null</code>.
91       */
92      public static <T> Closure<T> getInstance(Map<Predicate<? super T>, Closure<? super T>> predicatesAndClosures)
93      {
94          return new SwitchClosure<T>(predicatesAndClosures);
95      }
96  
97      /***
98       * Creates a new <code>Closure</code> that calls one of a number of
99       * <code>Closures</code> in a map depending on whether or not the input
100      * object satisfies any of the <code>Predicates</code> in the map.
101      * <p/>
102      * The map consists of <code>Predicate</code> keys and <code>Closure</code>
103      * values. A <code>Closure</code> is executed on the input object if the
104      * input matches its corresponding <code>Predicate</code>. A default
105      * <code>Closure</code> can be specified in the map with a <code>null</code>
106      * key, and is executed on an input object if it doesn't satisfy any of the
107      * other non-null <code>Predicate</code> keys.
108      *
109      * @param predicatesAndClosures A map of <code>Predicate</code>s to
110      *                              <code>Closure</code>s. An input object is
111      *                              evaluated against the non-null
112      *                              <code>Predicate</code> keys, in the order
113      *                              defined by the entry set's iterator. For the
114      *                              first <code>Predicate</code> to evaluate to
115      *                              <code>true</code> against the key, the
116      *                              corresponding <code>Closure</code> is
117      *                              executed.
118      *                              <p/>
119      *                              If none of the non-null <code>Predicate</code>s
120      *                              evaluates to <code>true</code>, and a
121      *                              default <code>Closure</code> is defined, it
122      *                              is executed against the key, othersize an
123      *                              <code>IllegalArgumentException</code> will
124      *                              be thrown.
125      * @throws IllegalArgumentException If the map is <code>null</code>.
126      * @throws IllegalArgumentException If any <code>Closure</code> in the map
127      *                                  is <code>null</code>.
128      */
129     protected SwitchClosure(Map<Predicate<? super E>, Closure<? super E>> predicatesAndClosures)
130     {
131         if (predicatesAndClosures == null) {
132             throw new IllegalArgumentException("The predicate and closure map must not be null");
133         }
134         int size = predicatesAndClosures.size();
135         predicates = new ArrayList<Predicate<? super E>>(size);
136         closures = new ArrayList<Closure<? super E>>(size);
137         Closure<? super E> defaultClosure = ExceptionClosure.getInstance(
138                 "Unknown case - SwitchClosure cannot execute the specified target object");
139         for (Map.Entry<Predicate<? super E>, Closure<? super E>> entry : predicatesAndClosures.entrySet()) {
140             Predicate<? super E> predicate = entry.getKey();
141             Closure<? super E> closure = entry.getValue();
142             if (closure == null) {
143                 throw new IllegalArgumentException("null closure found in predicates and closures map");
144             }
145             if (predicate == null) {
146                 defaultClosure = closure;
147                 continue;
148             }
149             predicates.add(predicate);
150             closures.add(closure);
151         }
152         this.defaultClosure = defaultClosure;
153     }
154 
155     /***
156      * Executes the <code>Closure</code> whose matching <code>Predicate</code>
157      * evaluates to <code>true</code>.
158      *
159      * @param input The input object to act upon.
160      * @throws FunctorException Thrown if the input doesn't match any of the
161      *                          <code>Predicate</code> cases, and no default
162      *                          <code>Closure</code> is defined.
163      */
164     public void execute(E input)
165     {
166         int size = predicates.size();
167         for (int i = 0; i < size; i++) {
168             if (predicates.get(i).evaluate(input)) {
169                 closures.get(i).execute(input);
170                 return;
171             }
172         }
173         defaultClosure.execute(input);
174     }
175 
176     /***
177      * Returns the <code>Predicate</code>s that define the case conditions for
178      * this <code>SwitchClosure</code> - see {@link #getClosures()}.
179      *
180      * @return A list of the <code>Predicate</code>s used by this
181      *         <code>SwitchClosure</code> to determine which
182      *         <code>Closure</code> to apply to an input object. The order of
183      *         the list represents the order in which the <code>Predicate</code>s
184      *         are evaluated, and there is a one to one mapping between the
185      *         <code>Predicates</code> in the list and the <code>Closure</code>s
186      *         in the list returned by {@link #getClosures()}.
187      * @since Collections15 1.0
188      */
189     public List<Predicate<? super E>> getPredicates()
190     {
191         return Collections.unmodifiableList(predicates);
192     }
193 
194     /***
195      * Returns the <code>Closure</code>s that are executed by each case for this
196      * <code>SwitchClosure</code> - see {@link #getPredicates()}.
197      *
198      * @return A list of the <code>Closure</code>s applied to an input object
199      *         used by this <code>SwitchClosure</code>. There is a one to one
200      *         mapping between the <code>Closure</code>s in this list and the
201      *         <code>Predicate</code>s in the list returned by {@link
202      *         #getPredicates()}.
203      * @see #getDefaultClosure()
204      * @since Collections15 1.0
205      */
206     public List<Closure<? super E>> getClosures()
207     {
208         return Collections.unmodifiableList(closures);
209     }
210 
211     /***
212      * Returns the default <code>Closure</code>.
213      *
214      * @return The default <code>Closure</code> applied to an input object if it
215      *         doesn't satisfy any of the cases defined for this
216      *         <code>SwitchClosure</code>. May be <code>null</code> if no
217      *         default is defined.
218      * @see #getPredicates()
219      * @see #getClosures()
220      * @since Collections15 1.0
221      */
222     public Closure<? super E> getDefaultClosure()
223     {
224         return defaultClosure;
225     }
226 
227 }