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