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.collections.functors;
18
19 import java.io.Serializable;
20 import java.util.Map;
21
22 import org.apache.commons.collections.Closure;
23 import org.apache.commons.collections.Predicate;
24
25 /**
26 * Closure implementation calls the closure whose predicate returns true,
27 * like a switch statement.
28 *
29 * @since 3.0
30 * @version $Id: SwitchClosure.java 1436601 2013-01-21 20:41:58Z tn $
31 */
32 public class SwitchClosure<E> implements Closure<E>, Serializable {
33
34 /** Serial version UID */
35 private static final long serialVersionUID = 3518477308466486130L;
36
37 /** The tests to consider */
38 private final Predicate<? super E>[] iPredicates;
39 /** The matching closures to call */
40 private final Closure<? super E>[] iClosures;
41 /** The default closure to call if no tests match */
42 private final Closure<? super E> iDefault;
43
44 /**
45 * Factory method that performs validation and copies the parameter arrays.
46 *
47 * @param <E> the type that the closure acts on
48 * @param predicates array of predicates, cloned, no nulls
49 * @param closures matching array of closures, cloned, no nulls
50 * @param defaultClosure the closure to use if no match, null means nop
51 * @return the <code>chained</code> closure
52 * @throws IllegalArgumentException if array is null
53 * @throws IllegalArgumentException if any element in the array is null
54 */
55 @SuppressWarnings("unchecked")
56 public static <E> Closure<E> switchClosure(final Predicate<? super E>[] predicates,
57 final Closure<? super E>[] closures,
58 final Closure<? super E> defaultClosure) {
59 FunctorUtils.validate(predicates);
60 FunctorUtils.validate(closures);
61 if (predicates.length != closures.length) {
62 throw new IllegalArgumentException("The predicate and closure arrays must be the same size");
63 }
64 if (predicates.length == 0) {
65 return (Closure<E>) (defaultClosure == null ? NOPClosure.<E>nopClosure(): defaultClosure);
66 }
67 return new SwitchClosure<E>(FunctorUtils.copy(predicates), FunctorUtils.copy(closures), defaultClosure);
68 }
69
70 /**
71 * Create a new Closure that calls one of the closures depending
72 * on the predicates.
73 * <p>
74 * The Map consists of Predicate keys and Closure values. A closure
75 * is called if its matching predicate returns true. Each predicate is evaluated
76 * until one returns true. If no predicates evaluate to true, the default
77 * closure is called. The default closure is set in the map with a
78 * null key. The ordering is that of the iterator() method on the entryset
79 * collection of the map.
80 *
81 * @param <E> the type that the closure acts on
82 * @param predicatesAndClosures a map of predicates to closures
83 * @return the <code>switch</code> closure
84 * @throws IllegalArgumentException if the map is null
85 * @throws IllegalArgumentException if any closure in the map is null
86 * @throws ClassCastException if the map elements are of the wrong type
87 */
88 @SuppressWarnings("unchecked")
89 public static <E> Closure<E> switchClosure(final Map<Predicate<E>, Closure<E>> predicatesAndClosures) {
90 if (predicatesAndClosures == null) {
91 throw new IllegalArgumentException("The predicate and closure map must not be null");
92 }
93 // convert to array like this to guarantee iterator() ordering
94 final Closure<? super E> defaultClosure = predicatesAndClosures.remove(null);
95 final int size = predicatesAndClosures.size();
96 if (size == 0) {
97 return (Closure<E>) (defaultClosure == null ? NOPClosure.<E>nopClosure() : defaultClosure);
98 }
99 final Closure<E>[] closures = new Closure[size];
100 final Predicate<E>[] preds = new Predicate[size];
101 int i = 0;
102 for (final Map.Entry<Predicate<E>, Closure<E>> entry : predicatesAndClosures.entrySet()) {
103 preds[i] = entry.getKey();
104 closures[i] = entry.getValue();
105 i++;
106 }
107 return new SwitchClosure<E>(preds, closures, defaultClosure);
108 }
109
110 /**
111 * Constructor that performs no validation.
112 * Use <code>getInstance</code> if you want that.
113 *
114 * @param predicates array of predicates, not cloned, no nulls
115 * @param closures matching array of closures, not cloned, no nulls
116 * @param defaultClosure the closure to use if no match, null means nop
117 */
118 @SuppressWarnings("unchecked")
119 public SwitchClosure(final Predicate<? super E>[] predicates, final Closure<? super E>[] closures,
120 final Closure<? super E> defaultClosure) {
121 super();
122 iPredicates = predicates;
123 iClosures = closures;
124 iDefault = (Closure<? super E>) (defaultClosure == null ? NOPClosure.<E>nopClosure() : defaultClosure);
125 }
126
127 /**
128 * Executes the closure whose matching predicate returns true
129 *
130 * @param input the input object
131 */
132 public void execute(final E input) {
133 for (int i = 0; i < iPredicates.length; i++) {
134 if (iPredicates[i].evaluate(input) == true) {
135 iClosures[i].execute(input);
136 return;
137 }
138 }
139 iDefault.execute(input);
140 }
141
142 /**
143 * Gets the predicates.
144 *
145 * @return a copy of the predicates
146 * @since 3.1
147 */
148 public Predicate<? super E>[] getPredicates() {
149 return FunctorUtils.<E>copy(iPredicates);
150 }
151
152 /**
153 * Gets the closures.
154 *
155 * @return a copy of the closures
156 * @since 3.1
157 */
158 public Closure<? super E>[] getClosures() {
159 return FunctorUtils.<E>copy(iClosures);
160 }
161
162 /**
163 * Gets the default closure.
164 *
165 * @return the default closure
166 * @since 3.1
167 */
168 public Closure<? super E> getDefaultClosure() {
169 return iDefault;
170 }
171
172 }