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