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.collection;
018
019import java.io.Serializable;
020import java.util.Collection;
021import java.util.Iterator;
022import java.util.function.Predicate;
023
024/**
025 * Decorates another {@link Collection} to synchronize its behaviour
026 * for a multi-threaded environment.
027 * <p>
028 * Iterators must be manually synchronized:
029 * </p>
030 * <pre>
031 * synchronized (coll) {
032 *   Iterator it = coll.iterator();
033 *   // do stuff with iterator
034 * }
035 * </pre>
036 * <p>
037 * This class is Serializable from Commons Collections 3.1.
038 * </p>
039 *
040 * @param <E> the type of the elements in the collection
041 * @since 3.0
042 */
043public class SynchronizedCollection<E> implements Collection<E>, Serializable {
044
045    /** Serialization version */
046    private static final long serialVersionUID = 2412805092710877986L;
047
048    /** The collection to decorate */
049    private final Collection<E> collection;
050    /** The object to lock on, needed for List/SortedSet views */
051    protected final Object lock;
052
053    /**
054     * Factory method to create a synchronized collection.
055     *
056     * @param <T> the type of the elements in the collection
057     * @param coll  the collection to decorate, must not be null
058     * @return a new synchronized collection
059     * @throws NullPointerException if collection is null
060     * @since 4.0
061     */
062    public static <T> SynchronizedCollection<T> synchronizedCollection(final Collection<T> coll) {
063        return new SynchronizedCollection<>(coll);
064    }
065
066    //-----------------------------------------------------------------------
067    /**
068     * Constructor that wraps (not copies).
069     *
070     * @param collection  the collection to decorate, must not be null
071     * @throws NullPointerException if the collection is null
072     */
073    protected SynchronizedCollection(final Collection<E> collection) {
074        if (collection == null) {
075            throw new NullPointerException("Collection must not be null.");
076        }
077        this.collection = collection;
078        this.lock = this;
079    }
080
081    /**
082     * Constructor that wraps (not copies).
083     *
084     * @param collection  the collection to decorate, must not be null
085     * @param lock  the lock object to use, must not be null
086     * @throws NullPointerException if the collection or lock is null
087     */
088    protected SynchronizedCollection(final Collection<E> collection, final Object lock) {
089        if (collection == null) {
090            throw new NullPointerException("Collection must not be null.");
091        }
092        if (lock == null) {
093            throw new NullPointerException("Lock must not be null.");
094        }
095        this.collection = collection;
096        this.lock = lock;
097    }
098
099    /**
100     * Gets the collection being decorated.
101     *
102     * @return the decorated collection
103     */
104    protected Collection<E> decorated() {
105        return collection;
106    }
107
108    //-----------------------------------------------------------------------
109
110    @Override
111    public boolean add(final E object) {
112        synchronized (lock) {
113            return decorated().add(object);
114        }
115    }
116
117    @Override
118    public boolean addAll(final Collection<? extends E> coll) {
119        synchronized (lock) {
120            return decorated().addAll(coll);
121        }
122    }
123
124    @Override
125    public void clear() {
126        synchronized (lock) {
127            decorated().clear();
128        }
129    }
130
131    @Override
132    public boolean contains(final Object object) {
133        synchronized (lock) {
134            return decorated().contains(object);
135        }
136    }
137
138    @Override
139    public boolean containsAll(final Collection<?> coll) {
140        synchronized (lock) {
141            return decorated().containsAll(coll);
142        }
143    }
144
145    @Override
146    public boolean isEmpty() {
147        synchronized (lock) {
148            return decorated().isEmpty();
149        }
150    }
151
152    /**
153     * Iterators must be manually synchronized.
154     * <pre>
155     * synchronized (coll) {
156     *   Iterator it = coll.iterator();
157     *   // do stuff with iterator
158     * }
159     * </pre>
160     *
161     * @return an iterator that must be manually synchronized on the collection
162     */
163    @Override
164    public Iterator<E> iterator() {
165        return decorated().iterator();
166    }
167
168    @Override
169    public Object[] toArray() {
170        synchronized (lock) {
171            return decorated().toArray();
172        }
173    }
174
175    @Override
176    public <T> T[] toArray(final T[] object) {
177        synchronized (lock) {
178            return decorated().toArray(object);
179        }
180    }
181
182    @Override
183    public boolean remove(final Object object) {
184        synchronized (lock) {
185            return decorated().remove(object);
186        }
187    }
188
189    /**
190     * @since 4.4
191     */
192    @Override
193    public boolean removeIf(final Predicate<? super E> filter) {
194        synchronized (lock) {
195            return decorated().removeIf(filter);
196        }
197    }
198
199    @Override
200    public boolean removeAll(final Collection<?> coll) {
201        synchronized (lock) {
202            return decorated().removeAll(coll);
203        }
204    }
205
206    @Override
207    public boolean retainAll(final Collection<?> coll) {
208        synchronized (lock) {
209            return decorated().retainAll(coll);
210        }
211    }
212
213    @Override
214    public int size() {
215        synchronized (lock) {
216            return decorated().size();
217        }
218    }
219
220    @Override
221    public boolean equals(final Object object) {
222        synchronized (lock) {
223            if (object == this) {
224                return true;
225            }
226            return object == this || decorated().equals(object);
227        }
228    }
229
230    @Override
231    public int hashCode() {
232        synchronized (lock) {
233            return decorated().hashCode();
234        }
235    }
236
237    @Override
238    public String toString() {
239        synchronized (lock) {
240            return decorated().toString();
241        }
242    }
243
244}