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       * Constructor that wraps (not copies).
61       *
62       * @param bag  the bag to decorate, must not be null
63       * @throws NullPointerException if bag is null
64       */
65      public CollectionBag(final Bag<E> bag) {
66          super(bag);
67      }
68  
69      /**
70       * <i>(Change)</i>
71       * Adds one copy of the specified object to the Bag.
72       * <p>
73       * Since this method always increases the size of the bag, it
74       * will always return {@code true}.
75       *
76       * @param object  the object to add
77       * @return {@code true}, always
78       */
79      @Override
80      public boolean add(final E object) {
81          return add(object, 1);
82      }
83  
84      /**
85       * <i>(Change)</i>
86       * Adds {@code count} copies of the specified object to the Bag.
87       * <p>
88       * Since this method always increases the size of the bag, it
89       * will always return {@code true}.
90       *
91       * @param object  the object to add
92       * @param count  the number of copies to add
93       * @return {@code true}, always
94       */
95      @Override
96      public boolean add(final E object, final int count) {
97          decorated().add(object, count);
98          return true;
99      }
100 
101     // Collection interface
102 
103     @Override
104     public boolean addAll(final Collection<? extends E> coll) {
105         boolean changed = false;
106         for (final E current : coll) {
107             final boolean added = add(current, 1);
108             changed = changed || added;
109         }
110         return changed;
111     }
112 
113     /**
114      * <i>(Change)</i>
115      * Returns {@code true} if the bag contains all elements in
116      * the given collection, <b>not</b> respecting cardinality. That is,
117      * if the given collection {@code coll} contains at least one of
118      * every object contained in this object.
119      *
120      * @param coll  the collection to check against
121      * @return {@code true} if the Bag contains at least one of every object in the collection
122      */
123     @Override
124     public boolean containsAll(final Collection<?> coll) {
125         return coll.stream().allMatch(this::contains);
126     }
127 
128     /**
129      * Read the collection in using a custom routine.
130      *
131      * @param in  the input stream
132      * @throws IOException if an error occurs while reading from the stream
133      * @throws ClassNotFoundException if an object read from the stream can not be loaded
134      * @throws ClassCastException if deserialized object has wrong type
135      */
136     @SuppressWarnings("unchecked") // will throw CCE, see Javadoc
137     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
138         in.defaultReadObject();
139         setCollection((Collection<E>) in.readObject());
140     }
141 
142     /**
143      * <i>(Change)</i>
144      * Removes the first occurrence of the given object from the bag.
145      * <p>
146      * This will also remove the object from the {@link #uniqueSet()} if the
147      * bag contains no occurrence anymore of the object after this operation.
148      *
149      * @param object  the object to remove
150      * @return {@code true} if this call changed the collection
151      */
152     @Override
153     public boolean remove(final Object object) {
154         return remove(object, 1);
155     }
156 
157     /**
158      * <i>(Change)</i>
159      * Remove all elements represented in the given collection,
160      * <b>not</b> respecting cardinality. That is, remove <i>all</i>
161      * occurrences of every object contained in the given collection.
162      *
163      * @param coll  the collection to remove
164      * @return {@code true} if this call changed the collection
165      */
166     @Override
167     public boolean removeAll(final Collection<?> coll) {
168         if (coll != null) {
169             boolean result = false;
170             for (final Object obj : coll) {
171                 final boolean changed = remove(obj, getCount(obj));
172                 result = result || changed;
173             }
174             return result;
175         }
176         // let the decorated bag handle the case of null argument
177         return decorated().removeAll(null);
178     }
179 
180     /**
181      * <i>(Change)</i>
182      * Remove any members of the bag that are not in the given collection,
183      * <i>not</i> respecting cardinality. That is, any object in the given
184      * collection {@code coll} will be retained in the bag with the same
185      * number of copies prior to this operation. All other objects will be
186      * completely removed from this bag.
187      * <p>
188      * This implementation iterates over the elements of this bag, checking
189      * each element in turn to see if it's contained in {@code coll}.
190      * If it's not contained, it's removed from this bag. As a consequence,
191      * it is advised to use a collection type for {@code coll} that provides
192      * a fast (e.g. O(1)) implementation of {@link Collection#contains(Object)}.
193      *
194      * @param coll  the collection to retain
195      * @return {@code true} if this call changed the collection
196      */
197     @Override
198     public boolean retainAll(final Collection<?> coll) {
199         if (coll != null) {
200             boolean modified = false;
201             final Iterator<E> e = iterator();
202             while (e.hasNext()) {
203                 if (!coll.contains(e.next())) {
204                     e.remove();
205                     modified = true;
206                 }
207             }
208             return modified;
209         }
210         // let the decorated bag handle the case of null argument
211         return decorated().retainAll(null);
212     }
213 
214     // Bag interface
215 
216     /**
217      * Write the collection out using a custom routine.
218      *
219      * @param out  the output stream
220      * @throws IOException if an error occurs while writing to the stream
221      */
222     private void writeObject(final ObjectOutputStream out) throws IOException {
223         out.defaultWriteObject();
224         out.writeObject(decorated());
225     }
226 
227 }