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 }