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.collections4.bag;
18  
19  import java.io.IOException;
20  import java.io.ObjectInputStream;
21  import java.io.ObjectOutputStream;
22  import java.util.Collection;
23  import java.util.Iterator;
24  
25  import org.apache.commons.collections4.Bag;
26  
27  /**
28   * Decorates another {@link Bag} to comply with the Collection contract.
29   * <p>
30   * By decorating an existing {@link Bag} instance with a {@link CollectionBag},
31   * it can be safely passed on to methods that require Collection types that
32   * are fully compliant with the Collection contract.
33   * </p>
34   * <p>
35   * The method javadoc highlights the differences compared to the original Bag interface.
36   * </p>
37   *
38   * @see Bag
39   * @param <E> the type of elements in this bag
40   * @since 4.0
41   */
42  public final class CollectionBag<E> extends AbstractBagDecorator<E> {
43  
44      /** Serialization version */
45      private static final long serialVersionUID = -2560033712679053143L;
46  
47      /**
48       * Factory method to create a bag that complies to the Collection contract.
49       *
50       * @param <E> the type of the elements in the bag
51       * @param bag  the bag to decorate, must not be null
52       * @return a Bag that complies to the Collection contract
53       * @throws NullPointerException if bag is null
54       */
55      public static <E> Bag<E> collectionBag(final Bag<E> bag) {
56          return new CollectionBag<>(bag);
57      }
58  
59      //-----------------------------------------------------------------------
60      /**
61       * Constructor that wraps (not copies).
62       *
63       * @param bag  the bag to decorate, must not be null
64       * @throws NullPointerException if bag is null
65       */
66      public CollectionBag(final Bag<E> bag) {
67          super(bag);
68      }
69  
70      //-----------------------------------------------------------------------
71      /**
72       * Write the collection out using a custom routine.
73       *
74       * @param out  the output stream
75       * @throws IOException if an error occurs while writing to the stream
76       */
77      private void writeObject(final ObjectOutputStream out) throws IOException {
78          out.defaultWriteObject();
79          out.writeObject(decorated());
80      }
81  
82      /**
83       * Read the collection in using a custom routine.
84       *
85       * @param in  the input stream
86       * @throws IOException if an error occurs while reading from the stream
87       * @throws ClassNotFoundException if an object read from the stream can not be loaded
88       * @throws ClassCastException if deserialised object has wrong type
89       */
90      @SuppressWarnings("unchecked") // will throw CCE, see Javadoc
91      private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
92          in.defaultReadObject();
93          setCollection((Collection<E>) in.readObject());
94      }
95  
96      //-----------------------------------------------------------------------
97      // Collection interface
98      //-----------------------------------------------------------------------
99  
100     /**
101      * <i>(Change)</i>
102      * Returns <code>true</code> if the bag contains all elements in
103      * the given collection, <b>not</b> respecting cardinality. That is,
104      * if the given collection <code>coll</code> contains at least one of
105      * every object contained in this object.
106      *
107      * @param coll  the collection to check against
108      * @return <code>true</code> if the Bag contains at least one of every object in the collection
109      */
110     @Override
111     public boolean containsAll(final Collection<?> coll) {
112         final Iterator<?> e = coll.iterator();
113         while (e.hasNext()) {
114             if(!contains(e.next())) {
115                 return false;
116             }
117         }
118         return true;
119     }
120 
121     /**
122      * <i>(Change)</i>
123      * Adds one copy of the specified object to the Bag.
124      * <p>
125      * Since this method always increases the size of the bag, it
126      * will always return <code>true</code>.
127      *
128      * @param object  the object to add
129      * @return <code>true</code>, always
130      */
131     @Override
132     public boolean add(final E object) {
133         return add(object, 1);
134     }
135 
136     @Override
137     public boolean addAll(final Collection<? extends E> coll) {
138         boolean changed = false;
139         final Iterator<? extends E> i = coll.iterator();
140         while (i.hasNext()) {
141             final boolean added = add(i.next(), 1);
142             changed = changed || added;
143         }
144         return changed;
145     }
146 
147     /**
148      * <i>(Change)</i>
149      * Removes the first occurrence of the given object from the bag.
150      * <p>
151      * This will also remove the object from the {@link #uniqueSet()} if the
152      * bag contains no occurrence anymore of the object after this operation.
153      *
154      * @param object  the object to remove
155      * @return <code>true</code> if this call changed the collection
156      */
157     @Override
158     public boolean remove(final Object object) {
159         return remove(object, 1);
160     }
161 
162     /**
163      * <i>(Change)</i>
164      * Remove all elements represented in the given collection,
165      * <b>not</b> respecting cardinality. That is, remove <i>all</i>
166      * occurrences of every object contained in the given collection.
167      *
168      * @param coll  the collection to remove
169      * @return <code>true</code> if this call changed the collection
170      */
171     @Override
172     public boolean removeAll(final Collection<?> coll) {
173         if (coll != null) {
174             boolean result = false;
175             final Iterator<?> i = coll.iterator();
176             while (i.hasNext()) {
177                 final Object obj = i.next();
178                 final boolean changed = remove(obj, getCount(obj));
179                 result = result || changed;
180             }
181             return result;
182         }
183         // let the decorated bag handle the case of null argument
184         return decorated().removeAll(null);
185     }
186 
187     /**
188      * <i>(Change)</i>
189      * Remove any members of the bag that are not in the given collection,
190      * <i>not</i> respecting cardinality. That is, any object in the given
191      * collection <code>coll</code> will be retained in the bag with the same
192      * number of copies prior to this operation. All other objects will be
193      * completely removed from this bag.
194      * <p>
195      * This implementation iterates over the elements of this bag, checking
196      * each element in turn to see if it's contained in <code>coll</code>.
197      * If it's not contained, it's removed from this bag. As a consequence,
198      * it is advised to use a collection type for <code>coll</code> that provides
199      * a fast (e.g. O(1)) implementation of {@link Collection#contains(Object)}.
200      *
201      * @param coll  the collection to retain
202      * @return <code>true</code> if this call changed the collection
203      */
204     @Override
205     public boolean retainAll(final Collection<?> coll) {
206         if (coll != null) {
207             boolean modified = false;
208             final Iterator<E> e = iterator();
209             while (e.hasNext()) {
210                 if (!coll.contains(e.next())) {
211                     e.remove();
212                     modified = true;
213                 }
214             }
215             return modified;
216         }
217         // let the decorated bag handle the case of null argument
218         return decorated().retainAll(null);
219     }
220 
221     //-----------------------------------------------------------------------
222     // Bag interface
223     //-----------------------------------------------------------------------
224 
225     /**
226      * <i>(Change)</i>
227      * Adds <code>count</code> copies of the specified object to the Bag.
228      * <p>
229      * Since this method always increases the size of the bag, it
230      * will always return <code>true</code>.
231      *
232      * @param object  the object to add
233      * @param count  the number of copies to add
234      * @return <code>true</code>, always
235      */
236     @Override
237     public boolean add(final E object, final int count) {
238         decorated().add(object, count);
239         return true;
240     }
241 
242 }