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.set;
018    
019    import java.io.Serializable;
020    import java.util.Collection;
021    import java.util.Iterator;
022    import java.util.Map;
023    import java.util.Set;
024    
025    /**
026     * Decorates a <code>Map</code> to obtain <code>Set</code> behaviour.
027     * <p>
028     * This class is used to create a <code>Set</code> with the same properties as
029     * the key set of any map. Thus, a ReferenceSet can be created by wrapping a
030     * <code>ReferenceMap</code> in an instance of this class.
031     * <p>
032     * Most map implementation can be used to create a set by passing in dummy values.
033     * Exceptions include <code>BidiMap</code> implementations, as they require unique values.
034     *
035     * @since Commons Collections 3.1
036     * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $
037     * 
038     * @author Stephen Colebourne
039     */
040    public final class MapBackedSet implements Set, Serializable {
041    
042        /** Serialization version */
043        private static final long serialVersionUID = 6723912213766056587L;
044    
045        /** The map being used as the backing store */
046        protected final Map map;
047        /** The dummyValue to use */
048        protected final Object dummyValue;
049    
050        /**
051         * Factory method to create a set from a map.
052         * 
053         * @param map  the map to decorate, must not be null
054         * @throws IllegalArgumentException if set is null
055         */
056        public static Set decorate(Map map) {
057            return decorate(map, null);
058        }
059    
060        /**
061         * Factory method to create a set from a map.
062         * 
063         * @param map  the map to decorate, must not be null
064         * @param dummyValue  the dummy value to use
065         * @throws IllegalArgumentException if map is null
066         */
067        public static Set decorate(Map map, Object dummyValue) {
068            if (map == null) {
069                throw new IllegalArgumentException("The map must not be null");
070            }
071            return new MapBackedSet(map, dummyValue);
072        }
073    
074        //-----------------------------------------------------------------------
075        /**
076         * Constructor that wraps (not copies).
077         * 
078         * @param map  the map to decorate, must not be null
079         * @param dummyValue  the dummy value to use
080         * @throws IllegalArgumentException if map is null
081         */
082        private MapBackedSet(Map map, Object dummyValue) {
083            super();
084            this.map = map;
085            this.dummyValue = dummyValue;
086        }
087    
088        //-----------------------------------------------------------------------
089        public int size() {
090            return map.size();
091        }
092    
093        public boolean isEmpty() {
094            return map.isEmpty();
095        }
096    
097        public Iterator iterator() {
098            return map.keySet().iterator();
099        }
100    
101        public boolean contains(Object obj) {
102            return map.containsKey(obj);
103        }
104    
105        public boolean containsAll(Collection coll) {
106            return map.keySet().containsAll(coll);
107        }
108    
109        public boolean add(Object obj) {
110            int size = map.size();
111            map.put(obj, dummyValue);
112            return (map.size() != size);
113        }
114    
115        public boolean addAll(Collection coll) {
116            int size = map.size();
117            for (Iterator it = coll.iterator(); it.hasNext();) {
118                Object obj = it.next();
119                map.put(obj, dummyValue);
120            }
121            return (map.size() != size);
122        }
123    
124        public boolean remove(Object obj) {
125            int size = map.size();
126            map.remove(obj);
127            return (map.size() != size);
128        }
129    
130        public boolean removeAll(Collection coll) {
131            return map.keySet().removeAll(coll);
132        }
133    
134        public boolean retainAll(Collection coll) {
135            return map.keySet().retainAll(coll);
136        }
137    
138        public void clear() {
139            map.clear();
140        }
141    
142        public Object[] toArray() {
143            return map.keySet().toArray();
144        }
145    
146        public Object[] toArray(Object[] array) {
147            return map.keySet().toArray(array);
148        }
149    
150        public boolean equals(Object obj) {
151            return map.keySet().equals(obj);
152        }
153    
154        public int hashCode() {
155            return map.keySet().hashCode();
156        }
157    
158    }