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 */ 017package org.apache.commons.collections4.collection; 018 019import java.io.Serializable; 020import java.lang.reflect.Array; 021import java.util.ArrayList; 022import java.util.Arrays; 023import java.util.Collection; 024import java.util.Iterator; 025import java.util.List; 026 027import org.apache.commons.collections4.iterators.EmptyIterator; 028import org.apache.commons.collections4.iterators.IteratorChain; 029import org.apache.commons.collections4.list.UnmodifiableList; 030 031/** 032 * Decorates a collection of other collections to provide a single unified view. 033 * <p> 034 * Changes made to this collection will actually be made on the decorated collection. 035 * Add and remove operations require the use of a pluggable strategy. If no 036 * strategy is provided then add and remove are unsupported. 037 * 038 * @param <E> the type of the elements in the collection 039 * @since 3.0 040 * @version $Id: CompositeCollection.html 972421 2015-11-14 20:00:04Z tn $ 041 */ 042public class CompositeCollection<E> implements Collection<E>, Serializable { 043 044 /** Serialization version */ 045 private static final long serialVersionUID = 8417515734108306801L; 046 047 /** CollectionMutator to handle changes to the collection */ 048 private CollectionMutator<E> mutator; 049 050 /** Collections in the composite */ 051 private final List<Collection<E>> all = new ArrayList<Collection<E>>(); 052 053 /** 054 * Create an empty CompositeCollection. 055 */ 056 public CompositeCollection() { 057 super(); 058 } 059 060 /** 061 * Create a Composite Collection with one collection. 062 * 063 * @param compositeCollection the Collection to be appended to the composite 064 */ 065 public CompositeCollection(final Collection<E> compositeCollection) { 066 super(); 067 addComposited(compositeCollection); 068 } 069 070 /** 071 * Create a Composite Collection with two collections. 072 * 073 * @param compositeCollection1 the Collection to be appended to the composite 074 * @param compositeCollection2 the Collection to be appended to the composite 075 */ 076 public CompositeCollection(final Collection<E> compositeCollection1, 077 final Collection<E> compositeCollection2) { 078 super(); 079 addComposited(compositeCollection1, compositeCollection2); 080 } 081 082 /** 083 * Create a Composite Collection with an array of collections. 084 * 085 * @param compositeCollections the collections to composite 086 */ 087 public CompositeCollection(final Collection<E>... compositeCollections) { 088 super(); 089 addComposited(compositeCollections); 090 } 091 092 //----------------------------------------------------------------------- 093 /** 094 * Gets the size of this composite collection. 095 * <p> 096 * This implementation calls <code>size()</code> on each collection. 097 * 098 * @return total number of elements in all contained containers 099 */ 100 public int size() { 101 int size = 0; 102 for (final Collection<E> item : all) { 103 size += item.size(); 104 } 105 return size; 106 } 107 108 /** 109 * Checks whether this composite collection is empty. 110 * <p> 111 * This implementation calls <code>isEmpty()</code> on each collection. 112 * 113 * @return true if all of the contained collections are empty 114 */ 115 public boolean isEmpty() { 116 for (final Collection<E> item : all) { 117 if (item.isEmpty() == false) { 118 return false; 119 } 120 } 121 return true; 122 } 123 124 /** 125 * Checks whether this composite collection contains the object. 126 * <p> 127 * This implementation calls <code>contains()</code> on each collection. 128 * 129 * @param obj the object to search for 130 * @return true if obj is contained in any of the contained collections 131 */ 132 public boolean contains(final Object obj) { 133 for (final Collection<E> item : all) { 134 if (item.contains(obj)) { 135 return true; 136 } 137 } 138 return false; 139 } 140 141 /** 142 * Gets an iterator over all the collections in this composite. 143 * <p> 144 * This implementation uses an <code>IteratorChain</code>. 145 * 146 * @return an <code>IteratorChain</code> instance which supports 147 * <code>remove()</code>. Iteration occurs over contained collections in 148 * the order they were added, but this behavior should not be relied upon. 149 * @see IteratorChain 150 */ 151 public Iterator<E> iterator() { 152 if (all.isEmpty()) { 153 return EmptyIterator.<E>emptyIterator(); 154 } 155 final IteratorChain<E> chain = new IteratorChain<E>(); 156 for (final Collection<E> item : all) { 157 chain.addIterator(item.iterator()); 158 } 159 return chain; 160 } 161 162 /** 163 * Returns an array containing all of the elements in this composite. 164 * 165 * @return an object array of all the elements in the collection 166 */ 167 public Object[] toArray() { 168 final Object[] result = new Object[size()]; 169 int i = 0; 170 for (final Iterator<E> it = iterator(); it.hasNext(); i++) { 171 result[i] = it.next(); 172 } 173 return result; 174 } 175 176 /** 177 * Returns an object array, populating the supplied array if possible. 178 * See <code>Collection</code> interface for full details. 179 * 180 * @param <T> the type of the elements in the collection 181 * @param array the array to use, populating if possible 182 * @return an array of all the elements in the collection 183 */ 184 @SuppressWarnings("unchecked") 185 public <T> T[] toArray(final T[] array) { 186 final int size = size(); 187 Object[] result = null; 188 if (array.length >= size) { 189 result = array; 190 } else { 191 result = (Object[]) Array.newInstance(array.getClass().getComponentType(), size); 192 } 193 194 int offset = 0; 195 for (final Collection<E> item : all) { 196 for (final E e : item) { 197 result[offset++] = e; 198 } 199 } 200 if (result.length > size) { 201 result[size] = null; 202 } 203 return (T[]) result; 204 } 205 206 /** 207 * Adds an object to the collection, throwing UnsupportedOperationException 208 * unless a CollectionMutator strategy is specified. 209 * 210 * @param obj the object to add 211 * @return {@code true} if the collection was modified 212 * @throws UnsupportedOperationException if CollectionMutator hasn't been set 213 * @throws UnsupportedOperationException if add is unsupported 214 * @throws ClassCastException if the object cannot be added due to its type 215 * @throws NullPointerException if the object cannot be added because its null 216 * @throws IllegalArgumentException if the object cannot be added 217 */ 218 public boolean add(final E obj) { 219 if (mutator == null) { 220 throw new UnsupportedOperationException( 221 "add() is not supported on CompositeCollection without a CollectionMutator strategy"); 222 } 223 return mutator.add(this, all, obj); 224 } 225 226 /** 227 * Removes an object from the collection, throwing UnsupportedOperationException 228 * unless a CollectionMutator strategy is specified. 229 * 230 * @param obj the object being removed 231 * @return true if the collection is changed 232 * @throws UnsupportedOperationException if removed is unsupported 233 * @throws ClassCastException if the object cannot be removed due to its type 234 * @throws NullPointerException if the object cannot be removed because its null 235 * @throws IllegalArgumentException if the object cannot be removed 236 */ 237 public boolean remove(final Object obj) { 238 if (mutator == null) { 239 throw new UnsupportedOperationException( 240 "remove() is not supported on CompositeCollection without a CollectionMutator strategy"); 241 } 242 return mutator.remove(this, all, obj); 243 } 244 245 /** 246 * Checks whether this composite contains all the elements in the specified collection. 247 * <p> 248 * This implementation calls <code>contains()</code> for each element in the 249 * specified collection. 250 * 251 * @param coll the collection to check for 252 * @return true if all elements contained 253 */ 254 public boolean containsAll(final Collection<?> coll) { 255 for (final Object item : coll) { 256 if (contains(item) == false) { 257 return false; 258 } 259 } 260 return true; 261 } 262 263 /** 264 * Adds a collection of elements to this collection, throwing 265 * UnsupportedOperationException unless a CollectionMutator strategy is specified. 266 * 267 * @param coll the collection to add 268 * @return true if the collection was modified 269 * @throws UnsupportedOperationException if CollectionMutator hasn't been set 270 * @throws UnsupportedOperationException if add is unsupported 271 * @throws ClassCastException if the object cannot be added due to its type 272 * @throws NullPointerException if the object cannot be added because its null 273 * @throws IllegalArgumentException if the object cannot be added 274 */ 275 public boolean addAll(final Collection<? extends E> coll) { 276 if (mutator == null) { 277 throw new UnsupportedOperationException( 278 "addAll() is not supported on CompositeCollection without a CollectionMutator strategy"); 279 } 280 return mutator.addAll(this, all, coll); 281 } 282 283 /** 284 * Removes the elements in the specified collection from this composite collection. 285 * <p> 286 * This implementation calls <code>removeAll</code> on each collection. 287 * 288 * @param coll the collection to remove 289 * @return true if the collection was modified 290 * @throws UnsupportedOperationException if removeAll is unsupported 291 */ 292 public boolean removeAll(final Collection<?> coll) { 293 if (coll.size() == 0) { 294 return false; 295 } 296 boolean changed = false; 297 for (final Collection<E> item : all) { 298 changed |= item.removeAll(coll); 299 } 300 return changed; 301 } 302 303 /** 304 * Retains all the elements in the specified collection in this composite collection, 305 * removing all others. 306 * <p> 307 * This implementation calls <code>retainAll()</code> on each collection. 308 * 309 * @param coll the collection to remove 310 * @return true if the collection was modified 311 * @throws UnsupportedOperationException if retainAll is unsupported 312 */ 313 public boolean retainAll(final Collection<?> coll) { 314 boolean changed = false; 315 for (final Collection<E> item : all) { 316 changed |= item.retainAll(coll); 317 } 318 return changed; 319 } 320 321 /** 322 * Removes all of the elements from this collection . 323 * <p> 324 * This implementation calls <code>clear()</code> on each collection. 325 * 326 * @throws UnsupportedOperationException if clear is unsupported 327 */ 328 public void clear() { 329 for (final Collection<E> coll : all) { 330 coll.clear(); 331 } 332 } 333 334 //----------------------------------------------------------------------- 335 /** 336 * Specify a CollectionMutator strategy instance to handle changes. 337 * 338 * @param mutator the mutator to use 339 */ 340 public void setMutator(final CollectionMutator<E> mutator) { 341 this.mutator = mutator; 342 } 343 344 /** 345 * Add these Collections to the list of collections in this composite 346 * 347 * @param compositeCollection the Collection to be appended to the composite 348 */ 349 public void addComposited(final Collection<E> compositeCollection) { 350 all.add(compositeCollection); 351 } 352 353 /** 354 * Add these Collections to the list of collections in this composite 355 * 356 * @param compositeCollection1 the Collection to be appended to the composite 357 * @param compositeCollection2 the Collection to be appended to the composite 358 */ 359 public void addComposited(final Collection<E> compositeCollection1, 360 final Collection<E> compositeCollection2) { 361 all.add(compositeCollection1); 362 all.add(compositeCollection2); 363 } 364 365 /** 366 * Add these Collections to the list of collections in this composite 367 * 368 * @param compositeCollections the Collections to be appended to the composite 369 */ 370 public void addComposited(final Collection<E>... compositeCollections) { 371 all.addAll(Arrays.asList(compositeCollections)); 372 } 373 374 /** 375 * Removes a collection from the those being decorated in this composite. 376 * 377 * @param coll collection to be removed 378 */ 379 public void removeComposited(final Collection<E> coll) { 380 all.remove(coll); 381 } 382 383 //----------------------------------------------------------------------- 384 /** 385 * Returns a new collection containing all of the elements 386 * 387 * @return A new ArrayList containing all of the elements in this composite. 388 * The new collection is <i>not</i> backed by this composite. 389 */ 390 public Collection<E> toCollection() { 391 return new ArrayList<E>(this); 392 } 393 394 /** 395 * Gets the collections being decorated. 396 * 397 * @return Unmodifiable list of all collections in this composite. 398 */ 399 public List<Collection<E>> getCollections() { 400 return UnmodifiableList.unmodifiableList(all); 401 } 402 403 /** 404 * Get the collection mutator to be used for this CompositeCollection. 405 * @return CollectionMutator<E> 406 */ 407 protected CollectionMutator<E> getMutator() { 408 return mutator; 409 } 410 411 //----------------------------------------------------------------------- 412 /** 413 * Pluggable strategy to handle changes to the composite. 414 * 415 * @param <E> the element being held in the collection 416 */ 417 public interface CollectionMutator<E> extends Serializable { 418 419 /** 420 * Called when an object is to be added to the composite. 421 * 422 * @param composite the CompositeCollection being changed 423 * @param collections all of the Collection instances in this CompositeCollection 424 * @param obj the object being added 425 * @return true if the collection is changed 426 * @throws UnsupportedOperationException if add is unsupported 427 * @throws ClassCastException if the object cannot be added due to its type 428 * @throws NullPointerException if the object cannot be added because its null 429 * @throws IllegalArgumentException if the object cannot be added 430 */ 431 boolean add(CompositeCollection<E> composite, List<Collection<E>> collections, E obj); 432 433 /** 434 * Called when a collection is to be added to the composite. 435 * 436 * @param composite the CompositeCollection being changed 437 * @param collections all of the Collection instances in this CompositeCollection 438 * @param coll the collection being added 439 * @return true if the collection is changed 440 * @throws UnsupportedOperationException if add is unsupported 441 * @throws ClassCastException if the object cannot be added due to its type 442 * @throws NullPointerException if the object cannot be added because its null 443 * @throws IllegalArgumentException if the object cannot be added 444 */ 445 boolean addAll(CompositeCollection<E> composite, 446 List<Collection<E>> collections, 447 Collection<? extends E> coll); 448 449 /** 450 * Called when an object is to be removed to the composite. 451 * 452 * @param composite the CompositeCollection being changed 453 * @param collections all of the Collection instances in this CompositeCollection 454 * @param obj the object being removed 455 * @return true if the collection is changed 456 * @throws UnsupportedOperationException if removed is unsupported 457 * @throws ClassCastException if the object cannot be removed due to its type 458 * @throws NullPointerException if the object cannot be removed because its null 459 * @throws IllegalArgumentException if the object cannot be removed 460 */ 461 boolean remove(CompositeCollection<E> composite, 462 List<Collection<E>> collections, 463 Object obj); 464 465 } 466 467} 468