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.IOException;
20 import java.io.ObjectInputStream;
21 import java.io.ObjectOutputStream;
22 import java.io.Serializable;
23 
24 import org.apache.commons.collections.Closure;
25 import org.apache.commons.collections.Predicate;
26 
27 /**
28  * Closure implementation that executes a closure repeatedly until a condition is met,
29  * like a do-while or while loop.
30  * <p>
31  * <b>WARNING:</b> from v3.2.2 onwards this class will throw an
32  * {@link UnsupportedOperationException} when trying to serialize or
33  * de-serialize an instance to prevent potential remote code execution exploits.
34  * <p>
35  * In order to re-enable serialization support for {@code WhileClosure}
36  * the following system property can be used (via -Dproperty=true):
37  * <pre>
38  * org.apache.commons.collections.enableUnsafeSerialization
39  * </pre>
40  *
41  * @since Commons Collections 3.0
42  * @version $Revision: 1713845 $ $Date: 2015-11-11 15:02:16 +0100 (Wed, 11 Nov 2015) $
43  *
44  * @author Stephen Colebourne
45  */
46 public class WhileClosure implements Closure, Serializable {
47 
48     /** Serial version UID */
49     private static final long serialVersionUID = -3110538116913760108L;
50 
51     /** The test condition */
52     private final Predicate iPredicate;
53     /** The closure to call */
54     private final Closure iClosure;
55     /** The flag, true is a do loop, false is a while */
56     private final boolean iDoLoop;
57 
58     /**
59      * Factory method that performs validation.
60      *
61      * @param predicate  the predicate used to evaluate when the loop terminates, not null
62      * @param closure  the closure the execute, not null
63      * @param doLoop  true to act as a do-while loop, always executing the closure once
64      * @return the <code>while</code> closure
65      * @throws IllegalArgumentException if the predicate or closure is null
66      */
getInstance(Predicate predicate, Closure closure, boolean doLoop)67     public static Closure getInstance(Predicate predicate, Closure closure, boolean doLoop) {
68         if (predicate == null) {
69             throw new IllegalArgumentException("Predicate must not be null");
70         }
71         if (closure == null) {
72             throw new IllegalArgumentException("Closure must not be null");
73         }
74         return new WhileClosure(predicate, closure, doLoop);
75     }
76 
77     /**
78      * Constructor that performs no validation.
79      * Use <code>getInstance</code> if you want that.
80      *
81      * @param predicate  the predicate used to evaluate when the loop terminates, not null
82      * @param closure  the closure the execute, not null
83      * @param doLoop  true to act as a do-while loop, always executing the closure once
84      */
WhileClosure(Predicate predicate, Closure closure, boolean doLoop)85     public WhileClosure(Predicate predicate, Closure closure, boolean doLoop) {
86         super();
87         iPredicate = predicate;
88         iClosure = closure;
89         iDoLoop = doLoop;
90     }
91 
92     /**
93      * Executes the closure until the predicate is false.
94      *
95      * @param input  the input object
96      */
execute(Object input)97     public void execute(Object input) {
98         if (iDoLoop) {
99             iClosure.execute(input);
100         }
101         while (iPredicate.evaluate(input)) {
102             iClosure.execute(input);
103         }
104     }
105 
106     /**
107      * Gets the predicate in use.
108      *
109      * @return the predicate
110      * @since Commons Collections 3.1
111      */
getPredicate()112     public Predicate getPredicate() {
113         return iPredicate;
114     }
115 
116     /**
117      * Gets the closure.
118      *
119      * @return the closure
120      * @since Commons Collections 3.1
121      */
getClosure()122     public Closure getClosure() {
123         return iClosure;
124     }
125 
126     /**
127      * Is the loop a do-while loop.
128      *
129      * @return true is do-while, false if while
130      * @since Commons Collections 3.1
131      */
isDoLoop()132     public boolean isDoLoop() {
133         return iDoLoop;
134     }
135 
136     /**
137      * Overrides the default writeObject implementation to prevent
138      * serialization (see COLLECTIONS-580).
139      */
writeObject(ObjectOutputStream os)140     private void writeObject(ObjectOutputStream os) throws IOException {
141         FunctorUtils.checkUnsafeSerialization(WhileClosure.class);
142         os.defaultWriteObject();
143     }
144 
145     /**
146      * Overrides the default readObject implementation to prevent
147      * de-serialization (see COLLECTIONS-580).
148      */
readObject(ObjectInputStream is)149     private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException {
150         FunctorUtils.checkUnsafeSerialization(WhileClosure.class);
151         is.defaultReadObject();
152     }
153 
154 }
155