View Javadoc

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.set;
18  
19  import java.util.Collection;
20  import java.util.List;
21  import java.util.Set;
22  
23  import org.apache.commons.collections.CollectionUtils;
24  import org.apache.commons.collections.collection.CompositeCollection;
25  
26  /**
27   * Decorates a set of other sets to provide a single unified view.
28   * <p>
29   * Changes made to this set will actually be made on the decorated set.
30   * Add operations require the use of a pluggable strategy.
31   * If no strategy is provided then add is unsupported.
32   *
33   * @since 3.0
34   * @version $Id: CompositeSet.java 1436867 2013-01-22 12:28:31Z tn $
35   */
36  public class CompositeSet<E> extends CompositeCollection<E> implements Set<E> {
37  
38      /** Serialization version */
39      private static final long serialVersionUID = 5185069727540378940L;
40  
41      /**
42       * Create an empty CompositeSet
43       */
44      public CompositeSet() {
45          super();
46      }
47  
48      /**
49       * Create a CompositeSet with just <code>set</code> composited.
50       *
51       * @param set  the initial set in the composite
52       */
53      public CompositeSet(final Set<E> set) {
54          super(set);
55      }
56  
57      /**
58       * Create a composite set with sets as the initial set of composited Sets.
59       * 
60       * @param sets  the initial sets in the composite
61       */
62      public CompositeSet(final Set<E>... sets) {
63          super(sets);
64      }
65  
66      /**
67       * Add a Set to this composite
68       *
69       * @param c Must implement Set
70       * @throws IllegalArgumentException if c does not implement java.util.Set
71       *         or if a SetMutator is set, but fails to resolve a collision
72       * @throws UnsupportedOperationException if there is no SetMutator set, or
73       *         a CollectionMutator is set instead of a SetMutator
74       * @see org.apache.commons.collections.collection.CompositeCollection.CollectionMutator
75       * @see SetMutator
76       */
77      @Override
78      public synchronized void addComposited(final Collection<E> c) {
79          if (!(c instanceof Set)) {
80              throw new IllegalArgumentException("Collections added must implement java.util.Set");
81          }
82  
83          for (final Set<E> set : getCollections()) {
84              final Collection<E> intersects = CollectionUtils.intersection(set, c);
85              if (intersects.size() > 0) {
86                  if (this.mutator == null) {
87                      throw new UnsupportedOperationException(
88                          "Collision adding composited collection with no SetMutator set");
89                  }
90                  else if (!(this.mutator instanceof SetMutator)) {
91                      throw new UnsupportedOperationException(
92                          "Collision adding composited collection to a CompositeSet with a CollectionMutator " +
93                          "instead of a SetMutator");
94                  }
95                  getMutator().resolveCollision(this, set, (Set<E>) c, intersects);
96                  if (CollectionUtils.intersection(set, c).size() > 0) {
97                      throw new IllegalArgumentException(
98                          "Attempt to add illegal entry unresolved by SetMutator.resolveCollision()");
99                  }
100             }
101         }
102         super.addComposited(c);
103     }
104 
105     @SuppressWarnings("unchecked")
106     @Override
107     public List<? extends Set<E>> getCollections() {
108         return (List<Set<E>>) super.getCollections();
109     }
110 
111     /**
112      * Add two sets to this composite.
113      *
114      * @param c  the first {@link java.util.Set} to add to this composite
115      * @param d  the second {@link java.util.Set} to add to this composite
116      * @throws IllegalArgumentException if c or d does not implement {@link java.util.Set}
117      */
118     @Override
119     @SuppressWarnings("unchecked")
120     public synchronized void addComposited(final Collection<E> c, final Collection<E> d) {
121         if (!(c instanceof Set)) {
122             throw new IllegalArgumentException("Argument must implement java.util.Set");
123         }
124         if (!(d instanceof Set)) {
125             throw new IllegalArgumentException("Argument must implement java.util.Set");
126         }
127         this.addComposited(new Set[] { (Set<? extends E>) c, (Set<? extends E>) d });
128     }
129 
130     /**
131      * Add an array of sets to this composite
132      * @param comps  the {@link Collection} of {@link java.util.Set}s to add to this composite
133      * @throws IllegalArgumentException if any of the collections in comps does not implement {@link java.util.Set}
134      */
135     @Override
136     public synchronized void addComposited(final Collection<E>[] comps) {
137         for (int i = comps.length - 1; i >= 0; --i) {
138             this.addComposited(comps[i]);
139         }
140     }
141 
142     /**
143      * This can receive either a
144      * {@link org.apache.commons.collections.collection.CompositeCollection.CollectionMutator CollectionMutator}
145      * or a {@link CompositeSet.SetMutator}. 
146      * If a {@link org.apache.commons.collections.collection.CompositeCollection.CollectionMutator CollectionMutator}
147      * is used than conflicts when adding composited sets will throw IllegalArgumentException.
148      * 
149      * @param mutator
150      *   the {@link org.apache.commons.collections.collection.CompositeCollection.CollectionMutator CollectionMutator}
151      * to use for this composite
152      */
153     @Override
154     public void setMutator(final CollectionMutator<E> mutator) {
155         super.setMutator(mutator);
156     }
157 
158     /* Set operations */
159 
160     /**
161      * If a <code>CollectionMutator</code> is defined for this CompositeSet then this
162      * method will be called anyway.
163      *
164      * @param obj  object to be removed
165      * @return true if the object is removed, false otherwise
166      */
167     @Override
168     public boolean remove(final Object obj) {
169         for (final Set<? extends E> set : getCollections()) {
170             if (set.contains(obj)) {
171                 return set.remove(obj);
172             }
173         }
174         return false;
175     }
176 
177     /**
178      * {@inheritDoc}
179      * @see java.util.Set#equals
180      */
181     @Override
182     public boolean equals(final Object obj) {
183         if (obj instanceof Set) {
184             final Set<?> set = (Set<?>) obj;
185             return set.containsAll(this) && set.size() == this.size();
186         }
187         return false;
188     }
189 
190     /**
191      * {@inheritDoc}
192      * @see java.util.Set#hashCode
193      */
194     @Override
195     public int hashCode() {
196         int code = 0;
197         for (final E e : this) {
198             code += e == null ? 0 : e.hashCode();
199         }
200         return code;
201     }
202 
203     @Override
204     protected SetMutator<E> getMutator() {
205         return (SetMutator<E>) super.getMutator();
206     }
207 
208     /**
209      * Define callbacks for mutation operations.
210      * <p>
211      * Defining remove() on implementations of SetMutator is pointless
212      * as they are never called by CompositeSet.
213      */
214     public static interface SetMutator<E> extends CompositeCollection.CollectionMutator<E> {
215 
216         /**
217          * Called when a Set is added to the CompositeSet and there is a
218          * collision between existing and added sets.
219          * <p>
220          * If <code>added</code> and <code>existing</code> still have any intersects
221          * after this method returns an IllegalArgumentException will be thrown.
222          *
223          * @param comp  the CompositeSet being modified
224          * @param existing  the Set already existing in the composite
225          * @param added  the Set being added to the composite
226          * @param intersects  the intersection of the existing and added sets
227          */
228         public void resolveCollision(CompositeSet<E> comp, Set<E> existing, Set<E> added, Collection<E> intersects);
229     }
230 }