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.Iterator;
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 Commons Collections 3.0
34   * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $
35   *
36   * @author Brian McCallister
37   */
38  public class CompositeSet extends CompositeCollection implements Set {
39      /**
40       * Create an empty CompositeSet
41       */
42      public CompositeSet() {
43          super();
44      }
45      
46      /**
47       * Create a CompositeSet with just <code>set</code> composited
48       * @param set The initial set in the composite
49       */
50      public CompositeSet(Set set) {
51          super(set);
52      }
53      
54      /**
55       * Create a composite set with sets as the initial set of composited Sets
56       */
57      public CompositeSet(Set[] sets) {
58          super(sets);
59      }
60      
61      /**
62       * Add a Set to this composite
63       *
64       * @param c Must implement Set
65       * @throws IllegalArgumentException if c does not implement java.util.Set
66       *         or if a SetMutator is set, but fails to resolve a collision
67       * @throws UnsupportedOperationException if there is no SetMutator set, or
68       *         a CollectionMutator is set instead of a SetMutator
69       * @see org.apache.commons.collections.collection.CompositeCollection.CollectionMutator
70       * @see SetMutator
71       */
72      public synchronized void addComposited(Collection c) {
73          if (!(c instanceof Set)) {
74              throw new IllegalArgumentException("Collections added must implement java.util.Set");
75          }
76          
77          for (Iterator i = this.getCollections().iterator(); i.hasNext();) {
78              Set set = (Set) i.next();
79              Collection intersects = CollectionUtils.intersection(set, c);
80              if (intersects.size() > 0) {
81                  if (this.mutator == null) {
82                      throw new UnsupportedOperationException(
83                          "Collision adding composited collection with no SetMutator set");
84                  }
85                  else if (!(this.mutator instanceof SetMutator)) {
86                      throw new UnsupportedOperationException(
87                          "Collision adding composited collection to a CompositeSet with a CollectionMutator instead of a SetMutator");
88                  }
89                  ((SetMutator) this.mutator).resolveCollision(this, set, (Set) c, intersects);
90                  if (CollectionUtils.intersection(set, c).size() > 0) {
91                      throw new IllegalArgumentException(
92                          "Attempt to add illegal entry unresolved by SetMutator.resolveCollision()");
93                  }
94              }
95          }
96          super.addComposited(new Collection[]{c});
97      }
98      
99      /**
100      * Add two sets to this composite
101      *
102      * @throws IllegalArgumentException if c or d does not implement java.util.Set
103      */
104     public synchronized void addComposited(Collection c, Collection d) {
105         if (!(c instanceof Set)) throw new IllegalArgumentException("Argument must implement java.util.Set");
106         if (!(d instanceof Set)) throw new IllegalArgumentException("Argument must implement java.util.Set");
107         this.addComposited(new Set[]{(Set) c, (Set) d});
108     }
109     
110     /**
111      * Add an array of sets to this composite
112      * @param comps
113      * @throws IllegalArgumentException if any of the collections in comps do not implement Set
114      */
115     public synchronized void addComposited(Collection[] comps) {
116         for (int i = comps.length - 1; i >= 0; --i) {
117             this.addComposited(comps[i]);
118         }
119     }
120     
121     /**
122      * This can receive either a <code>CompositeCollection.CollectionMutator</code>
123      * or a <code>CompositeSet.SetMutator</code>. If a
124      * <code>CompositeCollection.CollectionMutator</code> is used than conflicts when adding
125      * composited sets will throw IllegalArgumentException
126      * <p>
127      */
128     public void setMutator(CollectionMutator mutator) {
129         super.setMutator(mutator);
130     }
131     
132     /* Set operations */
133     
134     /**
135      * If a <code>CollectionMutator</code> is defined for this CompositeSet then this
136      * method will be called anyway.
137      *
138      * @param obj Object to be removed
139      * @return true if the object is removed, false otherwise
140      */
141     public boolean remove(Object obj) {
142         for (Iterator i = this.getCollections().iterator(); i.hasNext();) {
143             Set set = (Set) i.next();
144             if (set.contains(obj)) return set.remove(obj);
145         }
146         return false;
147     }
148     
149     
150     /**
151      * @see Set#equals
152      */
153     public boolean equals(Object obj) {
154         if (obj instanceof Set) {
155             Set set = (Set) obj;
156             if (set.containsAll(this) && set.size() == this.size()) {
157                 return true;
158             }
159         }
160         return false;
161     }
162     
163     /**
164      * @see Set#hashCode
165      */
166     public int hashCode() {
167         int code = 0;
168         for (Iterator i = this.iterator(); i.hasNext();) {
169             Object next = i.next();
170             code += (next != null ? next.hashCode() : 0);
171         }
172         return code;
173     }
174     
175     /**
176      * Define callbacks for mutation operations.
177      * <p>
178      * Defining remove() on implementations of SetMutator is pointless
179      * as they are never called by CompositeSet.
180      */
181     public static interface SetMutator extends CompositeCollection.CollectionMutator {
182         /**
183          * <p>
184          * Called when a Set is added to the CompositeSet and there is a
185          * collision between existing and added sets.
186          * </p>
187          * <p>
188          * If <code>added</code> and <code>existing</code> still have any intersects
189          * after this method returns an IllegalArgumentException will be thrown.
190          * </p>
191          * @param comp The CompositeSet being modified
192          * @param existing The Set already existing in the composite
193          * @param added the Set being added to the composite
194          * @param intersects the intersection of th existing and added sets
195          */
196         public void resolveCollision(CompositeSet comp, Set existing, Set added, Collection intersects);
197     }
198 }