001: /*
002: * Copyright 2001-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.collections.functors;
017:
018: import java.io.Serializable;
019: import java.util.Iterator;
020: import java.util.Map;
021:
022: import org.apache.commons.collections.Closure;
023: import org.apache.commons.collections.Predicate;
024:
025: /**
026: * Closure implementation calls the closure whose predicate returns true,
027: * like a switch statement.
028: *
029: * @since Commons Collections 3.0
030: * @version $Revision: 348444 $ $Date: 2005-11-23 14:06:56 +0000 (Wed, 23 Nov 2005) $
031: *
032: * @author Stephen Colebourne
033: */
034: public class SwitchClosure implements Closure, Serializable {
035:
036: /** Serial version UID */
037: private static final long serialVersionUID = 3518477308466486130L;
038:
039: /** The tests to consider */
040: private final Predicate[] iPredicates;
041: /** The matching closures to call */
042: private final Closure[] iClosures;
043: /** The default closure to call if no tests match */
044: private final Closure iDefault;
045:
046: /**
047: * Factory method that performs validation and copies the parameter arrays.
048: *
049: * @param predicates array of predicates, cloned, no nulls
050: * @param closures matching array of closures, cloned, no nulls
051: * @param defaultClosure the closure to use if no match, null means nop
052: * @return the <code>chained</code> closure
053: * @throws IllegalArgumentException if array is null
054: * @throws IllegalArgumentException if any element in the array is null
055: */
056: public static Closure getInstance(Predicate[] predicates,
057: Closure[] closures, Closure defaultClosure) {
058: FunctorUtils.validate(predicates);
059: FunctorUtils.validate(closures);
060: if (predicates.length != closures.length) {
061: throw new IllegalArgumentException(
062: "The predicate and closure arrays must be the same size");
063: }
064: if (predicates.length == 0) {
065: return (defaultClosure == null ? NOPClosure.INSTANCE
066: : defaultClosure);
067: }
068: predicates = FunctorUtils.copy(predicates);
069: closures = FunctorUtils.copy(closures);
070: return new SwitchClosure(predicates, closures, defaultClosure);
071: }
072:
073: /**
074: * Create a new Closure that calls one of the closures depending
075: * on the predicates.
076: * <p>
077: * The Map consists of Predicate keys and Closure values. A closure
078: * is called if its matching predicate returns true. Each predicate is evaluated
079: * until one returns true. If no predicates evaluate to true, the default
080: * closure is called. The default closure is set in the map with a
081: * null key. The ordering is that of the iterator() method on the entryset
082: * collection of the map.
083: *
084: * @param predicatesAndClosures a map of predicates to closures
085: * @return the <code>switch</code> closure
086: * @throws IllegalArgumentException if the map is null
087: * @throws IllegalArgumentException if any closure in the map is null
088: * @throws ClassCastException if the map elements are of the wrong type
089: */
090: public static Closure getInstance(Map predicatesAndClosures) {
091: Closure[] closures = null;
092: Predicate[] preds = null;
093: if (predicatesAndClosures == null) {
094: throw new IllegalArgumentException(
095: "The predicate and closure map must not be null");
096: }
097: if (predicatesAndClosures.size() == 0) {
098: return NOPClosure.INSTANCE;
099: }
100: // convert to array like this to guarantee iterator() ordering
101: Closure defaultClosure = (Closure) predicatesAndClosures
102: .remove(null);
103: int size = predicatesAndClosures.size();
104: if (size == 0) {
105: return (defaultClosure == null ? NOPClosure.INSTANCE
106: : defaultClosure);
107: }
108: closures = new Closure[size];
109: preds = new Predicate[size];
110: int i = 0;
111: for (Iterator it = predicatesAndClosures.entrySet().iterator(); it
112: .hasNext();) {
113: Map.Entry entry = (Map.Entry) it.next();
114: preds[i] = (Predicate) entry.getKey();
115: closures[i] = (Closure) entry.getValue();
116: i++;
117: }
118: return new SwitchClosure(preds, closures, defaultClosure);
119: }
120:
121: /**
122: * Constructor that performs no validation.
123: * Use <code>getInstance</code> if you want that.
124: *
125: * @param predicates array of predicates, not cloned, no nulls
126: * @param closures matching array of closures, not cloned, no nulls
127: * @param defaultClosure the closure to use if no match, null means nop
128: */
129: public SwitchClosure(Predicate[] predicates, Closure[] closures,
130: Closure defaultClosure) {
131: super ();
132: iPredicates = predicates;
133: iClosures = closures;
134: iDefault = (defaultClosure == null ? NOPClosure.INSTANCE
135: : defaultClosure);
136: }
137:
138: /**
139: * Executes the closure whose matching predicate returns true
140: *
141: * @param input the input object
142: */
143: public void execute(Object input) {
144: for (int i = 0; i < iPredicates.length; i++) {
145: if (iPredicates[i].evaluate(input) == true) {
146: iClosures[i].execute(input);
147: return;
148: }
149: }
150: iDefault.execute(input);
151: }
152:
153: /**
154: * Gets the predicates, do not modify the array.
155: *
156: * @return the predicates
157: * @since Commons Collections 3.1
158: */
159: public Predicate[] getPredicates() {
160: return iPredicates;
161: }
162:
163: /**
164: * Gets the closures, do not modify the array.
165: *
166: * @return the closures
167: * @since Commons Collections 3.1
168: */
169: public Closure[] getClosures() {
170: return iClosures;
171: }
172:
173: /**
174: * Gets the default closure.
175: *
176: * @return the default closure
177: * @since Commons Collections 3.1
178: */
179: public Closure getDefaultClosure() {
180: return iDefault;
181: }
182:
183: }
|