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 * https://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 */ 017package org.apache.commons.collections4.functors; 018 019import java.io.Serializable; 020import java.util.LinkedHashMap; 021import java.util.Map; 022import java.util.Objects; 023 024import org.apache.commons.collections4.Closure; 025import org.apache.commons.collections4.Predicate; 026 027/** 028 * Closure implementation calls the closure whose predicate returns true, 029 * like a switch statement. 030 * 031 * @param <T> The type of the input to the operation. 032 * @since 3.0 033 */ 034public class SwitchClosure<T> implements Closure<T>, Serializable { 035 036 /** Serial version UID */ 037 private static final long serialVersionUID = 3518477308466486130L; 038 039 /** 040 * Create a new Closure that calls one of the closures depending 041 * on the predicates. 042 * <p> 043 * The Map consists of Predicate keys and Closure values. A closure 044 * is called if its matching predicate returns true. Each predicate is evaluated 045 * until one returns true. If no predicates evaluate to true, the default 046 * closure is called. The default closure is set in the map with a 047 * null key. The ordering is that of the iterator() method on the entryset 048 * collection of the map. 049 * </p> 050 * 051 * @param <E> The type that the closure acts on 052 * @param predicatesAndClosures A map of predicates to closures 053 * @return The {@code switch} closure 054 * @throws NullPointerException if the map is null 055 * @throws NullPointerException if any closure in the map is null 056 * @throws ClassCastException if the map elements are of the wrong type 057 */ 058 @SuppressWarnings("unchecked") 059 public static <E> Closure<E> switchClosure(final Map<Predicate<E>, Closure<E>> predicatesAndClosures) { 060 Objects.requireNonNull(predicatesAndClosures, "predicatesAndClosures"); 061 // copy so the caller's map is not mutated; LinkedHashMap preserves iterator() ordering 062 final Map<Predicate<E>, Closure<E>> entries = new LinkedHashMap<>(predicatesAndClosures); 063 final Closure<? super E> defaultClosure = entries.remove(null); 064 final int size = entries.size(); 065 if (size == 0) { 066 return (Closure<E>) (defaultClosure == null ? NOPClosure.<E>nopClosure() : defaultClosure); 067 } 068 final Closure<E>[] closures = new Closure[size]; 069 final Predicate<E>[] preds = new Predicate[size]; 070 int i = 0; 071 for (final Map.Entry<Predicate<E>, Closure<E>> entry : entries.entrySet()) { 072 preds[i] = entry.getKey(); 073 closures[i] = entry.getValue(); 074 i++; 075 } 076 return new SwitchClosure<>(false, preds, closures, defaultClosure); 077 } 078 079 /** 080 * Factory method that performs validation and copies the parameter arrays. 081 * 082 * @param <E> The type that the closure acts on 083 * @param predicates array of predicates, cloned, no nulls 084 * @param closures matching array of closures, cloned, no nulls 085 * @param defaultClosure The closure to use if no match, null means nop 086 * @return The {@code chained} closure 087 * @throws NullPointerException if array is null 088 * @throws NullPointerException if any element in the array is null 089 * @throws IllegalArgumentException if the array lengths of predicates and closures do not match 090 */ 091 @SuppressWarnings("unchecked") 092 public static <E> Closure<E> switchClosure(final Predicate<? super E>[] predicates, 093 final Closure<? super E>[] closures, 094 final Closure<? super E> defaultClosure) { 095 FunctorUtils.validate(predicates); 096 FunctorUtils.validate(closures); 097 if (predicates.length != closures.length) { 098 throw new IllegalArgumentException("The predicate and closure arrays must be the same size"); 099 } 100 if (predicates.length == 0) { 101 return (Closure<E>) (defaultClosure == null ? NOPClosure.<E>nopClosure() : defaultClosure); 102 } 103 return new SwitchClosure<>(predicates, closures, defaultClosure); 104 } 105 106 /** The tests to consider */ 107 private final Predicate<? super T>[] iPredicates; 108 109 /** The matching closures to call */ 110 private final Closure<? super T>[] iClosures; 111 112 /** The default closure to call if no tests match */ 113 private final Closure<? super T> iDefault; 114 115 /** 116 * Hidden constructor for the use by the static factory methods. 117 * 118 * @param clone if {@code true} the input arguments will be cloned 119 * @param predicates array of predicates, no nulls 120 * @param closures matching array of closures, no nulls 121 * @param defaultClosure The closure to use if no match, null means nop 122 */ 123 private SwitchClosure(final boolean clone, final Predicate<? super T>[] predicates, 124 final Closure<? super T>[] closures, final Closure<? super T> defaultClosure) { 125 iPredicates = clone ? FunctorUtils.copy(predicates) : predicates; 126 iClosures = clone ? FunctorUtils.copy(closures) : closures; 127 iDefault = defaultClosure == null ? NOPClosure.<T>nopClosure() : defaultClosure; 128 } 129 130 /** 131 * Constructor that performs no validation. 132 * Use {@code switchClosure} if you want that. 133 * 134 * @param predicates array of predicates, cloned, no nulls 135 * @param closures matching array of closures, cloned, no nulls 136 * @param defaultClosure The closure to use if no match, null means nop 137 */ 138 public SwitchClosure(final Predicate<? super T>[] predicates, final Closure<? super T>[] closures, 139 final Closure<? super T> defaultClosure) { 140 this(true, predicates, closures, defaultClosure); 141 } 142 143 /** 144 * Executes the closure whose matching predicate returns true 145 * 146 * @param input The input object 147 */ 148 @Override 149 public void execute(final T input) { 150 for (int i = 0; i < iPredicates.length; i++) { 151 if (iPredicates[i].test(input)) { 152 iClosures[i].accept(input); 153 return; 154 } 155 } 156 iDefault.accept(input); 157 } 158 159 /** 160 * Gets the closures. 161 * 162 * @return A copy of the closures 163 * @since 3.1 164 */ 165 public Closure<? super T>[] getClosures() { 166 return FunctorUtils.copy(iClosures); 167 } 168 169 /** 170 * Gets the default closure. 171 * 172 * @return The default closure 173 * @since 3.1 174 */ 175 public Closure<? super T> getDefaultClosure() { 176 return iDefault; 177 } 178 179 /** 180 * Gets the predicates. 181 * 182 * @return A copy of the predicates 183 * @since 3.1 184 */ 185 public Predicate<? super T>[] getPredicates() { 186 return FunctorUtils.copy(iPredicates); 187 } 188 189}