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;
018    
019    import java.util.Collection;
020    import java.util.Collections;
021    import java.util.Iterator;
022    import java.util.Set;
023    import java.util.SortedSet;
024    import java.util.TreeSet;
025    
026    import org.apache.commons.collections.set.ListOrderedSet;
027    import org.apache.commons.collections.set.PredicatedSet;
028    import org.apache.commons.collections.set.PredicatedSortedSet;
029    import org.apache.commons.collections.set.SynchronizedSet;
030    import org.apache.commons.collections.set.SynchronizedSortedSet;
031    import org.apache.commons.collections.set.TransformedSet;
032    import org.apache.commons.collections.set.TransformedSortedSet;
033    import org.apache.commons.collections.set.TypedSet;
034    import org.apache.commons.collections.set.TypedSortedSet;
035    import org.apache.commons.collections.set.UnmodifiableSet;
036    import org.apache.commons.collections.set.UnmodifiableSortedSet;
037    
038    /**
039     * Provides utility methods and decorators for
040     * {@link Set} and {@link SortedSet} instances.
041     *
042     * @since Commons Collections 2.1
043     * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $
044     * 
045     * @author Paul Jack
046     * @author Stephen Colebourne
047     * @author Neil O'Toole
048     * @author Matthew Hawthorne
049     */
050    public class SetUtils {
051    
052        /**
053         * An empty unmodifiable set.
054         * This uses the {@link Collections} implementation 
055         * and is provided for completeness.
056         */
057        public static final Set EMPTY_SET = Collections.EMPTY_SET;
058        /**
059         * An empty unmodifiable sorted set.
060         * This is not provided in the JDK.
061         */
062        public static final SortedSet EMPTY_SORTED_SET = UnmodifiableSortedSet.decorate(new TreeSet());
063    
064        /**
065         * <code>SetUtils</code> should not normally be instantiated.
066         */
067        public SetUtils() {
068        }
069    
070        //-----------------------------------------------------------------------
071        /**
072         * Tests two sets for equality as per the <code>equals()</code> contract
073         * in {@link java.util.Set#equals(java.lang.Object)}.
074         * <p>
075         * This method is useful for implementing <code>Set</code> when you cannot
076         * extend AbstractSet. The method takes Collection instances to enable other
077         * collection types to use the Set implementation algorithm.
078         * <p>
079         * The relevant text (slightly paraphrased as this is a static method) is:
080         * <blockquote>
081         * <p>Two sets are considered equal if they have
082         * the same size, and every member of the first set is contained in
083         * the second. This ensures that the <tt>equals</tt> method works
084         * properly across different implementations of the <tt>Set</tt>
085         * interface.</p>
086         * 
087         * <p>
088         * This implementation first checks if the two sets are the same object: 
089         * if so it returns <tt>true</tt>.  Then, it checks if the two sets are
090         * identical in size; if not, it returns false. If so, it returns
091         * <tt>a.containsAll((Collection) b)</tt>.</p>
092         * </blockquote>
093         * 
094         * @see java.util.Set
095         * @param set1  the first set, may be null
096         * @param set2  the second set, may be null
097         * @return whether the sets are equal by value comparison
098         */
099        public static boolean isEqualSet(final Collection set1, final Collection set2) {
100            if (set1 == set2) {
101                return true;
102            }
103            if (set1 == null || set2 == null || set1.size() != set2.size()) {
104                return false;
105            }
106    
107            return set1.containsAll(set2);
108        }
109    
110        /**
111         * Generates a hash code using the algorithm specified in 
112         * {@link java.util.Set#hashCode()}.
113         * <p>
114         * This method is useful for implementing <code>Set</code> when you cannot
115         * extend AbstractSet. The method takes Collection instances to enable other
116         * collection types to use the Set implementation algorithm.
117         * 
118         * @see java.util.Set#hashCode()
119         * @param set  the set to calculate the hash code for, may be null
120         * @return the hash code
121         */
122        public static int hashCodeForSet(final Collection set) {
123            if (set == null) {
124                return 0;
125            }
126            int hashCode = 0;
127            Iterator it = set.iterator();
128            Object obj = null;
129    
130            while (it.hasNext()) {
131                obj = it.next();
132                if (obj != null) {
133                    hashCode += obj.hashCode();
134                }
135            }
136            return hashCode;
137        }
138        
139        //-----------------------------------------------------------------------
140        /**
141         * Returns a synchronized set backed by the given set.
142         * <p>
143         * You must manually synchronize on the returned buffer's iterator to 
144         * avoid non-deterministic behavior:
145         *  
146         * <pre>
147         * Set s = SetUtils.synchronizedSet(mySet);
148         * synchronized (s) {
149         *     Iterator i = s.iterator();
150         *     while (i.hasNext()) {
151         *         process (i.next());
152         *     }
153         * }
154         * </pre>
155         * 
156         * This method uses the implementation in the decorators subpackage.
157         * 
158         * @param set  the set to synchronize, must not be null
159         * @return a synchronized set backed by the given set
160         * @throws IllegalArgumentException  if the set is null
161         */
162        public static Set synchronizedSet(Set set) {
163            return SynchronizedSet.decorate(set);
164        }
165    
166        /**
167         * Returns an unmodifiable set backed by the given set.
168         * <p>
169         * This method uses the implementation in the decorators subpackage.
170         *
171         * @param set  the set to make unmodifiable, must not be null
172         * @return an unmodifiable set backed by the given set
173         * @throws IllegalArgumentException  if the set is null
174         */
175        public static Set unmodifiableSet(Set set) {
176            return UnmodifiableSet.decorate(set);
177        }
178    
179        /**
180         * Returns a predicated (validating) set backed by the given set.
181         * <p>
182         * Only objects that pass the test in the given predicate can be added to the set.
183         * Trying to add an invalid object results in an IllegalArgumentException.
184         * It is important not to use the original set after invoking this method,
185         * as it is a backdoor for adding invalid objects.
186         *
187         * @param set  the set to predicate, must not be null
188         * @param predicate  the predicate for the set, must not be null
189         * @return a predicated set backed by the given set
190         * @throws IllegalArgumentException  if the Set or Predicate is null
191         */
192        public static Set predicatedSet(Set set, Predicate predicate) {
193            return PredicatedSet.decorate(set, predicate);
194        }
195    
196        /**
197         * Returns a typed set backed by the given set.
198         * <p>
199         * Only objects of the specified type can be added to the set.
200         * 
201         * @param set  the set to limit to a specific type, must not be null
202         * @param type  the type of objects which may be added to the set
203         * @return a typed set backed by the specified set
204         */
205        public static Set typedSet(Set set, Class type) {
206            return TypedSet.decorate(set, type);
207        }
208        
209        /**
210         * Returns a transformed set backed by the given set.
211         * <p>
212         * Each object is passed through the transformer as it is added to the
213         * Set. It is important not to use the original set after invoking this 
214         * method, as it is a backdoor for adding untransformed objects.
215         *
216         * @param set  the set to transform, must not be null
217         * @param transformer  the transformer for the set, must not be null
218         * @return a transformed set backed by the given set
219         * @throws IllegalArgumentException  if the Set or Transformer is null
220         */
221        public static Set transformedSet(Set set, Transformer transformer) {
222            return TransformedSet.decorate(set, transformer);
223        }
224        
225        /**
226         * Returns a set that maintains the order of elements that are added
227         * backed by the given set.
228         * <p>
229         * If an element is added twice, the order is determined by the first add.
230         * The order is observed through the iterator or toArray.
231         *
232         * @param set  the set to order, must not be null
233         * @return an ordered set backed by the given set
234         * @throws IllegalArgumentException  if the Set is null
235         */
236        public static Set orderedSet(Set set) {
237            return ListOrderedSet.decorate(set);
238        }
239        
240        //-----------------------------------------------------------------------
241        /**
242         * Returns a synchronized sorted set backed by the given sorted set.
243         * <p>
244         * You must manually synchronize on the returned buffer's iterator to 
245         * avoid non-deterministic behavior:
246         *  
247         * <pre>
248         * Set s = SetUtils.synchronizedSet(mySet);
249         * synchronized (s) {
250         *     Iterator i = s.iterator();
251         *     while (i.hasNext()) {
252         *         process (i.next());
253         *     }
254         * }
255         * </pre>
256         * 
257         * This method uses the implementation in the decorators subpackage.
258         * 
259         * @param set  the sorted set to synchronize, must not be null
260         * @return a synchronized set backed by the given set
261         * @throws IllegalArgumentException  if the set is null
262         */
263        public static SortedSet synchronizedSortedSet(SortedSet set) {
264            return SynchronizedSortedSet.decorate(set);
265        }
266    
267        /**
268         * Returns an unmodifiable sorted set backed by the given sorted set.
269         * <p>
270         * This method uses the implementation in the decorators subpackage.
271         *
272         * @param set  the sorted set to make unmodifiable, must not be null
273         * @return an unmodifiable set backed by the given set
274         * @throws IllegalArgumentException  if the set is null
275         */
276        public static SortedSet unmodifiableSortedSet(SortedSet set) {
277            return UnmodifiableSortedSet.decorate(set);
278        }
279    
280        /**
281         * Returns a predicated (validating) sorted set backed by the given sorted set.  
282         * <p>
283         * Only objects that pass the test in the given predicate can be added to the set.
284         * Trying to add an invalid object results in an IllegalArgumentException.
285         * It is important not to use the original set after invoking this method,
286         * as it is a backdoor for adding invalid objects.
287         *
288         * @param set  the sorted set to predicate, must not be null
289         * @param predicate  the predicate for the sorted set, must not be null
290         * @return a predicated sorted set backed by the given sorted set
291         * @throws IllegalArgumentException  if the Set or Predicate is null
292         */
293        public static SortedSet predicatedSortedSet(SortedSet set, Predicate predicate) {
294            return PredicatedSortedSet.decorate(set, predicate);
295        }
296    
297        /**
298         * Returns a typed sorted set backed by the given set.
299         * <p>
300         * Only objects of the specified type can be added to the set.
301         * 
302         * @param set  the set to limit to a specific type, must not be null
303         * @param type  the type of objects which may be added to the set
304         * @return a typed set backed by the specified set
305         */
306        public static SortedSet typedSortedSet(SortedSet set, Class type) {
307            return TypedSortedSet.decorate(set, type);
308        }
309        
310        /**
311         * Returns a transformed sorted set backed by the given set.
312         * <p>
313         * Each object is passed through the transformer as it is added to the
314         * Set. It is important not to use the original set after invoking this 
315         * method, as it is a backdoor for adding untransformed objects.
316         *
317         * @param set  the set to transform, must not be null
318         * @param transformer  the transformer for the set, must not be null
319         * @return a transformed set backed by the given set
320         * @throws IllegalArgumentException  if the Set or Transformer is null
321         */
322        public static SortedSet transformedSortedSet(SortedSet set, Transformer transformer) {
323            return TransformedSortedSet.decorate(set, transformer);
324        }
325        
326    }